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)

Reply via email to