Farooq Ayoade created FINERACT-2512:
---------------------------------------
Summary: Invalid fee_on_month / fee_on_day combination (e.g. day
30 for February) causes HTTP 500 when fetching savings product with template
Key: FINERACT-2512
URL: https://issues.apache.org/jira/browse/FINERACT-2512
Project: Apache Fineract
Issue Type: Bug
Components: Charges, Savings
Reporter: Farooq Ayoade
When fetching a savings product with template data via {{{}GET
/fineract-provider/api/v1/savingsproducts/\{productId}?template=true{}}}, the
API returns *HTTP 500 Internal Server Error* if any charge linked to that
product has an invalid month/day combination stored in the database (e.g.
{{{}fee_on_month=2{}}}, {{fee_on_day=30}} — February has no 30th).
h3. Steps to Reproduce
# Have a savings product (e.g. id=5) that has at least one charge with an
invalid month/day stored in {{m_charge}} (e.g. {{{}fee_on_month=2{}}},
{{{}fee_on_day=30{}}}).
# Call: {{GET
https://localhost:8443/fineract-provider/api/v1/savingsproducts/5?template=true}}
# Observe: *500 Internal Server Error* with stack trace showing
{{{}java.time.DateTimeException: Illegal value for DayOfMonth field, value 30
is not valid for month FEBRUARY{}}}.
h3. Expected Behavior
* The API should return *HTTP 200* with the savings product template.
* Invalid month/day combinations stored in the database (legacy data or “last
day of month” semantics) should be handled defensively when building
{{MonthDay}} for the response (e.g. clamp day to the last valid day of the
month) so that read operations do not fail.
h3. Actual Behavior
* *HTTP 500 Internal Server Error* is returned.
* Server log shows: {{java.time.DateTimeException: Illegal value for
DayOfMonth field, value 30 is not valid for month FEBRUARY}}
* Stack trace points to:
** {{ChargeReadPlatformServiceImpl$ChargeMapper.mapRow}} (line 342):
{{MonthDay.now(...).withDayOfMonth(feeOnDay).withMonth(feeOnMonth)}}
** Called from
{{ChargeReadPlatformServiceImpl.retrieveSavingsProductApplicableCharges}} →
{{SavingsProductsApiResource.handleTemplateRelatedData}} →
{{SavingsProductsApiResource.retrieveOne}}
h3. Root Cause
* Charges store *month* and *day* in separate columns:
{{{}m_charge.fee_on_month{}}}, {{{}m_charge.fee_on_day{}}}.
* When mapping a row to {{{}ChargeData{}}}, the code builds a {{MonthDay}} as:
{{MonthDay.now(...).withDayOfMonth(feeOnDay).withMonth(feeOnMonth)}} without
validating that {{feeOnDay}} is valid for {{feeOnMonth}} (e.g. February allows
1–28/29, not 30/31).
* Invalid combinations can exist due to:
** Legacy or migrated data.
** “Last day of month” semantics stored as day 30 or 31 for all months.
** Lack of or bypass of validation at charge create/update in some code paths.
* The same pattern exists in:
** *ChargeReadPlatformServiceImpl* (charge template / product-applicable
charges).
** *SavingsAccountChargeReadPlatformServiceImpl* (savings account charge
mapping).
** *StandingInstructionReadPlatformServiceImpl* (recurrence month/day mapping).
So any *read* that maps these columns to {{MonthDay}} can throw
{{DateTimeException}} when the stored (month, day) is invalid.
h3. Affected Code Paths
* *Primary:*
{{org.apache.fineract.portfolio.charge.service.ChargeReadPlatformServiceImpl$ChargeMapper#mapRow}}
(line 342).
* *Same bug pattern:*
**
{{org.apache.fineract.portfolio.savings.service.SavingsAccountChargeReadPlatformServiceImpl}}
(line 122).
**
{{org.apache.fineract.portfolio.account.service.StandingInstructionReadPlatformServiceImpl}}
(lines 443–444).
h3. Impact
* Savings product template endpoint is unusable when any linked charge has
invalid month/day (e.g. February 30).
* Similar 500s can occur when reading standing instructions or savings account
charges with invalid recurrence/fee month/day.
* Poor robustness against existing bad or legacy data.
--
This message was sent by Atlassian Jira
(v8.20.10#820010)