Skip to content

FINERACT-2284: Enforce Charge Rounding using Money rounding rules#5940

Open
Dhanno98 wants to merge 1 commit into
apache:developfrom
Dhanno98:FINERACT-2284/enforce-charge-rounding
Open

FINERACT-2284: Enforce Charge Rounding using Money rounding rules#5940
Dhanno98 wants to merge 1 commit into
apache:developfrom
Dhanno98:FINERACT-2284/enforce-charge-rounding

Conversation

@Dhanno98

@Dhanno98 Dhanno98 commented Jun 4, 2026

Copy link
Copy Markdown
Contributor

Description

FINERACT-2284: Enforce charge rounding using Money rounding rules

This PR fixes charge amount handling across Fineract by ensuring that charge values are rounded using the application's configured currency rules before they are persisted or processed.

Previously, several charge flows stored or processed raw BigDecimal values directly. As a result, charge amounts could bypass currency rounding settings such as:

  • digitsAfterDecimal
  • inMultiplesOf

This led to inconsistent behavior between charge calculations and other monetary operations that already rely on Money.

This PR standardizes charge amount handling by using Money for charge rounding across all supported charge types:

  • Loan Charges
  • Savings Account Charges
  • Share Account Charges
  • Client Charges

In addition, if a charge amount rounds to zero after applying currency rules, the charge is no longer persisted or created.

Functional Changes

Loan Charges

  • Applied currency rounding when enforcing minimum and maximum charge caps.
  • Ensured installment fee calculations and charge updates persist rounded values.
  • Prevented zero-value loan charges from being persisted.
  • Prevented creation of zero-value tranche disbursement charges.

Savings Account Charges

  • Applied currency rounding through Money.
  • Prevented persistence of flat charges whose final rounded value is zero.

Share Account Charges

  • Applied currency rounding when deriving charge amounts.
  • Applied currency rounding when updating charge amounts for additional share transactions.
  • Prevented creation of charge transactions for charges whose rounded value is zero.
  • Prevented zero-value activation, purchase, and redemption charge transactions from being generated.

Client Charges

  • Applied currency rounding before charge creation.
  • Prevented persistence of client charges whose rounded value becomes zero after rounding.

API Documentation

Updated Swagger/OpenAPI schemas where required to improve request model documentation and examples.

Why This Change

Fineract already provides currency-specific monetary rules through Money, including support for:

  • Currency precision (digitsAfterDecimal)
  • Rounding increments (inMultiplesOf)

Charges should follow the same monetary rules as all other financial amounts.

By enforcing rounding consistently:

  • Charge calculations become predictable.
  • Persisted charge values match configured currency behavior.
  • Zero-value charges generated by rounding are avoided.
  • Charge processing remains consistent across all portfolio modules.

Test Coverage

Added integration test coverage for all supported charge types:

  • LoanChargeRoundingTest
  • SavingsAccountChargeRoundingTest
  • ShareAccountChargeRoundingTest
  • ClientChargeRoundingTest

The tests verify:

  • Charge amounts respect configured currency rounding rules.
  • inMultiplesOf and digitsAfterDecimal rounding is enforced.
  • Rounded values are persisted correctly.
  • Charges that round to zero are not created or persisted.

Verification

Executed the complete charge rounding integration test suite successfully.

Result: 44/44 tests passing.

Checklist

Please make sure these boxes are checked before submitting your pull request - thanks!

  • Write the commit message as per our guidelines
  • 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.
  • Create/update unit or integration tests for verifying the changes made.
  • Follow our 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
  • This PR must not be a "code dump". Large changes can be made in a branch, with assistance. Ask for help on the developer mailing list.

Your assigned reviewer(s) will follow our guidelines for code reviews.

@Dhanno98 Dhanno98 force-pushed the FINERACT-2284/enforce-charge-rounding branch from f44e89e to 38bd72e Compare June 4, 2026 18:29
@Dhanno98 Dhanno98 force-pushed the FINERACT-2284/enforce-charge-rounding branch from 38bd72e to 8009f8c Compare June 6, 2026 18:06
update(loanCharge, chargeAmt, loanCharge.getDueLocalDate(), amount, loan.fetchNumberOfInstallmentsAfterExceptions(),
totalChargeAmt);

// Skip zero-value charges

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adding 0 charge is incorrect and it should be stopped at validation and or during charge amount calculation. Silent return is misleading and hiding a potential issue!

@adamsaghy adamsaghy Jun 18, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See no changes...no reply...

