Compare commits
13
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4522f1305d | ||
|
|
ac563badaa | ||
|
|
861e2e196e | ||
|
|
8ad90f0b00 | ||
|
|
5606b88fbc | ||
|
|
4bb71382a1 | ||
|
|
859cabd07d | ||
|
|
6380e0d7d2 | ||
|
|
4ad508ee96 | ||
|
|
6eb562548c | ||
|
|
41aeb2b89a | ||
|
|
2569e0f836 | ||
|
|
81b5850b62 |
+192
@@ -95,6 +95,198 @@ public class CommandWrapperBuilder {
|
||||
return this;
|
||||
}
|
||||
|
||||
public CommandWrapperBuilder createInvestmentParty() {
|
||||
this.actionName = "CREATE";
|
||||
this.entityName = "INVESTMENTPARTY";
|
||||
this.entityId = null;
|
||||
this.href = "/investment/parties";
|
||||
return this;
|
||||
}
|
||||
|
||||
public CommandWrapperBuilder updateInvestmentParty(final Long partyId) {
|
||||
this.actionName = "UPDATE";
|
||||
this.entityName = "INVESTMENTPARTY";
|
||||
this.entityId = partyId;
|
||||
this.href = "/investment/parties/" + partyId;
|
||||
return this;
|
||||
}
|
||||
|
||||
public CommandWrapperBuilder deleteInvestmentParty(final Long partyId) {
|
||||
this.actionName = "DELETE";
|
||||
this.entityName = "INVESTMENTPARTY";
|
||||
this.entityId = partyId;
|
||||
this.href = "/investment/parties/" + partyId;
|
||||
return this;
|
||||
}
|
||||
|
||||
public CommandWrapperBuilder createInvestmentScript() {
|
||||
this.actionName = "CREATE";
|
||||
this.entityName = "INVESTMENTSCRIPT";
|
||||
this.entityId = null;
|
||||
this.href = "/investment/scripts";
|
||||
return this;
|
||||
}
|
||||
|
||||
public CommandWrapperBuilder updateInvestmentScript(final Long scriptId) {
|
||||
this.actionName = "UPDATE";
|
||||
this.entityName = "INVESTMENTSCRIPT";
|
||||
this.entityId = scriptId;
|
||||
this.href = "/investment/scripts/" + scriptId;
|
||||
return this;
|
||||
}
|
||||
|
||||
public CommandWrapperBuilder createInvestmentPurchase() {
|
||||
this.actionName = "CREATE";
|
||||
this.entityName = "INVESTMENTPURCHASE";
|
||||
this.entityId = null;
|
||||
this.href = "/investment/purchases";
|
||||
return this;
|
||||
}
|
||||
|
||||
public CommandWrapperBuilder deleteInvestmentPurchase(final Long purchaseId) {
|
||||
this.actionName = "DELETE";
|
||||
this.entityName = "INVESTMENTPURCHASE";
|
||||
this.entityId = purchaseId;
|
||||
this.href = "/investment/purchases/" + purchaseId;
|
||||
return this;
|
||||
}
|
||||
|
||||
public CommandWrapperBuilder settleInvestmentPurchase(final Long purchaseId) {
|
||||
this.actionName = "SETTLE";
|
||||
this.entityName = "INVESTMENTPURCHASE";
|
||||
this.entityId = purchaseId;
|
||||
this.href = "/investment/purchases/" + purchaseId + "/settle";
|
||||
return this;
|
||||
}
|
||||
|
||||
public CommandWrapperBuilder createInvestmentSale() {
|
||||
this.actionName = "CREATE";
|
||||
this.entityName = "INVESTMENTSALE";
|
||||
this.entityId = null;
|
||||
this.href = "/investment/sales";
|
||||
return this;
|
||||
}
|
||||
|
||||
public CommandWrapperBuilder settleInvestmentSale(final Long saleId) {
|
||||
this.actionName = "SETTLE";
|
||||
this.entityName = "INVESTMENTSALE";
|
||||
this.entityId = saleId;
|
||||
this.href = "/investment/sales/" + saleId + "/settle";
|
||||
return this;
|
||||
}
|
||||
|
||||
public CommandWrapperBuilder deleteInvestmentSale(final Long saleId) {
|
||||
this.actionName = "DELETE";
|
||||
this.entityName = "INVESTMENTSALE";
|
||||
this.entityId = saleId;
|
||||
this.href = "/investment/sales/" + saleId;
|
||||
return this;
|
||||
}
|
||||
|
||||
public CommandWrapperBuilder createInvestmentFd() {
|
||||
this.actionName = "CREATE";
|
||||
this.entityName = "INVESTMENTFD";
|
||||
this.entityId = null;
|
||||
this.href = "/investment/fds";
|
||||
return this;
|
||||
}
|
||||
|
||||
public CommandWrapperBuilder matureInvestmentFd(final Long fdId) {
|
||||
this.actionName = "MATURE";
|
||||
this.entityName = "INVESTMENTFD";
|
||||
this.entityId = fdId;
|
||||
this.href = "/investment/fds/" + fdId + "/mature";
|
||||
return this;
|
||||
}
|
||||
|
||||
public CommandWrapperBuilder prematureInvestmentFd(final Long fdId) {
|
||||
this.actionName = "PREMATURE";
|
||||
this.entityName = "INVESTMENTFD";
|
||||
this.entityId = fdId;
|
||||
this.href = "/investment/fds/" + fdId + "/premature";
|
||||
return this;
|
||||
}
|
||||
|
||||
public CommandWrapperBuilder renewInvestmentFd(final Long fdId) {
|
||||
this.actionName = "RENEW";
|
||||
this.entityName = "INVESTMENTFD";
|
||||
this.entityId = fdId;
|
||||
this.href = "/investment/fds/" + fdId + "/renew";
|
||||
return this;
|
||||
}
|
||||
|
||||
public CommandWrapperBuilder postInvestmentFdInterest(final Long fdId) {
|
||||
this.actionName = "POSTINTEREST";
|
||||
this.entityName = "INVESTMENTFD";
|
||||
this.entityId = fdId;
|
||||
this.href = "/investment/fds/" + fdId + "/interest";
|
||||
return this;
|
||||
}
|
||||
|
||||
public CommandWrapperBuilder setInvestmentDepositBase() {
|
||||
this.actionName = "SETBASE";
|
||||
this.entityName = "INVESTMENTFD";
|
||||
this.entityId = null;
|
||||
this.href = "/investment/fds/deposit-base";
|
||||
return this;
|
||||
}
|
||||
|
||||
public CommandWrapperBuilder amortiseInvestmentPremium() {
|
||||
this.actionName = "AMORTISE";
|
||||
this.entityName = "INVESTMENTSALE";
|
||||
this.entityId = null;
|
||||
this.href = "/investment/amortisation";
|
||||
return this;
|
||||
}
|
||||
|
||||
public CommandWrapperBuilder writeOffInvestmentPurchase(final Long purchaseId) {
|
||||
this.actionName = "WRITEOFF";
|
||||
this.entityName = "INVESTMENTSALE";
|
||||
this.entityId = purchaseId;
|
||||
this.href = "/investment/purchases/" + purchaseId + "/write-off";
|
||||
return this;
|
||||
}
|
||||
|
||||
public CommandWrapperBuilder redeemInvestmentPurchase(final Long purchaseId) {
|
||||
this.actionName = "REDEEM";
|
||||
this.entityName = "INVESTMENTSALE";
|
||||
this.entityId = purchaseId;
|
||||
this.href = "/investment/purchases/" + purchaseId + "/redeem";
|
||||
return this;
|
||||
}
|
||||
|
||||
public CommandWrapperBuilder transferInvestmentSecurity(final Long purchaseId) {
|
||||
this.actionName = "CREATE";
|
||||
this.entityName = "INVESTMENTTRANSFER";
|
||||
this.entityId = purchaseId;
|
||||
this.href = "/investment/purchases/" + purchaseId + "/transfer";
|
||||
return this;
|
||||
}
|
||||
|
||||
public CommandWrapperBuilder postInvestmentInterest(final Long purchaseId) {
|
||||
this.actionName = "CREATE";
|
||||
this.entityName = "INVESTMENTINTEREST";
|
||||
this.entityId = purchaseId;
|
||||
this.href = "/investment/purchases/" + purchaseId + "/interest-postings";
|
||||
return this;
|
||||
}
|
||||
|
||||
public CommandWrapperBuilder createInvestmentDailyRate() {
|
||||
this.actionName = "CREATE";
|
||||
this.entityName = "INVESTMENTDAILYRATE";
|
||||
this.entityId = null;
|
||||
this.href = "/investment/daily-rates";
|
||||
return this;
|
||||
}
|
||||
|
||||
public CommandWrapperBuilder deleteInvestmentDailyRate(final Long rateId) {
|
||||
this.actionName = "DELETE";
|
||||
this.entityName = "INVESTMENTDAILYRATE";
|
||||
this.entityId = rateId;
|
||||
this.href = "/investment/daily-rates/" + rateId;
|
||||
return this;
|
||||
}
|
||||
|
||||
public CommandWrapperBuilder createChequeStockLot() {
|
||||
this.actionName = "CREATE";
|
||||
this.entityName = "CHEQUESTOCKLOT";
|
||||
|
||||
+17
@@ -46,6 +46,8 @@ import org.apache.fineract.infrastructure.core.service.DateUtils;
|
||||
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
|
||||
import org.apache.fineract.portfolio.cashreport.service.VaultPositionService;
|
||||
import org.apache.fineract.portfolio.denominationmaster.service.CountedCashLedger;
|
||||
import org.apache.fineract.portfolio.investment.service.InvestmentFdInterestService;
|
||||
import org.apache.fineract.portfolio.investment.service.InvestmentPurchaseReadPlatformService;
|
||||
import org.apache.fineract.portfolio.savings.dailyinterest.SavingsDailyInterestOrchestrator;
|
||||
import org.apache.fineract.portfolio.savings.dailyinterest.SavingsInterestPostingReportService;
|
||||
import org.apache.fineract.portfolio.savings.fdinterest.FixedDepositInterestPostingOrchestrator;
|
||||
@@ -95,8 +97,16 @@ public class BranchDayRegisterApiResource {
|
||||
private final SavingsDailyInterestOrchestrator savingsDailyInterestOrchestrator;
|
||||
private final FixedDepositInterestPostingReportService fdInterestPostingReportService;
|
||||
private final FixedDepositInterestPostingOrchestrator fdInterestPostingOrchestrator;
|
||||
private final InvestmentPurchaseReadPlatformService investmentPurchaseReadPlatformService;
|
||||
private final InvestmentFdInterestService investmentFdInterestService;
|
||||
private final Gson gson = new Gson();
|
||||
|
||||
private boolean isHeadOffice(final Long officeId) {
|
||||
final Integer topLevel = jdbcTemplate.queryForObject("SELECT COUNT(*) FROM m_office WHERE id = ? AND parent_id IS NULL",
|
||||
Integer.class, officeId);
|
||||
return topLevel != null && topLevel > 0;
|
||||
}
|
||||
|
||||
// ── GET /v1/branchday ── the register itself ──────────────────────────────
|
||||
|
||||
/**
|
||||
@@ -260,6 +270,13 @@ public class BranchDayRegisterApiResource {
|
||||
final Map<String, Object> fdInterest = fdInterestPostingReportService.dayCloseInterestView(officeId, businessDate);
|
||||
result.put("fdInterestPending", fdInterest.get("pending"));
|
||||
result.put("fdInterestDue", fdInterest.get("due"));
|
||||
// Investment interest vouchers (coupon / provision) whose date has come but which are not posted yet.
|
||||
// A reminder only: it never stops the close (Jitendra Sir). Investments are booked at Head Office, so
|
||||
// only the Head Office close shows it.
|
||||
result.put("investmentInterestPending",
|
||||
isHeadOffice(officeId) ? investmentPurchaseReadPlatformService.retrieveDueInterest(0) : List.of());
|
||||
// The same for FDs placed with other banks: interest vouchers and maturities due. A reminder only.
|
||||
result.put("investmentFdPending", isHeadOffice(officeId) ? investmentFdInterestService.due(0) : List.of());
|
||||
|
||||
return gson.toJson(result);
|
||||
}
|
||||
|
||||
+83
@@ -0,0 +1,83 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.investment.api;
|
||||
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.ws.rs.Consumes;
|
||||
import jakarta.ws.rs.GET;
|
||||
import jakarta.ws.rs.POST;
|
||||
import jakarta.ws.rs.Path;
|
||||
import jakarta.ws.rs.PathParam;
|
||||
import jakarta.ws.rs.Produces;
|
||||
import jakarta.ws.rs.QueryParam;
|
||||
import jakarta.ws.rs.core.MediaType;
|
||||
import java.time.LocalDate;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.List;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.fineract.commands.domain.CommandWrapper;
|
||||
import org.apache.fineract.commands.service.CommandWrapperBuilder;
|
||||
import org.apache.fineract.commands.service.PortfolioCommandSourceWritePlatformService;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
|
||||
import org.apache.fineract.portfolio.investment.data.InvestmentAmortisationData;
|
||||
import org.apache.fineract.portfolio.investment.data.InvestmentAmortisationDueData;
|
||||
import org.apache.fineract.portfolio.investment.service.InvestmentAmortisationService;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Path("/v1/investment/amortisation")
|
||||
@Component
|
||||
@Tag(name = "Investment Premium Amortisation", description = "Premium of HTM holdings amortised straight-line to maturity every 31 March.")
|
||||
@RequiredArgsConstructor
|
||||
public class InvestmentAmortisationApiResource {
|
||||
|
||||
private final PlatformSecurityContext context;
|
||||
private final InvestmentAmortisationService amortisationService;
|
||||
private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService;
|
||||
|
||||
@GET
|
||||
@Path("{purchaseId}")
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "Amortisation schedule of a holding (06 Amortize Entry)")
|
||||
public InvestmentAmortisationData schedule(@PathParam("purchaseId") @Parameter(description = "purchaseId") final Long purchaseId) {
|
||||
this.context.authenticatedUser().validateHasReadPermission(InvestmentSaleApiConstants.ENTITY);
|
||||
return this.amortisationService.schedule(purchaseId);
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("due")
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "Holdings due for amortisation on a 31 March (07 Amortize Yearly Process)", description = "fyEnd dd-MM-yyyy")
|
||||
public List<InvestmentAmortisationDueData> due(@QueryParam("fyEnd") @Parameter(description = "dd-MM-yyyy") final String fyEnd) {
|
||||
this.context.authenticatedUser().validateHasReadPermission(InvestmentSaleApiConstants.ENTITY);
|
||||
return this.amortisationService.due(LocalDate.parse(fyEnd.trim(), DateTimeFormatter.ofPattern("dd-MM-yyyy")));
|
||||
}
|
||||
|
||||
@POST
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "Post amortisation for a 31 March", description = "With purchaseId: that holding (Amortize Entry); without: "
|
||||
+ "every holding due (Amortize Yearly Process). One JV per holding: Dr 500604 / Cr premium GL.")
|
||||
public CommandProcessingResult amortise(@Parameter(hidden = true) final String apiRequestBodyAsJson) {
|
||||
final CommandWrapper request = new CommandWrapperBuilder().amortiseInvestmentPremium().withJson(apiRequestBodyAsJson).build();
|
||||
return this.commandsSourceWritePlatformService.logCommandSource(request);
|
||||
}
|
||||
}
|
||||
+96
@@ -0,0 +1,96 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.investment.api;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Constants for the Treasury Investment masters (Bank / Party Master and Script Master), modelled on the bank's existing
|
||||
* CBS Investment screens.
|
||||
*/
|
||||
public final class InvestmentApiConstants {
|
||||
|
||||
private InvestmentApiConstants() {}
|
||||
|
||||
public static final String ENTITY_PARTY = "INVESTMENTPARTY";
|
||||
public static final String ENTITY_SCRIPT = "INVESTMENTSCRIPT";
|
||||
public static final String RESOURCE_PARTY = "investmentparty";
|
||||
public static final String RESOURCE_SCRIPT = "investmentscript";
|
||||
|
||||
/** Instrument ("Bond Type") an investment party / script belongs to. */
|
||||
public static final Set<String> BOND_TYPES = Set.of("GOVT_SECURITY", "NON_SLR", "BOND", "MUTUAL_FUND", "FD", "SHARE");
|
||||
|
||||
/** Bond types that carry coupon terms (ISIN, interest rate, issue date are mandatory for these). */
|
||||
public static final Set<String> DEBT_BOND_TYPES = Set.of("GOVT_SECURITY", "NON_SLR", "BOND");
|
||||
|
||||
/** SLR classification on the Script Master ("GOI" = Central Government). */
|
||||
public static final Set<String> SLR_TYPES = Set.of("GOI", "STATE_GOVT", "TREASURY", "NON_SLR");
|
||||
|
||||
/** Script Code prefix per bond type, e.g. G00001 for the first Govt Security. */
|
||||
public static final Map<String, String> SCRIPT_CODE_PREFIX = Map.of("GOVT_SECURITY", "G", "NON_SLR", "N", "BOND", "B", "MUTUAL_FUND",
|
||||
"M", "FD", "F", "SHARE", "S");
|
||||
|
||||
public static final String PARTY_CODE_PREFIX = "PTY-";
|
||||
|
||||
// Common parameters
|
||||
public static final String BOND_TYPE = "bondType";
|
||||
public static final String GL_ACCOUNT_ID = "glAccountId";
|
||||
public static final String INTEREST_GL_ACCOUNT_ID = "interestGlAccountId";
|
||||
public static final String ADDRESS1 = "address1";
|
||||
public static final String ADDRESS2 = "address2";
|
||||
public static final String ADDRESS3 = "address3";
|
||||
public static final String CITY = "city";
|
||||
public static final String PINCODE = "pincode";
|
||||
public static final String PHONE = "phone";
|
||||
public static final String FAX = "fax";
|
||||
public static final String ACTIVE = "active";
|
||||
public static final String LOCALE = "locale";
|
||||
public static final String DATE_FORMAT = "dateFormat";
|
||||
|
||||
// Party parameters
|
||||
public static final String PARTY_NAME = "partyName";
|
||||
public static final String BANK_TYPE = "bankType";
|
||||
public static final String RBI_CODE = "rbiCode";
|
||||
public static final String EXPOSURE_LIMIT = "limit";
|
||||
public static final String LEI_NO = "leiNo";
|
||||
public static final String CP_BANK_NAME = "cpBankName";
|
||||
public static final String CP_BRANCH_NAME = "cpBranchName";
|
||||
public static final String CP_ACCOUNT_NO = "cpAccountNo";
|
||||
public static final String CP_IFSC = "cpIfsc";
|
||||
|
||||
// Script parameters
|
||||
public static final String SLR_TYPE = "slrType";
|
||||
public static final String SCRIPT_DESC = "scriptDesc";
|
||||
public static final String ISIN = "isin";
|
||||
public static final String FACE_VALUE_PER_UNIT = "faceValuePerUnit";
|
||||
public static final String INT_RATE = "intRate";
|
||||
public static final String ISSUE_DATE = "issueDate";
|
||||
public static final String MATURITY_DATE = "maturityDate";
|
||||
public static final String COMPANY_NAME = "companyName";
|
||||
public static final String CORP_CODE = "corpCode";
|
||||
|
||||
public static final Set<String> PARTY_PARAMS = Set.of(BOND_TYPE, PARTY_NAME, ADDRESS1, ADDRESS2, ADDRESS3, CITY, PINCODE, PHONE, FAX,
|
||||
BANK_TYPE, GL_ACCOUNT_ID, INTEREST_GL_ACCOUNT_ID, RBI_CODE, EXPOSURE_LIMIT, LEI_NO, CP_BANK_NAME, CP_BRANCH_NAME, CP_ACCOUNT_NO, CP_IFSC, ACTIVE,
|
||||
LOCALE);
|
||||
|
||||
public static final Set<String> SCRIPT_PARAMS = Set.of(BOND_TYPE, SLR_TYPE, SCRIPT_DESC, GL_ACCOUNT_ID, ISIN, FACE_VALUE_PER_UNIT,
|
||||
INT_RATE, ISSUE_DATE, MATURITY_DATE, COMPANY_NAME, ADDRESS1, ADDRESS2, ADDRESS3, CITY, PINCODE, PHONE, FAX, CORP_CODE,
|
||||
ACTIVE, LOCALE, DATE_FORMAT);
|
||||
}
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.investment.api;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/** Constants for the Daily Script Master (market rate of a script for a day). */
|
||||
public final class InvestmentDailyRateApiConstants {
|
||||
|
||||
private InvestmentDailyRateApiConstants() {}
|
||||
|
||||
public static final String ENTITY = "INVESTMENTDAILYRATE";
|
||||
public static final String RESOURCE = "investmentdailyrate";
|
||||
|
||||
public static final String SCRIPT_ID = "scriptId";
|
||||
public static final String RATE_DATE = "rateDate";
|
||||
public static final String OPEN_RATE = "openRate";
|
||||
public static final String HIGH_RATE = "highRate";
|
||||
public static final String LOW_RATE = "lowRate";
|
||||
public static final String CLOSE_RATE = "closeRate";
|
||||
public static final String LOCALE = "locale";
|
||||
public static final String DATE_FORMAT = "dateFormat";
|
||||
|
||||
public static final Set<String> PARAMS = Set.of(SCRIPT_ID, RATE_DATE, OPEN_RATE, HIGH_RATE, LOW_RATE, CLOSE_RATE, LOCALE, DATE_FORMAT);
|
||||
}
|
||||
+96
@@ -0,0 +1,96 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.investment.api;
|
||||
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.ws.rs.Consumes;
|
||||
import jakarta.ws.rs.DELETE;
|
||||
import jakarta.ws.rs.GET;
|
||||
import jakarta.ws.rs.POST;
|
||||
import jakarta.ws.rs.Path;
|
||||
import jakarta.ws.rs.PathParam;
|
||||
import jakarta.ws.rs.Produces;
|
||||
import jakarta.ws.rs.QueryParam;
|
||||
import jakarta.ws.rs.core.MediaType;
|
||||
import java.time.LocalDate;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.List;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.fineract.commands.domain.CommandWrapper;
|
||||
import org.apache.fineract.commands.service.CommandWrapperBuilder;
|
||||
import org.apache.fineract.commands.service.PortfolioCommandSourceWritePlatformService;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.apache.fineract.infrastructure.core.service.DateUtils;
|
||||
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
|
||||
import org.apache.fineract.portfolio.investment.data.InvestmentDailyRateData;
|
||||
import org.apache.fineract.portfolio.investment.service.InvestmentDailyRateReadPlatformService;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Path("/v1/investment/daily-rates")
|
||||
@Component
|
||||
@Tag(name = "Investment Daily Script Master", description = "Market rate of each script for a day; Purchase Entry takes its rate from here.")
|
||||
@RequiredArgsConstructor
|
||||
public class InvestmentDailyRateApiResource {
|
||||
|
||||
private final PlatformSecurityContext context;
|
||||
private final InvestmentDailyRateReadPlatformService readPlatformService;
|
||||
private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService;
|
||||
|
||||
@GET
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "List daily script rates, newest first")
|
||||
public List<InvestmentDailyRateData> retrieveAll(@QueryParam("scriptId") @Parameter(description = "scriptId") final Long scriptId) {
|
||||
this.context.authenticatedUser().validateHasReadPermission(InvestmentDailyRateApiConstants.ENTITY);
|
||||
return this.readPlatformService.retrieveAll(scriptId);
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("latest")
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "Newest rate of a script on or before a date", description = "onDate (dd-MM-yyyy) defaults to the business "
|
||||
+ "date. Returns no content when no rate has been entered for the script.")
|
||||
public InvestmentDailyRateData retrieveLatest(@QueryParam("scriptId") @Parameter(description = "scriptId") final Long scriptId,
|
||||
@QueryParam("onDate") @Parameter(description = "dd-MM-yyyy") final String onDate) {
|
||||
this.context.authenticatedUser().validateHasReadPermission(InvestmentDailyRateApiConstants.ENTITY);
|
||||
final LocalDate on = StringUtils.isBlank(onDate) ? DateUtils.getBusinessLocalDate()
|
||||
: LocalDate.parse(onDate.trim(), DateTimeFormatter.ofPattern("dd-MM-yyyy"));
|
||||
return this.readPlatformService.retrieveLatest(scriptId, on);
|
||||
}
|
||||
|
||||
@POST
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "Save a daily script rate")
|
||||
public CommandProcessingResult create(@Parameter(hidden = true) final String apiRequestBodyAsJson) {
|
||||
final CommandWrapper request = new CommandWrapperBuilder().createInvestmentDailyRate().withJson(apiRequestBodyAsJson).build();
|
||||
return this.commandsSourceWritePlatformService.logCommandSource(request);
|
||||
}
|
||||
|
||||
@DELETE
|
||||
@Path("{rateId}")
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "Drop a daily script rate")
|
||||
public CommandProcessingResult delete(@PathParam("rateId") @Parameter(description = "rateId") final Long rateId) {
|
||||
final CommandWrapper request = new CommandWrapperBuilder().deleteInvestmentDailyRate(rateId).build();
|
||||
return this.commandsSourceWritePlatformService.logCommandSource(request);
|
||||
}
|
||||
}
|
||||
+119
@@ -0,0 +1,119 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.investment.api;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Set;
|
||||
|
||||
/** Fixed deposits placed with other banks. */
|
||||
public final class InvestmentFdApiConstants {
|
||||
|
||||
private InvestmentFdApiConstants() {}
|
||||
|
||||
public static final String ENTITY = "INVESTMENTFD";
|
||||
public static final String BOND_TYPE_FD = "FD";
|
||||
|
||||
public static final String STATUS_ACTIVE = "ACTIVE";
|
||||
public static final String STATUS_MATURED = "MATURED";
|
||||
public static final String STATUS_PREMATURE = "PREMATURE";
|
||||
public static final String STATUS_RENEWED = "RENEWED";
|
||||
|
||||
/** When the interest is paid: all at maturity (the usual case), or every half year / year. */
|
||||
public static final String PAYOUT_AT_MATURITY = "MATURITY";
|
||||
public static final String PAYOUT_HALF_YEARLY = "HALF_YEARLY";
|
||||
public static final String PAYOUT_YEARLY = "YEARLY";
|
||||
public static final Set<String> PAYOUTS = Set.of(PAYOUT_AT_MATURITY, PAYOUT_HALF_YEARLY, PAYOUT_YEARLY);
|
||||
|
||||
/** How the maturity amount is worked out, to compare with the bank's figure (Jitendra Sir). */
|
||||
public static final String CALC_SIMPLE = "SIMPLE";
|
||||
public static final String CALC_MONTHLY = "MONTHLY";
|
||||
public static final String CALC_QUARTERLY = "QUARTERLY";
|
||||
public static final String CALC_HALF_YEARLY = "HALF_YEARLY";
|
||||
public static final String CALC_YEARLY = "YEARLY";
|
||||
public static final Set<String> CALC_TYPES = Set.of(CALC_SIMPLE, CALC_MONTHLY, CALC_QUARTERLY, CALC_HALF_YEARLY, CALC_YEARLY);
|
||||
|
||||
/** RBI: deposits with other banks within 20% of total deposits as on the previous 31 March, and within 5% with one bank. */
|
||||
public static final BigDecimal GROSS_LIMIT_PERCENT = BigDecimal.valueOf(20);
|
||||
public static final BigDecimal BANK_LIMIT_PERCENT = BigDecimal.valueOf(5);
|
||||
/** Current accounts with banks also count towards the 20% inter-bank exposure. */
|
||||
public static final String BANK_BALANCE_HEADER_GL_CODE = "100200";
|
||||
/** Deposits of the bank, for the suggested total-deposits figure: everything under the deposit header, plus RD. */
|
||||
public static final String DEPOSIT_HEADER_GL_CODE = "200100";
|
||||
public static final String RD_DEPOSIT_GL_CODE = "200201";
|
||||
|
||||
public static final String PARTY_ID = "partyId";
|
||||
public static final String CERT_NO = "certNo";
|
||||
public static final String CERT_DATE = "certDate";
|
||||
public static final String DEAL_DATE = "dealDate";
|
||||
public static final String AMOUNT = "amount";
|
||||
public static final String INT_RATE = "intRate";
|
||||
public static final String MATURITY_DATE = "maturityDate";
|
||||
public static final String PAYOUT = "payout";
|
||||
public static final String CALC_TYPE = "calcType";
|
||||
public static final String BANK_GL_ACCOUNT_ID = "bankGlAccountId";
|
||||
public static final String REMARKS = "remarks";
|
||||
public static final String AS_ON_DATE = "asOnDate";
|
||||
public static final String TOTAL_DEPOSITS = "totalDeposits";
|
||||
public static final String LOCALE = "locale";
|
||||
public static final String DATE_FORMAT = "dateFormat";
|
||||
|
||||
public static final String CLOSURE_MATURITY = "MATURITY";
|
||||
public static final String CLOSURE_PREMATURE = "PREMATURE";
|
||||
public static final String CLOSURE_RENEWAL = "RENEWAL";
|
||||
public static final String LINE_PROVISION = "PROVISION";
|
||||
public static final String LINE_PAYOUT = "PAYOUT";
|
||||
public static final String INTEREST_RECEIVABLE_GL_CODE = "100603";
|
||||
/** Tax the other bank deducts from the interest, recoverable from the tax department. */
|
||||
public static final String TDS_RECEIVABLE_GL_CODE = "100602";
|
||||
|
||||
public static final String CLOSE_DATE = "closeDate";
|
||||
public static final String RECEIVED_AMOUNT = "receivedAmount";
|
||||
public static final String TDS = "tds";
|
||||
public static final String PENAL_AMOUNT = "penalAmount";
|
||||
public static final String PRINCIPAL_BANK_GL_ACCOUNT_ID = "principalBankGlAccountId";
|
||||
public static final String INTEREST_BANK_GL_ACCOUNT_ID = "interestBankGlAccountId";
|
||||
public static final String NEW_CERT_NO = "newCertNo";
|
||||
public static final String NEW_CERT_DATE = "newCertDate";
|
||||
public static final String NEW_INT_RATE = "newIntRate";
|
||||
public static final String NEW_MATURITY_DATE = "newMaturityDate";
|
||||
public static final String NEW_PAYOUT = "newPayout";
|
||||
public static final String NEW_CALC_TYPE = "newCalcType";
|
||||
public static final String MATURITY_AMOUNT = "maturityAmount";
|
||||
public static final String RENEWAL_AMOUNT = "renewalAmount";
|
||||
public static final String DIFF_BANK_GL_ACCOUNT_ID = "diffBankGlAccountId";
|
||||
|
||||
public static final String LINE_MATURITY = "MATURITY";
|
||||
public static final String LINE_TYPE = "lineType";
|
||||
public static final String INT_DATE = "intDate";
|
||||
/** Reminders show lines due within this many days (as for Govt securities). */
|
||||
public static final int DUE_WITHIN_DAYS = 2;
|
||||
public static final Set<String> INTEREST_PARAMS = Set.of(LINE_TYPE, INT_DATE, RECEIVED_AMOUNT, TDS, BANK_GL_ACCOUNT_ID, LOCALE,
|
||||
DATE_FORMAT);
|
||||
|
||||
public static final Set<String> MATURE_PARAMS = Set.of(RECEIVED_AMOUNT, TDS, PRINCIPAL_BANK_GL_ACCOUNT_ID, INTEREST_BANK_GL_ACCOUNT_ID,
|
||||
REMARKS, LOCALE, DATE_FORMAT);
|
||||
public static final Set<String> PREMATURE_PARAMS = Set.of(CLOSE_DATE, RECEIVED_AMOUNT, TDS, PENAL_AMOUNT, PRINCIPAL_BANK_GL_ACCOUNT_ID,
|
||||
INTEREST_BANK_GL_ACCOUNT_ID, REMARKS, LOCALE, DATE_FORMAT);
|
||||
public static final Set<String> RENEW_PARAMS = Set.of(NEW_CERT_NO, NEW_CERT_DATE, NEW_INT_RATE, NEW_MATURITY_DATE, NEW_PAYOUT,
|
||||
NEW_CALC_TYPE, MATURITY_AMOUNT, RENEWAL_AMOUNT, TDS, DIFF_BANK_GL_ACCOUNT_ID, REMARKS, LOCALE, DATE_FORMAT);
|
||||
|
||||
public static final Set<String> FD_PARAMS = Set.of(PARTY_ID, CERT_NO, CERT_DATE, DEAL_DATE, AMOUNT, INT_RATE, MATURITY_DATE, PAYOUT,
|
||||
CALC_TYPE, BANK_GL_ACCOUNT_ID, REMARKS, LOCALE, DATE_FORMAT);
|
||||
public static final Set<String> BASE_PARAMS = Set.of(AS_ON_DATE, TOTAL_DEPOSITS, REMARKS, LOCALE, DATE_FORMAT);
|
||||
}
|
||||
+186
@@ -0,0 +1,186 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.investment.api;
|
||||
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.ws.rs.Consumes;
|
||||
import jakarta.ws.rs.GET;
|
||||
import jakarta.ws.rs.POST;
|
||||
import jakarta.ws.rs.Path;
|
||||
import jakarta.ws.rs.PathParam;
|
||||
import jakarta.ws.rs.Produces;
|
||||
import jakarta.ws.rs.QueryParam;
|
||||
import jakarta.ws.rs.core.MediaType;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.List;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.fineract.commands.domain.CommandWrapper;
|
||||
import org.apache.fineract.commands.service.CommandWrapperBuilder;
|
||||
import org.apache.fineract.commands.service.PortfolioCommandSourceWritePlatformService;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
|
||||
import org.apache.fineract.portfolio.investment.data.InvestmentFdClosureData;
|
||||
import org.apache.fineract.portfolio.investment.data.InvestmentFdData;
|
||||
import org.apache.fineract.portfolio.investment.data.InvestmentFdDueData;
|
||||
import org.apache.fineract.portfolio.investment.data.InvestmentFdScheduleData;
|
||||
import org.apache.fineract.portfolio.investment.service.InvestmentFdClosureService;
|
||||
import org.apache.fineract.portfolio.investment.service.InvestmentFdInterestService;
|
||||
import org.apache.fineract.portfolio.investment.service.InvestmentFdService;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Path("/v1/investment/fds")
|
||||
@Component
|
||||
@Tag(name = "Investment FD", description = "Fixed deposits the bank places with other banks.")
|
||||
@RequiredArgsConstructor
|
||||
public class InvestmentFdApiResource {
|
||||
|
||||
private static final DateTimeFormatter DATE = DateTimeFormatter.ofPattern("dd-MM-yyyy");
|
||||
|
||||
private final PlatformSecurityContext context;
|
||||
private final InvestmentFdService fdService;
|
||||
private final InvestmentFdClosureService closureService;
|
||||
private final InvestmentFdInterestService interestService;
|
||||
private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService;
|
||||
|
||||
@GET
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "All FDs placed with other banks")
|
||||
public List<InvestmentFdData> retrieveAll() {
|
||||
this.context.authenticatedUser().validateHasReadPermission(InvestmentFdApiConstants.ENTITY);
|
||||
return this.fdService.retrieveAll();
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("quote")
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "Preview an FD: interest, maturity amount and the RBI limits", description = "Dates dd-MM-yyyy")
|
||||
public InvestmentFdData quote(@QueryParam("partyId") final Long partyId, @QueryParam("amount") final BigDecimal amount,
|
||||
@QueryParam("intRate") final BigDecimal intRate, @QueryParam("dealDate") final String dealDate,
|
||||
@QueryParam("maturityDate") final String maturityDate, @QueryParam("payout") final String payout,
|
||||
@QueryParam("calcType") final String calcType) {
|
||||
this.context.authenticatedUser().validateHasReadPermission(InvestmentFdApiConstants.ENTITY);
|
||||
return this.fdService.quote(partyId, amount, intRate, date(dealDate), date(maturityDate), payout, calcType);
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("due")
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "FD interest vouchers and maturities due", description = "Due now or within withinDays (default 2)")
|
||||
public List<InvestmentFdDueData> due(@QueryParam("withinDays") final Integer withinDays) {
|
||||
this.context.authenticatedUser().validateHasReadPermission(InvestmentFdApiConstants.ENTITY);
|
||||
return this.interestService.due(withinDays == null ? InvestmentFdApiConstants.DUE_WITHIN_DAYS : withinDays);
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("{fdId}/interest-schedule")
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "Interest lines of an FD", description = "31 Mar / 30 Sep provisions, payouts and maturity")
|
||||
public InvestmentFdScheduleData schedule(@PathParam("fdId") final Long fdId) {
|
||||
this.context.authenticatedUser().validateHasReadPermission(InvestmentFdApiConstants.ENTITY);
|
||||
return this.interestService.schedule(fdId);
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("{fdId}/interest")
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "Post the next interest line of an FD", description = "lineType PROVISION | PAYOUT, intDate; payout: receivedAmount, tds")
|
||||
public CommandProcessingResult postInterest(@PathParam("fdId") final Long fdId, @Parameter(hidden = true) final String apiRequestBodyAsJson) {
|
||||
return this.commandsSourceWritePlatformService
|
||||
.logCommandSource(new CommandWrapperBuilder().postInvestmentFdInterest(fdId).withJson(apiRequestBodyAsJson).build());
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("{fdId}")
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "One FD")
|
||||
public InvestmentFdData retrieveOne(@PathParam("fdId") @Parameter(description = "fdId") final Long fdId) {
|
||||
this.context.authenticatedUser().validateHasReadPermission(InvestmentFdApiConstants.ENTITY);
|
||||
return this.fdService.retrieveOne(fdId);
|
||||
}
|
||||
|
||||
@POST
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "Place an FD", description = "Dr the bank's FD GL / Cr our bank GL, dated the deal date; refused beyond the RBI limits.")
|
||||
public CommandProcessingResult create(@Parameter(hidden = true) final String apiRequestBodyAsJson) {
|
||||
final CommandWrapper request = new CommandWrapperBuilder().createInvestmentFd().withJson(apiRequestBodyAsJson).build();
|
||||
return this.commandsSourceWritePlatformService.logCommandSource(request);
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("{fdId}/closure-quote")
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "Preview a maturity, premature closure or renewal", description = "type MATURITY | PREMATURE | RENEWAL")
|
||||
public InvestmentFdClosureData closureQuote(@PathParam("fdId") final Long fdId, @QueryParam("type") final String type,
|
||||
@QueryParam("closeDate") final String closeDate, @QueryParam("receivedAmount") final BigDecimal receivedAmount,
|
||||
@QueryParam("tds") final BigDecimal tds, @QueryParam("renewalAmount") final BigDecimal renewalAmount) {
|
||||
this.context.authenticatedUser().validateHasReadPermission(InvestmentFdApiConstants.ENTITY);
|
||||
return this.closureService.quote(fdId, type, date(closeDate), receivedAmount, tds, renewalAmount);
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("{fdId}/mature")
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "Close an FD at maturity", description = "Dated the maturity date; amount as credited by the bank, optional TDS.")
|
||||
public CommandProcessingResult mature(@PathParam("fdId") final Long fdId, @Parameter(hidden = true) final String apiRequestBodyAsJson) {
|
||||
return this.commandsSourceWritePlatformService
|
||||
.logCommandSource(new CommandWrapperBuilder().matureInvestmentFd(fdId).withJson(apiRequestBodyAsJson).build());
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("{fdId}/premature")
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "Close an FD before maturity", description = "Not calculated: the amount received (and any penal interest) is entered.")
|
||||
public CommandProcessingResult premature(@PathParam("fdId") final Long fdId, @Parameter(hidden = true) final String apiRequestBodyAsJson) {
|
||||
return this.commandsSourceWritePlatformService
|
||||
.logCommandSource(new CommandWrapperBuilder().prematureInvestmentFd(fdId).withJson(apiRequestBodyAsJson).build());
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("{fdId}/renew")
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "Renew an FD at maturity", description = "New certificate, rate and maturity; money added or taken out goes through a bank GL.")
|
||||
public CommandProcessingResult renew(@PathParam("fdId") final Long fdId, @Parameter(hidden = true) final String apiRequestBodyAsJson) {
|
||||
return this.commandsSourceWritePlatformService
|
||||
.logCommandSource(new CommandWrapperBuilder().renewInvestmentFd(fdId).withJson(apiRequestBodyAsJson).build());
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("deposit-base")
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "Record total deposits as on a 31 March", description = "The base of the RBI limits for the following year.")
|
||||
public CommandProcessingResult setDepositBase(@Parameter(hidden = true) final String apiRequestBodyAsJson) {
|
||||
final CommandWrapper request = new CommandWrapperBuilder().setInvestmentDepositBase().withJson(apiRequestBodyAsJson).build();
|
||||
return this.commandsSourceWritePlatformService.logCommandSource(request);
|
||||
}
|
||||
|
||||
private static LocalDate date(final String value) {
|
||||
return StringUtils.isBlank(value) ? null : LocalDate.parse(value.trim(), DATE);
|
||||
}
|
||||
}
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.investment.api;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/** Constants for posting an interest schedule line (coupon or closing provision) of a holding to GL. */
|
||||
public final class InvestmentInterestApiConstants {
|
||||
|
||||
private InvestmentInterestApiConstants() {}
|
||||
|
||||
public static final String ENTITY = "INVESTMENTINTEREST";
|
||||
public static final String RESOURCE = "investmentinterest";
|
||||
|
||||
public static final String LINE_TYPE = "lineType";
|
||||
public static final String INT_DATE = "intDate";
|
||||
/** Bank GL the coupon is received into; defaults to the purchase's paying bank. */
|
||||
public static final String BANK_GL_ACCOUNT_ID = "bankGlAccountId";
|
||||
public static final String LOCALE = "locale";
|
||||
public static final String DATE_FORMAT = "dateFormat";
|
||||
|
||||
public static final Set<String> PARAMS = Set.of(LINE_TYPE, INT_DATE, BANK_GL_ACCOUNT_ID, LOCALE, DATE_FORMAT);
|
||||
|
||||
/** A line shows as a task this many days before its date (Jitendra Sir). */
|
||||
public static final int DUE_WITHIN_DAYS = 2;
|
||||
|
||||
public static final String INTEREST_RECEIVABLE_GL_CODE = "100603";
|
||||
public static final String INTEREST_INCOME_GOVT_SECURITIES_GL_CODE = "400401";
|
||||
public static final String INTEREST_INCOME_BONDS_GL_CODE = "400402";
|
||||
|
||||
/** Income GL for a holding's interest: SLR govt securities, or bonds / debentures for the rest. */
|
||||
public static String interestIncomeGlCode(final String bondType) {
|
||||
return "GOVT_SECURITY".equals(bondType) ? INTEREST_INCOME_GOVT_SECURITIES_GL_CODE : INTEREST_INCOME_BONDS_GL_CODE;
|
||||
}
|
||||
}
|
||||
+65
@@ -0,0 +1,65 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.investment.api;
|
||||
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.ws.rs.GET;
|
||||
import jakarta.ws.rs.Path;
|
||||
import jakarta.ws.rs.Produces;
|
||||
import jakarta.ws.rs.QueryParam;
|
||||
import jakarta.ws.rs.core.MediaType;
|
||||
import java.util.List;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
|
||||
import org.apache.fineract.portfolio.investment.data.InvestmentInterestDueData;
|
||||
import org.apache.fineract.portfolio.investment.data.InvestmentInterestPostingData;
|
||||
import org.apache.fineract.portfolio.investment.service.InvestmentPurchaseReadPlatformService;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Path("/v1/investment/interest-postings")
|
||||
@Component
|
||||
@Tag(name = "Investment Interest Posting", description = "Interest lines of holdings that are due to be posted with a voucher.")
|
||||
@RequiredArgsConstructor
|
||||
public class InvestmentInterestApiResource {
|
||||
|
||||
private final PlatformSecurityContext context;
|
||||
private final InvestmentPurchaseReadPlatformService readPlatformService;
|
||||
|
||||
@GET
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "Posted interest lines", description = "Coupons and closing provisions already posted to GL with their "
|
||||
+ "voucher, newest first.")
|
||||
public List<InvestmentInterestPostingData> retrieveAll() {
|
||||
this.context.authenticatedUser().validateHasReadPermission(InvestmentInterestApiConstants.ENTITY);
|
||||
return this.readPlatformService.retrieveInterestPostings();
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("due")
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "Interest lines due", description = "For every active holding, its next unposted interest line when that "
|
||||
+ "line's date is past or within withinDays days (default 2).")
|
||||
public List<InvestmentInterestDueData> due(@QueryParam("withinDays") @Parameter(description = "withinDays") final Integer withinDays) {
|
||||
this.context.authenticatedUser().validateHasReadPermission(InvestmentInterestApiConstants.ENTITY);
|
||||
return this.readPlatformService
|
||||
.retrieveDueInterest(withinDays == null ? InvestmentInterestApiConstants.DUE_WITHIN_DAYS : withinDays);
|
||||
}
|
||||
}
|
||||
+99
@@ -0,0 +1,99 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.investment.api;
|
||||
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.ws.rs.Consumes;
|
||||
import jakarta.ws.rs.DELETE;
|
||||
import jakarta.ws.rs.GET;
|
||||
import jakarta.ws.rs.POST;
|
||||
import jakarta.ws.rs.PUT;
|
||||
import jakarta.ws.rs.Path;
|
||||
import jakarta.ws.rs.PathParam;
|
||||
import jakarta.ws.rs.Produces;
|
||||
import jakarta.ws.rs.core.MediaType;
|
||||
import java.util.List;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.fineract.commands.domain.CommandWrapper;
|
||||
import org.apache.fineract.commands.service.CommandWrapperBuilder;
|
||||
import org.apache.fineract.commands.service.PortfolioCommandSourceWritePlatformService;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
|
||||
import org.apache.fineract.portfolio.investment.data.InvestmentPartyData;
|
||||
import org.apache.fineract.portfolio.investment.service.InvestmentMasterReadPlatformService;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Path("/v1/investment/parties")
|
||||
@Component
|
||||
@Tag(name = "Investment Bank / Party Master", description = "Counterparties an investment is bought from or placed with.")
|
||||
@RequiredArgsConstructor
|
||||
public class InvestmentPartyApiResource {
|
||||
|
||||
private final PlatformSecurityContext context;
|
||||
private final InvestmentMasterReadPlatformService readPlatformService;
|
||||
private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService;
|
||||
|
||||
@GET
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "List investment parties")
|
||||
public List<InvestmentPartyData> retrieveAll() {
|
||||
this.context.authenticatedUser().validateHasReadPermission(InvestmentApiConstants.ENTITY_PARTY);
|
||||
return this.readPlatformService.retrieveAllParties();
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("{partyId}")
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "Retrieve an investment party")
|
||||
public InvestmentPartyData retrieveOne(@PathParam("partyId") @Parameter(description = "partyId") final Long partyId) {
|
||||
this.context.authenticatedUser().validateHasReadPermission(InvestmentApiConstants.ENTITY_PARTY);
|
||||
return this.readPlatformService.retrieveParty(partyId);
|
||||
}
|
||||
|
||||
@POST
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "Create an investment party")
|
||||
public CommandProcessingResult create(@Parameter(hidden = true) final String apiRequestBodyAsJson) {
|
||||
final CommandWrapper request = new CommandWrapperBuilder().createInvestmentParty().withJson(apiRequestBodyAsJson).build();
|
||||
return this.commandsSourceWritePlatformService.logCommandSource(request);
|
||||
}
|
||||
|
||||
@PUT
|
||||
@Path("{partyId}")
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "Update an investment party")
|
||||
public CommandProcessingResult update(@PathParam("partyId") @Parameter(description = "partyId") final Long partyId,
|
||||
@Parameter(hidden = true) final String apiRequestBodyAsJson) {
|
||||
final CommandWrapper request = new CommandWrapperBuilder().updateInvestmentParty(partyId).withJson(apiRequestBodyAsJson).build();
|
||||
return this.commandsSourceWritePlatformService.logCommandSource(request);
|
||||
}
|
||||
|
||||
@DELETE
|
||||
@Path("{partyId}")
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "Drop an investment party")
|
||||
public CommandProcessingResult delete(@PathParam("partyId") @Parameter(description = "partyId") final Long partyId) {
|
||||
final CommandWrapper request = new CommandWrapperBuilder().deleteInvestmentParty(partyId).build();
|
||||
return this.commandsSourceWritePlatformService.logCommandSource(request);
|
||||
}
|
||||
}
|
||||
+74
@@ -0,0 +1,74 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.investment.api;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/** Constants for the Investment Purchase Entry (a deal that creates a holding). */
|
||||
public final class InvestmentPurchaseApiConstants {
|
||||
|
||||
private InvestmentPurchaseApiConstants() {}
|
||||
|
||||
public static final String ENTITY = "INVESTMENTPURCHASE";
|
||||
public static final String RESOURCE = "investmentpurchase";
|
||||
|
||||
public static final Set<String> SETTLEMENT_TYPES = Set.of("T+0", "T+1", "T+2");
|
||||
public static final Set<String> CATEGORIES = Set.of("HTM (Held To Maturity)", "AFS (Available For Sale)", "HFT (Held For Trading)");
|
||||
|
||||
/** Saved deal; nothing posted to GL until it is settled. */
|
||||
public static final String STATUS_PENDING_SETTLEMENT = "PENDING_SETTLEMENT";
|
||||
/** Settled: posted to GL on the settlement date, now a holding. */
|
||||
public static final String STATUS_ACTIVE = "ACTIVE";
|
||||
/** Every unit sold before maturity. */
|
||||
public static final String STATUS_SOLD = "SOLD";
|
||||
/** Repaid at maturity. */
|
||||
public static final String STATUS_REDEEMED = "REDEEMED";
|
||||
|
||||
/** GLs the settlement entry debits besides the script's investment GL. */
|
||||
public static final String BROKEN_PERIOD_INTEREST_GL_CODE = "500606";
|
||||
public static final String STAMP_DUTY_GL_CODE = "500601";
|
||||
public static final String BROKERAGE_GL_CODE = "500602";
|
||||
public static final String OTHER_CHARGES_GL_CODE = "500603";
|
||||
|
||||
public static final String BOND_TYPE = "bondType";
|
||||
public static final String SCRIPT_ID = "scriptId";
|
||||
public static final String PARTY_ID = "partyId";
|
||||
public static final String DEAL_DATE = "dealDate";
|
||||
public static final String DEAL_TIME = "dealTime";
|
||||
public static final String SETTLEMENT_TYPE = "settlementType";
|
||||
public static final String FACE_VALUE = "faceValue";
|
||||
public static final String PURCHASE_RATE = "purchaseRate";
|
||||
public static final String CONSIDER_AMT = "considerAmt";
|
||||
public static final String NAV = "nav";
|
||||
public static final String STAMP_DUTY = "stampDuty";
|
||||
public static final String BROKERAGE = "brokerage";
|
||||
public static final String OTHER_CHARGES = "otherCharges";
|
||||
public static final String CATEGORY = "category";
|
||||
public static final String LAST_INT_DATE = "lastIntDate";
|
||||
public static final String CR_GL_ACCOUNT_ID = "crGlAccountId";
|
||||
public static final String SUB_TYPE = "subType";
|
||||
/** Optional legacy fields (trade no, authorities, lien, delivery mode, …) kept as a JSON object. */
|
||||
public static final String OTHER_DETAILS = "otherDetails";
|
||||
public static final String LOCALE = "locale";
|
||||
public static final String DATE_FORMAT = "dateFormat";
|
||||
|
||||
public static final Set<String> PARAMS = Set.of(BOND_TYPE, SCRIPT_ID, PARTY_ID, DEAL_DATE, DEAL_TIME, SETTLEMENT_TYPE, FACE_VALUE,
|
||||
PURCHASE_RATE, CONSIDER_AMT, NAV, STAMP_DUTY, BROKERAGE, OTHER_CHARGES, CATEGORY, LAST_INT_DATE, CR_GL_ACCOUNT_ID, SUB_TYPE,
|
||||
OTHER_DETAILS, LOCALE, DATE_FORMAT);
|
||||
}
|
||||
+184
@@ -0,0 +1,184 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.investment.api;
|
||||
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.ws.rs.Consumes;
|
||||
import jakarta.ws.rs.DELETE;
|
||||
import jakarta.ws.rs.GET;
|
||||
import jakarta.ws.rs.POST;
|
||||
import jakarta.ws.rs.Path;
|
||||
import jakarta.ws.rs.PathParam;
|
||||
import jakarta.ws.rs.Produces;
|
||||
import jakarta.ws.rs.QueryParam;
|
||||
import jakarta.ws.rs.core.MediaType;
|
||||
import java.time.LocalDate;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.List;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.fineract.commands.domain.CommandWrapper;
|
||||
import org.apache.fineract.commands.service.CommandWrapperBuilder;
|
||||
import org.apache.fineract.commands.service.PortfolioCommandSourceWritePlatformService;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
|
||||
import org.apache.fineract.portfolio.investment.data.InvestmentInterestScheduleData;
|
||||
import org.apache.fineract.portfolio.investment.data.InvestmentPurchaseData;
|
||||
import org.apache.fineract.portfolio.investment.data.InvestmentSaleData;
|
||||
import org.apache.fineract.portfolio.investment.service.InvestmentPurchaseReadPlatformService;
|
||||
import org.apache.fineract.portfolio.investment.service.InvestmentSaleReadPlatformService;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Path("/v1/investment/purchases")
|
||||
@Component
|
||||
@Tag(name = "Investment Purchase Entry", description = "Investment deals (Govt Security, Bond, Mutual Fund, …) — each becomes a holding.")
|
||||
@RequiredArgsConstructor
|
||||
public class InvestmentPurchaseApiResource {
|
||||
|
||||
private final PlatformSecurityContext context;
|
||||
private final InvestmentPurchaseReadPlatformService readPlatformService;
|
||||
private final InvestmentSaleReadPlatformService saleReadPlatformService;
|
||||
private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService;
|
||||
|
||||
@GET
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "List investment purchases (holdings)")
|
||||
public List<InvestmentPurchaseData> retrieveAll() {
|
||||
this.context.authenticatedUser().validateHasReadPermission(InvestmentPurchaseApiConstants.ENTITY);
|
||||
return this.readPlatformService.retrieveAll();
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("{purchaseId}")
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "Retrieve an investment purchase")
|
||||
public InvestmentPurchaseData retrieveOne(@PathParam("purchaseId") @Parameter(description = "purchaseId") final Long purchaseId) {
|
||||
this.context.authenticatedUser().validateHasReadPermission(InvestmentPurchaseApiConstants.ENTITY);
|
||||
return this.readPlatformService.retrieveOne(purchaseId);
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("{purchaseId}/interest-schedule")
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "Interest schedule of a holding", description = "Half-yearly coupons and 31 Mar / 30 Sep closing provisions "
|
||||
+ "from purchase to maturity. Calculated only — nothing is posted. asOnDate (dd-MM-yyyy) defaults to the business date.")
|
||||
public InvestmentInterestScheduleData retrieveInterestSchedule(
|
||||
@PathParam("purchaseId") @Parameter(description = "purchaseId") final Long purchaseId,
|
||||
@QueryParam("asOnDate") @Parameter(description = "dd-MM-yyyy") final String asOnDate) {
|
||||
this.context.authenticatedUser().validateHasReadPermission(InvestmentPurchaseApiConstants.ENTITY);
|
||||
final LocalDate asOn = StringUtils.isBlank(asOnDate) ? null
|
||||
: LocalDate.parse(asOnDate.trim(), DateTimeFormatter.ofPattern("dd-MM-yyyy"));
|
||||
return this.readPlatformService.retrieveInterestSchedule(purchaseId, asOn);
|
||||
}
|
||||
|
||||
@POST
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "Save a purchase entry")
|
||||
public CommandProcessingResult create(@Parameter(hidden = true) final String apiRequestBodyAsJson) {
|
||||
final CommandWrapper request = new CommandWrapperBuilder().createInvestmentPurchase().withJson(apiRequestBodyAsJson).build();
|
||||
return this.commandsSourceWritePlatformService.logCommandSource(request);
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("{purchaseId}/settle")
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "Settle a purchase", description = "On or after the settlement date: posts the purchase to GL dated the "
|
||||
+ "settlement date and makes the deal an active holding.")
|
||||
public CommandProcessingResult settle(@PathParam("purchaseId") @Parameter(description = "purchaseId") final Long purchaseId,
|
||||
@Parameter(hidden = true) final String apiRequestBodyAsJson) {
|
||||
final CommandWrapper request = new CommandWrapperBuilder().settleInvestmentPurchase(purchaseId)
|
||||
.withJson(StringUtils.isBlank(apiRequestBodyAsJson) ? "{}" : apiRequestBodyAsJson).build();
|
||||
return this.commandsSourceWritePlatformService.logCommandSource(request);
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("{purchaseId}/interest-postings")
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "Post an interest line", description = "Posts the next interest schedule line (coupon or closing provision) "
|
||||
+ "of a holding to GL with a JV voucher, on or after its date and in date order.")
|
||||
public CommandProcessingResult postInterest(@PathParam("purchaseId") @Parameter(description = "purchaseId") final Long purchaseId,
|
||||
@Parameter(hidden = true) final String apiRequestBodyAsJson) {
|
||||
final CommandWrapper request = new CommandWrapperBuilder().postInvestmentInterest(purchaseId).withJson(apiRequestBodyAsJson)
|
||||
.build();
|
||||
return this.commandsSourceWritePlatformService.logCommandSource(request);
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("{purchaseId}/redemption-quote")
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "What the redemption at maturity will post", description = "Face value back against the book value still "
|
||||
+ "held; refused before maturity or while a sale is pending.")
|
||||
public InvestmentSaleData redemptionQuote(@PathParam("purchaseId") @Parameter(description = "purchaseId") final Long purchaseId) {
|
||||
this.context.authenticatedUser().validateHasReadPermission(InvestmentSaleApiConstants.ENTITY);
|
||||
return this.saleReadPlatformService.quoteRedemption(purchaseId);
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("{purchaseId}/redeem")
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "Redeem at maturity", description = "On or after maturity, once every coupon is posted: posts the face value "
|
||||
+ "received, dated the maturity date, and closes the holding.")
|
||||
public CommandProcessingResult redeem(@PathParam("purchaseId") @Parameter(description = "purchaseId") final Long purchaseId,
|
||||
@Parameter(hidden = true) final String apiRequestBodyAsJson) {
|
||||
final CommandWrapper request = new CommandWrapperBuilder().redeemInvestmentPurchase(purchaseId)
|
||||
.withJson(StringUtils.isBlank(apiRequestBodyAsJson) ? "{}" : apiRequestBodyAsJson).build();
|
||||
return this.commandsSourceWritePlatformService.logCommandSource(request);
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("{purchaseId}/write-off")
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "Write off part of the book value", description = "Dr Loss on Investments / Cr Investment for the amount "
|
||||
+ "given; the units stay held at the reduced book value.")
|
||||
public CommandProcessingResult writeOff(@PathParam("purchaseId") @Parameter(description = "purchaseId") final Long purchaseId,
|
||||
@Parameter(hidden = true) final String apiRequestBodyAsJson) {
|
||||
final CommandWrapper request = new CommandWrapperBuilder().writeOffInvestmentPurchase(purchaseId).withJson(apiRequestBodyAsJson)
|
||||
.build();
|
||||
return this.commandsSourceWritePlatformService.logCommandSource(request);
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("{purchaseId}/transfer")
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "GOI Category Transfer", description = "Moves the holding to another security (scriptId) of the same coupon "
|
||||
+ "rate, maturity and unit value; the book value moves between GLs only when the two scripts use different GLs.")
|
||||
public CommandProcessingResult transfer(@PathParam("purchaseId") @Parameter(description = "purchaseId") final Long purchaseId,
|
||||
@Parameter(hidden = true) final String apiRequestBodyAsJson) {
|
||||
final CommandWrapper request = new CommandWrapperBuilder().transferInvestmentSecurity(purchaseId).withJson(apiRequestBodyAsJson)
|
||||
.build();
|
||||
return this.commandsSourceWritePlatformService.logCommandSource(request);
|
||||
}
|
||||
|
||||
@DELETE
|
||||
@Path("{purchaseId}")
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "Drop a purchase entry")
|
||||
public CommandProcessingResult delete(@PathParam("purchaseId") @Parameter(description = "purchaseId") final Long purchaseId) {
|
||||
final CommandWrapper request = new CommandWrapperBuilder().deleteInvestmentPurchase(purchaseId).build();
|
||||
return this.commandsSourceWritePlatformService.logCommandSource(request);
|
||||
}
|
||||
}
|
||||
+70
@@ -0,0 +1,70 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.investment.api;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/** Sale (before maturity) and redemption (at maturity) of investment holdings. */
|
||||
public final class InvestmentSaleApiConstants {
|
||||
|
||||
private InvestmentSaleApiConstants() {}
|
||||
|
||||
public static final String ENTITY = "INVESTMENTSALE";
|
||||
public static final String RESOURCE = "investmentsale";
|
||||
|
||||
public static final String TYPE_SELL = "SELL";
|
||||
public static final String TYPE_REDEEM = "REDEEM";
|
||||
/** Part of the book value written off as a loss; the units stay held. */
|
||||
public static final String TYPE_WRITEOFF = "WRITEOFF";
|
||||
/** A year-end amortisation of premium (31 March); recorded like a sale so the book value comes down. */
|
||||
public static final String TYPE_AMORTISE = "AMORTISE";
|
||||
|
||||
public static final String STATUS_PENDING_SETTLEMENT = "PENDING_SETTLEMENT";
|
||||
public static final String STATUS_SETTLED = "SETTLED";
|
||||
|
||||
/** Profit on sale / redemption (income) and loss on sale / redemption (expense). */
|
||||
public static final String PROFIT_ON_SALE_GL_CODE = "400405";
|
||||
public static final String LOSS_ON_SALE_GL_CODE = "500605";
|
||||
/** Premium still on the books at maturity is written off to the amortisation expense. */
|
||||
public static final String PREMIUM_AMORTISATION_GL_CODE = "500604";
|
||||
/** Premium paid over face value, held here until amortised (Jitendra Sir). */
|
||||
public static final String PREMIUM_ON_INVESTMENTS_GL_CODE = "100707";
|
||||
|
||||
public static final String PURCHASE_ID = "purchaseId";
|
||||
public static final String PARTY_ID = "partyId";
|
||||
public static final String DEAL_DATE = "dealDate";
|
||||
public static final String DEAL_TIME = "dealTime";
|
||||
public static final String SETTLEMENT_TYPE = "settlementType";
|
||||
public static final String UNITS = "units";
|
||||
public static final String SALE_RATE = "saleRate";
|
||||
public static final String BROKERAGE = "brokerage";
|
||||
public static final String BANK_GL_ACCOUNT_ID = "bankGlAccountId";
|
||||
public static final String SANCTION_AUTHORITY = "sanctionAuthority";
|
||||
public static final String REMARKS = "remarks";
|
||||
public static final String AMOUNT = "amount";
|
||||
public static final String FY_END = "fyEnd";
|
||||
public static final String LOCALE = "locale";
|
||||
public static final String DATE_FORMAT = "dateFormat";
|
||||
|
||||
public static final Set<String> SELL_PARAMS = Set.of(PURCHASE_ID, PARTY_ID, DEAL_DATE, DEAL_TIME, SETTLEMENT_TYPE, UNITS, SALE_RATE,
|
||||
BROKERAGE, BANK_GL_ACCOUNT_ID, SANCTION_AUTHORITY, REMARKS, LOCALE, DATE_FORMAT);
|
||||
public static final Set<String> REDEEM_PARAMS = Set.of(BANK_GL_ACCOUNT_ID, REMARKS, LOCALE, DATE_FORMAT);
|
||||
public static final Set<String> WRITEOFF_PARAMS = Set.of(AMOUNT, REMARKS, LOCALE, DATE_FORMAT);
|
||||
public static final Set<String> AMORTISE_PARAMS = Set.of(PURCHASE_ID, FY_END, LOCALE, DATE_FORMAT);
|
||||
}
|
||||
+116
@@ -0,0 +1,116 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.investment.api;
|
||||
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.ws.rs.Consumes;
|
||||
import jakarta.ws.rs.DELETE;
|
||||
import jakarta.ws.rs.GET;
|
||||
import jakarta.ws.rs.POST;
|
||||
import jakarta.ws.rs.Path;
|
||||
import jakarta.ws.rs.PathParam;
|
||||
import jakarta.ws.rs.Produces;
|
||||
import jakarta.ws.rs.QueryParam;
|
||||
import jakarta.ws.rs.core.MediaType;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.List;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.fineract.commands.domain.CommandWrapper;
|
||||
import org.apache.fineract.commands.service.CommandWrapperBuilder;
|
||||
import org.apache.fineract.commands.service.PortfolioCommandSourceWritePlatformService;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
|
||||
import org.apache.fineract.portfolio.investment.data.InvestmentSaleData;
|
||||
import org.apache.fineract.portfolio.investment.service.InvestmentSaleReadPlatformService;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Path("/v1/investment/sales")
|
||||
@Component
|
||||
@Tag(name = "Investment Sale", description = "Sale of holdings before maturity (all or part of the units), settled on the settlement date.")
|
||||
@RequiredArgsConstructor
|
||||
public class InvestmentSaleApiResource {
|
||||
|
||||
private static final DateTimeFormatter DATE = DateTimeFormatter.ofPattern("dd-MM-yyyy");
|
||||
|
||||
private final PlatformSecurityContext context;
|
||||
private final InvestmentSaleReadPlatformService readPlatformService;
|
||||
private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService;
|
||||
|
||||
@GET
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "List sales and redemptions, newest first")
|
||||
public List<InvestmentSaleData> retrieveAll() {
|
||||
this.context.authenticatedUser().validateHasReadPermission(InvestmentSaleApiConstants.ENTITY);
|
||||
return this.readPlatformService.retrieveAll();
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("quote")
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "Work out a sale before saving it", description = "Price, interest from the buyer, cost, profit or loss "
|
||||
+ "and the net amount for the units, rate and deal date given (dd-MM-yyyy).")
|
||||
public InvestmentSaleData quote(@QueryParam("purchaseId") @Parameter(description = "purchaseId") final Long purchaseId,
|
||||
@QueryParam("dealDate") @Parameter(description = "dd-MM-yyyy") final String dealDate,
|
||||
@QueryParam("settlementType") @Parameter(description = "T+0 / T+1 / T+2") final String settlementType,
|
||||
@QueryParam("units") @Parameter(description = "units") final BigDecimal units,
|
||||
@QueryParam("saleRate") @Parameter(description = "saleRate") final BigDecimal saleRate,
|
||||
@QueryParam("brokerage") @Parameter(description = "brokerage") final BigDecimal brokerage,
|
||||
@QueryParam("bankGlAccountId") @Parameter(description = "bankGlAccountId") final Long bankGlAccountId) {
|
||||
this.context.authenticatedUser().validateHasReadPermission(InvestmentSaleApiConstants.ENTITY);
|
||||
final LocalDate deal = LocalDate.parse(dealDate.trim(), DATE);
|
||||
return this.readPlatformService.quoteSale(purchaseId, deal, StringUtils.defaultIfBlank(settlementType, "T+0"), units, saleRate,
|
||||
brokerage, bankGlAccountId);
|
||||
}
|
||||
|
||||
@POST
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "Save a sale", description = "Saved pending; nothing reaches the GL until it is settled.")
|
||||
public CommandProcessingResult create(@Parameter(hidden = true) final String apiRequestBodyAsJson) {
|
||||
final CommandWrapper request = new CommandWrapperBuilder().createInvestmentSale().withJson(apiRequestBodyAsJson).build();
|
||||
return this.commandsSourceWritePlatformService.logCommandSource(request);
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("{saleId}/settle")
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "Settle a sale", description = "On or after the settlement date: posts the sale to GL dated the settlement "
|
||||
+ "date and reduces the units held.")
|
||||
public CommandProcessingResult settle(@PathParam("saleId") @Parameter(description = "saleId") final Long saleId,
|
||||
@Parameter(hidden = true) final String apiRequestBodyAsJson) {
|
||||
final CommandWrapper request = new CommandWrapperBuilder().settleInvestmentSale(saleId)
|
||||
.withJson(StringUtils.isBlank(apiRequestBodyAsJson) ? "{}" : apiRequestBodyAsJson).build();
|
||||
return this.commandsSourceWritePlatformService.logCommandSource(request);
|
||||
}
|
||||
|
||||
@DELETE
|
||||
@Path("{saleId}")
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "Drop a pending sale")
|
||||
public CommandProcessingResult delete(@PathParam("saleId") @Parameter(description = "saleId") final Long saleId) {
|
||||
final CommandWrapper request = new CommandWrapperBuilder().deleteInvestmentSale(saleId).build();
|
||||
return this.commandsSourceWritePlatformService.logCommandSource(request);
|
||||
}
|
||||
}
|
||||
+89
@@ -0,0 +1,89 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.investment.api;
|
||||
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.ws.rs.Consumes;
|
||||
import jakarta.ws.rs.GET;
|
||||
import jakarta.ws.rs.POST;
|
||||
import jakarta.ws.rs.PUT;
|
||||
import jakarta.ws.rs.Path;
|
||||
import jakarta.ws.rs.PathParam;
|
||||
import jakarta.ws.rs.Produces;
|
||||
import jakarta.ws.rs.core.MediaType;
|
||||
import java.util.List;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.fineract.commands.domain.CommandWrapper;
|
||||
import org.apache.fineract.commands.service.CommandWrapperBuilder;
|
||||
import org.apache.fineract.commands.service.PortfolioCommandSourceWritePlatformService;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
|
||||
import org.apache.fineract.portfolio.investment.data.InvestmentScriptData;
|
||||
import org.apache.fineract.portfolio.investment.service.InvestmentMasterReadPlatformService;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Path("/v1/investment/scripts")
|
||||
@Component
|
||||
@Tag(name = "Investment Script Master", description = "Securities / instruments the bank invests in (Govt Security, Bond, Mutual Fund, …).")
|
||||
@RequiredArgsConstructor
|
||||
public class InvestmentScriptApiResource {
|
||||
|
||||
private final PlatformSecurityContext context;
|
||||
private final InvestmentMasterReadPlatformService readPlatformService;
|
||||
private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService;
|
||||
|
||||
@GET
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "List investment scripts")
|
||||
public List<InvestmentScriptData> retrieveAll() {
|
||||
this.context.authenticatedUser().validateHasReadPermission(InvestmentApiConstants.ENTITY_SCRIPT);
|
||||
return this.readPlatformService.retrieveAllScripts();
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("{scriptId}")
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "Retrieve an investment script")
|
||||
public InvestmentScriptData retrieveOne(@PathParam("scriptId") @Parameter(description = "scriptId") final Long scriptId) {
|
||||
this.context.authenticatedUser().validateHasReadPermission(InvestmentApiConstants.ENTITY_SCRIPT);
|
||||
return this.readPlatformService.retrieveScript(scriptId);
|
||||
}
|
||||
|
||||
@POST
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "Create an investment script")
|
||||
public CommandProcessingResult create(@Parameter(hidden = true) final String apiRequestBodyAsJson) {
|
||||
final CommandWrapper request = new CommandWrapperBuilder().createInvestmentScript().withJson(apiRequestBodyAsJson).build();
|
||||
return this.commandsSourceWritePlatformService.logCommandSource(request);
|
||||
}
|
||||
|
||||
@PUT
|
||||
@Path("{scriptId}")
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "Update an investment script")
|
||||
public CommandProcessingResult update(@PathParam("scriptId") @Parameter(description = "scriptId") final Long scriptId,
|
||||
@Parameter(hidden = true) final String apiRequestBodyAsJson) {
|
||||
final CommandWrapper request = new CommandWrapperBuilder().updateInvestmentScript(scriptId).withJson(apiRequestBodyAsJson).build();
|
||||
return this.commandsSourceWritePlatformService.logCommandSource(request);
|
||||
}
|
||||
}
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.investment.api;
|
||||
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.ws.rs.GET;
|
||||
import jakarta.ws.rs.Path;
|
||||
import jakarta.ws.rs.Produces;
|
||||
import jakarta.ws.rs.core.MediaType;
|
||||
import java.util.List;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
|
||||
import org.apache.fineract.portfolio.investment.data.InvestmentSecurityTransferData;
|
||||
import org.apache.fineract.portfolio.investment.service.InvestmentSecurityTransferService;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Path("/v1/investment/transfers")
|
||||
@Component
|
||||
@Tag(name = "Investment Security Transfer", description = "GOI Category Transfer: a holding moved to another security of the same terms.")
|
||||
@RequiredArgsConstructor
|
||||
public class InvestmentTransferApiResource {
|
||||
|
||||
private final PlatformSecurityContext context;
|
||||
private final InvestmentSecurityTransferService transferService;
|
||||
|
||||
@GET
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "List security transfers, newest first")
|
||||
public List<InvestmentSecurityTransferData> retrieveAll() {
|
||||
this.context.authenticatedUser().validateHasReadPermission("INVESTMENTTRANSFER");
|
||||
return this.transferService.retrieveAll();
|
||||
}
|
||||
}
|
||||
+64
@@ -0,0 +1,64 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.investment.data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
|
||||
/** Premium amortisation schedule of an HTM holding: one line per 31 March to maturity, then the rest at maturity. */
|
||||
@Getter
|
||||
@Builder
|
||||
public class InvestmentAmortisationData {
|
||||
|
||||
private final Long purchaseId;
|
||||
private final String dealNo;
|
||||
private final String certNo;
|
||||
private final String scriptCode;
|
||||
private final String scriptDesc;
|
||||
private final String category;
|
||||
private final String settlementDate;
|
||||
private final String maturityDate;
|
||||
private final BigDecimal faceValue;
|
||||
private final BigDecimal premium;
|
||||
private final BigDecimal unamortisedPremium;
|
||||
private final BigDecimal bookValue;
|
||||
private final String premiumGlCode;
|
||||
private final List<Row> rows;
|
||||
|
||||
@Getter
|
||||
@Builder
|
||||
public static class Row {
|
||||
|
||||
/** YEAR (31 March) or MATURITY (the remainder, written off on redemption). */
|
||||
private final String kind;
|
||||
private final String fyEnd;
|
||||
private final String fromDate;
|
||||
private final String toDate;
|
||||
private final long days;
|
||||
private final BigDecimal amount;
|
||||
private final BigDecimal premiumAfter;
|
||||
private final boolean posted;
|
||||
private final String voucherNo;
|
||||
private final String glTransactionId;
|
||||
/** Next line to post and its 31 March has come. */
|
||||
private final boolean postable;
|
||||
}
|
||||
}
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.investment.data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
|
||||
/** A holding's premium amortisation due for a 31 March (Amortize Yearly Process). */
|
||||
@Getter
|
||||
@Builder
|
||||
public class InvestmentAmortisationDueData {
|
||||
|
||||
private final Long purchaseId;
|
||||
private final String dealNo;
|
||||
private final String certNo;
|
||||
private final String scriptCode;
|
||||
private final String scriptDesc;
|
||||
private final String fyEnd;
|
||||
private final String fromDate;
|
||||
private final long days;
|
||||
private final BigDecimal unamortisedPremium;
|
||||
private final BigDecimal amount;
|
||||
}
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.investment.data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
import org.apache.fineract.portfolio.investment.domain.InvestmentDailyRate;
|
||||
|
||||
@Getter
|
||||
@Builder
|
||||
public class InvestmentDailyRateData {
|
||||
|
||||
private static final DateTimeFormatter DATE_FORMAT = DateTimeFormatter.ofPattern("dd-MM-yyyy");
|
||||
|
||||
private final Long id;
|
||||
private final Long scriptId;
|
||||
private final String scriptCode;
|
||||
private final String scriptDesc;
|
||||
private final String bondType;
|
||||
private final String rateDate;
|
||||
private final BigDecimal openRate;
|
||||
private final BigDecimal highRate;
|
||||
private final BigDecimal lowRate;
|
||||
private final BigDecimal closeRate;
|
||||
|
||||
public static InvestmentDailyRateData from(final InvestmentDailyRate r) {
|
||||
return InvestmentDailyRateData.builder().id(r.getId()).scriptId(r.getScript().getId()).scriptCode(r.getScript().getScriptCode())
|
||||
.scriptDesc(r.getScript().getScriptDesc()).bondType(r.getScript().getBondType())
|
||||
.rateDate(r.getRateDate().format(DATE_FORMAT)).openRate(r.getOpenRate()).highRate(r.getHighRate()).lowRate(r.getLowRate())
|
||||
.closeRate(r.getCloseRate()).build();
|
||||
}
|
||||
}
|
||||
+65
@@ -0,0 +1,65 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.investment.data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
|
||||
/** What closing an FD (maturity, premature or renewal) works out to, and the voucher it posts. */
|
||||
@Getter
|
||||
@Builder
|
||||
public class InvestmentFdClosureData {
|
||||
|
||||
private final Long fdId;
|
||||
private final String dealNo;
|
||||
private final String type;
|
||||
private final String closeDate;
|
||||
private final BigDecimal principal;
|
||||
/** Interest the system works out for the period, to compare with the bank's figure. */
|
||||
private final BigDecimal expectedInterest;
|
||||
/** Interest already booked by provisions and not yet received. */
|
||||
private final BigDecimal provisioned;
|
||||
/** Money credited to our bank by the other bank (net of TDS). */
|
||||
private final BigDecimal receivedAmount;
|
||||
private final BigDecimal tds;
|
||||
private final BigDecimal penalAmount;
|
||||
/** Gross interest as per the bank: received + TDS − principal. */
|
||||
private final BigDecimal interest;
|
||||
/** Interest income of this entry: interest less what was provisioned (negative = reversed from income). */
|
||||
private final BigDecimal income;
|
||||
/** Bank's interest less the system's. */
|
||||
private final BigDecimal difference;
|
||||
private final BigDecimal renewalAmount;
|
||||
/** Renewal: money added (positive) or taken out (negative) against the maturity amount. */
|
||||
private final BigDecimal topUp;
|
||||
private final List<Line> lines;
|
||||
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public static class Line {
|
||||
|
||||
private final String side;
|
||||
private final String glCode;
|
||||
private final String glName;
|
||||
private final BigDecimal amount;
|
||||
}
|
||||
}
|
||||
+103
@@ -0,0 +1,103 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.investment.data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
import org.apache.fineract.accounting.glaccount.domain.GLAccount;
|
||||
import org.apache.fineract.portfolio.investment.domain.InvestmentFd;
|
||||
|
||||
/** An FD placed with another bank; also the shape of a purchase preview (with the limit check). */
|
||||
@Getter
|
||||
@Builder
|
||||
public class InvestmentFdData {
|
||||
|
||||
private static final DateTimeFormatter DATE = DateTimeFormatter.ofPattern("dd-MM-yyyy");
|
||||
|
||||
private final Long id;
|
||||
private final String dealNo;
|
||||
private final Long partyId;
|
||||
private final String partyCode;
|
||||
private final String partyName;
|
||||
private final String certNo;
|
||||
private final String certDate;
|
||||
private final String dealDate;
|
||||
private final BigDecimal principal;
|
||||
private final BigDecimal intRate;
|
||||
private final String maturityDate;
|
||||
private final long days;
|
||||
private final String payout;
|
||||
private final String calcType;
|
||||
private final BigDecimal expectedInterest;
|
||||
private final BigDecimal expectedMaturityAmount;
|
||||
/** Interest paid each half year / year for a payout FD. */
|
||||
private final BigDecimal periodInterest;
|
||||
private final String fdGlCode;
|
||||
private final String fdGlName;
|
||||
private final String interestGlCode;
|
||||
private final String interestGlName;
|
||||
private final Long bankGlAccountId;
|
||||
private final String bankGlCode;
|
||||
private final String bankGlName;
|
||||
private final String status;
|
||||
private final String remarks;
|
||||
private final String glTransactionId;
|
||||
private final String voucherNo;
|
||||
private final String renewedFromDealNo;
|
||||
private final String closedOnDate;
|
||||
private final String closureType;
|
||||
private final BigDecimal closureAmount;
|
||||
private final BigDecimal closureInterest;
|
||||
private final BigDecimal closureTds;
|
||||
private final BigDecimal closurePenal;
|
||||
private final String closureVoucherNo;
|
||||
private final String closureGlTransactionId;
|
||||
private final InvestmentFdLimitData limit;
|
||||
|
||||
public static InvestmentFdData from(final InvestmentFd f, final BigDecimal periodInterest) {
|
||||
return builder().id(f.getId()).dealNo(f.getDealNo()).partyId(f.getParty().getId()).partyCode(f.getParty().getPartyCode())
|
||||
.partyName(f.getParty().getPartyName()).certNo(f.getCertNo()).certDate(format(f.getCertDate()))
|
||||
.dealDate(format(f.getDealDate())).principal(f.getPrincipal()).intRate(f.getIntRate())
|
||||
.maturityDate(format(f.getMaturityDate())).days(ChronoUnit.DAYS.between(f.getDealDate(), f.getMaturityDate()))
|
||||
.payout(f.getPayout()).calcType(f.getCalcType()).expectedInterest(f.getExpectedInterest())
|
||||
.expectedMaturityAmount(f.getExpectedMaturityAmount()).periodInterest(periodInterest).fdGlCode(code(f.getFdGlAccount()))
|
||||
.fdGlName(name(f.getFdGlAccount())).interestGlCode(code(f.getInterestGlAccount())).interestGlName(name(f.getInterestGlAccount()))
|
||||
.bankGlAccountId(f.getBankGlAccount().getId()).bankGlCode(code(f.getBankGlAccount())).bankGlName(name(f.getBankGlAccount()))
|
||||
.status(f.getStatus()).remarks(f.getRemarks()).glTransactionId(f.getGlTransactionId()).voucherNo(f.getVoucherNo())
|
||||
.renewedFromDealNo(f.getRenewedFrom() == null ? null : f.getRenewedFrom().getDealNo()).closedOnDate(format(f.getClosedOnDate()))
|
||||
.closureType(f.getClosureType()).closureAmount(f.getClosureAmount()).closureInterest(f.getClosureInterest())
|
||||
.closureTds(f.getClosureTds()).closurePenal(f.getClosurePenal()).closureVoucherNo(f.getClosureVoucherNo()).closureGlTransactionId(f.getClosureGlTransactionId()).build();
|
||||
}
|
||||
|
||||
private static String code(final GLAccount gl) {
|
||||
return gl == null ? null : gl.getGlCode();
|
||||
}
|
||||
|
||||
private static String name(final GLAccount gl) {
|
||||
return gl == null ? null : gl.getName();
|
||||
}
|
||||
|
||||
private static String format(final LocalDate date) {
|
||||
return date == null ? null : date.format(DATE);
|
||||
}
|
||||
}
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.investment.data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
|
||||
/** The next interest voucher (or the maturity) of an FD that is due, or due within the reminder window. */
|
||||
@Getter
|
||||
@Builder
|
||||
public class InvestmentFdDueData {
|
||||
|
||||
private final Long fdId;
|
||||
private final String dealNo;
|
||||
private final String partyName;
|
||||
private final String certNo;
|
||||
/** PROVISION, PAYOUT or MATURITY. */
|
||||
private final String lineType;
|
||||
private final String intDate;
|
||||
private final BigDecimal amount;
|
||||
private final boolean postable;
|
||||
private final boolean overdue;
|
||||
}
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.investment.data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
|
||||
/** Where a new FD stands against the RBI limits on deposits with other banks. */
|
||||
@Getter
|
||||
@Builder
|
||||
public class InvestmentFdLimitData {
|
||||
|
||||
/** The 31 March the limits are based on. */
|
||||
private final String asOnDate;
|
||||
/** Total deposits as on that date, as entered; empty until someone enters it. */
|
||||
private final BigDecimal totalDeposits;
|
||||
/** The same figure worked out from the deposit GLs, as a suggestion. */
|
||||
private final BigDecimal glTotalDeposits;
|
||||
private final BigDecimal grossLimit;
|
||||
private final BigDecimal fdOutstanding;
|
||||
private final BigDecimal bankBalances;
|
||||
private final BigDecimal grossAfter;
|
||||
private final BigDecimal bankLimit;
|
||||
private final String partyName;
|
||||
private final BigDecimal bankAfter;
|
||||
private final BigDecimal newAmount;
|
||||
private final boolean withinLimits;
|
||||
private final String message;
|
||||
}
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.investment.data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
|
||||
/** Interest lines of an FD: 31 Mar / 30 Sep provisions, payouts of a half-yearly / yearly FD, and maturity. */
|
||||
@Getter
|
||||
@Builder
|
||||
public class InvestmentFdScheduleData {
|
||||
|
||||
private final InvestmentFdData fd;
|
||||
/** Provisioned interest not yet received (interest receivable of this FD). */
|
||||
private final BigDecimal provisioned;
|
||||
private final List<Row> rows;
|
||||
|
||||
@Getter
|
||||
@Builder
|
||||
public static class Row {
|
||||
|
||||
/** PROVISION, PAYOUT or MATURITY (shown for information; closed on the Maturity screen). */
|
||||
private final String lineType;
|
||||
private final String intDate;
|
||||
private final String fromDate;
|
||||
private final String toDate;
|
||||
private final long days;
|
||||
private final BigDecimal grossInterest;
|
||||
/** Payout: the part already provisioned, cleared from interest receivable. */
|
||||
private final BigDecimal alreadyBooked;
|
||||
private final BigDecimal tds;
|
||||
/** Provision: interest booked. Payout: interest income of the payout (gross less already booked). */
|
||||
private final BigDecimal amount;
|
||||
private final boolean posted;
|
||||
private final String voucherNo;
|
||||
private final String glTransactionId;
|
||||
/** The next line to post, and its date has come. */
|
||||
private final boolean postable;
|
||||
}
|
||||
}
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.investment.data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
|
||||
/** The next unposted interest line of a holding that is due, or due within the reminder window — a task to post. */
|
||||
@Getter
|
||||
@Builder
|
||||
public class InvestmentInterestDueData {
|
||||
|
||||
private final Long purchaseId;
|
||||
private final String dealNo;
|
||||
private final String certNo;
|
||||
private final String scriptCode;
|
||||
private final String scriptDesc;
|
||||
private final String lineType;
|
||||
private final String intDate;
|
||||
private final BigDecimal grossInterest;
|
||||
private final BigDecimal amount;
|
||||
/** The date has come: it can be posted now. */
|
||||
private final boolean postable;
|
||||
/** The date has passed without posting. */
|
||||
private final boolean overdue;
|
||||
}
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.investment.data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
|
||||
/** An interest line (coupon or closing provision) already posted to GL with its voucher — for the transaction log. */
|
||||
@Getter
|
||||
@Builder
|
||||
public class InvestmentInterestPostingData {
|
||||
|
||||
private final Long id;
|
||||
private final Long purchaseId;
|
||||
private final String dealNo;
|
||||
private final String certNo;
|
||||
private final String bondType;
|
||||
private final String scriptDesc;
|
||||
private final String lineType;
|
||||
private final String intDate;
|
||||
private final String fromDate;
|
||||
private final String toDate;
|
||||
private final BigDecimal grossInterest;
|
||||
private final BigDecimal alreadyBooked;
|
||||
private final BigDecimal amount;
|
||||
/** Debit side: the bank for a coupon, interest receivable for a provision. */
|
||||
private final String drGlCode;
|
||||
private final String drGlName;
|
||||
private final String incomeGlCode;
|
||||
private final String incomeGlName;
|
||||
private final String glTransactionId;
|
||||
private final String voucherNo;
|
||||
private final String postedOnDate;
|
||||
}
|
||||
+91
@@ -0,0 +1,91 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.investment.data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* Interest schedule of one holding: every half-yearly coupon and every 31 March / 30 September closing provision from
|
||||
* purchase to maturity. Calculated, not yet posted.
|
||||
*/
|
||||
@Getter
|
||||
@Builder
|
||||
public class InvestmentInterestScheduleData {
|
||||
|
||||
private final Long purchaseId;
|
||||
private final String dealNo;
|
||||
private final String certNo;
|
||||
private final String scriptCode;
|
||||
private final String scriptDesc;
|
||||
private final BigDecimal faceValue;
|
||||
private final BigDecimal unitValue;
|
||||
private final BigDecimal balanceUnits;
|
||||
private final BigDecimal purchaseRate;
|
||||
private final BigDecimal bookValue;
|
||||
private final BigDecimal intRate;
|
||||
private final String dealDate;
|
||||
private final String lastIntDate;
|
||||
private final String maturityDate;
|
||||
/**
|
||||
* Broken-period interest paid to the seller at purchase — booked as an expense (GL 500606), not in this schedule.
|
||||
*/
|
||||
private final BigDecimal purchasedAccruedInterest;
|
||||
private final BigDecimal couponPerHalfYear;
|
||||
private final String asOnDate;
|
||||
private final String interestGlCode;
|
||||
private final String interestGlName;
|
||||
/** Bank the coupon is received into by default: the purchase's paying bank. */
|
||||
private final Long bankGlAccountId;
|
||||
private final String bankGlCode;
|
||||
private final String bankGlName;
|
||||
private final String status;
|
||||
private final List<Row> rows;
|
||||
|
||||
@Getter
|
||||
@Builder
|
||||
public static class Row {
|
||||
|
||||
/** COUPON (cash received) or PROVISION (closing accrual). */
|
||||
private final String type;
|
||||
private final String intDate;
|
||||
private final String fromDate;
|
||||
private final String toDate;
|
||||
private final int days;
|
||||
/** Coupon received, or interest earned since the last coupon up to the closing. */
|
||||
private final BigDecimal grossInterest;
|
||||
/** Interest Receivable balance before this line (purchased accrued interest / earlier provision). */
|
||||
private final BigDecimal alreadyBooked;
|
||||
/** Provision to book now, or the part of the coupon that is new income. */
|
||||
private final BigDecimal amount;
|
||||
/** True when the date is on or before the as-on date. */
|
||||
private final boolean due;
|
||||
/** Posted to GL: its JV voucher and GL transaction. */
|
||||
private final boolean posted;
|
||||
private final String voucherNo;
|
||||
private final String glTransactionId;
|
||||
private final String postedOnDate;
|
||||
/** The next line to post and its date has come. */
|
||||
private final boolean postable;
|
||||
/** Unposted and its date is within the reminder window (or past). */
|
||||
private final boolean dueSoon;
|
||||
}
|
||||
}
|
||||
+68
@@ -0,0 +1,68 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.investment.data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
import org.apache.fineract.portfolio.investment.domain.InvestmentParty;
|
||||
|
||||
@Getter
|
||||
@Builder
|
||||
public class InvestmentPartyData {
|
||||
|
||||
private final Long id;
|
||||
private final String partyCode;
|
||||
private final String bondType;
|
||||
private final String partyName;
|
||||
private final String address1;
|
||||
private final String address2;
|
||||
private final String address3;
|
||||
private final String city;
|
||||
private final String pincode;
|
||||
private final String phone;
|
||||
private final String fax;
|
||||
private final String bankType;
|
||||
private final Long glAccountId;
|
||||
private final String glCode;
|
||||
private final String glName;
|
||||
private final Long interestGlAccountId;
|
||||
private final String interestGlCode;
|
||||
private final String interestGlName;
|
||||
private final String rbiCode;
|
||||
private final BigDecimal limit;
|
||||
private final String leiNo;
|
||||
private final String cpBankName;
|
||||
private final String cpBranchName;
|
||||
private final String cpAccountNo;
|
||||
private final String cpIfsc;
|
||||
private final boolean active;
|
||||
|
||||
public static InvestmentPartyData from(final InvestmentParty p) {
|
||||
return InvestmentPartyData.builder().id(p.getId()).partyCode(p.getPartyCode()).bondType(p.getBondType())
|
||||
.partyName(p.getPartyName()).address1(p.getAddress1()).address2(p.getAddress2()).address3(p.getAddress3())
|
||||
.city(p.getCity()).pincode(p.getPincode()).phone(p.getPhone()).fax(p.getFax()).bankType(p.getBankType())
|
||||
.glAccountId(p.getGlAccount().getId()).glCode(p.getGlAccount().getGlCode()).glName(p.getGlAccount().getName())
|
||||
.interestGlAccountId(p.getInterestGlAccount() == null ? null : p.getInterestGlAccount().getId())
|
||||
.interestGlCode(p.getInterestGlAccount() == null ? null : p.getInterestGlAccount().getGlCode())
|
||||
.interestGlName(p.getInterestGlAccount() == null ? null : p.getInterestGlAccount().getName())
|
||||
.rbiCode(p.getRbiCode()).limit(p.getExposureLimit()).leiNo(p.getLeiNo()).cpBankName(p.getCpBankName())
|
||||
.cpBranchName(p.getCpBranchName()).cpAccountNo(p.getCpAccountNo()).cpIfsc(p.getCpIfsc()).active(p.isActive()).build();
|
||||
}
|
||||
}
|
||||
+100
@@ -0,0 +1,100 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.investment.data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
import org.apache.fineract.portfolio.investment.domain.InvestmentPurchase;
|
||||
|
||||
@Getter
|
||||
@Builder
|
||||
public class InvestmentPurchaseData {
|
||||
|
||||
private static final DateTimeFormatter DATE_FORMAT = DateTimeFormatter.ofPattern("dd-MM-yyyy");
|
||||
|
||||
private final Long id;
|
||||
private final String dealNo;
|
||||
private final String certNo;
|
||||
private final String bondType;
|
||||
private final Long scriptId;
|
||||
private final String scriptCode;
|
||||
private final String scriptDesc;
|
||||
private final String isin;
|
||||
private final Long partyId;
|
||||
private final String partyCode;
|
||||
private final String partyName;
|
||||
private final String dealDate;
|
||||
private final String dealTime;
|
||||
private final String settlementType;
|
||||
private final String settlementDate;
|
||||
private final BigDecimal faceValue;
|
||||
private final BigDecimal purchaseRate;
|
||||
private final BigDecimal units;
|
||||
private final BigDecimal balanceUnits;
|
||||
private final BigDecimal brokerage;
|
||||
private final BigDecimal stampDuty;
|
||||
private final BigDecimal otherCharges;
|
||||
private final BigDecimal bookValue;
|
||||
private final BigDecimal premiumDiscount;
|
||||
private final BigDecimal intRate;
|
||||
private final String issueDate;
|
||||
private final String maturityDate;
|
||||
private final String lastIntDate;
|
||||
private final Integer accruedDays;
|
||||
private final BigDecimal accruedInterest;
|
||||
private final BigDecimal settlementAmount;
|
||||
private final BigDecimal ytm;
|
||||
private final String category;
|
||||
private final String subType;
|
||||
private final Long investmentGlAccountId;
|
||||
private final String investmentGlCode;
|
||||
private final String investmentGlName;
|
||||
private final Long crGlAccountId;
|
||||
private final String crGlCode;
|
||||
private final String crGlName;
|
||||
private final String otherDetails;
|
||||
private final String status;
|
||||
private final String glTransactionId;
|
||||
private final String settledOnDate;
|
||||
|
||||
public static InvestmentPurchaseData from(final InvestmentPurchase p) {
|
||||
return InvestmentPurchaseData.builder().id(p.getId()).dealNo(p.getDealNo()).certNo(p.getCertNo()).bondType(p.getBondType())
|
||||
.scriptId(p.getScript().getId()).scriptCode(p.getScript().getScriptCode()).scriptDesc(p.getScript().getScriptDesc())
|
||||
.isin(p.getScript().getIsin()).partyId(p.getParty().getId()).partyCode(p.getParty().getPartyCode())
|
||||
.partyName(p.getParty().getPartyName()).dealDate(format(p.getDealDate())).dealTime(p.getDealTime())
|
||||
.settlementType(p.getSettlementType()).settlementDate(format(p.getSettlementDate())).faceValue(p.getFaceValue())
|
||||
.purchaseRate(p.getPurchaseRate()).units(p.getUnits()).balanceUnits(p.getBalanceUnits()).brokerage(p.getBrokerage())
|
||||
.stampDuty(p.getStampDuty()).otherCharges(p.getOtherCharges()).bookValue(p.getBookValue())
|
||||
.premiumDiscount(p.getPremiumDiscount()).intRate(p.getIntRate()).issueDate(format(p.getIssueDate()))
|
||||
.maturityDate(format(p.getMaturityDate())).lastIntDate(format(p.getLastIntDate())).accruedDays(p.getAccruedDays())
|
||||
.accruedInterest(p.getAccruedInterest()).settlementAmount(p.getSettlementAmount()).ytm(p.getYtm()).category(p.getCategory())
|
||||
.subType(p.getSubType()).investmentGlAccountId(p.getInvestmentGlAccount().getId())
|
||||
.investmentGlCode(p.getInvestmentGlAccount().getGlCode()).investmentGlName(p.getInvestmentGlAccount().getName())
|
||||
.crGlAccountId(p.getCrGlAccount().getId()).crGlCode(p.getCrGlAccount().getGlCode()).crGlName(p.getCrGlAccount().getName())
|
||||
.otherDetails(p.getOtherDetails()).status(p.getStatus()).glTransactionId(p.getGlTransactionId())
|
||||
.settledOnDate(format(p.getSettledOnDate())).build();
|
||||
}
|
||||
|
||||
private static String format(final LocalDate date) {
|
||||
return date == null ? null : date.format(DATE_FORMAT);
|
||||
}
|
||||
}
|
||||
+102
@@ -0,0 +1,102 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.investment.data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
import org.apache.fineract.portfolio.investment.domain.InvestmentSale;
|
||||
|
||||
/** A sale or redemption of a holding — also the shape of a quote before it is saved. */
|
||||
@Getter
|
||||
@Builder
|
||||
public class InvestmentSaleData {
|
||||
|
||||
private static final DateTimeFormatter DATE_FORMAT = DateTimeFormatter.ofPattern("dd-MM-yyyy");
|
||||
|
||||
private final Long id;
|
||||
private final Long purchaseId;
|
||||
private final String dealNo;
|
||||
private final String certNo;
|
||||
private final String bondType;
|
||||
private final String scriptCode;
|
||||
private final String scriptDesc;
|
||||
private final String saleType;
|
||||
private final Long partyId;
|
||||
private final String partyName;
|
||||
private final String dealDate;
|
||||
private final String dealTime;
|
||||
private final String settlementType;
|
||||
private final String settlementDate;
|
||||
private final BigDecimal units;
|
||||
private final BigDecimal faceValue;
|
||||
private final BigDecimal saleRate;
|
||||
private final BigDecimal consideration;
|
||||
private final Integer accruedDays;
|
||||
private final BigDecimal accruedInterest;
|
||||
private final BigDecimal receivableCleared;
|
||||
/** Interest from the buyer not yet booked: accrued interest less the receivable cleared. */
|
||||
private final BigDecimal interestIncome;
|
||||
private final BigDecimal brokerage;
|
||||
private final BigDecimal bookValueSold;
|
||||
private final BigDecimal premiumCleared;
|
||||
private final BigDecimal profitLoss;
|
||||
private final BigDecimal netAmount;
|
||||
private final Long bankGlAccountId;
|
||||
private final String bankGlCode;
|
||||
private final String bankGlName;
|
||||
private final String investmentGlCode;
|
||||
/** GL the premium share is credited to: 100707, or the investment GL for deals settled before it was used. */
|
||||
private final String premiumGlCode;
|
||||
private final String sanctionAuthority;
|
||||
private final String remarks;
|
||||
private final String status;
|
||||
private final String glTransactionId;
|
||||
private final String voucherNo;
|
||||
private final String settledOnDate;
|
||||
|
||||
public static InvestmentSaleData from(final InvestmentSale s) {
|
||||
return builder().id(s.getId()).purchaseId(s.getPurchase().getId()).dealNo(s.getPurchase().getDealNo())
|
||||
.certNo(s.getPurchase().getCertNo()).bondType(s.getPurchase().getBondType())
|
||||
.scriptCode(s.getPurchase().getScript().getScriptCode()).scriptDesc(s.getPurchase().getScript().getScriptDesc())
|
||||
.saleType(s.getSaleType()).partyId(s.getParty() == null ? null : s.getParty().getId())
|
||||
.partyName(s.getParty() == null ? null : s.getParty().getPartyName()).dealDate(format(s.getDealDate()))
|
||||
.dealTime(s.getDealTime()).settlementType(s.getSettlementType()).settlementDate(format(s.getSettlementDate()))
|
||||
.units(s.getUnits()).faceValue(s.getFaceValue()).saleRate(s.getSaleRate()).consideration(s.getConsideration())
|
||||
.accruedDays(s.getAccruedDays()).accruedInterest(s.getAccruedInterest()).receivableCleared(s.getReceivableCleared())
|
||||
.interestIncome(zero(s.getAccruedInterest()).subtract(zero(s.getReceivableCleared()))).brokerage(s.getBrokerage())
|
||||
.bookValueSold(s.getBookValueSold()).premiumCleared(s.getPremiumCleared()).profitLoss(s.getProfitLoss()).netAmount(s.getNetAmount())
|
||||
.bankGlAccountId(s.getBankGlAccount().getId()).bankGlCode(s.getBankGlAccount().getGlCode())
|
||||
.bankGlName(s.getBankGlAccount().getName()).investmentGlCode(s.getPurchase().getInvestmentGlAccount().getGlCode())
|
||||
.premiumGlCode(s.getPurchase().getPremiumGlAccount() != null ? s.getPurchase().getPremiumGlAccount().getGlCode()
|
||||
: s.getPurchase().getInvestmentGlAccount().getGlCode())
|
||||
.sanctionAuthority(s.getSanctionAuthority()).remarks(s.getRemarks()).status(s.getStatus())
|
||||
.glTransactionId(s.getGlTransactionId()).voucherNo(s.getVoucherNo()).settledOnDate(format(s.getSettledOnDate())).build();
|
||||
}
|
||||
|
||||
private static BigDecimal zero(final BigDecimal v) {
|
||||
return v == null ? BigDecimal.ZERO : v;
|
||||
}
|
||||
|
||||
private static String format(final LocalDate date) {
|
||||
return date == null ? null : date.format(DATE_FORMAT);
|
||||
}
|
||||
}
|
||||
+72
@@ -0,0 +1,72 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.investment.data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
import org.apache.fineract.portfolio.investment.domain.InvestmentScript;
|
||||
|
||||
@Getter
|
||||
@Builder
|
||||
public class InvestmentScriptData {
|
||||
|
||||
/** Dates go out as DD-MM-YYYY, the format the Investment screens use throughout. */
|
||||
private static final DateTimeFormatter DATE_FORMAT = DateTimeFormatter.ofPattern("dd-MM-yyyy");
|
||||
|
||||
private final Long id;
|
||||
private final String scriptCode;
|
||||
private final String bondType;
|
||||
private final String slrType;
|
||||
private final String scriptDesc;
|
||||
private final Long glAccountId;
|
||||
private final String glCode;
|
||||
private final String glName;
|
||||
private final String isin;
|
||||
private final BigDecimal faceValuePerUnit;
|
||||
private final BigDecimal intRate;
|
||||
private final String issueDate;
|
||||
private final String maturityDate;
|
||||
private final String companyName;
|
||||
private final String address1;
|
||||
private final String address2;
|
||||
private final String address3;
|
||||
private final String city;
|
||||
private final String pincode;
|
||||
private final String phone;
|
||||
private final String fax;
|
||||
private final String corpCode;
|
||||
private final boolean active;
|
||||
|
||||
public static InvestmentScriptData from(final InvestmentScript s) {
|
||||
return InvestmentScriptData.builder().id(s.getId()).scriptCode(s.getScriptCode()).bondType(s.getBondType())
|
||||
.slrType(s.getSlrType()).scriptDesc(s.getScriptDesc()).glAccountId(s.getGlAccount().getId())
|
||||
.glCode(s.getGlAccount().getGlCode()).glName(s.getGlAccount().getName()).isin(s.getIsin())
|
||||
.faceValuePerUnit(s.getFaceValuePerUnit()).intRate(s.getIntRate()).issueDate(format(s.getIssueDate()))
|
||||
.maturityDate(format(s.getMaturityDate())).companyName(s.getCompanyName()).address1(s.getAddress1())
|
||||
.address2(s.getAddress2()).address3(s.getAddress3()).city(s.getCity()).pincode(s.getPincode()).phone(s.getPhone())
|
||||
.fax(s.getFax()).corpCode(s.getCorpCode()).active(s.isActive()).build();
|
||||
}
|
||||
|
||||
private static String format(final LocalDate date) {
|
||||
return date == null ? null : date.format(DATE_FORMAT);
|
||||
}
|
||||
}
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.investment.data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
import org.apache.fineract.portfolio.investment.domain.InvestmentSecurityTransfer;
|
||||
|
||||
/** A GOI Category Transfer of a holding, for the transaction log. */
|
||||
@Getter
|
||||
@Builder
|
||||
public class InvestmentSecurityTransferData {
|
||||
|
||||
private static final DateTimeFormatter DATE_FORMAT = DateTimeFormatter.ofPattern("dd-MM-yyyy");
|
||||
|
||||
private final Long id;
|
||||
private final Long purchaseId;
|
||||
private final String dealNo;
|
||||
private final String certNo;
|
||||
private final String bondType;
|
||||
private final String fromScriptCode;
|
||||
private final String fromScriptDesc;
|
||||
private final String toScriptCode;
|
||||
private final String toScriptDesc;
|
||||
private final String fromGlCode;
|
||||
private final String toGlCode;
|
||||
private final String toGlName;
|
||||
private final String transferDate;
|
||||
private final BigDecimal units;
|
||||
private final BigDecimal bookValue;
|
||||
private final String remarks;
|
||||
private final String glTransactionId;
|
||||
private final String voucherNo;
|
||||
|
||||
public static InvestmentSecurityTransferData from(final InvestmentSecurityTransfer t) {
|
||||
return builder().id(t.getId()).purchaseId(t.getPurchase().getId()).dealNo(t.getPurchase().getDealNo())
|
||||
.certNo(t.getPurchase().getCertNo()).bondType(t.getPurchase().getBondType()).fromScriptCode(t.getFromScript().getScriptCode())
|
||||
.fromScriptDesc(t.getFromScript().getScriptDesc()).toScriptCode(t.getToScript().getScriptCode())
|
||||
.toScriptDesc(t.getToScript().getScriptDesc()).fromGlCode(t.getFromGlAccount().getGlCode())
|
||||
.toGlCode(t.getToGlAccount().getGlCode()).toGlName(t.getToGlAccount().getName())
|
||||
.transferDate(t.getTransferDate().format(DATE_FORMAT)).units(t.getUnits()).bookValue(t.getBookValue())
|
||||
.remarks(t.getRemarks()).glTransactionId(t.getGlTransactionId()).voucherNo(t.getVoucherNo()).build();
|
||||
}
|
||||
}
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.investment.domain;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.JoinColumn;
|
||||
import jakarta.persistence.ManyToOne;
|
||||
import jakarta.persistence.Table;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import org.apache.fineract.infrastructure.core.domain.AbstractAuditableWithUTCDateTimeCustom;
|
||||
|
||||
/**
|
||||
* Daily Script Master: the market rate of a script for one day, as the bank's existing CBS keeps it. Purchase Entry
|
||||
* takes its rate from here (Jitendra Sir). Close rate is mandatory; open, high and low are optional.
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "m_investment_daily_rate")
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
public class InvestmentDailyRate extends AbstractAuditableWithUTCDateTimeCustom<Long> {
|
||||
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "script_id", nullable = false)
|
||||
private InvestmentScript script;
|
||||
|
||||
@Column(name = "rate_date", nullable = false)
|
||||
private LocalDate rateDate;
|
||||
|
||||
@Column(name = "open_rate", scale = 6, precision = 19)
|
||||
private BigDecimal openRate;
|
||||
|
||||
@Column(name = "high_rate", scale = 6, precision = 19)
|
||||
private BigDecimal highRate;
|
||||
|
||||
@Column(name = "low_rate", scale = 6, precision = 19)
|
||||
private BigDecimal lowRate;
|
||||
|
||||
@Column(name = "close_rate", scale = 6, precision = 19, nullable = false)
|
||||
private BigDecimal closeRate;
|
||||
}
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.investment.domain;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
public interface InvestmentDailyRateRepository extends JpaRepository<InvestmentDailyRate, Long> {
|
||||
|
||||
boolean existsByScriptIdAndRateDate(Long scriptId, LocalDate rateDate);
|
||||
|
||||
/** The newest rate of a script on or before a date. */
|
||||
Optional<InvestmentDailyRate> findFirstByScriptIdAndRateDateLessThanEqualOrderByRateDateDesc(Long scriptId, LocalDate onDate);
|
||||
|
||||
List<InvestmentDailyRate> findByScriptIdOrderByRateDateDesc(Long scriptId);
|
||||
}
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.investment.domain;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.Table;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import org.apache.fineract.infrastructure.core.domain.AbstractAuditableWithUTCDateTimeCustom;
|
||||
|
||||
/** The bank's total deposits as on a 31 March: the base for the RBI limits on deposits placed with other banks. */
|
||||
@Entity
|
||||
@Table(name = "m_investment_deposit_base")
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
public class InvestmentDepositBase extends AbstractAuditableWithUTCDateTimeCustom<Long> {
|
||||
|
||||
@Column(name = "as_on_date", nullable = false, unique = true)
|
||||
private LocalDate asOnDate;
|
||||
|
||||
@Column(name = "total_deposits", scale = 6, precision = 19, nullable = false)
|
||||
private BigDecimal totalDeposits;
|
||||
|
||||
@Column(name = "remarks", length = 500)
|
||||
private String remarks;
|
||||
}
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.investment.domain;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.Optional;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
public interface InvestmentDepositBaseRepository extends JpaRepository<InvestmentDepositBase, Long> {
|
||||
|
||||
Optional<InvestmentDepositBase> findByAsOnDate(LocalDate asOnDate);
|
||||
}
|
||||
+135
@@ -0,0 +1,135 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.investment.domain;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.JoinColumn;
|
||||
import jakarta.persistence.ManyToOne;
|
||||
import jakarta.persistence.Table;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import org.apache.fineract.accounting.glaccount.domain.GLAccount;
|
||||
import org.apache.fineract.infrastructure.core.domain.AbstractAuditableWithUTCDateTimeCustom;
|
||||
|
||||
/** A fixed deposit the bank has placed with another bank, from placement to maturity, premature closure or renewal. */
|
||||
@Entity
|
||||
@Table(name = "m_investment_fd")
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
public class InvestmentFd extends AbstractAuditableWithUTCDateTimeCustom<Long> {
|
||||
|
||||
@Column(name = "deal_no", nullable = false, length = 20, unique = true)
|
||||
private String dealNo;
|
||||
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "party_id", nullable = false)
|
||||
private InvestmentParty party;
|
||||
|
||||
/** The bank's FD GL (from the party), debited when the money is placed. */
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "fd_gl_account_id", nullable = false)
|
||||
private GLAccount fdGlAccount;
|
||||
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "interest_gl_account_id", nullable = false)
|
||||
private GLAccount interestGlAccount;
|
||||
|
||||
/** Certificate (receipt) number issued by the other bank. */
|
||||
@Column(name = "cert_no", nullable = false, length = 50)
|
||||
private String certNo;
|
||||
|
||||
@Column(name = "cert_date")
|
||||
private LocalDate certDate;
|
||||
|
||||
@Column(name = "deal_date", nullable = false)
|
||||
private LocalDate dealDate;
|
||||
|
||||
@Column(name = "principal", scale = 6, precision = 19, nullable = false)
|
||||
private BigDecimal principal;
|
||||
|
||||
@Column(name = "int_rate", scale = 6, precision = 19, nullable = false)
|
||||
private BigDecimal intRate;
|
||||
|
||||
@Column(name = "maturity_date", nullable = false)
|
||||
private LocalDate maturityDate;
|
||||
|
||||
@Column(name = "payout", nullable = false, length = 20)
|
||||
private String payout;
|
||||
|
||||
@Column(name = "calc_type", nullable = false, length = 20)
|
||||
private String calcType;
|
||||
|
||||
@Column(name = "expected_interest", scale = 6, precision = 19)
|
||||
private BigDecimal expectedInterest;
|
||||
|
||||
@Column(name = "expected_maturity_amount", scale = 6, precision = 19)
|
||||
private BigDecimal expectedMaturityAmount;
|
||||
|
||||
/** Our bank account the money was paid from. */
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "bank_gl_account_id", nullable = false)
|
||||
private GLAccount bankGlAccount;
|
||||
|
||||
/** The FD this one renews, if any. */
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "renewed_from_id")
|
||||
private InvestmentFd renewedFrom;
|
||||
|
||||
@Column(name = "status", nullable = false, length = 20)
|
||||
private String status;
|
||||
|
||||
@Column(name = "remarks", length = 500)
|
||||
private String remarks;
|
||||
|
||||
@Column(name = "gl_transaction_id", length = 50)
|
||||
private String glTransactionId;
|
||||
|
||||
@Column(name = "voucher_no", length = 30)
|
||||
private String voucherNo;
|
||||
|
||||
@Column(name = "closed_on_date")
|
||||
private LocalDate closedOnDate;
|
||||
|
||||
/** MATURITY, PREMATURE or RENEWAL. */
|
||||
@Column(name = "closure_type", length = 20)
|
||||
private String closureType;
|
||||
|
||||
@Column(name = "closure_amount", scale = 6, precision = 19)
|
||||
private BigDecimal closureAmount;
|
||||
|
||||
@Column(name = "closure_interest", scale = 6, precision = 19)
|
||||
private BigDecimal closureInterest;
|
||||
|
||||
@Column(name = "closure_tds", scale = 6, precision = 19)
|
||||
private BigDecimal closureTds;
|
||||
|
||||
@Column(name = "closure_penal", scale = 6, precision = 19)
|
||||
private BigDecimal closurePenal;
|
||||
|
||||
@Column(name = "closure_gl_transaction_id", length = 50)
|
||||
private String closureGlTransactionId;
|
||||
|
||||
@Column(name = "closure_voucher_no", length = 30)
|
||||
private String closureVoucherNo;
|
||||
}
|
||||
+87
@@ -0,0 +1,87 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.investment.domain;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.JoinColumn;
|
||||
import jakarta.persistence.ManyToOne;
|
||||
import jakarta.persistence.Table;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import org.apache.fineract.accounting.glaccount.domain.GLAccount;
|
||||
import org.apache.fineract.infrastructure.core.domain.AbstractAuditableWithUTCDateTimeCustom;
|
||||
|
||||
/** A posted interest line of an FD: a 31 Mar / 30 Sep provision, or a half-yearly / yearly payout. */
|
||||
@Entity
|
||||
@Table(name = "m_investment_fd_interest")
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
public class InvestmentFdInterest extends AbstractAuditableWithUTCDateTimeCustom<Long> {
|
||||
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "fd_id", nullable = false)
|
||||
private InvestmentFd fd;
|
||||
|
||||
/** PROVISION or PAYOUT. */
|
||||
@Column(name = "line_type", nullable = false, length = 20)
|
||||
private String lineType;
|
||||
|
||||
@Column(name = "int_date", nullable = false)
|
||||
private LocalDate intDate;
|
||||
|
||||
@Column(name = "from_date")
|
||||
private LocalDate fromDate;
|
||||
|
||||
@Column(name = "to_date")
|
||||
private LocalDate toDate;
|
||||
|
||||
@Column(name = "days")
|
||||
private Integer days;
|
||||
|
||||
@Column(name = "gross_interest", scale = 6, precision = 19)
|
||||
private BigDecimal grossInterest;
|
||||
|
||||
/** For a payout: the part already provisioned, cleared from interest receivable. */
|
||||
@Column(name = "already_booked", scale = 6, precision = 19)
|
||||
private BigDecimal alreadyBooked;
|
||||
|
||||
@Column(name = "tds", scale = 6, precision = 19)
|
||||
private BigDecimal tds;
|
||||
|
||||
@Column(name = "amount", scale = 6, precision = 19, nullable = false)
|
||||
private BigDecimal amount;
|
||||
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "bank_gl_account_id")
|
||||
private GLAccount bankGlAccount;
|
||||
|
||||
@Column(name = "gl_transaction_id", length = 50)
|
||||
private String glTransactionId;
|
||||
|
||||
@Column(name = "voucher_no", length = 30)
|
||||
private String voucherNo;
|
||||
|
||||
@Column(name = "posted_on_date")
|
||||
private LocalDate postedOnDate;
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.investment.domain;
|
||||
|
||||
import java.util.List;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
public interface InvestmentFdInterestRepository extends JpaRepository<InvestmentFdInterest, Long> {
|
||||
|
||||
List<InvestmentFdInterest> findByFdIdOrderByIntDateAscIdAsc(Long fdId);
|
||||
}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.investment.domain;
|
||||
|
||||
import jakarta.persistence.LockModeType;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.Lock;
|
||||
import org.springframework.data.jpa.repository.Query;
|
||||
import org.springframework.data.repository.query.Param;
|
||||
|
||||
public interface InvestmentFdRepository extends JpaRepository<InvestmentFd, Long> {
|
||||
|
||||
@Lock(value = LockModeType.PESSIMISTIC_WRITE)
|
||||
@Query("select f from InvestmentFd f where f.id = :fdId")
|
||||
Optional<InvestmentFd> findByIdWithLock(@Param("fdId") Long fdId);
|
||||
|
||||
List<InvestmentFd> findByStatusOrderByIdAsc(String status);
|
||||
|
||||
boolean existsByPartyIdAndCertNoIgnoreCase(Long partyId, String certNo);
|
||||
}
|
||||
+84
@@ -0,0 +1,84 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.investment.domain;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.JoinColumn;
|
||||
import jakarta.persistence.ManyToOne;
|
||||
import jakarta.persistence.Table;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import org.apache.fineract.accounting.glaccount.domain.GLAccount;
|
||||
import org.apache.fineract.infrastructure.core.domain.AbstractAuditableWithUTCDateTimeCustom;
|
||||
|
||||
/** One interest schedule line of a holding (coupon received or closing provision) that has been posted to GL. */
|
||||
@Entity
|
||||
@Table(name = "m_investment_interest_posting")
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
public class InvestmentInterestPosting extends AbstractAuditableWithUTCDateTimeCustom<Long> {
|
||||
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "purchase_id", nullable = false)
|
||||
private InvestmentPurchase purchase;
|
||||
|
||||
/** COUPON or PROVISION. */
|
||||
@Column(name = "line_type", nullable = false, length = 20)
|
||||
private String lineType;
|
||||
|
||||
@Column(name = "int_date", nullable = false)
|
||||
private LocalDate intDate;
|
||||
|
||||
@Column(name = "from_date")
|
||||
private LocalDate fromDate;
|
||||
|
||||
@Column(name = "to_date")
|
||||
private LocalDate toDate;
|
||||
|
||||
@Column(name = "days")
|
||||
private Integer days;
|
||||
|
||||
@Column(name = "gross_interest", scale = 6, precision = 19)
|
||||
private BigDecimal grossInterest;
|
||||
|
||||
@Column(name = "already_booked", scale = 6, precision = 19)
|
||||
private BigDecimal alreadyBooked;
|
||||
|
||||
@Column(name = "amount", scale = 6, precision = 19, nullable = false)
|
||||
private BigDecimal amount;
|
||||
|
||||
/** Bank the coupon was received into (coupon lines only). */
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "bank_gl_account_id")
|
||||
private GLAccount bankGlAccount;
|
||||
|
||||
@Column(name = "gl_transaction_id", length = 50)
|
||||
private String glTransactionId;
|
||||
|
||||
@Column(name = "voucher_no", length = 30)
|
||||
private String voucherNo;
|
||||
|
||||
@Column(name = "posted_on_date")
|
||||
private LocalDate postedOnDate;
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.investment.domain;
|
||||
|
||||
import java.util.List;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
public interface InvestmentInterestPostingRepository extends JpaRepository<InvestmentInterestPosting, Long> {
|
||||
|
||||
List<InvestmentInterestPosting> findByPurchaseIdOrderByIntDateAsc(Long purchaseId);
|
||||
}
|
||||
+110
@@ -0,0 +1,110 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.investment.domain;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.JoinColumn;
|
||||
import jakarta.persistence.ManyToOne;
|
||||
import jakarta.persistence.Table;
|
||||
import java.math.BigDecimal;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import org.apache.fineract.accounting.glaccount.domain.GLAccount;
|
||||
import org.apache.fineract.infrastructure.core.domain.AbstractAuditableWithUTCDateTimeCustom;
|
||||
|
||||
/**
|
||||
* Bank / Party Master — the counterparty an investment is bought from or placed with (bank, RBI, issuer, AMC, broker),
|
||||
* following the bank's existing CBS Bank / Party Master.
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "m_investment_party")
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
public class InvestmentParty extends AbstractAuditableWithUTCDateTimeCustom<Long> {
|
||||
|
||||
@Column(name = "party_code", nullable = false, length = 20, unique = true)
|
||||
private String partyCode;
|
||||
|
||||
@Column(name = "bond_type", nullable = false, length = 20)
|
||||
private String bondType;
|
||||
|
||||
@Column(name = "party_name", nullable = false, length = 150)
|
||||
private String partyName;
|
||||
|
||||
@Column(name = "address1", length = 150)
|
||||
private String address1;
|
||||
|
||||
@Column(name = "address2", length = 150)
|
||||
private String address2;
|
||||
|
||||
@Column(name = "address3", length = 150)
|
||||
private String address3;
|
||||
|
||||
@Column(name = "city", length = 100)
|
||||
private String city;
|
||||
|
||||
@Column(name = "pincode", length = 10)
|
||||
private String pincode;
|
||||
|
||||
@Column(name = "phone", length = 20)
|
||||
private String phone;
|
||||
|
||||
@Column(name = "fax", length = 20)
|
||||
private String fax;
|
||||
|
||||
@Column(name = "bank_type", length = 60)
|
||||
private String bankType;
|
||||
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "gl_account_id", nullable = false)
|
||||
private GLAccount glAccount;
|
||||
|
||||
/** Interest income GL of the FDs placed with this bank (Bond Type FD only). */
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "interest_gl_account_id")
|
||||
private GLAccount interestGlAccount;
|
||||
|
||||
@Column(name = "rbi_code", length = 30)
|
||||
private String rbiCode;
|
||||
|
||||
@Column(name = "exposure_limit", scale = 6, precision = 19)
|
||||
private BigDecimal exposureLimit;
|
||||
|
||||
@Column(name = "lei_no", length = 30)
|
||||
private String leiNo;
|
||||
|
||||
@Column(name = "cp_bank_name", length = 150)
|
||||
private String cpBankName;
|
||||
|
||||
@Column(name = "cp_branch_name", length = 150)
|
||||
private String cpBranchName;
|
||||
|
||||
@Column(name = "cp_account_no", length = 40)
|
||||
private String cpAccountNo;
|
||||
|
||||
@Column(name = "cp_ifsc", length = 11)
|
||||
private String cpIfsc;
|
||||
|
||||
/** An inactive party is kept for history but not offered for new deals. */
|
||||
@Column(name = "is_active", nullable = false)
|
||||
private boolean active = true;
|
||||
}
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.investment.domain;
|
||||
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
public interface InvestmentPartyRepository extends JpaRepository<InvestmentParty, Long> {
|
||||
|
||||
/** Locks the bank so two FDs with it are checked against the 5% limit one after the other. */
|
||||
@org.springframework.data.jpa.repository.Lock(value = jakarta.persistence.LockModeType.PESSIMISTIC_WRITE)
|
||||
@org.springframework.data.jpa.repository.Query("select p from InvestmentParty p where p.id = :partyId")
|
||||
java.util.Optional<InvestmentParty> findByIdWithLock(@org.springframework.data.repository.query.Param("partyId") Long partyId);}
|
||||
+163
@@ -0,0 +1,163 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.investment.domain;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.JoinColumn;
|
||||
import jakarta.persistence.ManyToOne;
|
||||
import jakarta.persistence.Table;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import org.apache.fineract.accounting.glaccount.domain.GLAccount;
|
||||
import org.apache.fineract.infrastructure.core.domain.AbstractAuditableWithUTCDateTimeCustom;
|
||||
|
||||
/**
|
||||
* Investment Purchase Entry — one deal (as in the bank's existing CBS "Govt.Security/Bond Entry"). Once saved it is the
|
||||
* holding that interest, sale and redemption later work against; {@link #balanceUnits} tracks what is still held. Every
|
||||
* derived figure (units, book value, accrued interest, settlement amount) is computed by the backend.
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "m_investment_purchase")
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
public class InvestmentPurchase extends AbstractAuditableWithUTCDateTimeCustom<Long> {
|
||||
|
||||
/** Financial-year deal number, e.g. 202627000001 for the first deal of FY 2026-27. */
|
||||
@Column(name = "deal_no", nullable = false, length = 20, unique = true)
|
||||
private String dealNo;
|
||||
|
||||
/** Certificate number — system-generated running number. */
|
||||
@Column(name = "cert_no", nullable = false, length = 20, unique = true)
|
||||
private String certNo;
|
||||
|
||||
@Column(name = "bond_type", nullable = false, length = 20)
|
||||
private String bondType;
|
||||
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "script_id", nullable = false)
|
||||
private InvestmentScript script;
|
||||
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "party_id", nullable = false)
|
||||
private InvestmentParty party;
|
||||
|
||||
@Column(name = "deal_date", nullable = false)
|
||||
private LocalDate dealDate;
|
||||
|
||||
@Column(name = "deal_time", length = 8)
|
||||
private String dealTime;
|
||||
|
||||
@Column(name = "settlement_type", nullable = false, length = 5)
|
||||
private String settlementType;
|
||||
|
||||
@Column(name = "settlement_date", nullable = false)
|
||||
private LocalDate settlementDate;
|
||||
|
||||
@Column(name = "face_value", scale = 6, precision = 19, nullable = false)
|
||||
private BigDecimal faceValue;
|
||||
|
||||
/** Price per ₹100 face value (Govt Security / Bond); NAV for a mutual fund. */
|
||||
@Column(name = "purchase_rate", scale = 6, precision = 19, nullable = false)
|
||||
private BigDecimal purchaseRate;
|
||||
|
||||
@Column(name = "units", scale = 6, precision = 19, nullable = false)
|
||||
private BigDecimal units;
|
||||
|
||||
@Column(name = "balance_units", scale = 6, precision = 19, nullable = false)
|
||||
private BigDecimal balanceUnits;
|
||||
|
||||
@Column(name = "brokerage", scale = 6, precision = 19)
|
||||
private BigDecimal brokerage;
|
||||
|
||||
@Column(name = "stamp_duty", scale = 6, precision = 19)
|
||||
private BigDecimal stampDuty;
|
||||
|
||||
@Column(name = "other_charges", scale = 6, precision = 19)
|
||||
private BigDecimal otherCharges;
|
||||
|
||||
@Column(name = "book_value", scale = 6, precision = 19, nullable = false)
|
||||
private BigDecimal bookValue;
|
||||
|
||||
@Column(name = "premium_discount", scale = 6, precision = 19)
|
||||
private BigDecimal premiumDiscount;
|
||||
|
||||
@Column(name = "int_rate", scale = 4, precision = 9)
|
||||
private BigDecimal intRate;
|
||||
|
||||
@Column(name = "issue_date")
|
||||
private LocalDate issueDate;
|
||||
|
||||
@Column(name = "maturity_date")
|
||||
private LocalDate maturityDate;
|
||||
|
||||
@Column(name = "last_int_date")
|
||||
private LocalDate lastIntDate;
|
||||
|
||||
@Column(name = "accrued_days")
|
||||
private Integer accruedDays;
|
||||
|
||||
/** Interest paid to the seller from the last coupon date up to the deal date (30/360). */
|
||||
@Column(name = "accrued_interest", scale = 6, precision = 19)
|
||||
private BigDecimal accruedInterest;
|
||||
|
||||
@Column(name = "settlement_amount", scale = 6, precision = 19, nullable = false)
|
||||
private BigDecimal settlementAmount;
|
||||
|
||||
@Column(name = "ytm", scale = 4, precision = 9)
|
||||
private BigDecimal ytm;
|
||||
|
||||
@Column(name = "category", nullable = false, length = 40)
|
||||
private String category;
|
||||
|
||||
@Column(name = "sub_type", length = 40)
|
||||
private String subType;
|
||||
|
||||
/** Investment GL the holding is booked to (from the script). */
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "investment_gl_account_id", nullable = false)
|
||||
private GLAccount investmentGlAccount;
|
||||
|
||||
/** Bank GL the purchase is paid from ("CR. To A/c"). */
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "cr_gl_account_id", nullable = false)
|
||||
private GLAccount crGlAccount;
|
||||
|
||||
/** GL holding the premium paid over face value (100707) until it is amortised; empty when it sits in the investment GL. */
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "premium_gl_account_id")
|
||||
private GLAccount premiumGlAccount;
|
||||
|
||||
@Column(name = "other_details", length = 4000)
|
||||
private String otherDetails;
|
||||
|
||||
@Column(name = "status", nullable = false, length = 20)
|
||||
private String status;
|
||||
|
||||
/** Journal entry transaction posted when the deal was settled. */
|
||||
@Column(name = "gl_transaction_id", length = 50)
|
||||
private String glTransactionId;
|
||||
|
||||
@Column(name = "settled_on_date")
|
||||
private LocalDate settledOnDate;
|
||||
}
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.investment.domain;
|
||||
|
||||
import jakarta.persistence.LockModeType;
|
||||
import java.util.Optional;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.Lock;
|
||||
import org.springframework.data.jpa.repository.Query;
|
||||
import org.springframework.data.repository.query.Param;
|
||||
|
||||
public interface InvestmentPurchaseRepository extends JpaRepository<InvestmentPurchase, Long> {
|
||||
|
||||
/** Locks the deal row so two postings on one deal (double click, two users) run one after the other. */
|
||||
@Lock(value = LockModeType.PESSIMISTIC_WRITE)
|
||||
@Query("select p from InvestmentPurchase p where p.id = :purchaseId")
|
||||
Optional<InvestmentPurchase> findByIdWithLock(@Param("purchaseId") Long purchaseId);
|
||||
|
||||
boolean existsByScriptId(Long scriptId);
|
||||
}
|
||||
+132
@@ -0,0 +1,132 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.investment.domain;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.JoinColumn;
|
||||
import jakarta.persistence.ManyToOne;
|
||||
import jakarta.persistence.Table;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import org.apache.fineract.accounting.glaccount.domain.GLAccount;
|
||||
import org.apache.fineract.infrastructure.core.domain.AbstractAuditableWithUTCDateTimeCustom;
|
||||
|
||||
/** A sale of some or all units of a holding before maturity (SELL), or its repayment at maturity (REDEEM). */
|
||||
@Entity
|
||||
@Table(name = "m_investment_sale")
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
public class InvestmentSale extends AbstractAuditableWithUTCDateTimeCustom<Long> {
|
||||
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "purchase_id", nullable = false)
|
||||
private InvestmentPurchase purchase;
|
||||
|
||||
/** SELL, REDEEM, WRITEOFF or AMORTISE. */
|
||||
@Column(name = "sale_type", nullable = false, length = 10)
|
||||
private String saleType;
|
||||
|
||||
/** Buyer of a sale; empty for a redemption. */
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "party_id")
|
||||
private InvestmentParty party;
|
||||
|
||||
@Column(name = "deal_date", nullable = false)
|
||||
private LocalDate dealDate;
|
||||
|
||||
@Column(name = "deal_time", length = 8)
|
||||
private String dealTime;
|
||||
|
||||
@Column(name = "settlement_type", length = 5)
|
||||
private String settlementType;
|
||||
|
||||
@Column(name = "settlement_date", nullable = false)
|
||||
private LocalDate settlementDate;
|
||||
|
||||
@Column(name = "units", scale = 6, precision = 19, nullable = false)
|
||||
private BigDecimal units;
|
||||
|
||||
@Column(name = "face_value", scale = 6, precision = 19, nullable = false)
|
||||
private BigDecimal faceValue;
|
||||
|
||||
/** Price per ₹100 of face value; 100 for a redemption. */
|
||||
@Column(name = "sale_rate", scale = 6, precision = 19)
|
||||
private BigDecimal saleRate;
|
||||
|
||||
/** Price received for the units, without interest. */
|
||||
@Column(name = "consideration", scale = 6, precision = 19, nullable = false)
|
||||
private BigDecimal consideration;
|
||||
|
||||
@Column(name = "accrued_days")
|
||||
private Integer accruedDays;
|
||||
|
||||
/** Interest the buyer pays us for the days since the last coupon. */
|
||||
@Column(name = "accrued_interest", scale = 6, precision = 19)
|
||||
private BigDecimal accruedInterest;
|
||||
|
||||
/** Part of that interest already booked by a closing provision, cleared from interest receivable. */
|
||||
@Column(name = "receivable_cleared", scale = 6, precision = 19)
|
||||
private BigDecimal receivableCleared;
|
||||
|
||||
@Column(name = "brokerage", scale = 6, precision = 19)
|
||||
private BigDecimal brokerage;
|
||||
|
||||
/** Cost of the units sold, taken off the investment GL. */
|
||||
@Column(name = "book_value_sold", scale = 6, precision = 19, nullable = false)
|
||||
private BigDecimal bookValueSold;
|
||||
|
||||
/** Part of the book value taken off that was unamortised premium (credited to the premium GL). */
|
||||
@Column(name = "premium_cleared", scale = 6, precision = 19)
|
||||
private BigDecimal premiumCleared;
|
||||
|
||||
/** Consideration less cost: positive is profit, negative is loss. */
|
||||
@Column(name = "profit_loss", scale = 6, precision = 19, nullable = false)
|
||||
private BigDecimal profitLoss;
|
||||
|
||||
/** Money into the bank: consideration plus interest, less brokerage. */
|
||||
@Column(name = "net_amount", scale = 6, precision = 19, nullable = false)
|
||||
private BigDecimal netAmount;
|
||||
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "bank_gl_account_id", nullable = false)
|
||||
private GLAccount bankGlAccount;
|
||||
|
||||
@Column(name = "sanction_authority", length = 100)
|
||||
private String sanctionAuthority;
|
||||
|
||||
@Column(name = "remarks", length = 500)
|
||||
private String remarks;
|
||||
|
||||
@Column(name = "status", nullable = false, length = 20)
|
||||
private String status;
|
||||
|
||||
@Column(name = "gl_transaction_id", length = 50)
|
||||
private String glTransactionId;
|
||||
|
||||
@Column(name = "voucher_no", length = 30)
|
||||
private String voucherNo;
|
||||
|
||||
@Column(name = "settled_on_date")
|
||||
private LocalDate settledOnDate;
|
||||
}
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.investment.domain;
|
||||
|
||||
import jakarta.persistence.LockModeType;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.Lock;
|
||||
import org.springframework.data.jpa.repository.Query;
|
||||
import org.springframework.data.repository.query.Param;
|
||||
|
||||
public interface InvestmentSaleRepository extends JpaRepository<InvestmentSale, Long> {
|
||||
|
||||
List<InvestmentSale> findByPurchaseIdOrderByIdAsc(Long purchaseId);
|
||||
|
||||
@Lock(value = LockModeType.PESSIMISTIC_WRITE)
|
||||
@Query("select s from InvestmentSale s where s.id = :saleId")
|
||||
Optional<InvestmentSale> findByIdWithLock(@Param("saleId") Long saleId);
|
||||
|
||||
/** The deal of a sale, without loading either, so the deal can be locked first. */
|
||||
@Query("select s.purchase.id from InvestmentSale s where s.id = :saleId")
|
||||
Optional<Long> findPurchaseIdBySaleId(@Param("saleId") Long saleId);
|
||||
}
|
||||
+107
@@ -0,0 +1,107 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.investment.domain;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.JoinColumn;
|
||||
import jakarta.persistence.ManyToOne;
|
||||
import jakarta.persistence.Table;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import org.apache.fineract.accounting.glaccount.domain.GLAccount;
|
||||
import org.apache.fineract.infrastructure.core.domain.AbstractAuditableWithUTCDateTimeCustom;
|
||||
|
||||
/**
|
||||
* Script Master — what is being bought (a Govt Security, bond, mutual fund scheme, share, …), following the bank's
|
||||
* existing CBS Script Master. A purchase picks these terms up instead of the dealer re-typing them.
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "m_investment_script")
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
public class InvestmentScript extends AbstractAuditableWithUTCDateTimeCustom<Long> {
|
||||
|
||||
/** System-generated: bond-type letter + running number, e.g. G00001. */
|
||||
@Column(name = "script_code", nullable = false, length = 20, unique = true)
|
||||
private String scriptCode;
|
||||
|
||||
@Column(name = "bond_type", nullable = false, length = 20)
|
||||
private String bondType;
|
||||
|
||||
/** GOI / STATE_GOVT / TREASURY / NON_SLR. */
|
||||
@Column(name = "slr_type", length = 20)
|
||||
private String slrType;
|
||||
|
||||
@Column(name = "script_desc", nullable = false, length = 150)
|
||||
private String scriptDesc;
|
||||
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "gl_account_id", nullable = false)
|
||||
private GLAccount glAccount;
|
||||
|
||||
@Column(name = "isin", length = 20)
|
||||
private String isin;
|
||||
|
||||
@Column(name = "face_value_per_unit", scale = 6, precision = 19, nullable = false)
|
||||
private BigDecimal faceValuePerUnit;
|
||||
|
||||
@Column(name = "int_rate", scale = 4, precision = 9)
|
||||
private BigDecimal intRate;
|
||||
|
||||
@Column(name = "issue_date")
|
||||
private LocalDate issueDate;
|
||||
|
||||
@Column(name = "maturity_date")
|
||||
private LocalDate maturityDate;
|
||||
|
||||
@Column(name = "company_name", length = 150)
|
||||
private String companyName;
|
||||
|
||||
@Column(name = "address1", length = 150)
|
||||
private String address1;
|
||||
|
||||
@Column(name = "address2", length = 150)
|
||||
private String address2;
|
||||
|
||||
@Column(name = "address3", length = 150)
|
||||
private String address3;
|
||||
|
||||
@Column(name = "city", length = 100)
|
||||
private String city;
|
||||
|
||||
@Column(name = "pincode", length = 10)
|
||||
private String pincode;
|
||||
|
||||
@Column(name = "phone", length = 20)
|
||||
private String phone;
|
||||
|
||||
@Column(name = "fax", length = 20)
|
||||
private String fax;
|
||||
|
||||
@Column(name = "corp_code", length = 30)
|
||||
private String corpCode;
|
||||
|
||||
@Column(name = "is_active", nullable = false)
|
||||
private boolean active = true;
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.investment.domain;
|
||||
|
||||
import java.util.List;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
public interface InvestmentScriptRepository extends JpaRepository<InvestmentScript, Long> {
|
||||
|
||||
List<InvestmentScript> findByIsinIgnoreCase(String isin);
|
||||
}
|
||||
+80
@@ -0,0 +1,80 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.investment.domain;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.JoinColumn;
|
||||
import jakarta.persistence.ManyToOne;
|
||||
import jakarta.persistence.Table;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import org.apache.fineract.accounting.glaccount.domain.GLAccount;
|
||||
import org.apache.fineract.infrastructure.core.domain.AbstractAuditableWithUTCDateTimeCustom;
|
||||
|
||||
/** A holding moved from one government security script to another of the same terms (GOI Category Transfer). */
|
||||
@Entity
|
||||
@Table(name = "m_investment_security_transfer")
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
public class InvestmentSecurityTransfer extends AbstractAuditableWithUTCDateTimeCustom<Long> {
|
||||
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "purchase_id", nullable = false)
|
||||
private InvestmentPurchase purchase;
|
||||
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "from_script_id", nullable = false)
|
||||
private InvestmentScript fromScript;
|
||||
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "to_script_id", nullable = false)
|
||||
private InvestmentScript toScript;
|
||||
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "from_gl_account_id", nullable = false)
|
||||
private GLAccount fromGlAccount;
|
||||
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "to_gl_account_id", nullable = false)
|
||||
private GLAccount toGlAccount;
|
||||
|
||||
@Column(name = "transfer_date", nullable = false)
|
||||
private LocalDate transferDate;
|
||||
|
||||
@Column(name = "units", scale = 6, precision = 19, nullable = false)
|
||||
private BigDecimal units;
|
||||
|
||||
/** Book value carried over; moved between GLs only when the two scripts map to different GLs. */
|
||||
@Column(name = "book_value", scale = 6, precision = 19, nullable = false)
|
||||
private BigDecimal bookValue;
|
||||
|
||||
@Column(name = "remarks", length = 500)
|
||||
private String remarks;
|
||||
|
||||
@Column(name = "gl_transaction_id", length = 50)
|
||||
private String glTransactionId;
|
||||
|
||||
@Column(name = "voucher_no", length = 30)
|
||||
private String voucherNo;
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.investment.domain;
|
||||
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
public interface InvestmentSecurityTransferRepository extends JpaRepository<InvestmentSecurityTransfer, Long> {}
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.investment.exception;
|
||||
|
||||
import org.apache.fineract.infrastructure.core.exception.AbstractPlatformResourceNotFoundException;
|
||||
|
||||
public class InvestmentDailyRateNotFoundException extends AbstractPlatformResourceNotFoundException {
|
||||
|
||||
public InvestmentDailyRateNotFoundException(final Long id) {
|
||||
super("error.msg.investmentdailyrate.id.invalid", "Daily script rate with identifier " + id + " does not exist", id);
|
||||
}
|
||||
}
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.investment.exception;
|
||||
|
||||
import org.apache.fineract.infrastructure.core.exception.AbstractPlatformResourceNotFoundException;
|
||||
|
||||
public class InvestmentPartyNotFoundException extends AbstractPlatformResourceNotFoundException {
|
||||
|
||||
public InvestmentPartyNotFoundException(final Long id) {
|
||||
super("error.msg.investmentparty.id.invalid", "Investment party with identifier " + id + " does not exist", id);
|
||||
}
|
||||
}
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.investment.exception;
|
||||
|
||||
import org.apache.fineract.infrastructure.core.exception.AbstractPlatformResourceNotFoundException;
|
||||
|
||||
public class InvestmentPurchaseNotFoundException extends AbstractPlatformResourceNotFoundException {
|
||||
|
||||
public InvestmentPurchaseNotFoundException(final Long id) {
|
||||
super("error.msg.investmentpurchase.id.invalid", "Investment purchase with identifier " + id + " does not exist", id);
|
||||
}
|
||||
}
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.investment.exception;
|
||||
|
||||
import org.apache.fineract.infrastructure.core.exception.AbstractPlatformResourceNotFoundException;
|
||||
|
||||
public class InvestmentScriptNotFoundException extends AbstractPlatformResourceNotFoundException {
|
||||
|
||||
public InvestmentScriptNotFoundException(final Long id) {
|
||||
super("error.msg.investmentscript.id.invalid", "Investment script with identifier " + id + " does not exist", id);
|
||||
}
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.investment.handler;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.fineract.commands.annotation.CommandType;
|
||||
import org.apache.fineract.commands.handler.NewCommandSourceHandler;
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.apache.fineract.portfolio.investment.service.InvestmentAmortisationService;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Service
|
||||
@CommandType(entity = "INVESTMENTSALE", action = "AMORTISE")
|
||||
@RequiredArgsConstructor
|
||||
public class AmortiseInvestmentPremiumCommandHandler implements NewCommandSourceHandler {
|
||||
|
||||
private final InvestmentAmortisationService amortisationService;
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult processCommand(final JsonCommand command) {
|
||||
return this.amortisationService.amortise(command);
|
||||
}
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.investment.handler;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.fineract.commands.annotation.CommandType;
|
||||
import org.apache.fineract.commands.handler.NewCommandSourceHandler;
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.apache.fineract.portfolio.investment.service.InvestmentDailyRateWritePlatformService;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Service
|
||||
@CommandType(entity = "INVESTMENTDAILYRATE", action = "CREATE")
|
||||
@RequiredArgsConstructor
|
||||
public class CreateInvestmentDailyRateCommandHandler implements NewCommandSourceHandler {
|
||||
|
||||
private final InvestmentDailyRateWritePlatformService writePlatformService;
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult processCommand(final JsonCommand command) {
|
||||
return this.writePlatformService.createDailyRate(command);
|
||||
}
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.investment.handler;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.fineract.commands.annotation.CommandType;
|
||||
import org.apache.fineract.commands.handler.NewCommandSourceHandler;
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.apache.fineract.portfolio.investment.service.InvestmentFdService;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Service
|
||||
@CommandType(entity = "INVESTMENTFD", action = "CREATE")
|
||||
@RequiredArgsConstructor
|
||||
public class CreateInvestmentFdCommandHandler implements NewCommandSourceHandler {
|
||||
|
||||
private final InvestmentFdService fdService;
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult processCommand(final JsonCommand command) {
|
||||
return this.fdService.create(command);
|
||||
}
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.investment.handler;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.fineract.commands.annotation.CommandType;
|
||||
import org.apache.fineract.commands.handler.NewCommandSourceHandler;
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.apache.fineract.portfolio.investment.service.InvestmentMasterWritePlatformService;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Service
|
||||
@CommandType(entity = "INVESTMENTPARTY", action = "CREATE")
|
||||
@RequiredArgsConstructor
|
||||
public class CreateInvestmentPartyCommandHandler implements NewCommandSourceHandler {
|
||||
|
||||
private final InvestmentMasterWritePlatformService writePlatformService;
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult processCommand(final JsonCommand command) {
|
||||
return this.writePlatformService.createParty(command);
|
||||
}
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.investment.handler;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.fineract.commands.annotation.CommandType;
|
||||
import org.apache.fineract.commands.handler.NewCommandSourceHandler;
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.apache.fineract.portfolio.investment.service.InvestmentPurchaseWritePlatformService;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Service
|
||||
@CommandType(entity = "INVESTMENTPURCHASE", action = "CREATE")
|
||||
@RequiredArgsConstructor
|
||||
public class CreateInvestmentPurchaseCommandHandler implements NewCommandSourceHandler {
|
||||
|
||||
private final InvestmentPurchaseWritePlatformService writePlatformService;
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult processCommand(final JsonCommand command) {
|
||||
return this.writePlatformService.createPurchase(command);
|
||||
}
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.investment.handler;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.fineract.commands.annotation.CommandType;
|
||||
import org.apache.fineract.commands.handler.NewCommandSourceHandler;
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.apache.fineract.portfolio.investment.service.InvestmentSaleWritePlatformService;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Service
|
||||
@CommandType(entity = "INVESTMENTSALE", action = "CREATE")
|
||||
@RequiredArgsConstructor
|
||||
public class CreateInvestmentSaleCommandHandler implements NewCommandSourceHandler {
|
||||
|
||||
private final InvestmentSaleWritePlatformService writePlatformService;
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult processCommand(final JsonCommand command) {
|
||||
return this.writePlatformService.createSale(command);
|
||||
}
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.investment.handler;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.fineract.commands.annotation.CommandType;
|
||||
import org.apache.fineract.commands.handler.NewCommandSourceHandler;
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.apache.fineract.portfolio.investment.service.InvestmentMasterWritePlatformService;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Service
|
||||
@CommandType(entity = "INVESTMENTSCRIPT", action = "CREATE")
|
||||
@RequiredArgsConstructor
|
||||
public class CreateInvestmentScriptCommandHandler implements NewCommandSourceHandler {
|
||||
|
||||
private final InvestmentMasterWritePlatformService writePlatformService;
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult processCommand(final JsonCommand command) {
|
||||
return this.writePlatformService.createScript(command);
|
||||
}
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.investment.handler;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.fineract.commands.annotation.CommandType;
|
||||
import org.apache.fineract.commands.handler.NewCommandSourceHandler;
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.apache.fineract.portfolio.investment.service.InvestmentDailyRateWritePlatformService;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Service
|
||||
@CommandType(entity = "INVESTMENTDAILYRATE", action = "DELETE")
|
||||
@RequiredArgsConstructor
|
||||
public class DeleteInvestmentDailyRateCommandHandler implements NewCommandSourceHandler {
|
||||
|
||||
private final InvestmentDailyRateWritePlatformService writePlatformService;
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult processCommand(final JsonCommand command) {
|
||||
return this.writePlatformService.deleteDailyRate(command.entityId());
|
||||
}
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.investment.handler;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.fineract.commands.annotation.CommandType;
|
||||
import org.apache.fineract.commands.handler.NewCommandSourceHandler;
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.apache.fineract.portfolio.investment.service.InvestmentMasterWritePlatformService;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Service
|
||||
@CommandType(entity = "INVESTMENTPARTY", action = "DELETE")
|
||||
@RequiredArgsConstructor
|
||||
public class DeleteInvestmentPartyCommandHandler implements NewCommandSourceHandler {
|
||||
|
||||
private final InvestmentMasterWritePlatformService writePlatformService;
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult processCommand(final JsonCommand command) {
|
||||
return this.writePlatformService.deleteParty(command.entityId());
|
||||
}
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.investment.handler;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.fineract.commands.annotation.CommandType;
|
||||
import org.apache.fineract.commands.handler.NewCommandSourceHandler;
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.apache.fineract.portfolio.investment.service.InvestmentPurchaseWritePlatformService;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Service
|
||||
@CommandType(entity = "INVESTMENTPURCHASE", action = "DELETE")
|
||||
@RequiredArgsConstructor
|
||||
public class DeleteInvestmentPurchaseCommandHandler implements NewCommandSourceHandler {
|
||||
|
||||
private final InvestmentPurchaseWritePlatformService writePlatformService;
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult processCommand(final JsonCommand command) {
|
||||
return this.writePlatformService.deletePurchase(command.entityId());
|
||||
}
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.investment.handler;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.fineract.commands.annotation.CommandType;
|
||||
import org.apache.fineract.commands.handler.NewCommandSourceHandler;
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.apache.fineract.portfolio.investment.service.InvestmentSaleWritePlatformService;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Service
|
||||
@CommandType(entity = "INVESTMENTSALE", action = "DELETE")
|
||||
@RequiredArgsConstructor
|
||||
public class DeleteInvestmentSaleCommandHandler implements NewCommandSourceHandler {
|
||||
|
||||
private final InvestmentSaleWritePlatformService writePlatformService;
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult processCommand(final JsonCommand command) {
|
||||
return this.writePlatformService.deleteSale(command.entityId());
|
||||
}
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.investment.handler;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.fineract.commands.annotation.CommandType;
|
||||
import org.apache.fineract.commands.handler.NewCommandSourceHandler;
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.apache.fineract.portfolio.investment.service.InvestmentFdClosureService;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Service
|
||||
@CommandType(entity = "INVESTMENTFD", action = "MATURE")
|
||||
@RequiredArgsConstructor
|
||||
public class MatureInvestmentFdCommandHandler implements NewCommandSourceHandler {
|
||||
|
||||
private final InvestmentFdClosureService closureService;
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult processCommand(final JsonCommand command) {
|
||||
return this.closureService.mature(command.entityId(), command);
|
||||
}
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.investment.handler;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.fineract.commands.annotation.CommandType;
|
||||
import org.apache.fineract.commands.handler.NewCommandSourceHandler;
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.apache.fineract.portfolio.investment.service.InvestmentFdInterestService;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Service
|
||||
@CommandType(entity = "INVESTMENTFD", action = "POSTINTEREST")
|
||||
@RequiredArgsConstructor
|
||||
public class PostInvestmentFdInterestCommandHandler implements NewCommandSourceHandler {
|
||||
|
||||
private final InvestmentFdInterestService interestService;
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult processCommand(final JsonCommand command) {
|
||||
return this.interestService.post(command.entityId(), command);
|
||||
}
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.investment.handler;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.fineract.commands.annotation.CommandType;
|
||||
import org.apache.fineract.commands.handler.NewCommandSourceHandler;
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.apache.fineract.portfolio.investment.service.InvestmentInterestPostingWritePlatformService;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Service
|
||||
@CommandType(entity = "INVESTMENTINTEREST", action = "CREATE")
|
||||
@RequiredArgsConstructor
|
||||
public class PostInvestmentInterestCommandHandler implements NewCommandSourceHandler {
|
||||
|
||||
private final InvestmentInterestPostingWritePlatformService writePlatformService;
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult processCommand(final JsonCommand command) {
|
||||
return this.writePlatformService.postInterest(command.entityId(), command);
|
||||
}
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.investment.handler;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.fineract.commands.annotation.CommandType;
|
||||
import org.apache.fineract.commands.handler.NewCommandSourceHandler;
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.apache.fineract.portfolio.investment.service.InvestmentFdClosureService;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Service
|
||||
@CommandType(entity = "INVESTMENTFD", action = "PREMATURE")
|
||||
@RequiredArgsConstructor
|
||||
public class PrematureInvestmentFdCommandHandler implements NewCommandSourceHandler {
|
||||
|
||||
private final InvestmentFdClosureService closureService;
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult processCommand(final JsonCommand command) {
|
||||
return this.closureService.premature(command.entityId(), command);
|
||||
}
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.investment.handler;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.fineract.commands.annotation.CommandType;
|
||||
import org.apache.fineract.commands.handler.NewCommandSourceHandler;
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.apache.fineract.portfolio.investment.service.InvestmentSaleWritePlatformService;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Service
|
||||
@CommandType(entity = "INVESTMENTSALE", action = "REDEEM")
|
||||
@RequiredArgsConstructor
|
||||
public class RedeemInvestmentPurchaseCommandHandler implements NewCommandSourceHandler {
|
||||
|
||||
private final InvestmentSaleWritePlatformService writePlatformService;
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult processCommand(final JsonCommand command) {
|
||||
return this.writePlatformService.redeem(command.entityId(), command);
|
||||
}
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.investment.handler;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.fineract.commands.annotation.CommandType;
|
||||
import org.apache.fineract.commands.handler.NewCommandSourceHandler;
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.apache.fineract.portfolio.investment.service.InvestmentFdClosureService;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Service
|
||||
@CommandType(entity = "INVESTMENTFD", action = "RENEW")
|
||||
@RequiredArgsConstructor
|
||||
public class RenewInvestmentFdCommandHandler implements NewCommandSourceHandler {
|
||||
|
||||
private final InvestmentFdClosureService closureService;
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult processCommand(final JsonCommand command) {
|
||||
return this.closureService.renew(command.entityId(), command);
|
||||
}
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.investment.handler;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.fineract.commands.annotation.CommandType;
|
||||
import org.apache.fineract.commands.handler.NewCommandSourceHandler;
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.apache.fineract.portfolio.investment.service.InvestmentFdService;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Service
|
||||
@CommandType(entity = "INVESTMENTFD", action = "SETBASE")
|
||||
@RequiredArgsConstructor
|
||||
public class SetInvestmentDepositBaseCommandHandler implements NewCommandSourceHandler {
|
||||
|
||||
private final InvestmentFdService fdService;
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult processCommand(final JsonCommand command) {
|
||||
return this.fdService.setDepositBase(command);
|
||||
}
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.investment.handler;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.fineract.commands.annotation.CommandType;
|
||||
import org.apache.fineract.commands.handler.NewCommandSourceHandler;
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.apache.fineract.portfolio.investment.service.InvestmentPurchaseWritePlatformService;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Service
|
||||
@CommandType(entity = "INVESTMENTPURCHASE", action = "SETTLE")
|
||||
@RequiredArgsConstructor
|
||||
public class SettleInvestmentPurchaseCommandHandler implements NewCommandSourceHandler {
|
||||
|
||||
private final InvestmentPurchaseWritePlatformService writePlatformService;
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult processCommand(final JsonCommand command) {
|
||||
return this.writePlatformService.settlePurchase(command.entityId(), command);
|
||||
}
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.investment.handler;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.fineract.commands.annotation.CommandType;
|
||||
import org.apache.fineract.commands.handler.NewCommandSourceHandler;
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.apache.fineract.portfolio.investment.service.InvestmentSaleWritePlatformService;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Service
|
||||
@CommandType(entity = "INVESTMENTSALE", action = "SETTLE")
|
||||
@RequiredArgsConstructor
|
||||
public class SettleInvestmentSaleCommandHandler implements NewCommandSourceHandler {
|
||||
|
||||
private final InvestmentSaleWritePlatformService writePlatformService;
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult processCommand(final JsonCommand command) {
|
||||
return this.writePlatformService.settleSale(command.entityId(), command);
|
||||
}
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.investment.handler;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.fineract.commands.annotation.CommandType;
|
||||
import org.apache.fineract.commands.handler.NewCommandSourceHandler;
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.apache.fineract.portfolio.investment.service.InvestmentSecurityTransferService;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Service
|
||||
@CommandType(entity = "INVESTMENTTRANSFER", action = "CREATE")
|
||||
@RequiredArgsConstructor
|
||||
public class TransferInvestmentSecurityCommandHandler implements NewCommandSourceHandler {
|
||||
|
||||
private final InvestmentSecurityTransferService transferService;
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult processCommand(final JsonCommand command) {
|
||||
return this.transferService.transfer(command.entityId(), command);
|
||||
}
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.investment.handler;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.fineract.commands.annotation.CommandType;
|
||||
import org.apache.fineract.commands.handler.NewCommandSourceHandler;
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.apache.fineract.portfolio.investment.service.InvestmentMasterWritePlatformService;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Service
|
||||
@CommandType(entity = "INVESTMENTPARTY", action = "UPDATE")
|
||||
@RequiredArgsConstructor
|
||||
public class UpdateInvestmentPartyCommandHandler implements NewCommandSourceHandler {
|
||||
|
||||
private final InvestmentMasterWritePlatformService writePlatformService;
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult processCommand(final JsonCommand command) {
|
||||
return this.writePlatformService.updateParty(command.entityId(), command);
|
||||
}
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.investment.handler;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.fineract.commands.annotation.CommandType;
|
||||
import org.apache.fineract.commands.handler.NewCommandSourceHandler;
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.apache.fineract.portfolio.investment.service.InvestmentMasterWritePlatformService;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Service
|
||||
@CommandType(entity = "INVESTMENTSCRIPT", action = "UPDATE")
|
||||
@RequiredArgsConstructor
|
||||
public class UpdateInvestmentScriptCommandHandler implements NewCommandSourceHandler {
|
||||
|
||||
private final InvestmentMasterWritePlatformService writePlatformService;
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult processCommand(final JsonCommand command) {
|
||||
return this.writePlatformService.updateScript(command.entityId(), command);
|
||||
}
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.investment.handler;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.fineract.commands.annotation.CommandType;
|
||||
import org.apache.fineract.commands.handler.NewCommandSourceHandler;
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.apache.fineract.portfolio.investment.service.InvestmentSaleWritePlatformService;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Service
|
||||
@CommandType(entity = "INVESTMENTSALE", action = "WRITEOFF")
|
||||
@RequiredArgsConstructor
|
||||
public class WriteOffInvestmentPurchaseCommandHandler implements NewCommandSourceHandler {
|
||||
|
||||
private final InvestmentSaleWritePlatformService writePlatformService;
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult processCommand(final JsonCommand command) {
|
||||
return this.writePlatformService.writeOff(command.entityId(), command);
|
||||
}
|
||||
}
|
||||
+103
@@ -0,0 +1,103 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.investment.serialization;
|
||||
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentDailyRateApiConstants.CLOSE_RATE;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentDailyRateApiConstants.HIGH_RATE;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentDailyRateApiConstants.LOW_RATE;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentDailyRateApiConstants.OPEN_RATE;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentDailyRateApiConstants.PARAMS;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentDailyRateApiConstants.RATE_DATE;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentDailyRateApiConstants.RESOURCE;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentDailyRateApiConstants.SCRIPT_ID;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import java.lang.reflect.Type;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.fineract.infrastructure.core.data.ApiParameterError;
|
||||
import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder;
|
||||
import org.apache.fineract.infrastructure.core.exception.InvalidJsonException;
|
||||
import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException;
|
||||
import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/** Validates a Daily Script Master entry: close rate is mandatory and the day's rates must sit inside low to high. */
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public final class InvestmentDailyRateCommandFromApiJsonDeserializer {
|
||||
|
||||
private final FromJsonHelper fromApiJsonHelper;
|
||||
|
||||
public void validateForCreate(final String json) {
|
||||
if (StringUtils.isBlank(json)) {
|
||||
throw new InvalidJsonException();
|
||||
}
|
||||
final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
|
||||
this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, PARAMS);
|
||||
final JsonElement element = this.fromApiJsonHelper.parse(json);
|
||||
|
||||
final List<ApiParameterError> errors = new ArrayList<>();
|
||||
final DataValidatorBuilder v = new DataValidatorBuilder(errors).resource(RESOURCE);
|
||||
|
||||
v.reset().parameter(SCRIPT_ID).value(this.fromApiJsonHelper.extractLongNamed(SCRIPT_ID, element)).notNull().longGreaterThanZero();
|
||||
v.reset().parameter(RATE_DATE).value(this.fromApiJsonHelper.extractLocalDateNamed(RATE_DATE, element)).notNull();
|
||||
final BigDecimal close = amount(CLOSE_RATE, element);
|
||||
final BigDecimal open = amount(OPEN_RATE, element);
|
||||
final BigDecimal high = amount(HIGH_RATE, element);
|
||||
final BigDecimal low = amount(LOW_RATE, element);
|
||||
v.reset().parameter(CLOSE_RATE).value(close).notNull().positiveAmount();
|
||||
v.reset().parameter(OPEN_RATE).value(open).ignoreIfNull().positiveAmount();
|
||||
v.reset().parameter(HIGH_RATE).value(high).ignoreIfNull().positiveAmount();
|
||||
v.reset().parameter(LOW_RATE).value(low).ignoreIfNull().positiveAmount();
|
||||
|
||||
if (high != null && low != null && high.compareTo(low) < 0) {
|
||||
v.reset().parameter(HIGH_RATE).failWithCodeAndMessage("less.than.low.rate", "High Rate cannot be below Low Rate");
|
||||
}
|
||||
outsideRange(v, CLOSE_RATE, "Close Rate", close, low, high);
|
||||
outsideRange(v, OPEN_RATE, "Open Rate", open, low, high);
|
||||
|
||||
if (!errors.isEmpty()) {
|
||||
throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist", "Validation errors exist.", errors);
|
||||
}
|
||||
}
|
||||
|
||||
private static void outsideRange(final DataValidatorBuilder v, final String param, final String label, final BigDecimal rate,
|
||||
final BigDecimal low, final BigDecimal high) {
|
||||
if (rate == null) {
|
||||
return;
|
||||
}
|
||||
if (low != null && rate.compareTo(low) < 0) {
|
||||
v.reset().parameter(param).failWithCodeAndMessage("below.low.rate", label + " cannot be below Low Rate");
|
||||
} else if (high != null && rate.compareTo(high) > 0) {
|
||||
v.reset().parameter(param).failWithCodeAndMessage("above.high.rate", label + " cannot be above High Rate");
|
||||
}
|
||||
}
|
||||
|
||||
private BigDecimal amount(final String param, final JsonElement element) {
|
||||
return this.fromApiJsonHelper.parameterExists(param, element)
|
||||
? this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(param, element)
|
||||
: null;
|
||||
}
|
||||
}
|
||||
+73
@@ -0,0 +1,73 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.investment.serialization;
|
||||
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentInterestApiConstants.BANK_GL_ACCOUNT_ID;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentInterestApiConstants.INT_DATE;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentInterestApiConstants.LINE_TYPE;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentInterestApiConstants.PARAMS;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentInterestApiConstants.RESOURCE;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.fineract.infrastructure.core.data.ApiParameterError;
|
||||
import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder;
|
||||
import org.apache.fineract.infrastructure.core.exception.InvalidJsonException;
|
||||
import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException;
|
||||
import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
|
||||
import org.apache.fineract.portfolio.investment.service.InvestmentInterestCalculator;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/** Validates a request to post one interest schedule line. */
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public final class InvestmentInterestCommandFromApiJsonDeserializer {
|
||||
|
||||
private final FromJsonHelper fromApiJsonHelper;
|
||||
|
||||
public void validateForPost(final String json) {
|
||||
if (StringUtils.isBlank(json)) {
|
||||
throw new InvalidJsonException();
|
||||
}
|
||||
final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
|
||||
this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, PARAMS);
|
||||
final JsonElement element = this.fromApiJsonHelper.parse(json);
|
||||
|
||||
final List<ApiParameterError> errors = new ArrayList<>();
|
||||
final DataValidatorBuilder v = new DataValidatorBuilder(errors).resource(RESOURCE);
|
||||
final String lineType = this.fromApiJsonHelper.extractStringNamed(LINE_TYPE, element);
|
||||
v.reset().parameter(LINE_TYPE).value(lineType).notBlank();
|
||||
if (StringUtils.isNotBlank(lineType)) {
|
||||
v.reset().parameter(LINE_TYPE).value(lineType).isOneOfTheseValues(InvestmentInterestCalculator.TYPE_COUPON,
|
||||
InvestmentInterestCalculator.TYPE_PROVISION);
|
||||
}
|
||||
v.reset().parameter(INT_DATE).value(this.fromApiJsonHelper.extractLocalDateNamed(INT_DATE, element)).notNull();
|
||||
v.reset().parameter(BANK_GL_ACCOUNT_ID).value(this.fromApiJsonHelper.extractLongNamed(BANK_GL_ACCOUNT_ID, element)).ignoreIfNull()
|
||||
.longGreaterThanZero();
|
||||
if (!errors.isEmpty()) {
|
||||
throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist", "Validation errors exist.", errors);
|
||||
}
|
||||
}
|
||||
}
|
||||
+264
@@ -0,0 +1,264 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.investment.serialization;
|
||||
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentApiConstants.ADDRESS1;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentApiConstants.ADDRESS2;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentApiConstants.ADDRESS3;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentApiConstants.BANK_TYPE;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentApiConstants.BOND_TYPE;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentApiConstants.BOND_TYPES;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentApiConstants.CITY;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentApiConstants.COMPANY_NAME;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentApiConstants.CORP_CODE;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentApiConstants.CP_ACCOUNT_NO;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentApiConstants.CP_BANK_NAME;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentApiConstants.CP_BRANCH_NAME;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentApiConstants.CP_IFSC;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentApiConstants.DEBT_BOND_TYPES;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentApiConstants.EXPOSURE_LIMIT;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentApiConstants.FACE_VALUE_PER_UNIT;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentApiConstants.FAX;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentApiConstants.GL_ACCOUNT_ID;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentApiConstants.INTEREST_GL_ACCOUNT_ID;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentApiConstants.INT_RATE;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentApiConstants.ISIN;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentApiConstants.ISSUE_DATE;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentApiConstants.LEI_NO;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentApiConstants.MATURITY_DATE;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentApiConstants.PARTY_NAME;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentApiConstants.PARTY_PARAMS;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentApiConstants.PHONE;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentApiConstants.PINCODE;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentApiConstants.RBI_CODE;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentApiConstants.RESOURCE_PARTY;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentApiConstants.RESOURCE_SCRIPT;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentApiConstants.SCRIPT_DESC;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentApiConstants.SCRIPT_PARAMS;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentApiConstants.SLR_TYPE;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentApiConstants.SLR_TYPES;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import java.lang.reflect.Type;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.fineract.infrastructure.core.data.ApiParameterError;
|
||||
import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder;
|
||||
import org.apache.fineract.infrastructure.core.exception.InvalidJsonException;
|
||||
import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException;
|
||||
import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/** Validates Bank / Party Master and Script Master requests before anything is written. */
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public final class InvestmentMasterCommandFromApiJsonDeserializer {
|
||||
|
||||
private static final String IFSC_PATTERN = "^[A-Z]{4}0[A-Z0-9]{6}$";
|
||||
private static final String PINCODE_PATTERN = "^[0-9]{6}$";
|
||||
|
||||
private final FromJsonHelper fromApiJsonHelper;
|
||||
|
||||
// -------------------- Bank / Party Master --------------------
|
||||
|
||||
public void validateForCreateParty(final String json) {
|
||||
validateParty(json, true);
|
||||
}
|
||||
|
||||
public void validateForUpdateParty(final String json) {
|
||||
validateParty(json, false);
|
||||
}
|
||||
|
||||
private void validateParty(final String json, final boolean create) {
|
||||
final JsonElement element = parse(json, PARTY_PARAMS);
|
||||
final List<ApiParameterError> errors = new ArrayList<>();
|
||||
final DataValidatorBuilder v = new DataValidatorBuilder(errors).resource(RESOURCE_PARTY);
|
||||
|
||||
validateBondType(element, v, create);
|
||||
validateRequiredString(element, v, PARTY_NAME, 150, create);
|
||||
validateGlAccountId(element, v, create);
|
||||
if (this.fromApiJsonHelper.parameterExists(INTEREST_GL_ACCOUNT_ID, element)) {
|
||||
v.reset().parameter(INTEREST_GL_ACCOUNT_ID).value(this.fromApiJsonHelper.extractLongNamed(INTEREST_GL_ACCOUNT_ID, element))
|
||||
.ignoreIfNull().longGreaterThanZero();
|
||||
}
|
||||
validateAddress(element, v);
|
||||
validateOptionalString(element, v, BANK_TYPE, 60);
|
||||
validateOptionalString(element, v, RBI_CODE, 30);
|
||||
validateOptionalString(element, v, LEI_NO, 30);
|
||||
validateOptionalString(element, v, CP_BANK_NAME, 150);
|
||||
validateOptionalString(element, v, CP_BRANCH_NAME, 150);
|
||||
validateOptionalString(element, v, CP_ACCOUNT_NO, 40);
|
||||
|
||||
final String ifsc = this.fromApiJsonHelper.extractStringNamed(CP_IFSC, element);
|
||||
if (StringUtils.isNotBlank(ifsc)) {
|
||||
v.reset().parameter(CP_IFSC).value(ifsc.trim().toUpperCase()).matchesRegularExpression(IFSC_PATTERN,
|
||||
"IFSC must be 11 characters: 4 letters, 0, then 6 letters/digits");
|
||||
}
|
||||
|
||||
if (this.fromApiJsonHelper.parameterExists(EXPOSURE_LIMIT, element)) {
|
||||
final BigDecimal limit = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(EXPOSURE_LIMIT, element);
|
||||
v.reset().parameter(EXPOSURE_LIMIT).value(limit).ignoreIfNull().zeroOrPositiveAmount();
|
||||
}
|
||||
throwIfErrors(errors);
|
||||
}
|
||||
|
||||
// -------------------- Script Master --------------------
|
||||
|
||||
public void validateForCreateScript(final String json) {
|
||||
validateScript(json, true);
|
||||
}
|
||||
|
||||
public void validateForUpdateScript(final String json, final String existingBondType) {
|
||||
validateScript(json, false, existingBondType);
|
||||
}
|
||||
|
||||
private void validateScript(final String json, final boolean create) {
|
||||
validateScript(json, create, null);
|
||||
}
|
||||
|
||||
private void validateScript(final String json, final boolean create, final String existingBondType) {
|
||||
final JsonElement element = parse(json, SCRIPT_PARAMS);
|
||||
final List<ApiParameterError> errors = new ArrayList<>();
|
||||
final DataValidatorBuilder v = new DataValidatorBuilder(errors).resource(RESOURCE_SCRIPT);
|
||||
|
||||
validateBondType(element, v, create);
|
||||
validateRequiredString(element, v, SCRIPT_DESC, 150, create);
|
||||
validateGlAccountId(element, v, create);
|
||||
|
||||
final String slrType = this.fromApiJsonHelper.extractStringNamed(SLR_TYPE, element);
|
||||
if (StringUtils.isNotBlank(slrType)) {
|
||||
v.reset().parameter(SLR_TYPE).value(slrType).isOneOfTheseValues(SLR_TYPES.toArray());
|
||||
}
|
||||
|
||||
final String bondType = create ? this.fromApiJsonHelper.extractStringNamed(BOND_TYPE, element) : existingBondType;
|
||||
final boolean debt = bondType != null && DEBT_BOND_TYPES.contains(bondType);
|
||||
|
||||
final String isin = this.fromApiJsonHelper.extractStringNamed(ISIN, element);
|
||||
if (create && debt) {
|
||||
v.reset().parameter(ISIN).value(isin).notBlank();
|
||||
}
|
||||
v.reset().parameter(ISIN).value(isin).ignoreIfNull().notExceedingLengthOf(20);
|
||||
|
||||
if (this.fromApiJsonHelper.parameterExists(INT_RATE, element) || (create && debt)) {
|
||||
final BigDecimal intRate = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(INT_RATE, element);
|
||||
final DataValidatorBuilder rate = v.reset().parameter(INT_RATE).value(intRate);
|
||||
if (create && debt) {
|
||||
rate.notNull();
|
||||
}
|
||||
rate.ignoreIfNull().zeroOrPositiveAmount();
|
||||
}
|
||||
|
||||
if (this.fromApiJsonHelper.parameterExists(FACE_VALUE_PER_UNIT, element)) {
|
||||
final BigDecimal fv = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(FACE_VALUE_PER_UNIT, element);
|
||||
v.reset().parameter(FACE_VALUE_PER_UNIT).value(fv).ignoreIfNull().positiveAmount();
|
||||
}
|
||||
|
||||
LocalDate issueDate = null;
|
||||
if (this.fromApiJsonHelper.parameterExists(ISSUE_DATE, element) || (create && debt)) {
|
||||
issueDate = this.fromApiJsonHelper.extractLocalDateNamed(ISSUE_DATE, element);
|
||||
if (create && debt) {
|
||||
v.reset().parameter(ISSUE_DATE).value(issueDate).notNull();
|
||||
}
|
||||
}
|
||||
if (this.fromApiJsonHelper.parameterExists(MATURITY_DATE, element) || (create && debt)) {
|
||||
final LocalDate maturityDate = this.fromApiJsonHelper.extractLocalDateNamed(MATURITY_DATE, element);
|
||||
final DataValidatorBuilder maturity = v.reset().parameter(MATURITY_DATE).value(maturityDate);
|
||||
if (debt) {
|
||||
// Interest, sale, redemption and amortisation all run to the maturity date.
|
||||
maturity.notNull();
|
||||
}
|
||||
maturity.ignoreIfNull().validateDateAfter(issueDate);
|
||||
}
|
||||
|
||||
validateOptionalString(element, v, COMPANY_NAME, 150);
|
||||
validateOptionalString(element, v, CORP_CODE, 30);
|
||||
validateAddress(element, v);
|
||||
throwIfErrors(errors);
|
||||
}
|
||||
|
||||
// -------------------- shared --------------------
|
||||
|
||||
private void validateBondType(final JsonElement element, final DataValidatorBuilder v, final boolean required) {
|
||||
final String bondType = this.fromApiJsonHelper.extractStringNamed(BOND_TYPE, element);
|
||||
if (required) {
|
||||
v.reset().parameter(BOND_TYPE).value(bondType).notBlank();
|
||||
}
|
||||
if (StringUtils.isNotBlank(bondType)) {
|
||||
v.reset().parameter(BOND_TYPE).value(bondType).isOneOfTheseValues(BOND_TYPES.toArray());
|
||||
}
|
||||
}
|
||||
|
||||
private void validateGlAccountId(final JsonElement element, final DataValidatorBuilder v, final boolean required) {
|
||||
if (!required && !this.fromApiJsonHelper.parameterExists(GL_ACCOUNT_ID, element)) {
|
||||
return;
|
||||
}
|
||||
final Long glAccountId = this.fromApiJsonHelper.extractLongNamed(GL_ACCOUNT_ID, element);
|
||||
v.reset().parameter(GL_ACCOUNT_ID).value(glAccountId).notNull().longGreaterThanZero();
|
||||
}
|
||||
|
||||
private void validateRequiredString(final JsonElement element, final DataValidatorBuilder v, final String param, final int maxLength,
|
||||
final boolean required) {
|
||||
if (!required && !this.fromApiJsonHelper.parameterExists(param, element)) {
|
||||
return;
|
||||
}
|
||||
final String value = this.fromApiJsonHelper.extractStringNamed(param, element);
|
||||
v.reset().parameter(param).value(value).notBlank().notExceedingLengthOf(maxLength);
|
||||
}
|
||||
|
||||
private void validateOptionalString(final JsonElement element, final DataValidatorBuilder v, final String param,
|
||||
final int maxLength) {
|
||||
final String value = this.fromApiJsonHelper.extractStringNamed(param, element);
|
||||
v.reset().parameter(param).value(value).ignoreIfNull().notExceedingLengthOf(maxLength);
|
||||
}
|
||||
|
||||
private void validateAddress(final JsonElement element, final DataValidatorBuilder v) {
|
||||
validateOptionalString(element, v, ADDRESS1, 150);
|
||||
validateOptionalString(element, v, ADDRESS2, 150);
|
||||
validateOptionalString(element, v, ADDRESS3, 150);
|
||||
validateOptionalString(element, v, CITY, 100);
|
||||
validateOptionalString(element, v, PHONE, 20);
|
||||
validateOptionalString(element, v, FAX, 20);
|
||||
final String pincode = this.fromApiJsonHelper.extractStringNamed(PINCODE, element);
|
||||
if (StringUtils.isNotBlank(pincode)) {
|
||||
v.reset().parameter(PINCODE).value(pincode.trim()).matchesRegularExpression(PINCODE_PATTERN, "Pincode must be 6 digits");
|
||||
}
|
||||
}
|
||||
|
||||
private JsonElement parse(final String json, final Set<String> supportedParams) {
|
||||
if (StringUtils.isBlank(json)) {
|
||||
throw new InvalidJsonException();
|
||||
}
|
||||
final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
|
||||
this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, supportedParams);
|
||||
return this.fromApiJsonHelper.parse(json);
|
||||
}
|
||||
|
||||
private void throwIfErrors(final List<ApiParameterError> errors) {
|
||||
if (!errors.isEmpty()) {
|
||||
throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist", "Validation errors exist.", errors);
|
||||
}
|
||||
}
|
||||
}
|
||||
+142
@@ -0,0 +1,142 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.investment.serialization;
|
||||
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentPurchaseApiConstants.BOND_TYPE;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentPurchaseApiConstants.BROKERAGE;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentPurchaseApiConstants.CATEGORIES;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentPurchaseApiConstants.CATEGORY;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentPurchaseApiConstants.CONSIDER_AMT;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentPurchaseApiConstants.CR_GL_ACCOUNT_ID;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentPurchaseApiConstants.DEAL_DATE;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentPurchaseApiConstants.DEAL_TIME;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentPurchaseApiConstants.FACE_VALUE;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentPurchaseApiConstants.LAST_INT_DATE;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentPurchaseApiConstants.NAV;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentPurchaseApiConstants.OTHER_CHARGES;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentPurchaseApiConstants.OTHER_DETAILS;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentPurchaseApiConstants.PARAMS;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentPurchaseApiConstants.PARTY_ID;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentPurchaseApiConstants.PURCHASE_RATE;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentPurchaseApiConstants.RESOURCE;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentPurchaseApiConstants.SCRIPT_ID;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentPurchaseApiConstants.SETTLEMENT_TYPE;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentPurchaseApiConstants.SETTLEMENT_TYPES;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentPurchaseApiConstants.STAMP_DUTY;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentPurchaseApiConstants.SUB_TYPE;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import java.lang.reflect.Type;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.fineract.infrastructure.core.data.ApiParameterError;
|
||||
import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder;
|
||||
import org.apache.fineract.infrastructure.core.exception.InvalidJsonException;
|
||||
import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException;
|
||||
import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
|
||||
import org.apache.fineract.portfolio.investment.api.InvestmentApiConstants;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/** Validates a Purchase Entry before anything is computed or written. */
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public final class InvestmentPurchaseCommandFromApiJsonDeserializer {
|
||||
|
||||
private static final String TIME_PATTERN = "^([01][0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9]$";
|
||||
|
||||
private final FromJsonHelper fromApiJsonHelper;
|
||||
|
||||
public void validateForCreate(final String json) {
|
||||
if (StringUtils.isBlank(json)) {
|
||||
throw new InvalidJsonException();
|
||||
}
|
||||
final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
|
||||
this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, PARAMS);
|
||||
final JsonElement element = this.fromApiJsonHelper.parse(json);
|
||||
|
||||
final List<ApiParameterError> errors = new ArrayList<>();
|
||||
final DataValidatorBuilder v = new DataValidatorBuilder(errors).resource(RESOURCE);
|
||||
|
||||
final String bondType = this.fromApiJsonHelper.extractStringNamed(BOND_TYPE, element);
|
||||
v.reset().parameter(BOND_TYPE).value(bondType).notBlank();
|
||||
if (StringUtils.isNotBlank(bondType)) {
|
||||
v.reset().parameter(BOND_TYPE).value(bondType).isOneOfTheseValues(InvestmentApiConstants.BOND_TYPES.toArray());
|
||||
}
|
||||
v.reset().parameter(SCRIPT_ID).value(this.fromApiJsonHelper.extractLongNamed(SCRIPT_ID, element)).notNull().longGreaterThanZero();
|
||||
v.reset().parameter(PARTY_ID).value(this.fromApiJsonHelper.extractLongNamed(PARTY_ID, element)).notNull().longGreaterThanZero();
|
||||
v.reset().parameter(CR_GL_ACCOUNT_ID).value(this.fromApiJsonHelper.extractLongNamed(CR_GL_ACCOUNT_ID, element)).notNull()
|
||||
.longGreaterThanZero();
|
||||
v.reset().parameter(DEAL_DATE).value(this.fromApiJsonHelper.extractLocalDateNamed(DEAL_DATE, element)).notNull();
|
||||
|
||||
final String settlementType = this.fromApiJsonHelper.extractStringNamed(SETTLEMENT_TYPE, element);
|
||||
v.reset().parameter(SETTLEMENT_TYPE).value(settlementType).notBlank();
|
||||
if (StringUtils.isNotBlank(settlementType)) {
|
||||
v.reset().parameter(SETTLEMENT_TYPE).value(settlementType).isOneOfTheseValues(SETTLEMENT_TYPES.toArray());
|
||||
}
|
||||
|
||||
final String category = this.fromApiJsonHelper.extractStringNamed(CATEGORY, element);
|
||||
v.reset().parameter(CATEGORY).value(category).notBlank();
|
||||
if (StringUtils.isNotBlank(category)) {
|
||||
v.reset().parameter(CATEGORY).value(category).isOneOfTheseValues(CATEGORIES.toArray());
|
||||
}
|
||||
|
||||
if ("MUTUAL_FUND".equals(bondType)) {
|
||||
v.reset().parameter(CONSIDER_AMT).value(amount(CONSIDER_AMT, element)).notNull().positiveAmount();
|
||||
v.reset().parameter(NAV).value(amount(NAV, element)).notNull().positiveAmount();
|
||||
} else {
|
||||
v.reset().parameter(FACE_VALUE).value(amount(FACE_VALUE, element)).notNull().positiveAmount();
|
||||
v.reset().parameter(PURCHASE_RATE).value(amount(PURCHASE_RATE, element)).notNull().positiveAmount();
|
||||
v.reset().parameter(LAST_INT_DATE).value(this.fromApiJsonHelper.extractLocalDateNamed(LAST_INT_DATE, element)).notNull();
|
||||
}
|
||||
v.reset().parameter(BROKERAGE).value(amount(BROKERAGE, element)).ignoreIfNull().zeroOrPositiveAmount();
|
||||
v.reset().parameter(STAMP_DUTY).value(amount(STAMP_DUTY, element)).ignoreIfNull().zeroOrPositiveAmount();
|
||||
v.reset().parameter(OTHER_CHARGES).value(amount(OTHER_CHARGES, element)).ignoreIfNull().zeroOrPositiveAmount();
|
||||
|
||||
final String dealTime = this.fromApiJsonHelper.extractStringNamed(DEAL_TIME, element);
|
||||
if (StringUtils.isNotBlank(dealTime)) {
|
||||
v.reset().parameter(DEAL_TIME).value(dealTime).matchesRegularExpression(TIME_PATTERN, "Deal Time must be HH:mm:ss (24 hr)");
|
||||
}
|
||||
v.reset().parameter(SUB_TYPE).value(this.fromApiJsonHelper.extractStringNamed(SUB_TYPE, element)).ignoreIfNull()
|
||||
.notExceedingLengthOf(40);
|
||||
|
||||
if (this.fromApiJsonHelper.parameterExists(OTHER_DETAILS, element)) {
|
||||
final JsonElement other = element.getAsJsonObject().get(OTHER_DETAILS);
|
||||
if (!other.isJsonNull() && !other.isJsonObject()) {
|
||||
v.reset().parameter(OTHER_DETAILS).failWithCode("must.be.an.object");
|
||||
} else if (other.isJsonObject() && other.toString().length() > 4000) {
|
||||
v.reset().parameter(OTHER_DETAILS).failWithCode("too.long");
|
||||
}
|
||||
}
|
||||
|
||||
if (!errors.isEmpty()) {
|
||||
throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist", "Validation errors exist.", errors);
|
||||
}
|
||||
}
|
||||
|
||||
private BigDecimal amount(final String param, final JsonElement element) {
|
||||
return this.fromApiJsonHelper.parameterExists(param, element)
|
||||
? this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(param, element)
|
||||
: null;
|
||||
}
|
||||
}
|
||||
+126
@@ -0,0 +1,126 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.investment.serialization;
|
||||
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentSaleApiConstants.AMOUNT;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentSaleApiConstants.BANK_GL_ACCOUNT_ID;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentSaleApiConstants.BROKERAGE;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentSaleApiConstants.DEAL_DATE;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentSaleApiConstants.PARTY_ID;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentSaleApiConstants.PURCHASE_ID;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentSaleApiConstants.REDEEM_PARAMS;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentSaleApiConstants.REMARKS;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentSaleApiConstants.RESOURCE;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentSaleApiConstants.SALE_RATE;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentSaleApiConstants.SANCTION_AUTHORITY;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentSaleApiConstants.SELL_PARAMS;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentSaleApiConstants.SETTLEMENT_TYPE;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentSaleApiConstants.UNITS;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentSaleApiConstants.WRITEOFF_PARAMS;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.fineract.infrastructure.core.data.ApiParameterError;
|
||||
import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder;
|
||||
import org.apache.fineract.infrastructure.core.exception.InvalidJsonException;
|
||||
import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException;
|
||||
import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
|
||||
import org.apache.fineract.portfolio.investment.api.InvestmentPurchaseApiConstants;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/** Validates a sale entry and a redemption request. */
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public final class InvestmentSaleCommandFromApiJsonDeserializer {
|
||||
|
||||
private final FromJsonHelper fromApiJsonHelper;
|
||||
|
||||
public void validateForSell(final String json) {
|
||||
final JsonElement element = parse(json, SELL_PARAMS);
|
||||
final List<ApiParameterError> errors = new ArrayList<>();
|
||||
final DataValidatorBuilder v = new DataValidatorBuilder(errors).resource(RESOURCE);
|
||||
v.reset().parameter(PURCHASE_ID).value(this.fromApiJsonHelper.extractLongNamed(PURCHASE_ID, element)).notNull()
|
||||
.longGreaterThanZero();
|
||||
v.reset().parameter(PARTY_ID).value(this.fromApiJsonHelper.extractLongNamed(PARTY_ID, element)).notNull().longGreaterThanZero();
|
||||
v.reset().parameter(BANK_GL_ACCOUNT_ID).value(this.fromApiJsonHelper.extractLongNamed(BANK_GL_ACCOUNT_ID, element)).notNull()
|
||||
.longGreaterThanZero();
|
||||
v.reset().parameter(DEAL_DATE).value(this.fromApiJsonHelper.extractLocalDateNamed(DEAL_DATE, element)).notNull();
|
||||
final String settlementType = this.fromApiJsonHelper.extractStringNamed(SETTLEMENT_TYPE, element);
|
||||
v.reset().parameter(SETTLEMENT_TYPE).value(settlementType).notBlank();
|
||||
if (StringUtils.isNotBlank(settlementType)) {
|
||||
v.reset().parameter(SETTLEMENT_TYPE).value(settlementType)
|
||||
.isOneOfTheseValues(InvestmentPurchaseApiConstants.SETTLEMENT_TYPES.toArray());
|
||||
}
|
||||
v.reset().parameter(UNITS).value(this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(UNITS, element)).notNull()
|
||||
.positiveAmount();
|
||||
v.reset().parameter(SALE_RATE).value(this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(SALE_RATE, element)).notNull()
|
||||
.positiveAmount();
|
||||
v.reset().parameter(BROKERAGE).value(this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(BROKERAGE, element)).ignoreIfNull()
|
||||
.zeroOrPositiveAmount();
|
||||
v.reset().parameter(SANCTION_AUTHORITY).value(this.fromApiJsonHelper.extractStringNamed(SANCTION_AUTHORITY, element))
|
||||
.ignoreIfNull().notExceedingLengthOf(100);
|
||||
v.reset().parameter(REMARKS).value(this.fromApiJsonHelper.extractStringNamed(REMARKS, element)).ignoreIfNull()
|
||||
.notExceedingLengthOf(500);
|
||||
throwIfAny(errors);
|
||||
}
|
||||
|
||||
public void validateForRedeem(final String json) {
|
||||
final JsonElement element = parse(json, REDEEM_PARAMS);
|
||||
final List<ApiParameterError> errors = new ArrayList<>();
|
||||
final DataValidatorBuilder v = new DataValidatorBuilder(errors).resource(RESOURCE);
|
||||
v.reset().parameter(BANK_GL_ACCOUNT_ID).value(this.fromApiJsonHelper.extractLongNamed(BANK_GL_ACCOUNT_ID, element)).ignoreIfNull()
|
||||
.longGreaterThanZero();
|
||||
v.reset().parameter(REMARKS).value(this.fromApiJsonHelper.extractStringNamed(REMARKS, element)).ignoreIfNull()
|
||||
.notExceedingLengthOf(500);
|
||||
throwIfAny(errors);
|
||||
}
|
||||
|
||||
public void validateForWriteOff(final String json) {
|
||||
final JsonElement element = parse(json, WRITEOFF_PARAMS);
|
||||
final List<ApiParameterError> errors = new ArrayList<>();
|
||||
final DataValidatorBuilder v = new DataValidatorBuilder(errors).resource(RESOURCE);
|
||||
v.reset().parameter(AMOUNT).value(this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(AMOUNT, element)).notNull()
|
||||
.positiveAmount();
|
||||
v.reset().parameter(REMARKS).value(this.fromApiJsonHelper.extractStringNamed(REMARKS, element)).notBlank()
|
||||
.notExceedingLengthOf(500);
|
||||
throwIfAny(errors);
|
||||
}
|
||||
|
||||
private JsonElement parse(final String json, final Set<String> params) {
|
||||
if (StringUtils.isBlank(json)) {
|
||||
throw new InvalidJsonException();
|
||||
}
|
||||
final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
|
||||
this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, params);
|
||||
return this.fromApiJsonHelper.parse(json);
|
||||
}
|
||||
|
||||
private static void throwIfAny(final List<ApiParameterError> errors) {
|
||||
if (!errors.isEmpty()) {
|
||||
throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist", "Validation errors exist.", errors);
|
||||
}
|
||||
}
|
||||
}
|
||||
+304
@@ -0,0 +1,304 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.investment.service;
|
||||
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentPurchaseApiConstants.STATUS_ACTIVE;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentSaleApiConstants.AMORTISE_PARAMS;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentSaleApiConstants.FY_END;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentSaleApiConstants.PREMIUM_AMORTISATION_GL_CODE;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentSaleApiConstants.PURCHASE_ID;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentSaleApiConstants.STATUS_SETTLED;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentSaleApiConstants.TYPE_AMORTISE;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import java.lang.reflect.Type;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.time.LocalDate;
|
||||
import java.time.Month;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.infrastructure.core.data.ApiParameterError;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuilder;
|
||||
import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder;
|
||||
import org.apache.fineract.infrastructure.core.exception.InvalidJsonException;
|
||||
import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException;
|
||||
import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
|
||||
import org.apache.fineract.infrastructure.core.service.DateUtils;
|
||||
import org.apache.fineract.portfolio.investment.data.InvestmentAmortisationData;
|
||||
import org.apache.fineract.portfolio.investment.data.InvestmentAmortisationDueData;
|
||||
import org.apache.fineract.portfolio.investment.domain.InvestmentPurchase;
|
||||
import org.apache.fineract.portfolio.investment.domain.InvestmentPurchaseRepository;
|
||||
import org.apache.fineract.portfolio.investment.domain.InvestmentSale;
|
||||
import org.apache.fineract.portfolio.investment.domain.InvestmentSaleRepository;
|
||||
import org.apache.fineract.portfolio.investment.exception.InvestmentPurchaseNotFoundException;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
/**
|
||||
* Premium amortisation of HTM holdings: the premium paid over face value is written off straight-line, day by day, over
|
||||
* the period remaining to maturity (RBI; method and 31 March timing confirmed with Jitendra Sir). Each 31 March posts the
|
||||
* share for the days since the last amortisation (or settlement): unamortised premium x days to 31 March / days to
|
||||
* maturity — so a part sale, which takes its share of premium with it, is followed automatically. The remainder after
|
||||
* the last 31 March is written off with the redemption. Entry: Dr 500604 Amortisation of Premium / Cr Premium GL.
|
||||
*/
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class InvestmentAmortisationService {
|
||||
|
||||
private static final DateTimeFormatter DATE = DateTimeFormatter.ofPattern("dd-MM-yyyy");
|
||||
private static final String HTM_PREFIX = "HTM";
|
||||
|
||||
private final FromJsonHelper fromApiJsonHelper;
|
||||
private final InvestmentPurchaseRepository purchaseRepository;
|
||||
private final InvestmentSaleRepository saleRepository;
|
||||
private final InvestmentSaleQuoter quoter;
|
||||
private final InvestmentJournalPoster journalPoster;
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
public InvestmentAmortisationData schedule(final Long purchaseId) {
|
||||
final InvestmentPurchase p = purchase(purchaseId);
|
||||
final List<InvestmentSale> posted = amortisations(p);
|
||||
final LocalDate today = DateUtils.getBusinessLocalDate();
|
||||
final List<InvestmentAmortisationData.Row> rows = new ArrayList<>();
|
||||
BigDecimal premium = this.quoter.unamortisedPremium(p);
|
||||
LocalDate start = p.getSettlementDate();
|
||||
boolean nextFound = false;
|
||||
for (final LocalDate fyEnd : yearEnds(p)) {
|
||||
final Optional<InvestmentSale> done = posted.stream().filter(x -> x.getDealDate().equals(fyEnd)).findFirst();
|
||||
final long days = ChronoUnit.DAYS.between(start, fyEnd);
|
||||
if (done.isPresent()) {
|
||||
rows.add(row("YEAR", fyEnd, start, days, done.get().getBookValueSold(), null, true, done.get(), false));
|
||||
} else {
|
||||
final BigDecimal amount = share(premium, start, fyEnd, p.getMaturityDate());
|
||||
premium = premium.subtract(amount);
|
||||
final boolean postable = STATUS_ACTIVE.equals(p.getStatus()) && !nextFound && !fyEnd.isAfter(today);
|
||||
nextFound = true;
|
||||
rows.add(row("YEAR", fyEnd, start, days, amount, premium, false, null, postable));
|
||||
}
|
||||
start = fyEnd;
|
||||
}
|
||||
if (premium.signum() > 0 && STATUS_ACTIVE.equals(p.getStatus())) {
|
||||
rows.add(row("MATURITY", p.getMaturityDate(), start, ChronoUnit.DAYS.between(start, p.getMaturityDate()), premium,
|
||||
BigDecimal.ZERO, false, null, false));
|
||||
}
|
||||
return InvestmentAmortisationData.builder().purchaseId(p.getId()).dealNo(p.getDealNo()).certNo(p.getCertNo())
|
||||
.scriptCode(p.getScript().getScriptCode()).scriptDesc(p.getScript().getScriptDesc()).category(p.getCategory())
|
||||
.settlementDate(p.getSettlementDate().format(DATE)).maturityDate(p.getMaturityDate().format(DATE))
|
||||
.faceValue(p.getBalanceUnits().multiply(p.getScript().getFaceValuePerUnit()))
|
||||
.premium(p.getPremiumDiscount() == null ? BigDecimal.ZERO : p.getPremiumDiscount().max(BigDecimal.ZERO))
|
||||
.unamortisedPremium(this.quoter.unamortisedPremium(p)).bookValue(this.quoter.remainingBook(p))
|
||||
.premiumGlCode(p.getPremiumGlAccount() != null ? p.getPremiumGlAccount().getGlCode() : p.getInvestmentGlAccount().getGlCode())
|
||||
.rows(rows).build();
|
||||
}
|
||||
|
||||
/** HTM holdings with premium whose next amortisation falls on {@code fyEnd} (Amortize Yearly Process list). */
|
||||
@Transactional(readOnly = true)
|
||||
public List<InvestmentAmortisationDueData> due(final LocalDate fyEnd) {
|
||||
final List<InvestmentAmortisationDueData> due = new ArrayList<>();
|
||||
for (final InvestmentPurchase p : this.purchaseRepository.findAll(Sort.by(Sort.Direction.ASC, "id"))) {
|
||||
if (eligible(p) && fyEnd.equals(nextYearEnd(p))) {
|
||||
final LocalDate start = periodStart(p);
|
||||
final BigDecimal premium = this.quoter.unamortisedPremium(p);
|
||||
due.add(InvestmentAmortisationDueData.builder().purchaseId(p.getId()).dealNo(p.getDealNo()).certNo(p.getCertNo())
|
||||
.scriptCode(p.getScript().getScriptCode()).scriptDesc(p.getScript().getScriptDesc()).fyEnd(fyEnd.format(DATE))
|
||||
.fromDate(start.format(DATE)).days(ChronoUnit.DAYS.between(start, fyEnd)).unamortisedPremium(premium)
|
||||
.amount(share(premium, start, fyEnd, p.getMaturityDate())).build());
|
||||
}
|
||||
}
|
||||
return due;
|
||||
}
|
||||
|
||||
/**
|
||||
* Posts the amortisation for {@code fyEnd} (a 31 March on or before today): for one holding when purchaseId is
|
||||
* given (Amortize Entry), else for every holding due that year (Amortize Yearly Process). One JV per holding.
|
||||
*/
|
||||
@Transactional
|
||||
public CommandProcessingResult amortise(final JsonCommand command) {
|
||||
validate(command.json());
|
||||
final LocalDate fyEnd = command.localDateValueOfParameterNamed(FY_END);
|
||||
if (fyEnd.getMonth() != Month.MARCH || fyEnd.getDayOfMonth() != 31) {
|
||||
throw InvestmentSaleQuoter.rule("amortise.not.yearend", "Amortisation is done as on 31 March");
|
||||
}
|
||||
if (fyEnd.isAfter(DateUtils.getBusinessLocalDate())) {
|
||||
throw InvestmentSaleQuoter.rule("amortise.future", "Amortisation for " + fyEnd.format(DATE) + " can be done on or after that date");
|
||||
}
|
||||
final Long purchaseId = command.longValueOfParameterNamed(PURCHASE_ID);
|
||||
final List<InvestmentPurchase> targets = new ArrayList<>();
|
||||
if (purchaseId != null) {
|
||||
final InvestmentPurchase p = lockedPurchase(purchaseId);
|
||||
if (!eligible(p)) {
|
||||
throw InvestmentSaleQuoter.rule("amortise.not.eligible",
|
||||
"Deal " + p.getDealNo() + " has no premium left to amortise (only active HTM holdings bought above face value)");
|
||||
}
|
||||
final LocalDate next = nextYearEnd(p);
|
||||
if (!fyEnd.equals(next)) {
|
||||
throw InvestmentSaleQuoter.rule("amortise.out.of.order", next == null ? "Deal " + p.getDealNo() + " has no 31 March to amortise"
|
||||
: "Amortise deal " + p.getDealNo() + " year by year: the next is " + next.format(DATE));
|
||||
}
|
||||
targets.add(p);
|
||||
} else {
|
||||
for (final InvestmentPurchase found : this.purchaseRepository.findAll(Sort.by(Sort.Direction.ASC, "id"))) {
|
||||
if (eligible(found) && fyEnd.equals(nextYearEnd(found))) {
|
||||
// Lock, then check again: another run may have posted this year meanwhile.
|
||||
final InvestmentPurchase p = lockedPurchase(found.getId());
|
||||
if (eligible(p) && fyEnd.equals(nextYearEnd(p))) {
|
||||
targets.add(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (targets.isEmpty()) {
|
||||
throw InvestmentSaleQuoter.rule("amortise.nothing.due", "No holding has premium amortisation due for " + fyEnd.format(DATE));
|
||||
}
|
||||
}
|
||||
final List<String> vouchers = new ArrayList<>();
|
||||
Long lastId = null;
|
||||
for (final InvestmentPurchase p : targets) {
|
||||
final InvestmentSale s = post(p, fyEnd);
|
||||
if (s.getVoucherNo() != null) {
|
||||
vouchers.add(s.getVoucherNo());
|
||||
}
|
||||
lastId = s.getId();
|
||||
}
|
||||
return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withEntityId(lastId)
|
||||
.withResourceIdAsString(String.join(", ", vouchers)).build();
|
||||
}
|
||||
|
||||
private InvestmentSale post(final InvestmentPurchase p, final LocalDate fyEnd) {
|
||||
final LocalDate start = periodStart(p);
|
||||
final BigDecimal amount = share(this.quoter.unamortisedPremium(p), start, fyEnd, p.getMaturityDate());
|
||||
final String fy = (fyEnd.getYear() - 1) + "-" + String.format("%02d", fyEnd.getYear() % 100);
|
||||
// A share that rounds to nothing is still recorded, so the year is done and the next one can follow; it needs no JV.
|
||||
final CommandProcessingResult posted = amount.signum() == 0 ? null
|
||||
: this.journalPoster.post(fyEnd, p.getDealNo(),
|
||||
"Premium amortisation FY " + fy + ": deal " + p.getDealNo() + ", " + p.getScript().getScriptCode() + " "
|
||||
+ p.getScript().getScriptDesc() + " (" + ChronoUnit.DAYS.between(start, fyEnd) + " days)",
|
||||
new InvestmentJournalPoster.Lines().debit(this.journalPoster.glId(PREMIUM_AMORTISATION_GL_CODE), amount)
|
||||
.credit(InvestmentSaleQuoter.premiumGlId(p), amount));
|
||||
final InvestmentSale s = new InvestmentSale();
|
||||
s.setPurchase(p);
|
||||
s.setSaleType(TYPE_AMORTISE);
|
||||
s.setDealDate(fyEnd);
|
||||
s.setSettlementDate(fyEnd);
|
||||
s.setUnits(BigDecimal.ZERO);
|
||||
s.setFaceValue(BigDecimal.ZERO);
|
||||
s.setConsideration(BigDecimal.ZERO);
|
||||
s.setAccruedDays((int) ChronoUnit.DAYS.between(start, fyEnd));
|
||||
s.setAccruedInterest(BigDecimal.ZERO);
|
||||
s.setReceivableCleared(BigDecimal.ZERO);
|
||||
s.setBrokerage(BigDecimal.ZERO);
|
||||
s.setBookValueSold(amount);
|
||||
s.setPremiumCleared(amount);
|
||||
s.setProfitLoss(amount.negate());
|
||||
s.setNetAmount(BigDecimal.ZERO);
|
||||
s.setBankGlAccount(p.getCrGlAccount());
|
||||
s.setRemarks("Premium amortisation FY " + fy);
|
||||
s.setStatus(STATUS_SETTLED);
|
||||
s.setGlTransactionId(posted == null ? null : posted.getTransactionId());
|
||||
final Object voucherNo = posted == null || posted.getChanges() == null ? null : posted.getChanges().get("voucherNo");
|
||||
s.setVoucherNo(voucherNo == null ? null : voucherNo.toString());
|
||||
s.setSettledOnDate(DateUtils.getBusinessLocalDate());
|
||||
return this.saleRepository.saveAndFlush(s);
|
||||
}
|
||||
|
||||
/** Straight-line by days: the share of the unamortised premium for start..yearEnd out of start..maturity. */
|
||||
static BigDecimal share(final BigDecimal premium, final LocalDate start, final LocalDate yearEnd, final LocalDate maturity) {
|
||||
final long total = ChronoUnit.DAYS.between(start, maturity);
|
||||
if (premium.signum() <= 0 || total <= 0) {
|
||||
return BigDecimal.ZERO;
|
||||
}
|
||||
return premium.multiply(BigDecimal.valueOf(ChronoUnit.DAYS.between(start, yearEnd))).divide(BigDecimal.valueOf(total), 2,
|
||||
RoundingMode.HALF_UP);
|
||||
}
|
||||
|
||||
private boolean eligible(final InvestmentPurchase p) {
|
||||
return STATUS_ACTIVE.equals(p.getStatus()) && p.getCategory() != null && p.getCategory().startsWith(HTM_PREFIX)
|
||||
&& p.getMaturityDate() != null && this.quoter.unamortisedPremium(p).signum() > 0;
|
||||
}
|
||||
|
||||
/** The first 31 March after the last amortisation (or the settlement) that is still before maturity. */
|
||||
private LocalDate nextYearEnd(final InvestmentPurchase p) {
|
||||
final LocalDate start = periodStart(p);
|
||||
return yearEnds(p).stream().filter(d -> d.isAfter(start)).findFirst().orElse(null);
|
||||
}
|
||||
|
||||
private LocalDate periodStart(final InvestmentPurchase p) {
|
||||
return amortisations(p).stream().map(InvestmentSale::getDealDate).max(LocalDate::compareTo).orElse(p.getSettlementDate());
|
||||
}
|
||||
|
||||
/** 31 March dates after the settlement and before maturity. */
|
||||
private static List<LocalDate> yearEnds(final InvestmentPurchase p) {
|
||||
final List<LocalDate> ends = new ArrayList<>();
|
||||
for (int y = p.getSettlementDate().getYear(); y <= p.getMaturityDate().getYear(); y++) {
|
||||
final LocalDate d = LocalDate.of(y, Month.MARCH, 31);
|
||||
if (d.isAfter(p.getSettlementDate()) && d.isBefore(p.getMaturityDate())) {
|
||||
ends.add(d);
|
||||
}
|
||||
}
|
||||
return ends;
|
||||
}
|
||||
|
||||
private List<InvestmentSale> amortisations(final InvestmentPurchase p) {
|
||||
return this.saleRepository.findByPurchaseIdOrderByIdAsc(p.getId()).stream()
|
||||
.filter(s -> TYPE_AMORTISE.equals(s.getSaleType()) && STATUS_SETTLED.equals(s.getStatus())).toList();
|
||||
}
|
||||
|
||||
private static InvestmentAmortisationData.Row row(final String kind, final LocalDate end, final LocalDate start, final long days,
|
||||
final BigDecimal amount, final BigDecimal after, final boolean posted, final InvestmentSale done, final boolean postable) {
|
||||
return InvestmentAmortisationData.Row.builder().kind(kind).fyEnd(end.format(DATE)).fromDate(start.format(DATE))
|
||||
.toDate(end.format(DATE)).days(days).amount(amount).premiumAfter(after).posted(posted)
|
||||
.voucherNo(done == null ? null : done.getVoucherNo()).glTransactionId(done == null ? null : done.getGlTransactionId())
|
||||
.postable(postable).build();
|
||||
}
|
||||
|
||||
private InvestmentPurchase lockedPurchase(final Long purchaseId) {
|
||||
return this.purchaseRepository.findByIdWithLock(purchaseId).orElseThrow(() -> new InvestmentPurchaseNotFoundException(purchaseId));
|
||||
}
|
||||
|
||||
private InvestmentPurchase purchase(final Long purchaseId) {
|
||||
return this.purchaseRepository.findById(purchaseId).orElseThrow(() -> new InvestmentPurchaseNotFoundException(purchaseId));
|
||||
}
|
||||
|
||||
private void validate(final String json) {
|
||||
if (StringUtils.isBlank(json)) {
|
||||
throw new InvalidJsonException();
|
||||
}
|
||||
final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
|
||||
this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, AMORTISE_PARAMS);
|
||||
final JsonElement element = this.fromApiJsonHelper.parse(json);
|
||||
final List<ApiParameterError> errors = new ArrayList<>();
|
||||
final DataValidatorBuilder v = new DataValidatorBuilder(errors).resource("investmentsale");
|
||||
v.reset().parameter(FY_END).value(this.fromApiJsonHelper.extractLocalDateNamed(FY_END, element)).notNull();
|
||||
v.reset().parameter(PURCHASE_ID).value(this.fromApiJsonHelper.extractLongNamed(PURCHASE_ID, element)).ignoreIfNull()
|
||||
.longGreaterThanZero();
|
||||
if (!errors.isEmpty()) {
|
||||
throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist", "Validation errors exist.", errors);
|
||||
}
|
||||
}
|
||||
}
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.investment.service;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.fineract.portfolio.investment.data.InvestmentDailyRateData;
|
||||
import org.apache.fineract.portfolio.investment.domain.InvestmentDailyRateRepository;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
@Transactional(readOnly = true)
|
||||
public class InvestmentDailyRateReadPlatformService {
|
||||
|
||||
/** The register shows the latest rates first; older ones are reached by picking a script. */
|
||||
private static final int MAX_ROWS = 500;
|
||||
|
||||
private final InvestmentDailyRateRepository dailyRateRepository;
|
||||
|
||||
public List<InvestmentDailyRateData> retrieveAll(final Long scriptId) {
|
||||
if (scriptId != null) {
|
||||
return this.dailyRateRepository.findByScriptIdOrderByRateDateDesc(scriptId).stream().map(InvestmentDailyRateData::from)
|
||||
.toList();
|
||||
}
|
||||
return this.dailyRateRepository
|
||||
.findAll(PageRequest.of(0, MAX_ROWS, Sort.by(Sort.Direction.DESC, "rateDate").and(Sort.by(Sort.Direction.DESC, "id"))))
|
||||
.stream().map(InvestmentDailyRateData::from).toList();
|
||||
}
|
||||
|
||||
/** The newest rate of a script on or before {@code onDate}; null when none has been entered. */
|
||||
public InvestmentDailyRateData retrieveLatest(final Long scriptId, final LocalDate onDate) {
|
||||
return this.dailyRateRepository.findFirstByScriptIdAndRateDateLessThanEqualOrderByRateDateDesc(scriptId, onDate)
|
||||
.map(InvestmentDailyRateData::from).orElse(null);
|
||||
}
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.investment.service;
|
||||
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
|
||||
public interface InvestmentDailyRateWritePlatformService {
|
||||
|
||||
CommandProcessingResult createDailyRate(JsonCommand command);
|
||||
|
||||
CommandProcessingResult deleteDailyRate(Long rateId);
|
||||
}
|
||||
+87
@@ -0,0 +1,87 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.investment.service;
|
||||
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentDailyRateApiConstants.CLOSE_RATE;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentDailyRateApiConstants.HIGH_RATE;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentDailyRateApiConstants.LOW_RATE;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentDailyRateApiConstants.OPEN_RATE;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentDailyRateApiConstants.RATE_DATE;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentDailyRateApiConstants.SCRIPT_ID;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuilder;
|
||||
import org.apache.fineract.infrastructure.core.exception.GeneralPlatformDomainRuleException;
|
||||
import org.apache.fineract.portfolio.investment.domain.InvestmentDailyRate;
|
||||
import org.apache.fineract.portfolio.investment.domain.InvestmentDailyRateRepository;
|
||||
import org.apache.fineract.portfolio.investment.domain.InvestmentScript;
|
||||
import org.apache.fineract.portfolio.investment.domain.InvestmentScriptRepository;
|
||||
import org.apache.fineract.portfolio.investment.exception.InvestmentDailyRateNotFoundException;
|
||||
import org.apache.fineract.portfolio.investment.exception.InvestmentScriptNotFoundException;
|
||||
import org.apache.fineract.portfolio.investment.serialization.InvestmentDailyRateCommandFromApiJsonDeserializer;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
/** Saves and drops Daily Script Master rates. A wrong rate is corrected by dropping it and entering it again. */
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class InvestmentDailyRateWritePlatformServiceImpl implements InvestmentDailyRateWritePlatformService {
|
||||
|
||||
private final InvestmentDailyRateCommandFromApiJsonDeserializer validator;
|
||||
private final InvestmentDailyRateRepository dailyRateRepository;
|
||||
private final InvestmentScriptRepository scriptRepository;
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public CommandProcessingResult createDailyRate(final JsonCommand command) {
|
||||
this.validator.validateForCreate(command.json());
|
||||
final Long scriptId = command.longValueOfParameterNamed(SCRIPT_ID);
|
||||
final InvestmentScript script = this.scriptRepository.findById(scriptId)
|
||||
.orElseThrow(() -> new InvestmentScriptNotFoundException(scriptId));
|
||||
final LocalDate rateDate = command.localDateValueOfParameterNamed(RATE_DATE);
|
||||
if (this.dailyRateRepository.existsByScriptIdAndRateDate(scriptId, rateDate)) {
|
||||
throw new GeneralPlatformDomainRuleException("error.msg.investmentdailyrate.duplicate",
|
||||
"A rate for " + script.getScriptCode() + " on this date already exists. Drop it first to correct it.");
|
||||
}
|
||||
|
||||
final InvestmentDailyRate rate = new InvestmentDailyRate();
|
||||
rate.setScript(script);
|
||||
rate.setRateDate(rateDate);
|
||||
rate.setOpenRate(command.bigDecimalValueOfParameterNamed(OPEN_RATE));
|
||||
rate.setHighRate(command.bigDecimalValueOfParameterNamed(HIGH_RATE));
|
||||
rate.setLowRate(command.bigDecimalValueOfParameterNamed(LOW_RATE));
|
||||
rate.setCloseRate(command.bigDecimalValueOfParameterNamed(CLOSE_RATE));
|
||||
final InvestmentDailyRate saved = this.dailyRateRepository.saveAndFlush(rate);
|
||||
return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withEntityId(saved.getId())
|
||||
.withResourceIdAsString(script.getScriptCode()).build();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public CommandProcessingResult deleteDailyRate(final Long rateId) {
|
||||
final InvestmentDailyRate rate = this.dailyRateRepository.findById(rateId)
|
||||
.orElseThrow(() -> new InvestmentDailyRateNotFoundException(rateId));
|
||||
this.dailyRateRepository.delete(rate);
|
||||
this.dailyRateRepository.flush();
|
||||
return new CommandProcessingResultBuilder().withEntityId(rateId).build();
|
||||
}
|
||||
}
|
||||
+79
@@ -0,0 +1,79 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.investment.service;
|
||||
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentFdApiConstants.CALC_HALF_YEARLY;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentFdApiConstants.CALC_MONTHLY;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentFdApiConstants.CALC_QUARTERLY;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentFdApiConstants.CALC_SIMPLE;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentFdApiConstants.CALC_YEARLY;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentFdApiConstants.PAYOUT_AT_MATURITY;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentFdApiConstants.PAYOUT_HALF_YEARLY;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.time.LocalDate;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
|
||||
/**
|
||||
* Interest on an FD with another bank, on actual days over a 365-day year. A payout FD (interest paid every half
|
||||
* year / year) earns simple interest; an FD paid at maturity compounds at the chosen frequency, or is simple. The
|
||||
* user also enters the bank's own figure at maturity, so a different compounding shows up as a difference.
|
||||
*/
|
||||
public final class InvestmentFdCalculator {
|
||||
|
||||
private static final BigDecimal HUNDRED = BigDecimal.valueOf(100);
|
||||
private static final BigDecimal DAYS_IN_YEAR = BigDecimal.valueOf(365);
|
||||
|
||||
private InvestmentFdCalculator() {}
|
||||
|
||||
public static BigDecimal interest(final BigDecimal principal, final BigDecimal ratePercent, final LocalDate from, final LocalDate to,
|
||||
final String calcType, final String payout) {
|
||||
final long days = ChronoUnit.DAYS.between(from, to);
|
||||
if (days <= 0 || principal == null || ratePercent == null || principal.signum() <= 0 || ratePercent.signum() <= 0) {
|
||||
return BigDecimal.ZERO;
|
||||
}
|
||||
if (!PAYOUT_AT_MATURITY.equals(payout) || CALC_SIMPLE.equals(calcType)) {
|
||||
return principal.multiply(ratePercent).multiply(BigDecimal.valueOf(days)).divide(HUNDRED.multiply(DAYS_IN_YEAR), 2,
|
||||
RoundingMode.HALF_UP);
|
||||
}
|
||||
final int n = periodsPerYear(calcType);
|
||||
final double factor = Math.pow(1 + ratePercent.doubleValue() / 100 / n, n * (days / 365.0));
|
||||
return principal.multiply(BigDecimal.valueOf(factor - 1)).setScale(2, RoundingMode.HALF_UP);
|
||||
}
|
||||
|
||||
/** Interest paid each half year or year on a payout FD; zero for an FD paid at maturity. */
|
||||
public static BigDecimal periodInterest(final BigDecimal principal, final BigDecimal ratePercent, final String payout) {
|
||||
if (PAYOUT_AT_MATURITY.equals(payout) || principal == null || ratePercent == null) {
|
||||
return BigDecimal.ZERO;
|
||||
}
|
||||
final BigDecimal perYear = principal.multiply(ratePercent).divide(HUNDRED, 2, RoundingMode.HALF_UP);
|
||||
return PAYOUT_HALF_YEARLY.equals(payout) ? perYear.divide(BigDecimal.valueOf(2), 2, RoundingMode.HALF_UP) : perYear;
|
||||
}
|
||||
|
||||
private static int periodsPerYear(final String calcType) {
|
||||
return switch (calcType) {
|
||||
case CALC_MONTHLY -> 12;
|
||||
case CALC_QUARTERLY -> 4;
|
||||
case CALC_HALF_YEARLY -> 2;
|
||||
case CALC_YEARLY -> 1;
|
||||
default -> throw new IllegalArgumentException("Unknown calculation type " + calcType);
|
||||
};
|
||||
}
|
||||
}
|
||||
+463
@@ -0,0 +1,463 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.investment.service;
|
||||
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentFdApiConstants.CALC_TYPES;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentFdApiConstants.CLOSE_DATE;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentFdApiConstants.CLOSURE_MATURITY;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentFdApiConstants.CLOSURE_PREMATURE;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentFdApiConstants.CLOSURE_RENEWAL;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentFdApiConstants.DIFF_BANK_GL_ACCOUNT_ID;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentFdApiConstants.INTEREST_BANK_GL_ACCOUNT_ID;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentFdApiConstants.INTEREST_RECEIVABLE_GL_CODE;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentFdApiConstants.LINE_PAYOUT;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentFdApiConstants.LINE_PROVISION;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentFdApiConstants.MATURE_PARAMS;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentFdApiConstants.MATURITY_AMOUNT;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentFdApiConstants.NEW_CALC_TYPE;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentFdApiConstants.NEW_CERT_DATE;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentFdApiConstants.NEW_CERT_NO;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentFdApiConstants.NEW_INT_RATE;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentFdApiConstants.NEW_MATURITY_DATE;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentFdApiConstants.NEW_PAYOUT;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentFdApiConstants.PAYOUTS;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentFdApiConstants.PENAL_AMOUNT;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentFdApiConstants.PREMATURE_PARAMS;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentFdApiConstants.PRINCIPAL_BANK_GL_ACCOUNT_ID;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentFdApiConstants.RECEIVED_AMOUNT;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentFdApiConstants.REMARKS;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentFdApiConstants.RENEWAL_AMOUNT;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentFdApiConstants.RENEW_PARAMS;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentFdApiConstants.STATUS_ACTIVE;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentFdApiConstants.STATUS_MATURED;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentFdApiConstants.STATUS_PREMATURE;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentFdApiConstants.STATUS_RENEWED;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentFdApiConstants.TDS;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentFdApiConstants.TDS_RECEIVABLE_GL_CODE;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import java.lang.reflect.Type;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.time.LocalDate;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.fineract.accounting.glaccount.domain.GLAccount;
|
||||
import org.apache.fineract.accounting.glaccount.domain.GLAccountRepositoryWrapper;
|
||||
import org.apache.fineract.accounting.glaccount.domain.GLAccountType;
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.infrastructure.core.data.ApiParameterError;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuilder;
|
||||
import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder;
|
||||
import org.apache.fineract.infrastructure.core.exception.InvalidJsonException;
|
||||
import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException;
|
||||
import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
|
||||
import org.apache.fineract.infrastructure.core.service.DateUtils;
|
||||
import org.apache.fineract.portfolio.investment.data.InvestmentFdClosureData;
|
||||
import org.apache.fineract.portfolio.investment.data.InvestmentFdLimitData;
|
||||
import org.apache.fineract.portfolio.investment.domain.InvestmentFd;
|
||||
import org.apache.fineract.portfolio.investment.domain.InvestmentFdInterest;
|
||||
import org.apache.fineract.portfolio.investment.domain.InvestmentFdInterestRepository;
|
||||
import org.apache.fineract.portfolio.investment.domain.InvestmentFdRepository;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
/**
|
||||
* Closing an FD placed with another bank (Jitendra Sir). At maturity the system works out the interest and the user
|
||||
* enters what the bank actually paid; the two are shown side by side, so a different compounding shows up. A premature
|
||||
* closure is not calculated: the user enters the amount received (and any penal interest). The interest is received
|
||||
* plus TDS less the principal; what was already provisioned clears interest receivable and the rest is income (a
|
||||
* shortfall is reversed from income). Renewal closes the FD and opens a new certificate on the maturity date: renewed as
|
||||
* it is, no money moves; money added or taken out goes through the bank GL the user names. TDS, when the bank deducts
|
||||
* it, goes to TDS receivable.
|
||||
*/
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class InvestmentFdClosureService {
|
||||
|
||||
private static final DateTimeFormatter DATE = DateTimeFormatter.ofPattern("dd-MM-yyyy");
|
||||
|
||||
private final FromJsonHelper fromApiJsonHelper;
|
||||
private final InvestmentFdRepository fdRepository;
|
||||
private final InvestmentFdInterestRepository interestRepository;
|
||||
private final GLAccountRepositoryWrapper glAccountRepository;
|
||||
private final InvestmentJournalPoster journalPoster;
|
||||
private final InvestmentFdService fdService;
|
||||
private final InvestmentFdInterestService interestService;
|
||||
|
||||
/** Preview of a closure; nothing is posted. */
|
||||
@Transactional(readOnly = true)
|
||||
public InvestmentFdClosureData quote(final Long fdId, final String type, final LocalDate closeDate, final BigDecimal receivedAmount,
|
||||
final BigDecimal tds, final BigDecimal renewalAmount) {
|
||||
final InvestmentFd f = this.fdRepository.findById(fdId).orElseThrow(() -> InvestmentFdService.rule("not.found", "FD " + fdId + " does not exist"));
|
||||
final String t = CLOSURE_PREMATURE.equals(type) || CLOSURE_RENEWAL.equals(type) ? type : CLOSURE_MATURITY;
|
||||
final LocalDate date = CLOSURE_PREMATURE.equals(t) ? (closeDate == null ? DateUtils.getBusinessLocalDate() : closeDate) : f.getMaturityDate();
|
||||
final Figures x = figures(f, t, date, receivedAmount, tds, renewalAmount);
|
||||
final Entry e = CLOSURE_RENEWAL.equals(t) ? renewalEntry(f, x, f.getBankGlAccount()) : closureEntry(f, x, f.getBankGlAccount(), f.getBankGlAccount());
|
||||
return x.toData(f, t, date, null, e);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public CommandProcessingResult mature(final Long fdId, final JsonCommand command) {
|
||||
validate(command.json(), MATURE_PARAMS, (v, el) -> {
|
||||
amountOptional(v, el, RECEIVED_AMOUNT);
|
||||
amountOptional(v, el, TDS);
|
||||
});
|
||||
final InvestmentFd f = active(fdId);
|
||||
final LocalDate date = f.getMaturityDate();
|
||||
if (date.isAfter(DateUtils.getBusinessLocalDate())) {
|
||||
throw InvestmentFdService.rule("mature.before.date", "FD " + f.getDealNo() + " matures on " + date.format(DATE)
|
||||
+ "; close it before then as a premature closure");
|
||||
}
|
||||
return close(f, command, CLOSURE_MATURITY, date, STATUS_MATURED);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public CommandProcessingResult premature(final Long fdId, final JsonCommand command) {
|
||||
validate(command.json(), PREMATURE_PARAMS, (v, el) -> {
|
||||
v.reset().parameter(CLOSE_DATE).value(this.fromApiJsonHelper.extractLocalDateNamed(CLOSE_DATE, el)).notNull();
|
||||
v.reset().parameter(RECEIVED_AMOUNT).value(this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(RECEIVED_AMOUNT, el)).notNull()
|
||||
.positiveAmount();
|
||||
amountOptional(v, el, TDS);
|
||||
amountOptional(v, el, PENAL_AMOUNT);
|
||||
});
|
||||
final InvestmentFd f = active(fdId);
|
||||
final LocalDate date = command.localDateValueOfParameterNamed(CLOSE_DATE);
|
||||
if (date.isAfter(DateUtils.getBusinessLocalDate())) {
|
||||
throw InvestmentFdService.rule("close.future", "The closure date cannot be after today");
|
||||
}
|
||||
if (!date.isAfter(f.getDealDate()) || !date.isBefore(f.getMaturityDate())) {
|
||||
throw InvestmentFdService.rule("premature.date", "A premature closure falls after the deal date " + f.getDealDate().format(DATE)
|
||||
+ " and before maturity " + f.getMaturityDate().format(DATE));
|
||||
}
|
||||
return close(f, command, CLOSURE_PREMATURE, date, STATUS_PREMATURE);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public CommandProcessingResult renew(final Long fdId, final JsonCommand command) {
|
||||
validate(command.json(), RENEW_PARAMS, (v, el) -> {
|
||||
v.reset().parameter(NEW_CERT_NO).value(this.fromApiJsonHelper.extractStringNamed(NEW_CERT_NO, el)).notBlank().notExceedingLengthOf(50);
|
||||
v.reset().parameter(NEW_INT_RATE).value(this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(NEW_INT_RATE, el)).notNull()
|
||||
.positiveAmount();
|
||||
v.reset().parameter(NEW_MATURITY_DATE).value(this.fromApiJsonHelper.extractLocalDateNamed(NEW_MATURITY_DATE, el)).notNull();
|
||||
v.reset().parameter(NEW_PAYOUT).value(this.fromApiJsonHelper.extractStringNamed(NEW_PAYOUT, el)).ignoreIfNull()
|
||||
.isOneOfTheseValues(PAYOUTS.toArray());
|
||||
v.reset().parameter(NEW_CALC_TYPE).value(this.fromApiJsonHelper.extractStringNamed(NEW_CALC_TYPE, el)).ignoreIfNull()
|
||||
.isOneOfTheseValues(CALC_TYPES.toArray());
|
||||
amountOptional(v, el, MATURITY_AMOUNT);
|
||||
amountOptional(v, el, RENEWAL_AMOUNT);
|
||||
amountOptional(v, el, TDS);
|
||||
});
|
||||
final InvestmentFd f = active(fdId);
|
||||
final LocalDate date = f.getMaturityDate();
|
||||
if (date.isAfter(DateUtils.getBusinessLocalDate())) {
|
||||
throw InvestmentFdService.rule("renew.before.date", "FD " + f.getDealNo() + " can be renewed on or after its maturity " + date.format(DATE));
|
||||
}
|
||||
final LocalDate newMaturity = command.localDateValueOfParameterNamed(NEW_MATURITY_DATE);
|
||||
if (!newMaturity.isAfter(date)) {
|
||||
throw InvestmentFdService.rule("renew.maturity", "The new maturity date must be after " + date.format(DATE));
|
||||
}
|
||||
final String newCertNo = command.stringValueOfParameterNamed(NEW_CERT_NO).trim();
|
||||
if (this.fdRepository.existsByPartyIdAndCertNoIgnoreCase(f.getParty().getId(), newCertNo)) {
|
||||
throw InvestmentFdService.rule("cert.duplicate", "Certificate " + newCertNo + " of " + f.getParty().getPartyName() + " is already entered");
|
||||
}
|
||||
final Figures x = figures(f, CLOSURE_RENEWAL, date, amount(command, MATURITY_AMOUNT), amount(command, TDS), amount(command, RENEWAL_AMOUNT));
|
||||
if (x.renewalAmount.signum() <= 0) {
|
||||
throw InvestmentFdService.rule("renew.amount", "The renewal amount must be more than zero");
|
||||
}
|
||||
GLAccount diffGl = null;
|
||||
if (x.topUp.signum() != 0) {
|
||||
final Long diffGlId = command.longValueOfParameterNamed(DIFF_BANK_GL_ACCOUNT_ID);
|
||||
if (diffGlId == null) {
|
||||
throw InvestmentFdService.rule("renew.bank.gl", x.topUp.signum() > 0
|
||||
? "Choose the bank account the extra " + plain(x.topUp) + " is paid from"
|
||||
: "Choose the bank account the " + plain(x.topUp.negate()) + " taken out is credited to");
|
||||
}
|
||||
diffGl = bankGl(diffGlId, f);
|
||||
}
|
||||
final BigDecimal added = x.renewalAmount.subtract(f.getPrincipal());
|
||||
if (added.signum() > 0) {
|
||||
final InvestmentFdLimitData limit = this.fdService.limits(date, f.getParty(), added);
|
||||
if (!limit.isWithinLimits()) {
|
||||
throw InvestmentFdService.rule("limit.exceeded", limit.getMessage());
|
||||
}
|
||||
}
|
||||
this.interestService.requirePostedBefore(f, date);
|
||||
final Entry e = renewalEntry(f, x, diffGl);
|
||||
final CommandProcessingResult posted = e.isEmpty() ? null
|
||||
: this.journalPoster.post(date, f.getDealNo(), "FD renewed: deal " + f.getDealNo() + ", certificate " + f.getCertNo() + " → "
|
||||
+ newCertNo + " with " + f.getParty().getPartyName() + " (interest " + plain(x.interest) + tdsNote(x) + ")", e.lines);
|
||||
|
||||
final InvestmentFd n = new InvestmentFd();
|
||||
n.setDealNo(this.fdService.nextDealNo(date));
|
||||
n.setParty(f.getParty());
|
||||
n.setFdGlAccount(f.getParty().getGlAccount());
|
||||
n.setInterestGlAccount(f.getParty().getInterestGlAccount() != null ? f.getParty().getInterestGlAccount() : f.getInterestGlAccount());
|
||||
n.setCertNo(newCertNo);
|
||||
n.setCertDate(command.localDateValueOfParameterNamed(NEW_CERT_DATE));
|
||||
n.setDealDate(date);
|
||||
n.setPrincipal(x.renewalAmount);
|
||||
n.setIntRate(command.bigDecimalValueOfParameterNamed(NEW_INT_RATE));
|
||||
n.setMaturityDate(newMaturity);
|
||||
n.setPayout(StringUtils.defaultIfBlank(command.stringValueOfParameterNamed(NEW_PAYOUT), f.getPayout()));
|
||||
n.setCalcType(StringUtils.defaultIfBlank(command.stringValueOfParameterNamed(NEW_CALC_TYPE), f.getCalcType()));
|
||||
final BigDecimal newInterest = InvestmentFdCalculator.interest(n.getPrincipal(), n.getIntRate(), date, newMaturity, n.getCalcType(),
|
||||
n.getPayout());
|
||||
n.setExpectedInterest(newInterest);
|
||||
n.setExpectedMaturityAmount(InvestmentFdService.maturityAmount(n.getPrincipal(), newInterest, n.getPayout()));
|
||||
n.setBankGlAccount(diffGl != null ? diffGl : f.getBankGlAccount());
|
||||
n.setRenewedFrom(f);
|
||||
n.setStatus(STATUS_ACTIVE);
|
||||
n.setRemarks(StringUtils.trimToNull(command.stringValueOfParameterNamed(REMARKS)));
|
||||
n.setGlTransactionId(posted == null ? null : posted.getTransactionId());
|
||||
n.setVoucherNo(posted == null ? null : voucherNo(posted));
|
||||
|
||||
closeRecord(f, x, CLOSURE_RENEWAL, date, STATUS_RENEWED, posted);
|
||||
this.fdRepository.saveAndFlush(f);
|
||||
final InvestmentFd saved = this.fdRepository.saveAndFlush(n);
|
||||
return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withEntityId(saved.getId()).withSubEntityId(f.getId())
|
||||
.withResourceIdAsString(saved.getVoucherNo()).with(Map.<String, Object>of("dealNo", saved.getDealNo(), "voucherNo",
|
||||
String.valueOf(saved.getVoucherNo()), "interest", x.interest))
|
||||
.build();
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------------------------------- shared
|
||||
|
||||
private CommandProcessingResult close(final InvestmentFd f, final JsonCommand command, final String type, final LocalDate date,
|
||||
final String status) {
|
||||
this.interestService.requirePostedBefore(f, date);
|
||||
final Figures x = figures(f, type, date, amount(command, RECEIVED_AMOUNT), amount(command, TDS),
|
||||
null).withPenal(amount(command, PENAL_AMOUNT));
|
||||
if (x.received.compareTo(f.getPrincipal()) < 0) {
|
||||
throw InvestmentFdService.rule("received.below.principal", "The amount received " + plain(x.received)
|
||||
+ " is less than the principal " + plain(f.getPrincipal()) + "; check the figure");
|
||||
}
|
||||
final GLAccount principalGl = command.longValueOfParameterNamed(PRINCIPAL_BANK_GL_ACCOUNT_ID) == null ? f.getBankGlAccount()
|
||||
: bankGl(command.longValueOfParameterNamed(PRINCIPAL_BANK_GL_ACCOUNT_ID), f);
|
||||
final GLAccount interestGl = command.longValueOfParameterNamed(INTEREST_BANK_GL_ACCOUNT_ID) == null ? principalGl
|
||||
: bankGl(command.longValueOfParameterNamed(INTEREST_BANK_GL_ACCOUNT_ID), f);
|
||||
final Entry e = closureEntry(f, x, principalGl, interestGl);
|
||||
final String what = CLOSURE_PREMATURE.equals(type) ? "FD closed premature" : "FD matured";
|
||||
final CommandProcessingResult posted = this.journalPoster.post(date, f.getDealNo(), what + ": deal " + f.getDealNo() + ", certificate "
|
||||
+ f.getCertNo() + " with " + f.getParty().getPartyName() + " (interest " + plain(x.interest) + tdsNote(x) + ")", e.lines);
|
||||
f.setRemarks(StringUtils.defaultIfBlank(StringUtils.trimToNull(command.stringValueOfParameterNamed(REMARKS)), f.getRemarks()));
|
||||
closeRecord(f, x, type, date, status, posted);
|
||||
this.fdRepository.saveAndFlush(f);
|
||||
return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withEntityId(f.getId())
|
||||
.withResourceIdAsString(f.getClosureVoucherNo()).withTransactionId(posted.getTransactionId())
|
||||
.with(Map.<String, Object>of("voucherNo", String.valueOf(f.getClosureVoucherNo()), "interest", x.interest)).build();
|
||||
}
|
||||
|
||||
/** Dr bank (principal, and interest net of TDS), Dr TDS receivable / Cr FD GL, Cr interest receivable, ± interest income. */
|
||||
private Entry closureEntry(final InvestmentFd f, final Figures x, final GLAccount principalGl, final GLAccount interestGl) {
|
||||
final Entry e = new Entry();
|
||||
e.dr(principalGl, f.getPrincipal());
|
||||
e.dr(interestGl, x.received.subtract(f.getPrincipal()));
|
||||
e.dr(gl(TDS_RECEIVABLE_GL_CODE), x.tds);
|
||||
e.cr(f.getFdGlAccount(), f.getPrincipal());
|
||||
income(e, f, x);
|
||||
return e;
|
||||
}
|
||||
|
||||
/** Interest income and receivable, plus the FD GL moving from the old principal to the renewal amount and the bank for any difference. */
|
||||
private Entry renewalEntry(final InvestmentFd f, final Figures x, final GLAccount diffGl) {
|
||||
final Entry e = new Entry();
|
||||
e.dr(gl(TDS_RECEIVABLE_GL_CODE), x.tds);
|
||||
income(e, f, x);
|
||||
final BigDecimal fdChange = x.renewalAmount.subtract(f.getPrincipal());
|
||||
if (fdChange.signum() > 0) {
|
||||
e.dr(f.getFdGlAccount(), fdChange);
|
||||
} else {
|
||||
e.cr(f.getFdGlAccount(), fdChange.negate());
|
||||
}
|
||||
if (diffGl != null) {
|
||||
if (x.topUp.signum() > 0) {
|
||||
e.cr(diffGl, x.topUp);
|
||||
} else {
|
||||
e.dr(diffGl, x.topUp.negate());
|
||||
}
|
||||
}
|
||||
return e;
|
||||
}
|
||||
|
||||
private void income(final Entry e, final InvestmentFd f, final Figures x) {
|
||||
e.cr(gl(INTEREST_RECEIVABLE_GL_CODE), x.provisioned);
|
||||
if (x.income.signum() > 0) {
|
||||
e.cr(f.getInterestGlAccount(), x.income);
|
||||
} else {
|
||||
// Less interest than already provisioned: the shortfall comes back out of income.
|
||||
e.dr(f.getInterestGlAccount(), x.income.negate());
|
||||
}
|
||||
}
|
||||
|
||||
private Figures figures(final InvestmentFd f, final String type, final LocalDate date, final BigDecimal amountIn, final BigDecimal tdsIn,
|
||||
final BigDecimal renewalIn) {
|
||||
final List<InvestmentFdInterest> lines = this.interestRepository.findByFdIdOrderByIntDateAscIdAsc(f.getId());
|
||||
BigDecimal provisioned = BigDecimal.ZERO;
|
||||
LocalDate lastPaid = f.getDealDate();
|
||||
for (final InvestmentFdInterest l : lines) {
|
||||
if (LINE_PROVISION.equals(l.getLineType())) {
|
||||
provisioned = provisioned.add(l.getAmount());
|
||||
} else if (LINE_PAYOUT.equals(l.getLineType())) {
|
||||
provisioned = provisioned.subtract(zero(l.getAlreadyBooked()));
|
||||
lastPaid = l.getIntDate();
|
||||
}
|
||||
}
|
||||
final BigDecimal expected = InvestmentFdCalculator.interest(f.getPrincipal(), f.getIntRate(), lastPaid, date, f.getCalcType(),
|
||||
f.getPayout());
|
||||
final BigDecimal tds = scale(zero(tdsIn));
|
||||
// Maturity / renewal default to the system's figure; a premature closure always needs the bank's amount.
|
||||
final BigDecimal received = amountIn != null ? scale(amountIn) : f.getPrincipal().add(expected).subtract(tds);
|
||||
final BigDecimal interest = received.add(tds).subtract(f.getPrincipal());
|
||||
final BigDecimal renewal = renewalIn != null ? scale(renewalIn) : received;
|
||||
return new Figures(expected, provisioned.max(BigDecimal.ZERO), received, tds, null, interest,
|
||||
interest.subtract(provisioned.max(BigDecimal.ZERO)), renewal, renewal.subtract(received));
|
||||
}
|
||||
|
||||
private void closeRecord(final InvestmentFd f, final Figures x, final String type, final LocalDate date, final String status,
|
||||
final CommandProcessingResult posted) {
|
||||
f.setStatus(status);
|
||||
f.setClosureType(type);
|
||||
f.setClosedOnDate(date);
|
||||
f.setClosureAmount(x.received);
|
||||
f.setClosureInterest(x.interest);
|
||||
f.setClosureTds(x.tds);
|
||||
f.setClosurePenal(x.penal);
|
||||
f.setClosureGlTransactionId(posted == null ? null : posted.getTransactionId());
|
||||
f.setClosureVoucherNo(posted == null ? null : voucherNo(posted));
|
||||
}
|
||||
|
||||
private InvestmentFd active(final Long fdId) {
|
||||
final InvestmentFd f = this.fdRepository.findByIdWithLock(fdId)
|
||||
.orElseThrow(() -> InvestmentFdService.rule("not.found", "FD " + fdId + " does not exist"));
|
||||
if (!STATUS_ACTIVE.equals(f.getStatus())) {
|
||||
throw InvestmentFdService.rule("not.active", "FD " + f.getDealNo() + " is already " + f.getStatus().toLowerCase());
|
||||
}
|
||||
return f;
|
||||
}
|
||||
|
||||
private GLAccount bankGl(final Long glId, final InvestmentFd f) {
|
||||
final GLAccount gl = this.glAccountRepository.findOneWithNotFoundDetection(glId);
|
||||
if (gl.isHeaderAccount() || gl.isDisabled() || !GLAccountType.ASSET.getValue().equals(gl.getType())) {
|
||||
throw InvestmentFdService.rule("bank.gl.invalid", "GL " + gl.getGlCode() + " must be an enabled detail Asset account (our bank account)");
|
||||
}
|
||||
if (gl.getId().equals(f.getFdGlAccount().getId())) {
|
||||
throw InvestmentFdService.rule("bank.gl.same", "Choose our bank account, not the FD GL itself");
|
||||
}
|
||||
return gl;
|
||||
}
|
||||
|
||||
private GLAccount gl(final String code) {
|
||||
return this.glAccountRepository.findOneByGlCodeWithNotFoundDetection(code);
|
||||
}
|
||||
|
||||
private BigDecimal amount(final JsonCommand command, final String param) {
|
||||
return command.parameterExists(param) ? command.bigDecimalValueOfParameterNamed(param) : null;
|
||||
}
|
||||
|
||||
private void amountOptional(final DataValidatorBuilder v, final JsonElement el, final String param) {
|
||||
v.reset().parameter(param).value(this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(param, el)).ignoreIfNull()
|
||||
.zeroOrPositiveAmount();
|
||||
}
|
||||
|
||||
private void validate(final String json, final Set<String> supported,
|
||||
final java.util.function.BiConsumer<DataValidatorBuilder, JsonElement> rules) {
|
||||
if (StringUtils.isBlank(json)) {
|
||||
throw new InvalidJsonException();
|
||||
}
|
||||
final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
|
||||
this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, supported);
|
||||
final List<ApiParameterError> errors = new ArrayList<>();
|
||||
rules.accept(new DataValidatorBuilder(errors).resource("investmentfd"), this.fromApiJsonHelper.parse(json));
|
||||
if (!errors.isEmpty()) {
|
||||
throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist", "Validation errors exist.", errors);
|
||||
}
|
||||
}
|
||||
|
||||
private static String tdsNote(final Figures x) {
|
||||
return x.tds.signum() > 0 ? ", TDS " + plain(x.tds) : "";
|
||||
}
|
||||
|
||||
private static BigDecimal zero(final BigDecimal v) {
|
||||
return v == null ? BigDecimal.ZERO : v;
|
||||
}
|
||||
|
||||
private static BigDecimal scale(final BigDecimal v) {
|
||||
return v.setScale(2, RoundingMode.HALF_UP);
|
||||
}
|
||||
|
||||
private static String plain(final BigDecimal v) {
|
||||
return v.setScale(2, RoundingMode.HALF_UP).toPlainString();
|
||||
}
|
||||
|
||||
private static String voucherNo(final CommandProcessingResult posted) {
|
||||
final Object voucherNo = posted.getChanges() == null ? null : posted.getChanges().get("voucherNo");
|
||||
return voucherNo == null ? null : voucherNo.toString();
|
||||
}
|
||||
|
||||
/** The figures of one closure. */
|
||||
private record Figures(BigDecimal expected, BigDecimal provisioned, BigDecimal received, BigDecimal tds, BigDecimal penal,
|
||||
BigDecimal interest, BigDecimal income, BigDecimal renewalAmount, BigDecimal topUp) {
|
||||
|
||||
Figures withPenal(final BigDecimal penalAmount) {
|
||||
return new Figures(expected, provisioned, received, tds, penalAmount, interest, income, renewalAmount, topUp);
|
||||
}
|
||||
|
||||
InvestmentFdClosureData toData(final InvestmentFd f, final String type, final LocalDate date, final BigDecimal penalAmount,
|
||||
final Entry e) {
|
||||
return InvestmentFdClosureData.builder().fdId(f.getId()).dealNo(f.getDealNo()).type(type).closeDate(date.format(DATE))
|
||||
.principal(f.getPrincipal()).expectedInterest(expected).provisioned(provisioned).receivedAmount(received).tds(tds)
|
||||
.penalAmount(penalAmount).interest(interest).income(income).difference(interest.subtract(expected))
|
||||
.renewalAmount(CLOSURE_RENEWAL.equals(type) ? renewalAmount : null).topUp(CLOSURE_RENEWAL.equals(type) ? topUp : null)
|
||||
.lines(e.view).build();
|
||||
}
|
||||
}
|
||||
|
||||
/** Journal lines and the same lines for the screen. */
|
||||
private static final class Entry {
|
||||
|
||||
private final InvestmentJournalPoster.Lines lines = new InvestmentJournalPoster.Lines();
|
||||
private final List<InvestmentFdClosureData.Line> view = new ArrayList<>();
|
||||
|
||||
void dr(final GLAccount gl, final BigDecimal amount) {
|
||||
if (amount != null && amount.signum() > 0) {
|
||||
this.lines.debit(gl.getId(), amount);
|
||||
this.view.add(new InvestmentFdClosureData.Line("Dr", gl.getGlCode(), gl.getName(), amount));
|
||||
}
|
||||
}
|
||||
|
||||
void cr(final GLAccount gl, final BigDecimal amount) {
|
||||
if (amount != null && amount.signum() > 0) {
|
||||
this.lines.credit(gl.getId(), amount);
|
||||
this.view.add(new InvestmentFdClosureData.Line("Cr", gl.getGlCode(), gl.getName(), amount));
|
||||
}
|
||||
}
|
||||
|
||||
boolean isEmpty() {
|
||||
return this.view.isEmpty();
|
||||
}
|
||||
}
|
||||
}
|
||||
+327
@@ -0,0 +1,327 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.investment.service;
|
||||
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentFdApiConstants.BANK_GL_ACCOUNT_ID;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentFdApiConstants.CALC_SIMPLE;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentFdApiConstants.INTEREST_PARAMS;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentFdApiConstants.INTEREST_RECEIVABLE_GL_CODE;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentFdApiConstants.INT_DATE;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentFdApiConstants.LINE_MATURITY;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentFdApiConstants.LINE_PAYOUT;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentFdApiConstants.LINE_PROVISION;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentFdApiConstants.LINE_TYPE;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentFdApiConstants.PAYOUT_AT_MATURITY;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentFdApiConstants.PAYOUT_HALF_YEARLY;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentFdApiConstants.RECEIVED_AMOUNT;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentFdApiConstants.STATUS_ACTIVE;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentFdApiConstants.TDS;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentFdApiConstants.TDS_RECEIVABLE_GL_CODE;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import java.lang.reflect.Type;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.time.LocalDate;
|
||||
import java.time.Month;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.fineract.accounting.glaccount.domain.GLAccount;
|
||||
import org.apache.fineract.accounting.glaccount.domain.GLAccountRepositoryWrapper;
|
||||
import org.apache.fineract.accounting.glaccount.domain.GLAccountType;
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.infrastructure.core.data.ApiParameterError;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuilder;
|
||||
import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder;
|
||||
import org.apache.fineract.infrastructure.core.exception.InvalidJsonException;
|
||||
import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException;
|
||||
import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
|
||||
import org.apache.fineract.infrastructure.core.service.DateUtils;
|
||||
import org.apache.fineract.portfolio.investment.data.InvestmentFdDueData;
|
||||
import org.apache.fineract.portfolio.investment.data.InvestmentFdScheduleData;
|
||||
import org.apache.fineract.portfolio.investment.domain.InvestmentFd;
|
||||
import org.apache.fineract.portfolio.investment.domain.InvestmentFdInterest;
|
||||
import org.apache.fineract.portfolio.investment.domain.InvestmentFdInterestRepository;
|
||||
import org.apache.fineract.portfolio.investment.domain.InvestmentFdRepository;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
/**
|
||||
* Interest of an FD placed with another bank, as for Govt securities: on 31 March and 30 September the interest
|
||||
* earned since the last payout (or the deal date) is provisioned (Dr Interest Receivable / Cr Interest on FDs); a
|
||||
* half-yearly / yearly FD pays its interest every six / twelve months (Dr Bank, Dr TDS receivable when deducted / Cr
|
||||
* Interest Receivable for what was provisioned, Cr Interest on FDs for the rest). Lines are posted in date order, on or
|
||||
* after their date, dated that date. The interest of the last period comes with the maturity.
|
||||
*/
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class InvestmentFdInterestService {
|
||||
|
||||
private static final DateTimeFormatter DATE = DateTimeFormatter.ofPattern("dd-MM-yyyy");
|
||||
|
||||
private final FromJsonHelper fromApiJsonHelper;
|
||||
private final InvestmentFdRepository fdRepository;
|
||||
private final InvestmentFdInterestRepository interestRepository;
|
||||
private final GLAccountRepositoryWrapper glAccountRepository;
|
||||
private final InvestmentJournalPoster journalPoster;
|
||||
|
||||
/** One worked-out interest line. */
|
||||
record Line(String type, LocalDate date, LocalDate from, BigDecimal gross, BigDecimal alreadyBooked, BigDecimal tds, BigDecimal amount,
|
||||
InvestmentFdInterest posted) {}
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
public InvestmentFdScheduleData schedule(final Long fdId) {
|
||||
final InvestmentFd f = this.fdRepository.findById(fdId).orElseThrow(() -> InvestmentFdService.rule("not.found", "FD " + fdId + " does not exist"));
|
||||
final List<Line> lines = lines(f);
|
||||
final LocalDate today = DateUtils.getBusinessLocalDate();
|
||||
final Optional<Line> next = lines.stream().filter(l -> l.posted() == null && !LINE_MATURITY.equals(l.type())).findFirst();
|
||||
final List<InvestmentFdScheduleData.Row> rows = new ArrayList<>();
|
||||
for (final Line l : lines) {
|
||||
rows.add(InvestmentFdScheduleData.Row.builder().lineType(l.type()).intDate(l.date().format(DATE)).fromDate(l.from().format(DATE))
|
||||
.toDate(l.date().format(DATE)).days(ChronoUnit.DAYS.between(l.from(), l.date())).grossInterest(l.gross())
|
||||
.alreadyBooked(l.alreadyBooked()).tds(l.tds()).amount(l.amount()).posted(l.posted() != null)
|
||||
.voucherNo(l.posted() == null ? null : l.posted().getVoucherNo())
|
||||
.glTransactionId(l.posted() == null ? null : l.posted().getGlTransactionId())
|
||||
.postable(STATUS_ACTIVE.equals(f.getStatus()) && next.isPresent() && next.get() == l && !l.date().isAfter(today)).build());
|
||||
}
|
||||
return InvestmentFdScheduleData.builder().fd(InvestmentFdService.data(f)).provisioned(provisioned(f)).rows(rows).build();
|
||||
}
|
||||
|
||||
/** For every active FD: its next interest voucher, or its maturity, when due now or within {@code withinDays}. */
|
||||
@Transactional(readOnly = true)
|
||||
public List<InvestmentFdDueData> due(final int withinDays) {
|
||||
final LocalDate today = DateUtils.getBusinessLocalDate();
|
||||
final LocalDate upTo = today.plusDays(Math.max(withinDays, 0));
|
||||
final List<InvestmentFdDueData> due = new ArrayList<>();
|
||||
for (final InvestmentFd f : this.fdRepository.findByStatusOrderByIdAsc(STATUS_ACTIVE)) {
|
||||
lines(f).stream().filter(l -> l.posted() == null).findFirst().filter(l -> !l.date().isAfter(upTo))
|
||||
.ifPresent(l -> due.add(InvestmentFdDueData.builder().fdId(f.getId()).dealNo(f.getDealNo())
|
||||
.partyName(f.getParty().getPartyName()).certNo(f.getCertNo()).lineType(l.type()).intDate(l.date().format(DATE))
|
||||
.amount(LINE_MATURITY.equals(l.type()) ? f.getExpectedMaturityAmount() : l.amount())
|
||||
.postable(!l.date().isAfter(today)).overdue(l.date().isBefore(today)).build()));
|
||||
}
|
||||
return due;
|
||||
}
|
||||
|
||||
/** Refuses a closure on {@code date} while an interest line before it is still unposted. */
|
||||
void requirePostedBefore(final InvestmentFd f, final LocalDate date) {
|
||||
lines(f).stream().filter(l -> l.posted() == null && !LINE_MATURITY.equals(l.type()) && l.date().isBefore(date)).findFirst()
|
||||
.ifPresent(l -> {
|
||||
throw InvestmentFdService.rule("interest.pending", "Post the " + l.type().toLowerCase() + " of "
|
||||
+ l.date().format(DATE) + " for FD " + f.getDealNo() + " first (FD Interest Entry)");
|
||||
});
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public CommandProcessingResult post(final Long fdId, final JsonCommand command) {
|
||||
validate(command.json());
|
||||
final InvestmentFd f = this.fdRepository.findByIdWithLock(fdId)
|
||||
.orElseThrow(() -> InvestmentFdService.rule("not.found", "FD " + fdId + " does not exist"));
|
||||
if (!STATUS_ACTIVE.equals(f.getStatus())) {
|
||||
throw InvestmentFdService.rule("not.active", "FD " + f.getDealNo() + " is already " + f.getStatus().toLowerCase());
|
||||
}
|
||||
final String type = command.stringValueOfParameterNamed(LINE_TYPE);
|
||||
final LocalDate date = command.localDateValueOfParameterNamed(INT_DATE);
|
||||
if (date.isAfter(DateUtils.getBusinessLocalDate())) {
|
||||
throw InvestmentFdService.rule("before.date", "This interest line can be posted on or after " + date.format(DATE));
|
||||
}
|
||||
final Line next = lines(f).stream().filter(l -> l.posted() == null && !LINE_MATURITY.equals(l.type())).findFirst()
|
||||
.orElseThrow(() -> InvestmentFdService.rule("all.posted", "FD " + f.getDealNo() + " has no interest line left to post; close it at maturity"));
|
||||
if (!next.type().equals(type) || !next.date().equals(date)) {
|
||||
throw InvestmentFdService.rule("out.of.order", "Post the interest lines in date order: next for FD " + f.getDealNo() + " is the "
|
||||
+ next.type().toLowerCase() + " of " + next.date().format(DATE));
|
||||
}
|
||||
final GLAccount receivable = gl(INTEREST_RECEIVABLE_GL_CODE);
|
||||
final InvestmentJournalPoster.Lines lines = new InvestmentJournalPoster.Lines();
|
||||
final InvestmentFdInterest x = new InvestmentFdInterest();
|
||||
x.setFd(f);
|
||||
x.setLineType(type);
|
||||
x.setIntDate(date);
|
||||
x.setFromDate(next.from());
|
||||
x.setToDate(date);
|
||||
x.setDays((int) ChronoUnit.DAYS.between(next.from(), date));
|
||||
final String what;
|
||||
if (LINE_PROVISION.equals(type)) {
|
||||
if (next.amount().signum() <= 0) {
|
||||
throw InvestmentFdService.rule("nothing.to.provision", "No interest to provision on " + date.format(DATE));
|
||||
}
|
||||
lines.debit(receivable.getId(), next.amount()).credit(f.getInterestGlAccount().getId(), next.amount());
|
||||
x.setGrossInterest(next.gross());
|
||||
x.setAmount(next.amount());
|
||||
what = "FD interest provision";
|
||||
} else {
|
||||
// The bank's own figure when given (net of TDS), else the system's interest for the period.
|
||||
final BigDecimal tds = amount(command, TDS);
|
||||
final BigDecimal received = command.parameterExists(RECEIVED_AMOUNT) && command.bigDecimalValueOfParameterNamed(RECEIVED_AMOUNT) != null
|
||||
? amount(command, RECEIVED_AMOUNT)
|
||||
: next.gross().subtract(tds);
|
||||
final BigDecimal gross = received.add(tds);
|
||||
final BigDecimal income = gross.subtract(next.alreadyBooked());
|
||||
final GLAccount bank = command.longValueOfParameterNamed(BANK_GL_ACCOUNT_ID) == null ? f.getBankGlAccount()
|
||||
: bankGl(command.longValueOfParameterNamed(BANK_GL_ACCOUNT_ID), f);
|
||||
lines.debit(bank.getId(), received).debit(gl(TDS_RECEIVABLE_GL_CODE).getId(), tds).credit(receivable.getId(), next.alreadyBooked());
|
||||
if (income.signum() >= 0) {
|
||||
lines.credit(f.getInterestGlAccount().getId(), income);
|
||||
} else {
|
||||
lines.debit(f.getInterestGlAccount().getId(), income.negate());
|
||||
}
|
||||
x.setGrossInterest(gross);
|
||||
x.setAlreadyBooked(next.alreadyBooked());
|
||||
x.setTds(tds);
|
||||
x.setAmount(income);
|
||||
x.setBankGlAccount(bank);
|
||||
what = "FD interest received";
|
||||
}
|
||||
final CommandProcessingResult posted = this.journalPoster.post(date, f.getDealNo(), what + " " + date.format(DATE) + ": deal "
|
||||
+ f.getDealNo() + ", certificate " + f.getCertNo() + " with " + f.getParty().getPartyName(), lines);
|
||||
x.setGlTransactionId(posted.getTransactionId());
|
||||
final Object voucherNo = posted.getChanges() == null ? null : posted.getChanges().get("voucherNo");
|
||||
x.setVoucherNo(voucherNo == null ? null : voucherNo.toString());
|
||||
x.setPostedOnDate(DateUtils.getBusinessLocalDate());
|
||||
final InvestmentFdInterest saved = this.interestRepository.saveAndFlush(x);
|
||||
return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withEntityId(saved.getId()).withSubEntityId(f.getId())
|
||||
.withResourceIdAsString(saved.getVoucherNo()).withTransactionId(posted.getTransactionId()).build();
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------------------------------- schedule
|
||||
|
||||
/** Every interest line of the FD in date order, with the posted figures where posted and the worked-out ones otherwise. */
|
||||
List<Line> lines(final InvestmentFd f) {
|
||||
final List<InvestmentFdInterest> posted = this.interestRepository.findByFdIdOrderByIntDateAscIdAsc(f.getId());
|
||||
final LocalDate end = f.getClosedOnDate() != null ? f.getClosedOnDate() : f.getMaturityDate();
|
||||
final List<Object[]> events = new ArrayList<>();
|
||||
if (!PAYOUT_AT_MATURITY.equals(f.getPayout())) {
|
||||
final int months = PAYOUT_HALF_YEARLY.equals(f.getPayout()) ? 6 : 12;
|
||||
for (int k = 1;; k++) {
|
||||
final LocalDate d = f.getDealDate().plusMonths((long) months * k);
|
||||
if (!d.isBefore(f.getMaturityDate())) {
|
||||
break;
|
||||
}
|
||||
events.add(new Object[] { d, LINE_PAYOUT });
|
||||
}
|
||||
}
|
||||
for (int y = f.getDealDate().getYear(); y <= f.getMaturityDate().getYear(); y++) {
|
||||
for (final LocalDate d : List.of(LocalDate.of(y, Month.MARCH, 31), LocalDate.of(y, Month.SEPTEMBER, 30))) {
|
||||
if (d.isAfter(f.getDealDate()) && d.isBefore(f.getMaturityDate())) {
|
||||
events.add(new Object[] { d, LINE_PROVISION });
|
||||
}
|
||||
}
|
||||
}
|
||||
// A payout falling on a provision date comes first: the provision then starts from the payout.
|
||||
events.sort(Comparator.<Object[], LocalDate>comparing(e -> (LocalDate) e[0]).thenComparing(e -> LINE_PAYOUT.equals(e[1]) ? 0 : 1));
|
||||
|
||||
final List<Line> out = new ArrayList<>();
|
||||
LocalDate lastPaid = f.getDealDate();
|
||||
BigDecimal sincePaid = BigDecimal.ZERO;
|
||||
for (final Object[] e : events) {
|
||||
final LocalDate d = (LocalDate) e[0];
|
||||
final String type = (String) e[1];
|
||||
if (d.isAfter(end)) {
|
||||
break;
|
||||
}
|
||||
final InvestmentFdInterest done = posted.stream().filter(p -> p.getLineType().equals(type) && p.getIntDate().equals(d)).findFirst()
|
||||
.orElse(null);
|
||||
if (LINE_PROVISION.equals(type)) {
|
||||
final BigDecimal accrued = InvestmentFdCalculator.interest(f.getPrincipal(), f.getIntRate(), lastPaid, d, f.getCalcType(),
|
||||
f.getPayout());
|
||||
final BigDecimal amount = done != null ? done.getAmount() : accrued.subtract(sincePaid).max(BigDecimal.ZERO);
|
||||
if (done == null && amount.signum() == 0) {
|
||||
continue;
|
||||
}
|
||||
out.add(new Line(type, d, lastPaid, done != null ? done.getGrossInterest() : accrued, null, null, amount, done));
|
||||
sincePaid = sincePaid.add(amount);
|
||||
} else {
|
||||
final BigDecimal gross = done != null ? done.getGrossInterest()
|
||||
: InvestmentFdCalculator.interest(f.getPrincipal(), f.getIntRate(), lastPaid, d, CALC_SIMPLE, f.getPayout());
|
||||
final BigDecimal booked = done != null ? zero(done.getAlreadyBooked()) : sincePaid;
|
||||
out.add(new Line(type, d, lastPaid, gross, booked, done == null ? null : done.getTds(),
|
||||
done != null ? done.getAmount() : gross.subtract(booked), done));
|
||||
lastPaid = d;
|
||||
sincePaid = BigDecimal.ZERO;
|
||||
}
|
||||
}
|
||||
if (f.getClosedOnDate() == null) {
|
||||
final BigDecimal last = InvestmentFdCalculator.interest(f.getPrincipal(), f.getIntRate(), lastPaid, f.getMaturityDate(),
|
||||
f.getCalcType(), f.getPayout());
|
||||
out.add(new Line(LINE_MATURITY, f.getMaturityDate(), lastPaid, last, sincePaid, null, last.subtract(sincePaid), null));
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
/** Interest provisioned and not yet received. */
|
||||
BigDecimal provisioned(final InvestmentFd f) {
|
||||
BigDecimal v = BigDecimal.ZERO;
|
||||
for (final InvestmentFdInterest l : this.interestRepository.findByFdIdOrderByIntDateAscIdAsc(f.getId())) {
|
||||
v = LINE_PROVISION.equals(l.getLineType()) ? v.add(l.getAmount()) : v.subtract(zero(l.getAlreadyBooked()));
|
||||
}
|
||||
return v.max(BigDecimal.ZERO);
|
||||
}
|
||||
|
||||
private GLAccount bankGl(final Long glId, final InvestmentFd f) {
|
||||
final GLAccount gl = this.glAccountRepository.findOneWithNotFoundDetection(glId);
|
||||
if (gl.isHeaderAccount() || gl.isDisabled() || !GLAccountType.ASSET.getValue().equals(gl.getType())
|
||||
|| gl.getId().equals(f.getFdGlAccount().getId())) {
|
||||
throw InvestmentFdService.rule("bank.gl.invalid", "GL " + gl.getGlCode() + " must be our bank account (an enabled detail Asset GL)");
|
||||
}
|
||||
return gl;
|
||||
}
|
||||
|
||||
private GLAccount gl(final String code) {
|
||||
return this.glAccountRepository.findOneByGlCodeWithNotFoundDetection(code);
|
||||
}
|
||||
|
||||
private static BigDecimal amount(final JsonCommand command, final String param) {
|
||||
final BigDecimal v = command.parameterExists(param) ? command.bigDecimalValueOfParameterNamed(param) : null;
|
||||
return v == null ? BigDecimal.ZERO : v.setScale(2, RoundingMode.HALF_UP);
|
||||
}
|
||||
|
||||
private static BigDecimal zero(final BigDecimal v) {
|
||||
return v == null ? BigDecimal.ZERO : v;
|
||||
}
|
||||
|
||||
private void validate(final String json) {
|
||||
if (StringUtils.isBlank(json)) {
|
||||
throw new InvalidJsonException();
|
||||
}
|
||||
final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
|
||||
this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, INTEREST_PARAMS);
|
||||
final JsonElement e = this.fromApiJsonHelper.parse(json);
|
||||
final List<ApiParameterError> errors = new ArrayList<>();
|
||||
final DataValidatorBuilder v = new DataValidatorBuilder(errors).resource("investmentfd");
|
||||
v.reset().parameter(LINE_TYPE).value(this.fromApiJsonHelper.extractStringNamed(LINE_TYPE, e)).notBlank()
|
||||
.isOneOfTheseValues(LINE_PROVISION, LINE_PAYOUT);
|
||||
v.reset().parameter(INT_DATE).value(this.fromApiJsonHelper.extractLocalDateNamed(INT_DATE, e)).notNull();
|
||||
v.reset().parameter(RECEIVED_AMOUNT).value(this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(RECEIVED_AMOUNT, e)).ignoreIfNull()
|
||||
.zeroOrPositiveAmount();
|
||||
v.reset().parameter(TDS).value(this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(TDS, e)).ignoreIfNull().zeroOrPositiveAmount();
|
||||
if (!errors.isEmpty()) {
|
||||
throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist", "Validation errors exist.", errors);
|
||||
}
|
||||
}
|
||||
}
|
||||
+386
@@ -0,0 +1,386 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.investment.service;
|
||||
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentFdApiConstants.AMOUNT;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentFdApiConstants.AS_ON_DATE;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentFdApiConstants.BANK_BALANCE_HEADER_GL_CODE;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentFdApiConstants.BANK_GL_ACCOUNT_ID;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentFdApiConstants.BANK_LIMIT_PERCENT;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentFdApiConstants.BASE_PARAMS;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentFdApiConstants.BOND_TYPE_FD;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentFdApiConstants.CALC_QUARTERLY;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentFdApiConstants.CALC_TYPE;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentFdApiConstants.CALC_TYPES;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentFdApiConstants.CERT_DATE;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentFdApiConstants.CERT_NO;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentFdApiConstants.DEAL_DATE;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentFdApiConstants.DEPOSIT_HEADER_GL_CODE;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentFdApiConstants.FD_PARAMS;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentFdApiConstants.GROSS_LIMIT_PERCENT;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentFdApiConstants.INT_RATE;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentFdApiConstants.MATURITY_DATE;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentFdApiConstants.PARTY_ID;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentFdApiConstants.PAYOUT;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentFdApiConstants.PAYOUTS;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentFdApiConstants.PAYOUT_AT_MATURITY;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentFdApiConstants.RD_DEPOSIT_GL_CODE;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentFdApiConstants.REMARKS;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentFdApiConstants.STATUS_ACTIVE;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentFdApiConstants.TOTAL_DEPOSITS;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import java.lang.reflect.Type;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.time.LocalDate;
|
||||
import java.time.Month;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.fineract.accounting.glaccount.domain.GLAccount;
|
||||
import org.apache.fineract.accounting.glaccount.domain.GLAccountRepositoryWrapper;
|
||||
import org.apache.fineract.accounting.glaccount.domain.GLAccountType;
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.infrastructure.core.data.ApiParameterError;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuilder;
|
||||
import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder;
|
||||
import org.apache.fineract.infrastructure.core.exception.GeneralPlatformDomainRuleException;
|
||||
import org.apache.fineract.infrastructure.core.exception.InvalidJsonException;
|
||||
import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException;
|
||||
import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
|
||||
import org.apache.fineract.infrastructure.core.service.DateUtils;
|
||||
import org.apache.fineract.portfolio.investment.data.InvestmentFdData;
|
||||
import org.apache.fineract.portfolio.investment.data.InvestmentFdLimitData;
|
||||
import org.apache.fineract.portfolio.investment.domain.InvestmentDepositBase;
|
||||
import org.apache.fineract.portfolio.investment.domain.InvestmentDepositBaseRepository;
|
||||
import org.apache.fineract.portfolio.investment.domain.InvestmentFd;
|
||||
import org.apache.fineract.portfolio.investment.domain.InvestmentFdRepository;
|
||||
import org.apache.fineract.portfolio.investment.domain.InvestmentParty;
|
||||
import org.apache.fineract.portfolio.investment.domain.InvestmentPartyRepository;
|
||||
import org.apache.fineract.portfolio.investment.exception.InvestmentPartyNotFoundException;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
/**
|
||||
* FDs the bank places with other banks (Jitendra Sir): the bank is an FD party with its own FD GL and an interest GL;
|
||||
* placing the money posts Dr FD GL / Cr our bank GL at once, dated the deal date. RBI limits: deposits with other
|
||||
* banks (current accounts included) within 20% of total deposits as on the previous 31 March, and within 5% with any
|
||||
* one bank.
|
||||
*/
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class InvestmentFdService {
|
||||
|
||||
private static final DateTimeFormatter DATE = DateTimeFormatter.ofPattern("dd-MM-yyyy");
|
||||
|
||||
private final FromJsonHelper fromApiJsonHelper;
|
||||
private final InvestmentFdRepository fdRepository;
|
||||
private final InvestmentDepositBaseRepository depositBaseRepository;
|
||||
private final InvestmentPartyRepository partyRepository;
|
||||
private final GLAccountRepositoryWrapper glAccountRepository;
|
||||
private final InvestmentJournalPoster journalPoster;
|
||||
private final JdbcTemplate jdbcTemplate;
|
||||
|
||||
// -------------------------------------------------------------------------------------------------- reads
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
public List<InvestmentFdData> retrieveAll() {
|
||||
return this.fdRepository.findAll(Sort.by(Sort.Direction.DESC, "id")).stream().map(InvestmentFdService::data).toList();
|
||||
}
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
public InvestmentFdData retrieveOne(final Long fdId) {
|
||||
return data(this.fdRepository.findById(fdId).orElseThrow(() -> rule("not.found", "FD " + fdId + " does not exist")));
|
||||
}
|
||||
|
||||
/** What an FD would earn and where it stands against the RBI limits, before it is saved. */
|
||||
@Transactional(readOnly = true)
|
||||
public InvestmentFdData quote(final Long partyId, final BigDecimal amount, final BigDecimal rate, final LocalDate dealDate,
|
||||
final LocalDate maturityDate, final String payout, final String calcType) {
|
||||
// Set.of(...).contains(null) throws, so blank choices fall back to the defaults first.
|
||||
final String pay = payout != null && PAYOUTS.contains(payout) ? payout : PAYOUT_AT_MATURITY;
|
||||
final String calc = calcType != null && CALC_TYPES.contains(calcType) ? calcType : CALC_QUARTERLY;
|
||||
final BigDecimal interest = maturityDate == null || dealDate == null ? BigDecimal.ZERO
|
||||
: InvestmentFdCalculator.interest(amount, rate, dealDate, maturityDate, calc, pay);
|
||||
final InvestmentParty party = partyId == null ? null : this.partyRepository.findById(partyId).orElse(null);
|
||||
return InvestmentFdData.builder().partyId(partyId).partyName(party == null ? null : party.getPartyName())
|
||||
.principal(amount).intRate(rate).dealDate(format(dealDate)).maturityDate(format(maturityDate))
|
||||
.days(dealDate == null || maturityDate == null ? 0 : java.time.temporal.ChronoUnit.DAYS.between(dealDate, maturityDate))
|
||||
.payout(pay).calcType(calc).expectedInterest(interest).expectedMaturityAmount(maturityAmount(amount, interest, pay))
|
||||
.periodInterest(InvestmentFdCalculator.periodInterest(amount, rate, pay))
|
||||
.fdGlCode(party == null ? null : party.getGlAccount().getGlCode())
|
||||
.fdGlName(party == null ? null : party.getGlAccount().getName())
|
||||
.interestGlCode(party == null || party.getInterestGlAccount() == null ? null : party.getInterestGlAccount().getGlCode())
|
||||
.interestGlName(party == null || party.getInterestGlAccount() == null ? null : party.getInterestGlAccount().getName())
|
||||
.limit(limits(dealDate == null ? DateUtils.getBusinessLocalDate() : dealDate, party, amount)).build();
|
||||
}
|
||||
|
||||
/** The RBI limits for an FD of {@code amount} with {@code party} (either may be empty) on {@code dealDate}. */
|
||||
@Transactional(readOnly = true)
|
||||
public InvestmentFdLimitData limits(final LocalDate dealDate, final InvestmentParty party, final BigDecimal amount) {
|
||||
final LocalDate base = previousYearEnd(dealDate);
|
||||
final BigDecimal total = this.depositBaseRepository.findByAsOnDate(base).map(InvestmentDepositBase::getTotalDeposits).orElse(null);
|
||||
final BigDecimal newAmount = amount == null ? BigDecimal.ZERO : amount;
|
||||
final BigDecimal fdOutstanding = this.fdRepository.findByStatusOrderByIdAsc(STATUS_ACTIVE).stream().map(InvestmentFd::getPrincipal)
|
||||
.reduce(BigDecimal.ZERO, BigDecimal::add);
|
||||
final BigDecimal bankBalances = bankBalances();
|
||||
final BigDecimal withParty = party == null ? BigDecimal.ZERO
|
||||
: this.fdRepository.findByStatusOrderByIdAsc(STATUS_ACTIVE).stream().filter(f -> f.getParty().getId().equals(party.getId()))
|
||||
.map(InvestmentFd::getPrincipal).reduce(BigDecimal.ZERO, BigDecimal::add);
|
||||
final BigDecimal grossAfter = fdOutstanding.add(bankBalances).add(newAmount);
|
||||
final BigDecimal bankAfter = withParty.add(newAmount);
|
||||
final InvestmentFdLimitData.InvestmentFdLimitDataBuilder b = InvestmentFdLimitData.builder().asOnDate(base.format(DATE))
|
||||
.totalDeposits(total).glTotalDeposits(glDeposits(base)).fdOutstanding(fdOutstanding).bankBalances(bankBalances)
|
||||
.grossAfter(grossAfter).partyName(party == null ? null : party.getPartyName()).bankAfter(bankAfter).newAmount(newAmount);
|
||||
if (total == null) {
|
||||
return b.withinLimits(false).message("Enter the bank's total deposits as on " + base.format(DATE)
|
||||
+ " first; the RBI limits on deposits with other banks are worked out from it").build();
|
||||
}
|
||||
final BigDecimal grossLimit = percent(total, GROSS_LIMIT_PERCENT);
|
||||
final BigDecimal bankLimit = percent(total, BANK_LIMIT_PERCENT);
|
||||
b.grossLimit(grossLimit).bankLimit(bankLimit);
|
||||
if (grossAfter.compareTo(grossLimit) > 0) {
|
||||
return b.withinLimits(false).message("Deposits with other banks would be " + plain(grossAfter) + ", above the RBI limit of "
|
||||
+ plain(grossLimit) + " (20% of total deposits " + plain(total) + " as on " + base.format(DATE) + ")").build();
|
||||
}
|
||||
if (party != null && bankAfter.compareTo(bankLimit) > 0) {
|
||||
return b.withinLimits(false).message("Deposits with " + party.getPartyName() + " would be " + plain(bankAfter)
|
||||
+ ", above the RBI limit of " + plain(bankLimit) + " with one bank (5% of total deposits " + plain(total) + ")")
|
||||
.build();
|
||||
}
|
||||
return b.withinLimits(true).message("Within the RBI limits").build();
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------------------------------- writes
|
||||
|
||||
/** Places an FD: Dr the bank's FD GL / Cr our bank GL, dated the deal date, voucher raised at once. */
|
||||
@Transactional
|
||||
public CommandProcessingResult create(final JsonCommand command) {
|
||||
validateFd(command.json());
|
||||
final Long partyId = command.longValueOfParameterNamed(PARTY_ID);
|
||||
final InvestmentParty party = this.partyRepository.findByIdWithLock(partyId)
|
||||
.orElseThrow(() -> new InvestmentPartyNotFoundException(partyId));
|
||||
if (!BOND_TYPE_FD.equals(party.getBondType())) {
|
||||
throw rule("party.not.fd", "Party " + party.getPartyCode() + " is not an FD bank (Bond Type " + party.getBondType() + ")");
|
||||
}
|
||||
if (!party.isActive()) {
|
||||
throw rule("party.inactive", "Party " + party.getPartyCode() + " is inactive");
|
||||
}
|
||||
if (party.getInterestGlAccount() == null) {
|
||||
throw rule("party.no.interest.gl", "Map the interest GL of " + party.getPartyName() + " in Bank / Party Master first");
|
||||
}
|
||||
final LocalDate today = DateUtils.getBusinessLocalDate();
|
||||
final LocalDate dealDate = command.localDateValueOfParameterNamed(DEAL_DATE);
|
||||
final LocalDate maturityDate = command.localDateValueOfParameterNamed(MATURITY_DATE);
|
||||
if (dealDate.isAfter(today)) {
|
||||
throw rule("deal.future", "Deal date cannot be after today");
|
||||
}
|
||||
if (!maturityDate.isAfter(dealDate)) {
|
||||
throw rule("maturity.before.deal", "Maturity date must be after the deal date");
|
||||
}
|
||||
final String certNo = command.stringValueOfParameterNamed(CERT_NO).trim();
|
||||
if (this.fdRepository.existsByPartyIdAndCertNoIgnoreCase(partyId, certNo)) {
|
||||
throw rule("cert.duplicate", "Certificate " + certNo + " of " + party.getPartyName() + " is already entered");
|
||||
}
|
||||
final GLAccount bankGl = postableAsset(command.longValueOfParameterNamed(BANK_GL_ACCOUNT_ID));
|
||||
if (bankGl.getId().equals(party.getGlAccount().getId())) {
|
||||
throw rule("bank.gl.same", "The money cannot be paid from the FD GL itself; choose our bank account");
|
||||
}
|
||||
final BigDecimal amount = command.bigDecimalValueOfParameterNamed(AMOUNT).setScale(2, RoundingMode.HALF_UP);
|
||||
final InvestmentFdLimitData limit = limits(dealDate, party, amount);
|
||||
if (!limit.isWithinLimits()) {
|
||||
throw rule("limit.exceeded", limit.getMessage());
|
||||
}
|
||||
|
||||
final InvestmentFd f = new InvestmentFd();
|
||||
f.setDealNo(nextDealNo(dealDate));
|
||||
f.setParty(party);
|
||||
f.setFdGlAccount(party.getGlAccount());
|
||||
f.setInterestGlAccount(party.getInterestGlAccount());
|
||||
f.setCertNo(certNo);
|
||||
f.setCertDate(command.localDateValueOfParameterNamed(CERT_DATE));
|
||||
f.setDealDate(dealDate);
|
||||
f.setPrincipal(amount);
|
||||
f.setIntRate(command.bigDecimalValueOfParameterNamed(INT_RATE));
|
||||
f.setMaturityDate(maturityDate);
|
||||
f.setPayout(command.stringValueOfParameterNamed(PAYOUT));
|
||||
f.setCalcType(StringUtils.defaultIfBlank(command.stringValueOfParameterNamed(CALC_TYPE), CALC_QUARTERLY));
|
||||
final BigDecimal interest = InvestmentFdCalculator.interest(amount, f.getIntRate(), dealDate, maturityDate, f.getCalcType(),
|
||||
f.getPayout());
|
||||
f.setExpectedInterest(interest);
|
||||
f.setExpectedMaturityAmount(maturityAmount(amount, interest, f.getPayout()));
|
||||
f.setBankGlAccount(bankGl);
|
||||
f.setRemarks(StringUtils.trimToNull(command.stringValueOfParameterNamed(REMARKS)));
|
||||
f.setStatus(STATUS_ACTIVE);
|
||||
|
||||
final CommandProcessingResult posted = this.journalPoster.post(dealDate, f.getDealNo(),
|
||||
"FD placed: deal " + f.getDealNo() + " with " + party.getPartyName() + ", certificate " + certNo + ", "
|
||||
+ f.getIntRate().stripTrailingZeros().toPlainString() + "% to " + maturityDate.format(DATE),
|
||||
new InvestmentJournalPoster.Lines().debit(party.getGlAccount().getId(), amount).credit(bankGl.getId(), amount));
|
||||
f.setGlTransactionId(posted.getTransactionId());
|
||||
f.setVoucherNo(voucherNo(posted));
|
||||
final InvestmentFd saved = this.fdRepository.saveAndFlush(f);
|
||||
return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withEntityId(saved.getId())
|
||||
.withResourceIdAsString(saved.getVoucherNo()).withTransactionId(posted.getTransactionId())
|
||||
.with(Map.<String, Object>of("dealNo", saved.getDealNo(), "voucherNo", String.valueOf(saved.getVoucherNo()))).build();
|
||||
}
|
||||
|
||||
/** Records the bank's total deposits as on a 31 March, the base of the RBI limits for the following year. */
|
||||
@Transactional
|
||||
public CommandProcessingResult setDepositBase(final JsonCommand command) {
|
||||
validate(command.json(), BASE_PARAMS, v -> {
|
||||
final JsonElement e = this.fromApiJsonHelper.parse(command.json());
|
||||
v.reset().parameter(AS_ON_DATE).value(this.fromApiJsonHelper.extractLocalDateNamed(AS_ON_DATE, e)).notNull();
|
||||
v.reset().parameter(TOTAL_DEPOSITS).value(this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(TOTAL_DEPOSITS, e)).notNull()
|
||||
.positiveAmount();
|
||||
v.reset().parameter(REMARKS).value(this.fromApiJsonHelper.extractStringNamed(REMARKS, e)).ignoreIfNull().notExceedingLengthOf(500);
|
||||
});
|
||||
final LocalDate asOn = command.localDateValueOfParameterNamed(AS_ON_DATE);
|
||||
if (asOn.getMonth() != Month.MARCH || asOn.getDayOfMonth() != 31) {
|
||||
throw rule("base.not.yearend", "Total deposits are taken as on 31 March");
|
||||
}
|
||||
final InvestmentDepositBase base = this.depositBaseRepository.findByAsOnDate(asOn).orElseGet(InvestmentDepositBase::new);
|
||||
base.setAsOnDate(asOn);
|
||||
base.setTotalDeposits(command.bigDecimalValueOfParameterNamed(TOTAL_DEPOSITS));
|
||||
base.setRemarks(StringUtils.trimToNull(command.stringValueOfParameterNamed(REMARKS)));
|
||||
final InvestmentDepositBase saved = this.depositBaseRepository.saveAndFlush(base);
|
||||
return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withEntityId(saved.getId())
|
||||
.withResourceIdAsString(asOn.format(DATE)).build();
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------------------------------- helpers
|
||||
|
||||
static InvestmentFdData data(final InvestmentFd f) {
|
||||
return InvestmentFdData.from(f, InvestmentFdCalculator.periodInterest(f.getPrincipal(), f.getIntRate(), f.getPayout()));
|
||||
}
|
||||
|
||||
static BigDecimal maturityAmount(final BigDecimal amount, final BigDecimal interest, final String payout) {
|
||||
if (amount == null) {
|
||||
return null;
|
||||
}
|
||||
// A payout FD returns only the principal at maturity (with the last period's interest, posted as a payout).
|
||||
return PAYOUT_AT_MATURITY.equals(payout) ? amount.add(interest) : amount;
|
||||
}
|
||||
|
||||
/** The 31 March of the previous financial year: for 16-10-2026 → 31-03-2026, for 31-03-2026 → 31-03-2025. */
|
||||
static LocalDate previousYearEnd(final LocalDate date) {
|
||||
final int year = date.getMonthValue() >= 4 ? date.getYear() : date.getYear() - 1;
|
||||
return LocalDate.of(year, Month.MARCH, 31);
|
||||
}
|
||||
|
||||
/** Balances in current accounts with banks today (debit balances only), part of the inter-bank exposure. */
|
||||
private BigDecimal bankBalances() {
|
||||
final BigDecimal v = this.jdbcTemplate.queryForObject("""
|
||||
SELECT COALESCE(SUM(GREATEST(t.bal, 0)), 0) FROM (
|
||||
SELECT SUM(CASE WHEN je.type_enum = 2 THEN je.amount ELSE -je.amount END) AS bal
|
||||
FROM acc_gl_journal_entry je JOIN acc_gl_account a ON a.id = je.account_id
|
||||
WHERE a.parent_id = (SELECT id FROM acc_gl_account WHERE gl_code = ?) AND je.reversed = false
|
||||
GROUP BY je.account_id) t""", BigDecimal.class, BANK_BALANCE_HEADER_GL_CODE);
|
||||
return v == null ? BigDecimal.ZERO : v.setScale(2, RoundingMode.HALF_UP);
|
||||
}
|
||||
|
||||
/** Total deposits on a date from the deposit GLs (credit balances), shown as a suggestion for the yearly figure. */
|
||||
private BigDecimal glDeposits(final LocalDate asOn) {
|
||||
final BigDecimal v = this.jdbcTemplate.queryForObject("""
|
||||
SELECT COALESCE(SUM(CASE WHEN je.type_enum = 1 THEN je.amount ELSE -je.amount END), 0)
|
||||
FROM acc_gl_journal_entry je JOIN acc_gl_account a ON a.id = je.account_id
|
||||
WHERE (a.parent_id = (SELECT id FROM acc_gl_account WHERE gl_code = ?) OR a.gl_code = ?)
|
||||
AND je.reversed = false AND je.entry_date <= ?""", BigDecimal.class, DEPOSIT_HEADER_GL_CODE, RD_DEPOSIT_GL_CODE, asOn);
|
||||
return v == null ? BigDecimal.ZERO : v.setScale(2, RoundingMode.HALF_UP);
|
||||
}
|
||||
|
||||
String nextDealNo(final LocalDate dealDate) {
|
||||
final String prefix = InvestmentPurchaseWritePlatformServiceImpl.dealNoPrefix(dealDate) + "F";
|
||||
final Integer max = this.jdbcTemplate.queryForObject(
|
||||
"SELECT MAX(CAST(SUBSTRING(deal_no FROM 8) AS INTEGER)) FROM m_investment_fd WHERE deal_no LIKE ?", Integer.class,
|
||||
prefix + "%");
|
||||
return prefix + String.format("%05d", (max == null ? 0 : max) + 1);
|
||||
}
|
||||
|
||||
private GLAccount postableAsset(final Long glAccountId) {
|
||||
final GLAccount gl = this.glAccountRepository.findOneWithNotFoundDetection(glAccountId);
|
||||
if (gl.isHeaderAccount() || gl.isDisabled() || !GLAccountType.ASSET.getValue().equals(gl.getType())) {
|
||||
throw rule("bank.gl.invalid", "GL " + gl.getGlCode() + " must be an enabled detail Asset account (our bank account)");
|
||||
}
|
||||
return gl;
|
||||
}
|
||||
|
||||
private void validateFd(final String json) {
|
||||
validate(json, FD_PARAMS, v -> {
|
||||
final JsonElement e = this.fromApiJsonHelper.parse(json);
|
||||
v.reset().parameter(PARTY_ID).value(this.fromApiJsonHelper.extractLongNamed(PARTY_ID, e)).notNull().longGreaterThanZero();
|
||||
v.reset().parameter(CERT_NO).value(this.fromApiJsonHelper.extractStringNamed(CERT_NO, e)).notBlank().notExceedingLengthOf(50);
|
||||
v.reset().parameter(DEAL_DATE).value(this.fromApiJsonHelper.extractLocalDateNamed(DEAL_DATE, e)).notNull();
|
||||
v.reset().parameter(MATURITY_DATE).value(this.fromApiJsonHelper.extractLocalDateNamed(MATURITY_DATE, e)).notNull();
|
||||
v.reset().parameter(AMOUNT).value(this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(AMOUNT, e)).notNull().positiveAmount();
|
||||
v.reset().parameter(INT_RATE).value(this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(INT_RATE, e)).notNull()
|
||||
.positiveAmount();
|
||||
v.reset().parameter(PAYOUT).value(this.fromApiJsonHelper.extractStringNamed(PAYOUT, e)).notBlank()
|
||||
.isOneOfTheseValues(PAYOUTS.toArray());
|
||||
v.reset().parameter(CALC_TYPE).value(this.fromApiJsonHelper.extractStringNamed(CALC_TYPE, e)).ignoreIfNull()
|
||||
.isOneOfTheseValues(CALC_TYPES.toArray());
|
||||
v.reset().parameter(BANK_GL_ACCOUNT_ID).value(this.fromApiJsonHelper.extractLongNamed(BANK_GL_ACCOUNT_ID, e)).notNull()
|
||||
.longGreaterThanZero();
|
||||
v.reset().parameter(REMARKS).value(this.fromApiJsonHelper.extractStringNamed(REMARKS, e)).ignoreIfNull().notExceedingLengthOf(500);
|
||||
});
|
||||
}
|
||||
|
||||
private void validate(final String json, final Set<String> supported, final java.util.function.Consumer<DataValidatorBuilder> rules) {
|
||||
if (StringUtils.isBlank(json)) {
|
||||
throw new InvalidJsonException();
|
||||
}
|
||||
final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
|
||||
this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, supported);
|
||||
final List<ApiParameterError> errors = new ArrayList<>();
|
||||
rules.accept(new DataValidatorBuilder(errors).resource("investmentfd"));
|
||||
if (!errors.isEmpty()) {
|
||||
throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist", "Validation errors exist.", errors);
|
||||
}
|
||||
}
|
||||
|
||||
private static BigDecimal percent(final BigDecimal total, final BigDecimal pct) {
|
||||
return total.multiply(pct).divide(BigDecimal.valueOf(100), 2, RoundingMode.HALF_UP);
|
||||
}
|
||||
|
||||
private static String plain(final BigDecimal v) {
|
||||
return v.setScale(2, RoundingMode.HALF_UP).toPlainString();
|
||||
}
|
||||
|
||||
private static String format(final LocalDate date) {
|
||||
return date == null ? null : date.format(DATE);
|
||||
}
|
||||
|
||||
private static String voucherNo(final CommandProcessingResult posted) {
|
||||
final Object voucherNo = posted.getChanges() == null ? null : posted.getChanges().get("voucherNo");
|
||||
return voucherNo == null ? null : voucherNo.toString();
|
||||
}
|
||||
|
||||
static GeneralPlatformDomainRuleException rule(final String code, final String message) {
|
||||
return new GeneralPlatformDomainRuleException("error.msg.investmentfd." + code, message);
|
||||
}
|
||||
}
|
||||
+169
@@ -0,0 +1,169 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.investment.service;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.time.LocalDate;
|
||||
import java.time.Month;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.TreeSet;
|
||||
|
||||
/**
|
||||
* Interest arithmetic for half-yearly coupon securities, as the bank's existing CBS does it (Jitendra Sir):
|
||||
* <ul>
|
||||
* <li>days are counted 30/360 — every month is 30 days, a half-year is 180, a year 360;</li>
|
||||
* <li>the coupon is fixed: face value × rate ÷ 2, paid on the maturity day/month and six months either side;</li>
|
||||
* <li>at each closing (31 March, 30 September) the interest earned since the last coupon is booked as a provision (Dr
|
||||
* Interest Receivable / Cr Interest Income), even though the cash arrives at the next coupon;</li>
|
||||
* <li>when the coupon arrives, the provision already booked is cleared and only the rest is new income.</li>
|
||||
* </ul>
|
||||
* Broken-period interest paid to the seller at purchase is an expense of its own (RBI UCB Investment Directions, para
|
||||
* 152; GL 500606), so it is not carried here: the first coupon is income in full.
|
||||
*/
|
||||
public final class InvestmentInterestCalculator {
|
||||
|
||||
private static final BigDecimal THIRTY_SIX_THOUSAND = BigDecimal.valueOf(36000);
|
||||
private static final BigDecimal TWO_HUNDRED = BigDecimal.valueOf(200);
|
||||
|
||||
public static final String TYPE_PROVISION = "PROVISION";
|
||||
public static final String TYPE_COUPON = "COUPON";
|
||||
|
||||
private InvestmentInterestCalculator() {}
|
||||
|
||||
/** One line of the interest schedule. */
|
||||
public record ScheduleRow(String type, LocalDate date, LocalDate fromDate, LocalDate toDate, int days, BigDecimal grossInterest,
|
||||
BigDecimal alreadyBooked, BigDecimal amount) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Days between two dates on a 30/360 basis. Counting runs from the day after {@code from} up to and including
|
||||
* {@code to}: 10-08 → 30-09 is 50 days, 10-08 → 08-10 is 58 days, 30-06 → 30-09 is 90 days.
|
||||
*/
|
||||
public static int days360(final LocalDate from, final LocalDate to) {
|
||||
if (from == null || to == null || !to.isAfter(from)) {
|
||||
return 0;
|
||||
}
|
||||
final int d1 = Math.min(from.getDayOfMonth(), 30);
|
||||
final int d2 = Math.min(to.getDayOfMonth(), 30);
|
||||
return (to.getYear() - from.getYear()) * 360 + (to.getMonthValue() - from.getMonthValue()) * 30 + (d2 - d1);
|
||||
}
|
||||
|
||||
/** Interest on {@code faceValue} at {@code ratePercent} p.a. for {@code days} (30/360), rounded to the rupee. */
|
||||
public static BigDecimal interestForDays(final BigDecimal faceValue, final BigDecimal ratePercent, final int days) {
|
||||
return faceValue.multiply(ratePercent).multiply(BigDecimal.valueOf(days)).divide(THIRTY_SIX_THOUSAND, 0, RoundingMode.HALF_UP);
|
||||
}
|
||||
|
||||
/** The fixed half-yearly coupon: face value × rate ÷ 2. */
|
||||
public static BigDecimal halfYearCoupon(final BigDecimal faceValue, final BigDecimal ratePercent) {
|
||||
return faceValue.multiply(ratePercent).divide(TWO_HUNDRED, 0, RoundingMode.HALF_UP);
|
||||
}
|
||||
|
||||
/** k-th coupon date counting back from maturity — measured from maturity each time so month-end days survive. */
|
||||
private static LocalDate couponBack(final LocalDate maturity, final int k) {
|
||||
return maturity.minusMonths(6L * k);
|
||||
}
|
||||
|
||||
/** The most recent coupon date on or before {@code on}. */
|
||||
public static LocalDate lastCouponOnOrBefore(final LocalDate maturity, final LocalDate on) {
|
||||
int k = 0;
|
||||
LocalDate d = maturity;
|
||||
while (d.isAfter(on)) {
|
||||
k++;
|
||||
d = couponBack(maturity, k);
|
||||
}
|
||||
return d;
|
||||
}
|
||||
|
||||
/** Coupon dates strictly after {@code from}, up to and including maturity, in date order. */
|
||||
public static List<LocalDate> couponDatesAfter(final LocalDate maturity, final LocalDate from) {
|
||||
final List<LocalDate> dates = new ArrayList<>();
|
||||
int k = 0;
|
||||
LocalDate d = maturity;
|
||||
while (d.isAfter(from)) {
|
||||
dates.add(0, d);
|
||||
k++;
|
||||
d = couponBack(maturity, k);
|
||||
}
|
||||
return dates;
|
||||
}
|
||||
|
||||
/** Closing dates (31 March, 30 September) from {@code from} up to {@code to}, inclusive. */
|
||||
public static List<LocalDate> closingDates(final LocalDate from, final LocalDate to) {
|
||||
final List<LocalDate> dates = new ArrayList<>();
|
||||
for (int year = from.getYear(); year <= to.getYear(); year++) {
|
||||
for (final LocalDate closing : List.of(LocalDate.of(year, Month.MARCH, 31), LocalDate.of(year, Month.SEPTEMBER, 30))) {
|
||||
if (!closing.isBefore(from) && !closing.isAfter(to)) {
|
||||
dates.add(closing);
|
||||
}
|
||||
}
|
||||
}
|
||||
return dates;
|
||||
}
|
||||
|
||||
/**
|
||||
* The full interest schedule of a holding, from purchase to maturity.
|
||||
*
|
||||
* @param faceValue
|
||||
* face value currently held
|
||||
* @param ratePercent
|
||||
* coupon rate % p.a.
|
||||
* @param maturity
|
||||
* maturity date (coupons fall on its day/month and six months either side)
|
||||
* @param lastIntDate
|
||||
* coupon date immediately before the purchase
|
||||
* @param dealDate
|
||||
* date the holding became ours (the purchase settlement date) — closings before it belong to the seller
|
||||
*/
|
||||
public static List<ScheduleRow> schedule(final BigDecimal faceValue, final BigDecimal ratePercent, final LocalDate maturity,
|
||||
final LocalDate lastIntDate, final LocalDate dealDate) {
|
||||
final List<ScheduleRow> rows = new ArrayList<>();
|
||||
if (faceValue == null || ratePercent == null || ratePercent.signum() <= 0 || maturity == null || lastIntDate == null
|
||||
|| dealDate == null || !maturity.isAfter(lastIntDate)) {
|
||||
return rows;
|
||||
}
|
||||
final List<LocalDate> coupons = couponDatesAfter(maturity, lastIntDate);
|
||||
final TreeSet<LocalDate> events = new TreeSet<>(coupons);
|
||||
for (final LocalDate closing : closingDates(dealDate, maturity)) {
|
||||
if (closing.isAfter(lastIntDate) && !events.contains(closing)) {
|
||||
events.add(closing);
|
||||
}
|
||||
}
|
||||
|
||||
final BigDecimal coupon = halfYearCoupon(faceValue, ratePercent);
|
||||
BigDecimal receivable = BigDecimal.ZERO;
|
||||
LocalDate previousCoupon = lastIntDate;
|
||||
for (final LocalDate date : events) {
|
||||
final int days = days360(previousCoupon, date);
|
||||
if (coupons.contains(date)) {
|
||||
rows.add(new ScheduleRow(TYPE_COUPON, date, previousCoupon.plusDays(1), date, days, coupon, receivable,
|
||||
coupon.subtract(receivable)));
|
||||
receivable = BigDecimal.ZERO;
|
||||
previousCoupon = date;
|
||||
} else {
|
||||
final BigDecimal earned = interestForDays(faceValue, ratePercent, days);
|
||||
rows.add(new ScheduleRow(TYPE_PROVISION, date, previousCoupon.plusDays(1), date, days, earned, receivable,
|
||||
earned.subtract(receivable)));
|
||||
receivable = earned;
|
||||
}
|
||||
}
|
||||
return rows;
|
||||
}
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.investment.service;
|
||||
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
|
||||
public interface InvestmentInterestPostingWritePlatformService {
|
||||
|
||||
CommandProcessingResult postInterest(Long purchaseId, JsonCommand command);
|
||||
}
|
||||
+145
@@ -0,0 +1,145 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.investment.service;
|
||||
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentInterestApiConstants.BANK_GL_ACCOUNT_ID;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentInterestApiConstants.INTEREST_RECEIVABLE_GL_CODE;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentInterestApiConstants.INT_DATE;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentInterestApiConstants.LINE_TYPE;
|
||||
import static org.apache.fineract.portfolio.investment.api.InvestmentPurchaseApiConstants.STATUS_ACTIVE;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.List;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.fineract.accounting.glaccount.domain.GLAccount;
|
||||
import org.apache.fineract.accounting.glaccount.domain.GLAccountRepositoryWrapper;
|
||||
import org.apache.fineract.accounting.glaccount.domain.GLAccountType;
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuilder;
|
||||
import org.apache.fineract.infrastructure.core.exception.GeneralPlatformDomainRuleException;
|
||||
import org.apache.fineract.infrastructure.core.service.DateUtils;
|
||||
import org.apache.fineract.portfolio.investment.api.InvestmentInterestApiConstants;
|
||||
import org.apache.fineract.portfolio.investment.domain.InvestmentInterestPosting;
|
||||
import org.apache.fineract.portfolio.investment.domain.InvestmentInterestPostingRepository;
|
||||
import org.apache.fineract.portfolio.investment.domain.InvestmentPurchase;
|
||||
import org.apache.fineract.portfolio.investment.domain.InvestmentPurchaseRepository;
|
||||
import org.apache.fineract.portfolio.investment.exception.InvestmentPurchaseNotFoundException;
|
||||
import org.apache.fineract.portfolio.investment.serialization.InvestmentInterestCommandFromApiJsonDeserializer;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
/**
|
||||
* Posts one interest schedule line of a holding to GL with its JV voucher, on or after its date and in date order:
|
||||
* <ul>
|
||||
* <li>provision (31 March / 30 September): Dr Interest Receivable / Cr Interest Income;</li>
|
||||
* <li>coupon: Dr Bank (full coupon) / Cr Interest Receivable (what the provision booked) / Cr Interest Income (the
|
||||
* rest).</li>
|
||||
* </ul>
|
||||
* The amounts come from the schedule, never from the request, so the voucher always matches what the screen shows.
|
||||
*/
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class InvestmentInterestPostingWritePlatformServiceImpl implements InvestmentInterestPostingWritePlatformService {
|
||||
|
||||
private static final DateTimeFormatter DATE = DateTimeFormatter.ofPattern("dd-MM-yyyy");
|
||||
|
||||
private final InvestmentInterestCommandFromApiJsonDeserializer validator;
|
||||
private final InvestmentPurchaseRepository purchaseRepository;
|
||||
private final InvestmentInterestPostingRepository postingRepository;
|
||||
private final InvestmentPurchaseReadPlatformService readPlatformService;
|
||||
private final InvestmentJournalPoster journalPoster;
|
||||
private final GLAccountRepositoryWrapper glAccountRepository;
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public CommandProcessingResult postInterest(final Long purchaseId, final JsonCommand command) {
|
||||
this.validator.validateForPost(command.json());
|
||||
final InvestmentPurchase p = this.purchaseRepository.findByIdWithLock(purchaseId)
|
||||
.orElseThrow(() -> new InvestmentPurchaseNotFoundException(purchaseId));
|
||||
if (!STATUS_ACTIVE.equals(p.getStatus())) {
|
||||
throw rule("not.active", "Deal " + p.getDealNo() + " is not an active (settled) holding");
|
||||
}
|
||||
final String lineType = command.stringValueOfParameterNamed(LINE_TYPE);
|
||||
final LocalDate intDate = command.localDateValueOfParameterNamed(INT_DATE);
|
||||
if (intDate.isAfter(DateUtils.getBusinessLocalDate())) {
|
||||
throw rule("before.date", "This interest line can be posted on or after " + intDate.format(DATE));
|
||||
}
|
||||
|
||||
final List<InvestmentInterestPosting> posted = this.postingRepository.findByPurchaseIdOrderByIntDateAsc(purchaseId);
|
||||
final InvestmentInterestCalculator.ScheduleRow next = this.readPlatformService.scheduleRows(p).stream()
|
||||
.filter(r -> posted.stream().noneMatch(x -> x.getLineType().equals(r.type()) && x.getIntDate().equals(r.date())))
|
||||
.findFirst().orElseThrow(() -> rule("all.posted", "All interest lines of deal " + p.getDealNo() + " are already posted"));
|
||||
if (!next.type().equals(lineType) || !next.date().equals(intDate)) {
|
||||
throw rule("out.of.order", "Post the interest lines in date order: next for deal " + p.getDealNo() + " is the "
|
||||
+ next.type().toLowerCase() + " of " + next.date().format(DATE));
|
||||
}
|
||||
|
||||
final Long receivableGl = this.journalPoster.glId(INTEREST_RECEIVABLE_GL_CODE);
|
||||
final Long incomeGl = this.journalPoster.glId(InvestmentInterestApiConstants.interestIncomeGlCode(p.getBondType()));
|
||||
final InvestmentJournalPoster.Lines lines = new InvestmentJournalPoster.Lines();
|
||||
GLAccount bankGl = null;
|
||||
if (InvestmentInterestCalculator.TYPE_COUPON.equals(lineType)) {
|
||||
bankGl = command.parameterExists(BANK_GL_ACCOUNT_ID) && command.longValueOfParameterNamed(BANK_GL_ACCOUNT_ID) != null
|
||||
? postableAssetGl(command.longValueOfParameterNamed(BANK_GL_ACCOUNT_ID))
|
||||
: p.getCrGlAccount();
|
||||
lines.debit(bankGl.getId(), next.grossInterest()).credit(receivableGl, next.alreadyBooked()).credit(incomeGl, next.amount());
|
||||
} else {
|
||||
lines.debit(receivableGl, next.amount()).credit(incomeGl, next.amount());
|
||||
}
|
||||
final String what = InvestmentInterestCalculator.TYPE_COUPON.equals(lineType) ? "Coupon received" : "Interest provision";
|
||||
final CommandProcessingResult result = this.journalPoster.post(intDate, p.getDealNo(), what + " " + intDate.format(DATE) + ": deal "
|
||||
+ p.getDealNo() + ", " + p.getScript().getScriptCode() + " " + p.getScript().getScriptDesc(), lines);
|
||||
|
||||
final InvestmentInterestPosting posting = new InvestmentInterestPosting();
|
||||
posting.setPurchase(p);
|
||||
posting.setLineType(lineType);
|
||||
posting.setIntDate(intDate);
|
||||
posting.setFromDate(next.fromDate());
|
||||
posting.setToDate(next.toDate());
|
||||
posting.setDays(next.days());
|
||||
posting.setGrossInterest(next.grossInterest());
|
||||
posting.setAlreadyBooked(next.alreadyBooked());
|
||||
posting.setAmount(next.amount());
|
||||
posting.setBankGlAccount(bankGl);
|
||||
posting.setGlTransactionId(result.getTransactionId());
|
||||
final Object voucherNo = result.getChanges() == null ? null : result.getChanges().get("voucherNo");
|
||||
posting.setVoucherNo(voucherNo == null ? null : voucherNo.toString());
|
||||
posting.setPostedOnDate(DateUtils.getBusinessLocalDate());
|
||||
final InvestmentInterestPosting saved = this.postingRepository.saveAndFlush(posting);
|
||||
|
||||
return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withEntityId(saved.getId())
|
||||
.withSubEntityId(purchaseId).withResourceIdAsString(posting.getVoucherNo()).withTransactionId(result.getTransactionId())
|
||||
.build();
|
||||
}
|
||||
|
||||
/** A bank the coupon is received into must be an enabled ASSET detail GL. */
|
||||
private GLAccount postableAssetGl(final Long glAccountId) {
|
||||
final GLAccount gl = this.glAccountRepository.findOneWithNotFoundDetection(glAccountId);
|
||||
if (gl.isHeaderAccount() || gl.isDisabled() || !GLAccountType.ASSET.getValue().equals(gl.getType())) {
|
||||
throw rule("bank.glaccount.invalid", "Bank GL " + gl.getGlCode() + " must be an enabled Asset detail account");
|
||||
}
|
||||
return gl;
|
||||
}
|
||||
|
||||
private static GeneralPlatformDomainRuleException rule(final String code, final String message) {
|
||||
return new GeneralPlatformDomainRuleException("error.msg.investmentinterest." + code, message);
|
||||
}
|
||||
}
|
||||
+112
@@ -0,0 +1,112 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.investment.service;
|
||||
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonObject;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.Locale;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.fineract.accounting.glaccount.domain.GLAccountRepositoryWrapper;
|
||||
import org.apache.fineract.accounting.journalentry.service.JournalEntryWritePlatformService;
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* Posts investment entries to GL through the journal entry command, like any manual entry: it checks the GLs and
|
||||
* accounting closures, joins the caller's transaction (so under maker-checker nothing persists until approval) and
|
||||
* raises the JV voucher. Investments are a Head Office function, so every entry is booked there, in INR.
|
||||
*/
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class InvestmentJournalPoster {
|
||||
|
||||
private static final String DATE_FORMAT = "dd MMMM yyyy";
|
||||
|
||||
private final JournalEntryWritePlatformService journalEntryWritePlatformService;
|
||||
private final FromJsonHelper fromApiJsonHelper;
|
||||
private final GLAccountRepositoryWrapper glAccountRepository;
|
||||
private final JdbcTemplate jdbcTemplate;
|
||||
|
||||
/** Debit and credit lines of one entry; zero or missing amounts are left out. */
|
||||
public static final class Lines {
|
||||
|
||||
private final JsonArray debits = new JsonArray();
|
||||
private final JsonArray credits = new JsonArray();
|
||||
|
||||
public Lines debit(final Long glAccountId, final BigDecimal amount) {
|
||||
add(this.debits, glAccountId, amount);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Lines credit(final Long glAccountId, final BigDecimal amount) {
|
||||
add(this.credits, glAccountId, amount);
|
||||
return this;
|
||||
}
|
||||
|
||||
private static void add(final JsonArray lines, final Long glAccountId, final BigDecimal amount) {
|
||||
if (amount != null && amount.signum() < 0) {
|
||||
// A negative line means the deal's figures are inconsistent; dropping it would post an unbalanced entry.
|
||||
throw new org.apache.fineract.infrastructure.core.exception.GeneralPlatformDomainRuleException(
|
||||
"error.msg.investment.journal.negative.line", "A journal line of " + amount.toPlainString()
|
||||
+ " came out negative, so the entry was not posted; check the deal's book value",
|
||||
glAccountId);
|
||||
}
|
||||
if (amount != null && amount.signum() > 0) {
|
||||
final JsonObject line = new JsonObject();
|
||||
line.addProperty("glAccountId", glAccountId);
|
||||
line.addProperty("amount", amount);
|
||||
lines.add(line);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Long glId(final String glCode) {
|
||||
return this.glAccountRepository.findOneByGlCodeWithNotFoundDetection(glCode).getId();
|
||||
}
|
||||
|
||||
/**
|
||||
* Posts the entry dated {@code date}; the result carries the GL transaction id and, in its changes, the voucherNo.
|
||||
*/
|
||||
public CommandProcessingResult post(final LocalDate date, final String referenceNumber, final String comments, final Lines lines) {
|
||||
final JsonObject payload = new JsonObject();
|
||||
payload.addProperty("officeId", headOfficeId());
|
||||
payload.addProperty("transactionDate", date.format(DateTimeFormatter.ofPattern(DATE_FORMAT, Locale.ENGLISH)));
|
||||
payload.addProperty("dateFormat", DATE_FORMAT);
|
||||
payload.addProperty("locale", "en");
|
||||
payload.addProperty("currencyCode", "INR");
|
||||
payload.addProperty("referenceNumber", referenceNumber);
|
||||
payload.addProperty("comments", comments);
|
||||
payload.add("debits", lines.debits);
|
||||
payload.add("credits", lines.credits);
|
||||
final String json = payload.toString();
|
||||
final JsonCommand journalEntryCommand = JsonCommand.from(json, this.fromApiJsonHelper.parse(json), this.fromApiJsonHelper, null,
|
||||
null, null, null, null, null, null, null, null, null, null, null, null, null);
|
||||
return this.journalEntryWritePlatformService.createJournalEntry(journalEntryCommand);
|
||||
}
|
||||
|
||||
private Long headOfficeId() {
|
||||
return this.jdbcTemplate.queryForObject("SELECT id FROM m_office WHERE parent_id IS NULL ORDER BY id LIMIT 1", Long.class);
|
||||
}
|
||||
}
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.investment.service;
|
||||
|
||||
import java.util.List;
|
||||
import org.apache.fineract.portfolio.investment.data.InvestmentPartyData;
|
||||
import org.apache.fineract.portfolio.investment.data.InvestmentScriptData;
|
||||
|
||||
public interface InvestmentMasterReadPlatformService {
|
||||
|
||||
List<InvestmentPartyData> retrieveAllParties();
|
||||
|
||||
InvestmentPartyData retrieveParty(Long partyId);
|
||||
|
||||
List<InvestmentScriptData> retrieveAllScripts();
|
||||
|
||||
InvestmentScriptData retrieveScript(Long scriptId);
|
||||
}
|
||||
+65
@@ -0,0 +1,65 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.investment.service;
|
||||
|
||||
import java.util.List;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.fineract.portfolio.investment.data.InvestmentPartyData;
|
||||
import org.apache.fineract.portfolio.investment.data.InvestmentScriptData;
|
||||
import org.apache.fineract.portfolio.investment.domain.InvestmentPartyRepository;
|
||||
import org.apache.fineract.portfolio.investment.domain.InvestmentScriptRepository;
|
||||
import org.apache.fineract.portfolio.investment.exception.InvestmentPartyNotFoundException;
|
||||
import org.apache.fineract.portfolio.investment.exception.InvestmentScriptNotFoundException;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
/** Masters are small (tens of rows), so they are listed whole, newest first, the way the screens show them. */
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
@Transactional(readOnly = true)
|
||||
public class InvestmentMasterReadPlatformServiceImpl implements InvestmentMasterReadPlatformService {
|
||||
|
||||
private static final Sort NEWEST_FIRST = Sort.by(Sort.Direction.DESC, "id");
|
||||
|
||||
private final InvestmentPartyRepository partyRepository;
|
||||
private final InvestmentScriptRepository scriptRepository;
|
||||
|
||||
@Override
|
||||
public List<InvestmentPartyData> retrieveAllParties() {
|
||||
return this.partyRepository.findAll(NEWEST_FIRST).stream().map(InvestmentPartyData::from).toList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public InvestmentPartyData retrieveParty(final Long partyId) {
|
||||
return this.partyRepository.findById(partyId).map(InvestmentPartyData::from)
|
||||
.orElseThrow(() -> new InvestmentPartyNotFoundException(partyId));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<InvestmentScriptData> retrieveAllScripts() {
|
||||
return this.scriptRepository.findAll(NEWEST_FIRST).stream().map(InvestmentScriptData::from).toList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public InvestmentScriptData retrieveScript(final Long scriptId) {
|
||||
return this.scriptRepository.findById(scriptId).map(InvestmentScriptData::from)
|
||||
.orElseThrow(() -> new InvestmentScriptNotFoundException(scriptId));
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user