Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
c25d0e0
FINERACT-2732: type savings transaction search results as an ordered …
DeathGun44 Aug 2, 2026
cc7e001
FINERACT-2732: expose the savings product fields typed clients need
DeathGun44 Aug 3, 2026
55f85a1
FINERACT-2732: extend the feign savings helpers for interest posting …
DeathGun44 Aug 2, 2026
c60a908
FINERACT-2732: migrate savings interest posting and search tests to f…
DeathGun44 Aug 2, 2026
eb92f8c
FINERACT-2732: correct the savings transaction adjust response schema
DeathGun44 Aug 20, 2026
fe93f1b
FINERACT-2732: extend the feign savings helpers for transaction reversal
DeathGun44 Aug 2, 2026
16398e1
FINERACT-2732: migrate the savings balance-after-reversal test to feign
DeathGun44 Aug 2, 2026
d33a1c1
FINERACT-2732: migrate the savings recalculate-balance test to feign
DeathGun44 Aug 2, 2026
9e7c8b1
FINERACT-2732: migrate the savings product creation test to feign
DeathGun44 Aug 2, 2026
2cb69e4
FINERACT-2732: migrate the savings accrual tests to feign
DeathGun44 Aug 2, 2026
710b48e
FINERACT-2732: expose the fields typed clients need for post-interest…
DeathGun44 Aug 2, 2026
e78edc8
FINERACT-2732: migrate the savings datatable and interest posting tes…
DeathGun44 Aug 2, 2026
954bb9f
FINERACT-2732: migrate the savings interest posting job test to feign
DeathGun44 Aug 2, 2026
f975568
FINERACT-2732: expose the note and transfer-fee fields typed clients …
DeathGun44 Aug 3, 2026
81145c9
FINERACT-2732: extend the feign helpers for transfers and failing bat…
DeathGun44 Aug 3, 2026
8ebd29b
FINERACT-2732: migrate the savings transaction and transfer-fee tests…
DeathGun44 Aug 3, 2026
9e10e4d
FINERACT-2732: assert scheduler jobs actually succeed
DeathGun44 Aug 7, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,12 @@ private PutDataTablesAppTableIdDatatableIdResponse() {
public Long loanId;
@Schema(example = "1")
public Long resourceId;
/**
* Carried by the {@code CommandProcessingResult} whenever the datatable hangs off a transaction - a savings
* transaction datatable, for instance - but it was missing here, so generated clients could not read it.
*/
@Schema(example = "1")
public String transactionId;
public Map<String, Object> changes;
}

Expand All @@ -278,6 +284,11 @@ private DeleteDataTablesDatatableAppTableIdResponse() {

@Schema(example = "1")
public Long resourceId;
/**
* See {@link PutDataTablesAppTableIdDatatableIdResponse#transactionId}.
*/
@Schema(example = "1")
public String transactionId;
}