numberOfRepayments = loanCharge.getLoan().fetchNumberOfInstallmentsAfterExceptions();
}
loanCharge.setAmount(amount.multiply(BigDecimal.valueOf(numberOfRepayments)));
loanCharge.setAmount(loanCharge.minimumAndMaximumCap(amount.multiply(BigDecimal.valueOf(numberOfRepayments))));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unrelated change? Why loanCharge.minimumAndMaximumCap is needed here? Charge amount calculation should cover the rounding, not this...

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See no changes...no reply...

Comment on lines +468 to 488
@Schema(example = "en")
public String locale;
@Schema(example = "dd MMMM yyyy")
public String dateFormat;
@Schema(example = "01 January 2026")
public String activatedDate;
@Schema(example = "05 May 2026")
public String requestedDate;
@Schema(example = "01 January 2026")
public String closedDate;
@Schema(description = "Can represent either number of shares or transaction IDs")
public Object requestedShares;

static final class PostAccountsRequestedShares {

private PostAccountsRequestedShares() {}

@Schema(example = "35")
public Long id;
}

public Set<PostAccountsRequestedShares> requestedShares;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unrelated changes?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See no changes...no reply...


public static ClientCharge createNew(final Client client, final Charge charge, final JsonCommand command) {
BigDecimal amount = command.bigDecimalValueOfParameterNamed(ClientApiConstants.amountParamName);
public static ClientCharge createNew(final Client client, final Charge charge, final BigDecimal amount, final JsonCommand command) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you’ve started providing the fields as incoming arguments, don’t stop halfway. Remove the command parameter and ensure that the due date is provided as a parameter.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See no changes...no reply...

}

final ClientCharge clientCharge = ClientCharge.createNew(client, charge, command);
BigDecimal roundedAmount = calculateRoundedChargeAmount(charge, command);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using Money here would be better, no?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See no changes...no reply...

