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]

Reply via email to