@Schema(description = "DeleteDataTablesDatatableAppTableIdDatatableIdResponse ")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@
import com.google.gson.JsonObject;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.media.ArraySchema;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.parameters.RequestBody;
Expand Down Expand Up @@ -331,7 +330,7 @@ private String transaction(final Long savingsId, final String savingsExternalIdS
+ "Accepted command = undo, reverse, modify, releaseAmount")
@AlternativeOperationId("adjustTransaction_1")
@RequestBody(required = true, content = @Content(schema = @Schema(implementation = SavingsAccountTransactionsApiResourceSwagger.PostSavingsAccountBulkReversalTransactionsRequest.class)))
@ApiResponse(responseCode = "200", description = "OK", content = @Content(array = @ArraySchema(schema = @Schema(implementation = SavingsAccountTransactionsApiResourceSwagger.PostSavingsAccountBulkReversalTransactionsRequest.class))))
@ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = CommandProcessingResult.class)))
public String adjustTransaction(@PathParam("savingsId") final Long savingsId, @PathParam("transactionId") final Long transactionId,
@QueryParam("command") final String commandParam, final String apiRequestBodyAsJson) {
return adjustTransaction(savingsId, null, transactionId, null, commandParam, apiRequestBodyAsJson);
Expand All @@ -346,7 +345,7 @@ public String adjustTransaction(@PathParam("savingsId") final Long savingsId, @P
+ "savingsaccounts/external-id/{savingsExternalId}/transactions/{transactionId}?command=reverse\n" + "\n"
+ "Accepted command = undo, reverse, modify, releaseAmount")
@RequestBody(required = true, content = @Content(schema = @Schema(implementation = SavingsAccountTransactionsApiResourceSwagger.PostSavingsAccountBulkReversalTransactionsRequest.class)))
@ApiResponse(responseCode = "200", description = "OK", content = @Content(array = @ArraySchema(schema = @Schema(implementation = SavingsAccountTransactionsApiResourceSwagger.PostSavingsAccountBulkReversalTransactionsRequest.class))))
@ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = CommandProcessingResult.class)))
public String adjustTransaction(@PathParam("savingsExternalId") final String savingsExternalId,
@PathParam("transactionId") final Long transactionId, @QueryParam("command") final String commandParam,
final String apiRequestBodyAsJson) {
Expand Down Expand Up @@ -391,7 +390,7 @@ private String adjustTransaction(final Long savingsId, final String savingsExter
+ "savingsaccounts/{savingsId}/transactions/external-id/{transactionExternalId}?command=reverse\n" + "\n"
+ "Accepted command = undo, reverse, modify, releaseAmount")
@RequestBody(required = true, content = @Content(schema = @Schema(implementation = SavingsAccountTransactionsApiResourceSwagger.PostSavingsAccountBulkReversalTransactionsRequest.class)))
@ApiResponse(responseCode = "200", description = "OK", content = @Content(array = @ArraySchema(schema = @Schema(implementation = SavingsAccountTransactionsApiResourceSwagger.PostSavingsAccountBulkReversalTransactionsRequest.class))))
@ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = CommandProcessingResult.class)))
public String adjustTransaction(@PathParam("savingsId") final Long savingsId,
@PathParam("transactionExternalId") final String transactionExternalId, @QueryParam("command") final String commandParam,
final String apiRequestBodyAsJson) {
Expand All @@ -407,7 +406,7 @@ public String adjustTransaction(@PathParam("savingsId") final Long savingsId,
+ "savingsaccounts/external-id/{savingsExternalId}/transactions/external-id/{transactionExternalId}?command=reverse\n" + "\n"
+ "Accepted command = undo, reverse, modify, releaseAmount")
@RequestBody(required = true, content = @Content(schema = @Schema(implementation = SavingsAccountTransactionsApiResourceSwagger.PostSavingsAccountBulkReversalTransactionsRequest.class)))
@ApiResponse(responseCode = "200", description = "OK", content = @Content(array = @ArraySchema(schema = @Schema(implementation = SavingsAccountTransactionsApiResourceSwagger.PostSavingsAccountBulkReversalTransactionsRequest.class))))
@ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = CommandProcessingResult.class)))
public String adjustTransaction(@PathParam("savingsExternalId") final String savingsExternalId,
@PathParam("transactionExternalId") final String transactionExternalId, @QueryParam("command") final String commandParam,
final String apiRequestBodyAsJson) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.apache.fineract.portfolio.TransactionEntryType;

Expand Down Expand Up @@ -188,7 +189,12 @@ private GetSavingsAccountChargesPaidByData() {}

@Schema(example = "2")
public Long total;
public Set<GetSavingsAccountTransactionsPageItem> content;
/**
* A page of search results is ordered by the request's {@code orderBy}/{@code sortOrder} (defaulting to most
* recent first), so it is documented as an ordered list. Declaring it as a set made generated clients drop that
* ordering. The serialized response is a JSON array either way, so the wire format is unchanged.
*/
public List<GetSavingsAccountTransactionsPageItem> content;
}

@Schema(description = "PostSavingsAccountTransactionsRequest")
Expand All @@ -209,11 +215,15 @@ private PostSavingsAccountTransactionsRequest() {}
@Schema(example = "true")
public Boolean isPostInterestAsOn;
@Schema(example = "true")
public Boolean postInterestManualOrAutomatic;
@Schema(example = "true")
public String lienAllowed;
@Schema(example = "String")
public String reasonForBlock;
@Schema(example = "1")
public Integer paymentTypeId;
@Schema(example = "A note about this transaction")
public String note;
}

@Schema(description = "PostSavingsAccountTransactionsResponse")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,8 @@ private PutSavingsAccountsAccountIdRequest() {}
public Long productId;
@Schema(example = "01 March 2011")
public String submittedOnDate;
@Schema(example = "true")
public Boolean withdrawalFeeForTransfers;
}