final ClientCharge clientCharge = ClientCharge.createNew(client, charge, command);
BigDecimal roundedAmount = calculateRoundedChargeAmount(charge, command);
if (roundedAmount.compareTo(BigDecimal.ZERO) == 0) {
return new CommandProcessingResultBuilder().withOfficeId(client.getOffice().getId()).withClientId(client.getId()).build();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why to accept 0 amount charge? 0 amount charge is incorrect outcome!

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See no changes...no reply...

LoanTrancheDisbursementCharge loanTrancheDisbursementCharge;
ExternalId externalId = externalIdFactory.createFromCommand(command, "externalId");
boolean isFirst = true;
boolean atLeastOneChargePersisted = false;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what are these flags for?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See no changes...no reply...


// if charge amount is rounded to zero, then continue and look for another disbursement
if (isZeroCharge(tempCharge)) {
continue;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

0 amount charge sounds incorrect to me.

}

// tempCharge was persisted so this is true
atLeastOneChargePersisted = true;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what does it matter?

}
if (loanCharge == null) {
// No eligible disbursement
if (!eligibleDisbursementFound) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I dont like this...

validateAddLoanCharge(loan, chargeDefinition, loanCharge);
isAppliedOnBackDate = addCharge(loan, chargeDefinition, loanCharge);

// if charge amount is rounded to zero, then return early

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds incorrect result to me.

if (!isZeroCharge(loanCharge)) {
loanCharge = this.loanChargeRepository.saveAndFlush(loanCharge);

// we want to apply charge transactions only for those loans charges that are applied when a loan is active

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why to change this?

.notifyPostBusinessEvent(new LoanAccrualTransactionCreatedBusinessEvent(applyLoanChargeTransaction));

// If charge amount is not zero
if (!isZeroCharge(loanCharge)) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I dont like this..

}
final SavingsAccountCharge savingsAccountCharge = SavingsAccountCharge.createNewFromJson(savingsAccount, chargeDefinition, command);

if (isFlatCharge(savingsAccountCharge) && isZeroCharge(savingsAccountCharge, savingsAccount.getCurrency())) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I dont like it... 0 amount charge is incorrect result.

@adamsaghy adamsaghy left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I stopped review at half time.. I dont like this approach, neither i think it is correct.

The introduced 0 amount handling is incorrect. 0 amount is incorrect outcome and charge cannot be created with this value, but nevertheless, we cannot accept the charge creation action and after not create the charge. This is incorrect and unwanted behaviour:

  • Add proper validation (first thing to do)
  • Calculate the amount, if it is incorrect (like 0 amount) -> Raise error and explain the issue!
  • Remove the unnecessary flags and checks... it makes the code hard to read and maintained...
  • Remove the unrelated changes, the PR should focus on 1 thing only!

@Dhanno98

Copy link
Copy Markdown
Contributor Author

@adamsaghy Thanks for your suggestions! I am working on the fix.

@Dhanno98

Copy link
Copy Markdown
Contributor Author

Hi @adamsaghy,
I have updated the charge attachment flows locally to reject charges whose calculated amount becomes 0 after applying account currency rounding rules, instead of returning silently.

I have a question regarding future charges such as Savings Account Percentage of Withdrawal Charges and Share Account Redeem Charges.

In these cases, the charge is attached successfully first and the amount is calculated only when the business transaction occurs. If the calculated charge amount becomes 0 after rounding, what is the expected behavior:

  1. Reject the business transaction (withdrawal/redeem) with a validation error.
  2. Allow the transaction but skip creating/applying the charge similar to silent return.
  3. Keep the current behavior and allow a 0 value charge result.

Could you please clarify the expected behavior for these future-charge scenarios so I can align the implementation consistently across Savings and Shares?

Thank you!

@adamsaghy

Copy link
Copy Markdown
Contributor

Hi @adamsaghy, I have updated the charge attachment flows locally to reject charges whose calculated amount becomes 0 after applying account currency rounding rules, instead of returning silently.

I have a question regarding future charges such as Savings Account Percentage of Withdrawal Charges and Share Account Redeem Charges.

In these cases, the charge is attached successfully first and the amount is calculated only when the business transaction occurs. If the calculated charge amount becomes 0 after rounding, what is the expected behavior:

  1. Reject the business transaction (withdrawal/redeem) with a validation error.
  2. Allow the transaction but skip creating/applying the charge similar to silent return.
  3. Keep the current behavior and allow a 0 value charge result.

Could you please clarify the expected behavior for these future-charge scenarios so I can align the implementation consistently across Savings and Shares?

Thank you!

@bharathc27 fyi

@Dhanno98 let me review

@adamsaghy

Copy link
Copy Markdown
Contributor

Hi @adamsaghy, I have updated the charge attachment flows locally to reject charges whose calculated amount becomes 0 after applying account currency rounding rules, instead of returning silently.

I have a question regarding future charges such as Savings Account Percentage of Withdrawal Charges and Share Account Redeem Charges.

In these cases, the charge is attached successfully first and the amount is calculated only when the business transaction occurs. If the calculated charge amount becomes 0 after rounding, what is the expected behavior:

  1. Reject the business transaction (withdrawal/redeem) with a validation error.
  2. Allow the transaction but skip creating/applying the charge similar to silent return.
  3. Keep the current behavior and allow a 0 value charge result.

Could you please clarify the expected behavior for these future-charge scenarios so I can align the implementation consistently across Savings and Shares?

Thank you!

I see no changes, no reply on my questions and concerns.. i have stopped reviewing the PR at half time...

@Dhanno98

Dhanno98 commented Jun 18, 2026

Copy link
Copy Markdown
Contributor Author

Hi @adamsaghy, I have updated the charge attachment flows locally to reject charges whose calculated amount becomes 0 after applying account currency rounding rules, instead of returning silently.
I have a question regarding future charges such as Savings Account Percentage of Withdrawal Charges and Share Account Redeem Charges.
In these cases, the charge is attached successfully first and the amount is calculated only when the business transaction occurs. If the calculated charge amount becomes 0 after rounding, what is the expected behavior:

  1. Reject the business transaction (withdrawal/redeem) with a validation error.
  2. Allow the transaction but skip creating/applying the charge similar to silent return.
  3. Keep the current behavior and allow a 0 value charge result.

Could you please clarify the expected behavior for these future-charge scenarios so I can align the implementation consistently across Savings and Shares?
Thank you!

I see no changes, no reply on my questions and concerns.. i have stopped reviewing the PR at half time...

Hi @adamsaghy,

Sorry for the confusion. When I wrote that I had updated the charge attachment flows locally, I meant that the changes were only completed in my local branch and had not yet been pushed or amended into the PR.

My intention was to first finish addressing all of your review comments, validate everything thoroughly, and then push a single updated commit together with replies to each review comment. Because of that, I had not yet responded individually to the review feedback.

I realize now that my comment made it sound like the PR had already been updated, which was not the case. Sorry for causing you to spend time reopening the review.

I am currently working through all of the review comments and will push the updated changes together with responses shortly.

Regarding the future-charge scenarios, that question was meant as a design clarification while implementing the remaining changes so I can keep the behavior consistent across Savings and Shares. This is the only thing that I need to understand and resolve and then I will ammend the commit.

Thank you for your patience and for the detailed review feedback.

@adamsaghy

Copy link
Copy Markdown
Contributor

Hi @adamsaghy, I have updated the charge attachment flows locally to reject charges whose calculated amount becomes 0 after applying account currency rounding rules, instead of returning silently.
I have a question regarding future charges such as Savings Account Percentage of Withdrawal Charges and Share Account Redeem Charges.
In these cases, the charge is attached successfully first and the amount is calculated only when the business transaction occurs. If the calculated charge amount becomes 0 after rounding, what is the expected behavior:

  1. Reject the business transaction (withdrawal/redeem) with a validation error.
  2. Allow the transaction but skip creating/applying the charge similar to silent return.
  3. Keep the current behavior and allow a 0 value charge result.

Could you please clarify the expected behavior for these future-charge scenarios so I can align the implementation consistently across Savings and Shares?
Thank you!

I see no changes, no reply on my questions and concerns.. i have stopped reviewing the PR at half time...

Hi @adamsaghy,

Sorry for the confusion. When I wrote that I had updated the charge attachment flows locally, I meant that the changes were only completed in my local branch and had not yet been pushed or amended into the PR.

My intention was to first finish addressing all of your review comments, validate everything thoroughly, and then push a single updated commit together with replies to each review comment. Because of that, I had not yet responded individually to the review feedback.

I realize now that my comment made it sound like the PR had already been updated, which was not the case. Sorry for causing you to spend time reopening the review.

I am currently working through all of the review comments and will push the updated changes together with responses shortly.

Regarding the future-charge scenarios, that question was meant as a design clarification while implementing the remaining changes so I can keep the behavior consistent across Savings and Shares. This is the only thing that I need to understand and resolve and then I will ammend the commit.

Thank you for your patience and for the detailed review feedback.

Thank you for the clarification ;)

