pwargulak opened a new pull request, #6666: URL: https://github.com/apache/fineract/pull/6666
## Description [FINERACT-2955](https://issues.apache.org/jira/browse/FINERACT-2955) When a transaction partly pays a loan charge with a tax group, `LoanJournalEntryPosterImpl` prorates each tax component as `tax × paid / chargeAmount` with `MoneyHelper.getMathContext()`. That limits precision, not scale, so the tax-liability line and the net fee/penalty income line carry sub-unit amounts. For example, a 400.00 payment of a 1000.00 fee carrying 186.991860 of tax posts 74.796744 and 325.203256. A 0.01 payment posts a tax line of 0.001870. While writing the test for a charge paid in several rows, I found that such a payment is refused. One repayment that pays an instalment fee for two instalments answers 403 `Meltdown in advanced accounting...sum of all charges is not equal to the fee charge for a transaction`. The cause is that `LoanCommonAccountingHelper#computeNetChargePayments` subtracts the charge's whole tax from each of its rows. This change: - **rounds the prorated tax cumulatively** to the currency's scale with the configured rounding mode. Each line is the charge's rounded tax on everything paid so far, less the rounded tax on what earlier transactions of the same kind already paid: - "earlier" means non-reversed transactions with a lower id; - "the same kind" is one of two streams, rounded separately: the types the accounting processors dispatch as repayments (repayment, merchant-issued refund, payout refund, goodwill credit, charge refund, down payment, repayment at disbursement, charge payment), or accrual and accrual adjustment; - the earlier amount is one query per taxed charge (`LoanTransactionRepository#findChargeAmountPaidByEarlierTransactions`), shaped like the existing `findChargeAccrualAmount`; - rows of one charge within one transaction accumulate in instalment order; - as a result, every line is a valid currency amount, and the lines of a fully paid charge sum to its tax rounded as a whole: 0.00 / 186.99 / 365.01, total 552.00, for a 2952.00 fee carrying 551.999971, instead of 0.001870 / 186.991860 / 365.006241 at six decimals. A line whose rounded tax is zero is not posted; - **subtracts each charge's tax once** in `computeNetChargePayments`. The rows are summed per GL account downstream, so the per-account net income is what it would be with the tax split across the rows, and the payment above goes through; - leaves the tax stored on the loan charge (`LoanChargeTaxDetails`, scale 6) unchanged, and adds one sentence on it to `taxes-on-loan-charges.adoc`. **Known bound:** a line is taken against the earlier rows as they stand when it is posted. If an earlier row later changes, the remaining lines of that charge can differ from its rounded total by one minor unit per change and component. That happens on: - a reversal; - an in-place reprocessing update; - a recomputed charge amount; - accruals created without posting; - charged-off cash repayments, which carry rows but post no tax. Every line is still a valid amount, and every entry still balances. Since each line is `round(T·(B+p)/A) − round(T·B/A)`, it is within one minor unit of `T·p/A` whatever the base `B`, so the bound holds unconditionally. Tests: - new `LoanJournalEntryPosterImplTest` (13): cumulative lines, a tax that does not divide evenly, the configured rounding mode, currency scales 0 and 3, the payment and accrual streams, accrual adjustment, two rows of one charge, two taxed charges, several components, untaxed charges alone and beside a taxed one, reversed and non-tax transactions; - new `LoanCommonAccountingHelperTest` (3); - `LoanChargeTaxIntegrationTest` gains 5 cases, which check amounts exactly because the shared journal matcher accepts any difference below 0.01: - partial payments of a 2952.00 fee; - a 1000.00 fee paid 400.00 + 600.00; - undoing a partial payment; - a periodic accrual; - a two-instalment fee paid at once. All 5 fail on `develop` (the last one with the 403). Run locally: spotless, checkstyle, rat, javadoc, spotbugs and modernizer are clean; the core build; the whole `fineract-provider` unit suite (1762 tests, no failures); and `LoanChargeTaxIntegrationTest`, `ChargesTest`, `TaxesTest`, `AccountingScenarioIntegrationTest`, `LoanAccrualTransactionOnChargeSubmittedDateTest`, `LoanAccrualTransactionReversalTest`, `LoanChargebackWithCreditAllocationsIntegrationTests` and `ClientLoanIntegrationTest` (136 tests) pass on PostgreSQL, MariaDB and MySQL. Not run: the other integration test classes and the e2e (Cucumber) suite. ## Checklist - [x] Write the commit message as per [our guidelines](https://github.com/apache/fineract/blob/develop/CONTRIBUTING.md#pull-requests) - [x] Acknowledge that we will not review PRs that are not passing the build _("green")_ - it is your responsibility to get a proposed PR to pass the build, not primarily the project's maintainers. - [x] Create/update [unit or integration tests](https://fineract.apache.org/docs/current/#_testing) for verifying the changes made. - [x] Follow our [coding conventions](https://cwiki.apache.org/confluence/display/FINERACT/Coding+Conventions). - [ ] Add required Swagger annotation and update API documentation at fineract-provider/src/main/resources/static/legacy-docs/apiLive.htm with details of any API changes - [x] [This PR must not be a "code dump"](https://cwiki.apache.org/confluence/display/FINERACT/Pull+Request+Size+Limit). Large changes can be made in a branch, with assistance. Ask for help on the [developer mailing list](https://fineract.apache.org/#contribute). - [x] If merging this PR resolves a JIRA issue, I will mark that issue as resolved and set "Fix Version/s" appropriately. - [x] I followed the [AI Policy](https://github.com/apache/fineract/blob/develop/CONTRIBUTING.md#ai-policy). -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: [email protected] For queries about this service, please contact Infrastructure at: [email protected]