@Schema(description = "PutSavingsAccountsAccountIdResponse")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,41 @@ private PostSavingsCharges() {}
public Boolean withHoldTax;
@Schema(example = "false")
public Boolean isDormancyTrackingActive;
/*
* The overdraft settings and the GL account mappings below have always been accepted by the endpoint - they are
* part of SAVINGS_PRODUCT_REQUEST_DATA_PARAMETERS - but were never documented, so a typed client could not
* create an accrual-based or overdraft-enabled product at all.
*/
@Schema(example = "10000")
public BigDecimal overdraftLimit;
@Schema(example = "21.0")
public BigDecimal nominalAnnualInterestRateOverdraft;
@Schema(example = "1")
public Long savingsReferenceAccountId;
@Schema(example = "1")
public Long savingsControlAccountId;
@Schema(example = "1")
public Long transfersInSuspenseAccountId;
@Schema(example = "1")
public Long interestOnSavingsAccountId;
@Schema(example = "1")
public Long writeOffAccountId;
@Schema(example = "1")
public Long incomeFromFeeAccountId;
@Schema(example = "1")
public Long incomeFromPenaltyAccountId;
@Schema(example = "1")
public Long incomeFromInterestId;
@Schema(example = "1")
public Long overdraftPortfolioControlId;
@Schema(example = "1")
public Long feesReceivableAccountId;
@Schema(example = "1")
public Long penaltiesReceivableAccountId;
@Schema(example = "1")
public Long interestPayableAccountId;
@Schema(example = "1")
public Long interestReceivableAccountId;
}

@Schema(description = "PostSavingsProductsResponse")
Expand All @@ -95,6 +130,11 @@ private PostSavingsProductsResponse() {}
public Long resourceId;
}

/**
* An update is validated against the same {@code SAVINGS_PRODUCT_REQUEST_DATA_PARAMETERS} set as a create, so it
* accepts the whole product body rather than the three fields that used to be documented here. The extra fields are
* listed so a typed client can repoint an accounting mapping or an overdraft setting without hand-building JSON.
*/
@Schema(description = "PutSavingsProductsProductIdRequest")
public static final class PutSavingsProductsProductIdRequest {

Expand All @@ -106,6 +146,68 @@ private PutSavingsProductsProductIdRequest() {}
public String locale;
@Schema(example = "5.73")
public Double interestRate;
@Schema(example = "Passbook Savings")
public String name;
@Schema(example = "PBSV")
public String shortName;
@Schema(example = "USD")
public String currencyCode;
@Schema(example = "2")
public Integer digitsAfterDecimal;
@Schema(example = "0")
public Integer inMultiplesOf;
@Schema(example = "5.0")
public Double nominalAnnualInterestRate;
@Schema(example = "10000.0")
public BigDecimal minRequiredOpeningBalance;
@Schema(example = "1")
public Integer interestCompoundingPeriodType;
@Schema(example = "4")
public Integer interestPostingPeriodType;
@Schema(example = "1")
public Integer interestCalculationType;
@Schema(example = "365")
public Integer interestCalculationDaysInYearType;
@Schema(example = "1")
public Integer accountingRule;
@Schema(example = "false")
public Boolean withdrawalFeeForTransfers;
@Schema(example = "false")
public Boolean enforceMinRequiredBalance;
@Schema(example = "false")
public Boolean allowOverdraft;
@Schema(example = "false")
public Boolean withHoldTax;
@Schema(example = "10000")
public BigDecimal overdraftLimit;
@Schema(example = "21.0")
public BigDecimal nominalAnnualInterestRateOverdraft;
@Schema(example = "1")
public Long savingsReferenceAccountId;
@Schema(example = "1")
public Long savingsControlAccountId;
@Schema(example = "1")
public Long transfersInSuspenseAccountId;
@Schema(example = "1")
public Long interestOnSavingsAccountId;
@Schema(example = "1")
public Long writeOffAccountId;
@Schema(example = "1")
public Long incomeFromFeeAccountId;
@Schema(example = "1")
public Long incomeFromPenaltyAccountId;
@Schema(example = "1")
public Long incomeFromInterestId;
@Schema(example = "1")
public Long overdraftPortfolioControlId;
@Schema(example = "1")
public Long feesReceivableAccountId;
@Schema(example = "1")
public Long penaltiesReceivableAccountId;
@Schema(example = "1")
public Long interestPayableAccountId;
@Schema(example = "1")
public Long interestReceivableAccountId;
}

@Schema(description = "PutSavingsProductsProductIdResponse")
Expand Down
Loading