Regarding zero amount: Lets use option 1 Reject the business transaction (withdrawal/redeem) with a validation error.

@Dhanno98

Copy link
Copy Markdown
Contributor Author

Hi @adamsaghy, I have updated the charge attachment flows locally to reject charges whose calculated amount becomes 0 after applying account currency rounding rules, instead of returning silently.
I have a question regarding future charges such as Savings Account Percentage of Withdrawal Charges and Share Account Redeem Charges.
In these cases, the charge is attached successfully first and the amount is calculated only when the business transaction occurs. If the calculated charge amount becomes 0 after rounding, what is the expected behavior:

  1. Reject the business transaction (withdrawal/redeem) with a validation error.
  2. Allow the transaction but skip creating/applying the charge similar to silent return.
  3. Keep the current behavior and allow a 0 value charge result.

Could you please clarify the expected behavior for these future-charge scenarios so I can align the implementation consistently across Savings and Shares?
Thank you!

I see no changes, no reply on my questions and concerns.. i have stopped reviewing the PR at half time...

Hi @adamsaghy,
Sorry for the confusion. When I wrote that I had updated the charge attachment flows locally, I meant that the changes were only completed in my local branch and had not yet been pushed or amended into the PR.
My intention was to first finish addressing all of your review comments, validate everything thoroughly, and then push a single updated commit together with replies to each review comment. Because of that, I had not yet responded individually to the review feedback.
I realize now that my comment made it sound like the PR had already been updated, which was not the case. Sorry for causing you to spend time reopening the review.
I am currently working through all of the review comments and will push the updated changes together with responses shortly.
Regarding the future-charge scenarios, that question was meant as a design clarification while implementing the remaining changes so I can keep the behavior consistent across Savings and Shares. This is the only thing that I need to understand and resolve and then I will ammend the commit.
Thank you for your patience and for the detailed review feedback.

Thank you for the clarification ;)

Regarding zero amount: Lets use option 1 Reject the business transaction (withdrawal/redeem) with a validation error.

Thank you for the clarification.

Just to confirm the expected behavior for percentage-based future charges:

Suppose a Savings Account has a "Percentage of Withdrawal" charge configured as 0.5%.

When the charge is attached to the account, the percentage value (0.5%) is stored successfully because the actual charge amount is not yet known.

Later, if a withdrawal of 100 is performed and the account currency uses 0 decimal places (for example JPY), the calculated charge amount would be:

0.5% × 100 = 0.5

which would then round to 0 according to the currency rounding rules.

With option 1, my understanding is that the withdrawal transaction itself should be rejected with a validation error because the calculated charge amount becomes zero after rounding.

Is that the intended behavior, even though the withdrawal amount itself is valid and only the associated charge rounds to zero?

I just want to confirm this edge case before implementing the validation in the future-charge flows.

@adamsaghy

Copy link
Copy Markdown
Contributor

Hi @adamsaghy, I have updated the charge attachment flows locally to reject charges whose calculated amount becomes 0 after applying account currency rounding rules, instead of returning silently.
I have a question regarding future charges such as Savings Account Percentage of Withdrawal Charges and Share Account Redeem Charges.
In these cases, the charge is attached successfully first and the amount is calculated only when the business transaction occurs. If the calculated charge amount becomes 0 after rounding, what is the expected behavior:

  1. Reject the business transaction (withdrawal/redeem) with a validation error.
  2. Allow the transaction but skip creating/applying the charge similar to silent return.
  3. Keep the current behavior and allow a 0 value charge result.

Could you please clarify the expected behavior for these future-charge scenarios so I can align the implementation consistently across Savings and Shares?
Thank you!

I see no changes, no reply on my questions and concerns.. i have stopped reviewing the PR at half time...

Hi @adamsaghy,
Sorry for the confusion. When I wrote that I had updated the charge attachment flows locally, I meant that the changes were only completed in my local branch and had not yet been pushed or amended into the PR.
My intention was to first finish addressing all of your review comments, validate everything thoroughly, and then push a single updated commit together with replies to each review comment. Because of that, I had not yet responded individually to the review feedback.
I realize now that my comment made it sound like the PR had already been updated, which was not the case. Sorry for causing you to spend time reopening the review.
I am currently working through all of the review comments and will push the updated changes together with responses shortly.
Regarding the future-charge scenarios, that question was meant as a design clarification while implementing the remaining changes so I can keep the behavior consistent across Savings and Shares. This is the only thing that I need to understand and resolve and then I will ammend the commit.
Thank you for your patience and for the detailed review feedback.

Thank you for the clarification ;)
Regarding zero amount: Lets use option 1 Reject the business transaction (withdrawal/redeem) with a validation error.

Thank you for the clarification.

Just to confirm the expected behavior for percentage-based future charges:

Suppose a Savings Account has a "Percentage of Withdrawal" charge configured as 0.5%.

When the charge is attached to the account, the percentage value (0.5%) is stored successfully because the actual charge amount is not yet known.

Later, if a withdrawal of 100 is performed and the account currency uses 0 decimal places (for example JPY), the calculated charge amount would be:

0.5% × 100 = 0.5

which would then round to 0 according to the currency rounding rules.

With option 1, my understanding is that the withdrawal transaction itself should be rejected with a validation error because the calculated charge amount becomes zero after rounding.

Is that the intended behavior, even though the withdrawal amount itself is valid and only the associated charge rounds to zero?

I just want to confirm this edge case before implementing the validation in the future-charge flows.

With this use case, it’s not as straightforward as it seems. I might have been overly strict here. Just because the charge is too small, we still want to allow the withdrawal. So, if the calculated charge is zero, we simply ignore it and don’t create it.

@bharathc27, what are your thoughts on this?

@bharathcgowda

bharathcgowda commented Jun 18, 2026

Copy link
Copy Markdown

@adamsaghy @Dhanno98 In any entity and scenario if the calculated charge amount is zero (after rounding) that charge should not be created.
In your latest example, "Allow the transaction but skip creating/applying the charge, similar to silent return," sounds right to me.
Any transactions associated with charges should not be affected. Hence withdraw transaction happens with no charge posting in the above example

@Dhanno98

Copy link
Copy Markdown
Contributor Author

@adamsaghy @Dhanno98 In any entity and scenario if the calculated charge amount is zero (after rounding) that charge should not be created. In your latest example, "Allow the transaction but skip creating/applying the charge, similar to silent return," sounds right to me. Any transactions associated with charges should not be affected. Hence withdraw transaction happens with no charge posting in the above example

Hi @bharathcgowda @adamsaghy
One follow-up question regarding future charges.

For percentage-based future charges (withdrawal/redeem), I understand that the charge definition should be stored and, when the calculated amount rounds to zero, the transaction should continue and the charge should simply not be applied.

For flat future charges (for example a Shares Flat Redeem Charge of 0.5 in a currency with 0 decimal places), the rounded charge amount is known to be zero already at attachment time and can never become non-zero later.

Currently, the existing behavior appears to allow such a Flat Redeem Charge to be attached to the share account without any validation. During the attachment flow, the configured charge amount (0.5 in this example) is stored as-is and no currency rounding is performed. The amount is only passed through Money.of() later during the redeem-shares flow, where the calculated charge amount becomes 0 and is effectively not applied.

For this scenario, what should be the expected behavior:

  1. Keep the current behavior: allow the charge to be attached as-is and, during redemption, simply ignore it if the rounded amount becomes 0, similar to Percentage of Redeem Charges.

  2. Round the flat redeem charge during attachment and reject it if the amount rounds to 0 by throwing a validation exception in the charge attachment flow itself.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants