feat(share): add cashier share collection endpoint with GL posting and activation validation

- New POST /cashier/sharecollection endpoint: collects cash for share purchase,
  posts Dr Cash Vault / Cr Share Sundry / Cr Income GL entries, marks charges
  as paid and sets cashier_collected flag on the account
- GET /cashier/sharecollection/:id endpoint for collection status check
- Block share account activation if cashier has not collected cash first
  (validates m_share_cashier_collection record within last 30 days)
- Skip duplicate charge GL on activation when charge already collected upfront
- Suppress automatic GL posting at account submission time to prevent
  double entries; GL is now posted only via cashier collection endpoint
- Fix JdbcTemplate injection in ShareAccountWritePlatformServiceJpaRepositoryImpl
  via ShareAccountsConfiguration

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Jaanu423
2026-06-04 13:22:33 +05:30
co-authored by Claude Sonnet 4.6
parent 6e31ac9acd
commit 9d944d0196
3 changed files with 1111 additions and 629 deletions
@@ -0,0 +1,424 @@
package org.apache.fineract.portfolio.cashtransfer.api;
import com.google.gson.Gson;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
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.core.MediaType;
import jakarta.ws.rs.core.Response;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.time.format.DateTimeParseException;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import lombok.RequiredArgsConstructor;
import org.apache.fineract.infrastructure.core.exception.PlatformDataIntegrityException;
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
import org.apache.fineract.useradministration.domain.AppUser;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.dao.IncorrectResultSizeDataAccessException;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
/**
* Cashier Share Collection API — POST /v1/cashier/sharecollection
*
* Records cash received from a member for share purchase and posts GL entries:
* Dr SHARES_REFERENCE (Cash at Vault) = shareAmount + chargeAmount
* Cr SHARES_SUSPENSE (Share Sundry) = shareAmount
* Cr INCOME_FROM_FEES = chargeAmount (if > 0)
*
* On account activation Fineract posts:
* Dr SHARES_SUSPENSE → Cr SHARES_EQUITY
* Activation skips charge GL if is_paid_derived = true (set here).
*/
@Path("/v1/cashier/sharecollection")
@Component
@RequiredArgsConstructor
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public class ShareCollectionApiResource {
// ── acc_gl_journal_entry.type_enum ─────────────────────────────────────
// Fineract convention: 1 = CREDIT (Cr), 2 = DEBIT (Dr)
// Opposite of intuition — verified from Fineract AccountingProcessorHelper.
private static final int DEBIT = 2;
private static final int CREDIT = 1;
// ── CashAccountsForShares financial_account_type ───────────────────────
private static final int FA_SHARES_REFERENCE = 1;
private static final int FA_SHARES_SUSPENSE = 2;
private static final int FA_INCOME_FROM_FEES = 3;
// ── acc_gl_journal_entry.entity_type_enum ─────────────────────────────
// Manual cashier entries use NULL — avoids NPE in PortfolioProductType.fromInt()
// which only handles values 1-5 and returns null for unknown values.
// ── acc_product_mapping.product_type for shares ────────────────────────
private static final int PRODUCT_TYPE_SHARES = 4;
// ── m_share_account.status_enum ───────────────────────────────────────
private static final int STATUS_SUBMITTED_PENDING = 100;
// ── Maximum days a collection record is considered valid ──────────────
private static final int MAX_COLLECTION_AGE_DAYS = 30;
private final PlatformSecurityContext context;
private final JdbcTemplate jdbcTemplate;
private final Gson gson = new Gson();
// ══════════════════════════════════════════════════════════════════════
// POST — Collect cash for a share account
// ══════════════════════════════════════════════════════════════════════
@POST
@Transactional
public Response collect(final String body) {
final AppUser currentUser = this.context.authenticatedUser();
currentUser.validateHasPermissionTo("CREATE_JOURNALENTRY");
// ── Parse request ──────────────────────────────────────────────────
if (body == null || body.isBlank()) {
throw new PlatformDataIntegrityException(
"error.msg.sharecollection.request.empty",
"Request body is required.");
}
final JsonObject json;
try {
json = JsonParser.parseString(body).getAsJsonObject();
} catch (Exception e) {
throw new PlatformDataIntegrityException(
"error.msg.sharecollection.request.invalid",
"Request body must be valid JSON.");
}
final Long shareAccountId = getLong(json, "shareAccountId");
final String transactionDateStr = getString(json, "transactionDate");
final BigDecimal shareAmount = getBigDecimal(json, "shareAmount", "shareAmount");
final BigDecimal chargeAmountRaw = getBigDecimal(json, "chargeAmount", "chargeAmount");
final String note = sanitiseNote(getString(json, "note"));
// ── Validate inputs ────────────────────────────────────────────────
if (shareAccountId == null) {
throw new PlatformDataIntegrityException(
"error.msg.sharecollection.accountid.required",
"shareAccountId is required.");
}
if (shareAmount.compareTo(BigDecimal.ZERO) < 0) {
throw new PlatformDataIntegrityException(
"error.msg.sharecollection.shareamount.negative",
"shareAmount cannot be negative.");
}
final BigDecimal safeCharge = chargeAmountRaw.compareTo(BigDecimal.ZERO) > 0
? chargeAmountRaw : BigDecimal.ZERO;
final BigDecimal totalAmount = shareAmount.add(safeCharge);
if (totalAmount.compareTo(BigDecimal.ZERO) <= 0) {
throw new PlatformDataIntegrityException(
"error.msg.sharecollection.total.zero",
"Total collection amount must be greater than zero.");
}
final LocalDate entryDate;
try {
entryDate = transactionDateStr != null
? LocalDate.parse(transactionDateStr) : LocalDate.now();
} catch (DateTimeParseException e) {
throw new PlatformDataIntegrityException(
"error.msg.sharecollection.date.invalid",
"transactionDate must be in YYYY-MM-DD format. Received: " + transactionDateStr);
}
// ── Load account + client details ──────────────────────────────────
final Map<String, Object> acct = loadAccount(shareAccountId);
// ── Account must be in 'Submitted and pending approval' ───────────
final int statusEnum = toInt(acct.get("status_enum"));
if (statusEnum != STATUS_SUBMITTED_PENDING) {
throw new PlatformDataIntegrityException(
"error.msg.sharecollection.account.invalid.status",
"Cash collection is only allowed for accounts in 'Submitted and pending approval' "
+ "status. Account " + acct.get("account_no") + " has status code " + statusEnum + ".");
}
// ── Prevent double collection ──────────────────────────────────────
if (Boolean.TRUE.equals(acct.get("cashier_collected"))) {
throw new PlatformDataIntegrityException(
"error.msg.sharecollection.already.collected",
"Cash has already been collected for share account "
+ acct.get("account_no") + ". Re-collection is not allowed.");
}
final Long officeId = toLong(acct.get("office_id"));
final Long productId = toLong(acct.get("product_id"));
final String currencyCode = (String) acct.get("currency_code");
final String clientName = (String) acct.get("client_name");
final String accountNo = (String) acct.get("account_no");
// ── Resolve GL accounts from product mapping ───────────────────────
final Long glReference = resolveGl(productId, FA_SHARES_REFERENCE);
final Long glSuspense = resolveGl(productId, FA_SHARES_SUSPENSE);
final Long glIncome = resolveGl(productId, FA_INCOME_FROM_FEES);
if (glReference == null) {
throw new PlatformDataIntegrityException(
"error.msg.sharecollection.gl.reference.notmapped",
"Share Reference (Cash at Vault) GL is not mapped for product " + productId
+ ". Configure Share Product accounting mappings first.");
}
if (glSuspense == null) {
throw new PlatformDataIntegrityException(
"error.msg.sharecollection.gl.suspense.notmapped",
"Share Suspense GL is not mapped for product " + productId
+ ". Configure Share Product accounting mappings first.");
}
if (safeCharge.compareTo(BigDecimal.ZERO) > 0 && glIncome == null) {
throw new PlatformDataIntegrityException(
"error.msg.sharecollection.gl.income.notmapped",
"Income from Fees GL is not mapped for product " + productId
+ " but charge amount " + safeCharge + " is present. "
+ "Configure Share Product accounting mappings first.");
}
// ── Generate unique transaction ID ─────────────────────────────────
final String txnId = "SC-" + shareAccountId + "-" + System.currentTimeMillis();
final long userId = currentUser.getId();
final String collectorName = currentUser.getUsername();
final LocalDateTime now = LocalDateTime.now();
// ── Post GL journal entries ────────────────────────────────────────
// Dr Cash at Vault (SHARES_REFERENCE) = totalAmount
insertEntry(officeId, glReference, DEBIT, totalAmount, txnId,
entryDate, shareAccountId, userId, now, currencyCode,
"Share collection received for " + accountNo
+ (note != null ? " | " + note : ""));
// Cr Share Sundry (SHARES_SUSPENSE) = shareAmount
insertEntry(officeId, glSuspense, CREDIT, shareAmount, txnId,
entryDate, shareAccountId, userId, now, currencyCode,
"Share amount held in suspense - " + accountNo);
// Cr Income from Fees = chargeAmount (only if > 0)
if (safeCharge.compareTo(BigDecimal.ZERO) > 0) {
insertEntry(officeId, glIncome, CREDIT, safeCharge, txnId,
entryDate, shareAccountId, userId, now, currencyCode,
"Charge collected upfront for " + accountNo);
// Mark charges paid → activation will skip duplicate charge GL entry
jdbcTemplate.update(
"UPDATE m_share_account_charge "
+ "SET is_paid_derived = true, "
+ " amount_paid_derived = amount, "
+ " amount_outstanding_derived = 0 "
+ "WHERE account_id = ? AND is_active = true AND is_paid_derived = false",
shareAccountId);
}
// ── Flip cashier_collected flag ────────────────────────────────────
jdbcTemplate.update(
"UPDATE m_share_account SET cashier_collected = true WHERE id = ?",
shareAccountId);
// ── Insert audit / validation record ──────────────────────────────
jdbcTemplate.update(
"INSERT INTO m_share_cashier_collection "
+ "(share_account_id, collected_by_user, collected_by_name, collection_date, "
+ " share_amount, charge_amount, total_amount, gl_transaction_id, "
+ " office_id, currency_code, note) "
+ "VALUES (?,?,?,?,?,?,?,?,?,?,?)",
shareAccountId, userId, collectorName,
java.sql.Date.valueOf(entryDate),
shareAmount, safeCharge, totalAmount,
txnId, officeId, currencyCode, note);
// ── Build GL entries summary for response ──────────────────────────
final List<Map<String, Object>> glEntries = new ArrayList<>();
glEntries.add(glEntryMap(resolveGlName(glReference), "Dr", totalAmount, currencyCode));
glEntries.add(glEntryMap(resolveGlName(glSuspense), "Cr", shareAmount, currencyCode));
if (safeCharge.compareTo(BigDecimal.ZERO) > 0) {
glEntries.add(glEntryMap(resolveGlName(glIncome), "Cr", safeCharge, currencyCode));
}
// ── Response ───────────────────────────────────────────────────────
final Map<String, Object> resp = new LinkedHashMap<>();
resp.put("transactionId", txnId);
resp.put("shareAccountId", shareAccountId);
resp.put("accountNo", accountNo);
resp.put("clientName", clientName);
resp.put("collectionDate", entryDate.toString());
resp.put("collectedBy", collectorName);
resp.put("shareAmount", shareAmount);
resp.put("chargeAmount", safeCharge);
resp.put("totalAmount", totalAmount);
resp.put("currencyCode", currencyCode);
resp.put("glEntries", glEntries);
return Response.ok(gson.toJson(resp)).build();
}
// ══════════════════════════════════════════════════════════════════════
// GET — Check collection status for a share account
// ══════════════════════════════════════════════════════════════════════
@GET
@Path("/{shareAccountId}")
public Response getStatus(@PathParam("shareAccountId") final Long shareAccountId) {
this.context.authenticatedUser();
try {
final Map<String, Object> rec = jdbcTemplate.queryForMap(
"SELECT scc.share_account_id, sa.account_no, "
+ " mc.display_name AS client_name, "
+ " scc.collected_by_name, scc.collection_date, "
+ " scc.share_amount, scc.charge_amount, scc.total_amount, "
+ " scc.gl_transaction_id, scc.currency_code, scc.note "
+ "FROM m_share_cashier_collection scc "
+ "JOIN m_share_account sa ON sa.id = scc.share_account_id "
+ "JOIN m_client mc ON mc.id = sa.client_id "
+ "WHERE scc.share_account_id = ?",
shareAccountId);
final Map<String, Object> resp = new LinkedHashMap<>();
resp.put("collected", true);
resp.put("shareAccountId", shareAccountId);
resp.put("accountNo", rec.get("account_no"));
resp.put("clientName", rec.get("client_name"));
resp.put("collectedBy", rec.get("collected_by_name"));
resp.put("collectionDate", rec.get("collection_date") != null
? rec.get("collection_date").toString() : null);
resp.put("shareAmount", rec.get("share_amount"));
resp.put("chargeAmount", rec.get("charge_amount"));
resp.put("totalAmount", rec.get("total_amount"));
resp.put("transactionId", rec.get("gl_transaction_id"));
resp.put("currencyCode", rec.get("currency_code"));
resp.put("note", rec.get("note"));
return Response.ok(gson.toJson(resp)).build();
} catch (EmptyResultDataAccessException e) {
final Map<String, Object> resp = new LinkedHashMap<>();
resp.put("collected", false);
resp.put("shareAccountId", shareAccountId);
return Response.ok(gson.toJson(resp)).build();
}
}
// ══════════════════════════════════════════════════════════════════════
// Private helpers
// ══════════════════════════════════════════════════════════════════════
private Map<String, Object> loadAccount(final Long shareAccountId) {
try {
return jdbcTemplate.queryForMap(
"SELECT sa.id, sa.account_no, sa.status_enum, sa.product_id, "
+ " sa.cashier_collected, sa.currency_code, "
+ " mc.office_id, mc.display_name AS client_name "
+ "FROM m_share_account sa "
+ "JOIN m_client mc ON mc.id = sa.client_id "
+ "WHERE sa.id = ?",
shareAccountId);
} catch (EmptyResultDataAccessException e) {
throw new PlatformDataIntegrityException(
"error.msg.sharecollection.account.notfound",
"Share account with id " + shareAccountId + " does not exist.");
}
}
private Long resolveGl(final Long productId, final int financialAccountType) {
try {
return jdbcTemplate.queryForObject(
"SELECT gl_account_id FROM acc_product_mapping "
+ "WHERE product_id = ? AND product_type = ? AND financial_account_type = ?",
Long.class, productId, PRODUCT_TYPE_SHARES, financialAccountType);
} catch (IncorrectResultSizeDataAccessException e) {
// covers both EmptyResultDataAccessException (0 rows) and multiple rows
return null;
}
}
private String resolveGlName(final Long glAccountId) {
if (glAccountId == null) return "Unknown GL";
try {
return jdbcTemplate.queryForObject(
"SELECT name FROM acc_gl_account WHERE id = ?",
String.class, glAccountId);
} catch (EmptyResultDataAccessException e) {
return "GL-" + glAccountId;
}
}
private void insertEntry(final Long officeId, final Long glAccountId,
final int typeEnum, final BigDecimal amount, final String txnId,
final LocalDate entryDate, final Long shareAccountId,
final long userId, final LocalDateTime now, final String currencyCode,
final String description) {
jdbcTemplate.update(
"INSERT INTO acc_gl_journal_entry "
+ "(account_id, office_id, currency_code, transaction_id, reversed, manual_entry, "
+ " entry_date, type_enum, amount, description, entity_id, "
+ " created_by, last_modified_by, created_date, lastmodified_date, "
+ " is_running_balance_calculated, office_running_balance, "
+ " organization_running_balance, submitted_on_date, transaction_date, "
+ " created_on_utc, last_modified_on_utc) "
+ "VALUES (?,?,?,?,false,true,?,?,?,?,?,?,?,?,?,false,0,0,?,?,?,?)",
glAccountId, officeId, currencyCode, txnId,
java.sql.Date.valueOf(entryDate), typeEnum, amount, description,
shareAccountId, // entity_id — links entry to the share account
userId, userId, now, now,
java.sql.Date.valueOf(entryDate),
java.sql.Date.valueOf(entryDate),
now.atOffset(ZoneOffset.UTC),
now.atOffset(ZoneOffset.UTC));
}
private Map<String, Object> glEntryMap(final String account, final String type,
final BigDecimal amount, final String currency) {
final Map<String, Object> m = new LinkedHashMap<>();
m.put("account", account);
m.put("type", type);
m.put("amount", amount);
m.put("currency", currency);
return m;
}
private String sanitiseNote(final String raw) {
if (raw == null || raw.isBlank()) return null;
final String trimmed = raw.trim();
return trimmed.length() > 490 ? trimmed.substring(0, 490) : trimmed;
}
private Long toLong(final Object o) {
return o instanceof Number ? ((Number) o).longValue() : null;
}
private int toInt(final Object o) {
return o instanceof Number ? ((Number) o).intValue() : 0;
}
private Long getLong(final JsonObject json, final String key) {
return json.has(key) && !json.get(key).isJsonNull() ? json.get(key).getAsLong() : null;
}
private BigDecimal getBigDecimal(final JsonObject json, final String key,
final String fieldLabel) {
if (!json.has(key) || json.get(key).isJsonNull()) return BigDecimal.ZERO;
try {
return json.get(key).getAsBigDecimal();
} catch (NumberFormatException e) {
throw new PlatformDataIntegrityException(
"error.msg.sharecollection.field.invalid.number",
"Field '" + fieldLabel + "' must be a valid number.");
}
}
private String getString(final JsonObject json, final String key) {
return json.has(key) && !json.get(key).isJsonNull() ? json.get(key).getAsString() : null;
}
}
@@ -1,123 +1,124 @@
/**
* 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.shareaccounts.start;
import org.apache.fineract.accounting.journalentry.service.JournalEntryWritePlatformService;
import org.apache.fineract.infrastructure.accountnumberformat.domain.AccountNumberFormatRepositoryWrapper;
import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
import org.apache.fineract.infrastructure.core.service.PaginationHelper;
import org.apache.fineract.infrastructure.core.service.database.DatabaseSpecificSQLGenerator;
import org.apache.fineract.infrastructure.event.business.service.BusinessEventNotifierService;
import org.apache.fineract.infrastructure.security.utils.ColumnValidator;
import org.apache.fineract.portfolio.account.service.AccountNumberGenerator;
import org.apache.fineract.portfolio.accounts.constants.AccountsApiConstants;
import org.apache.fineract.portfolio.accounts.service.AccountsCommandsService;
import org.apache.fineract.portfolio.charge.service.ChargeReadPlatformService;
import org.apache.fineract.portfolio.client.service.ClientReadPlatformService;
import org.apache.fineract.portfolio.note.domain.NoteRepository;
import org.apache.fineract.portfolio.savings.domain.SavingsAccountAssembler;
import org.apache.fineract.portfolio.savings.service.SavingsAccountDomainService;
import org.apache.fineract.portfolio.savings.service.SavingsAccountReadPlatformService;
import org.apache.fineract.portfolio.shareaccounts.domain.ShareAccountDividendRepository;
import org.apache.fineract.portfolio.shareaccounts.domain.ShareAccountRepositoryWrapper;
import org.apache.fineract.portfolio.shareaccounts.serialization.ShareAccountDataSerializer;
import org.apache.fineract.portfolio.shareaccounts.service.PurchasedSharesReadPlatformService;
import org.apache.fineract.portfolio.shareaccounts.service.PurchasedSharesReadPlatformServiceImpl;
import org.apache.fineract.portfolio.shareaccounts.service.ShareAccountChargeReadPlatformService;
import org.apache.fineract.portfolio.shareaccounts.service.ShareAccountChargeReadPlatformServiceImpl;
import org.apache.fineract.portfolio.shareaccounts.service.ShareAccountCommandsServiceImpl;
import org.apache.fineract.portfolio.shareaccounts.service.ShareAccountDividendReadPlatformService;
import org.apache.fineract.portfolio.shareaccounts.service.ShareAccountDividendReadPlatformServiceImpl;
import org.apache.fineract.portfolio.shareaccounts.service.ShareAccountReadPlatformService;
import org.apache.fineract.portfolio.shareaccounts.service.ShareAccountReadPlatformServiceImpl;
import org.apache.fineract.portfolio.shareaccounts.service.ShareAccountSchedularService;
import org.apache.fineract.portfolio.shareaccounts.service.ShareAccountSchedularServiceImpl;
import org.apache.fineract.portfolio.shareaccounts.service.ShareAccountWritePlatformService;
import org.apache.fineract.portfolio.shareaccounts.service.ShareAccountWritePlatformServiceJpaRepositoryImpl;
import org.apache.fineract.portfolio.shareproducts.domain.ShareProductRepositoryWrapper;
import org.apache.fineract.portfolio.shareproducts.service.ShareProductDropdownReadPlatformService;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.JdbcTemplate;
@Configuration
public class ShareAccountsConfiguration {
@Bean
@ConditionalOnMissingBean(PurchasedSharesReadPlatformService.class)
public PurchasedSharesReadPlatformService purchasedSharesReadPlatformService(JdbcTemplate jdbcTemplate) {
return new PurchasedSharesReadPlatformServiceImpl(jdbcTemplate);
}
@Bean
@ConditionalOnMissingBean(ShareAccountChargeReadPlatformService.class)
public ShareAccountChargeReadPlatformService shareAccountChargeReadPlatformService(JdbcTemplate jdbcTemplate) {
return new ShareAccountChargeReadPlatformServiceImpl(jdbcTemplate);
}
@Bean(value = "SHAREACCOUNT_COMMANDSERVICE")
@ConditionalOnMissingBean(AccountsCommandsService.class)
public AccountsCommandsService accountsCommandsService(FromJsonHelper fromApiJsonHelper,
ShareAccountDataSerializer shareAccountDataSerializer) {
return new ShareAccountCommandsServiceImpl(fromApiJsonHelper, shareAccountDataSerializer);
}
@Bean
@ConditionalOnMissingBean(ShareAccountDividendReadPlatformService.class)
public ShareAccountDividendReadPlatformService shareAccountDividendReadPlatformService(JdbcTemplate jdbcTemplate,
ColumnValidator columnValidator, PaginationHelper paginationHelper, DatabaseSpecificSQLGenerator sqlGenerator) {
return new ShareAccountDividendReadPlatformServiceImpl(jdbcTemplate, columnValidator, paginationHelper, sqlGenerator);
}
@Bean(value = "share" + AccountsApiConstants.READPLATFORM_NAME)
@ConditionalOnMissingBean(ShareAccountReadPlatformService.class)
public ShareAccountReadPlatformService shareAccountReadPlatformService(ApplicationContext applicationContext,
ChargeReadPlatformService chargeReadPlatformService,
ShareProductDropdownReadPlatformService shareProductDropdownReadPlatformService,
SavingsAccountReadPlatformService savingsAccountReadPlatformService, ClientReadPlatformService clientReadPlatformService,
ShareAccountChargeReadPlatformService shareAccountChargeReadPlatformService,
PurchasedSharesReadPlatformService purchasedSharesReadPlatformService, JdbcTemplate jdbcTemplate,
PaginationHelper paginationHelper, DatabaseSpecificSQLGenerator sqlGenerator) {
return new ShareAccountReadPlatformServiceImpl(applicationContext, chargeReadPlatformService,
shareProductDropdownReadPlatformService, savingsAccountReadPlatformService, clientReadPlatformService,
shareAccountChargeReadPlatformService, purchasedSharesReadPlatformService, jdbcTemplate, paginationHelper, sqlGenerator);
}
@Bean
@ConditionalOnMissingBean(ShareAccountSchedularService.class)
public ShareAccountSchedularService shareAccountSchedularService(ShareAccountDividendRepository shareAccountDividendRepository,
SavingsAccountDomainService savingsAccountDomainService, SavingsAccountAssembler savingsAccountAssembler) {
return new ShareAccountSchedularServiceImpl(shareAccountDividendRepository, savingsAccountDomainService, savingsAccountAssembler);
}
@Bean
@ConditionalOnMissingBean(ShareAccountWritePlatformService.class)
public ShareAccountWritePlatformService shareAccountWritePlatformService(ShareAccountDataSerializer accountDataSerializer,
ShareAccountRepositoryWrapper shareAccountRepository, ShareProductRepositoryWrapper shareProductRepository,
AccountNumberGenerator accountNumberGenerator, AccountNumberFormatRepositoryWrapper accountNumberFormatRepository,
JournalEntryWritePlatformService journalEntryWritePlatformService, NoteRepository noteRepository,
BusinessEventNotifierService businessEventNotifierService) {
return new ShareAccountWritePlatformServiceJpaRepositoryImpl(accountDataSerializer, shareAccountRepository, shareProductRepository,
accountNumberGenerator, accountNumberFormatRepository, journalEntryWritePlatformService, noteRepository,
businessEventNotifierService);
}
}
/**
* 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.shareaccounts.start;
import org.apache.fineract.accounting.journalentry.service.JournalEntryWritePlatformService;
import org.apache.fineract.infrastructure.accountnumberformat.domain.AccountNumberFormatRepositoryWrapper;
import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
import org.apache.fineract.infrastructure.core.service.PaginationHelper;
import org.apache.fineract.infrastructure.core.service.database.DatabaseSpecificSQLGenerator;
import org.apache.fineract.infrastructure.event.business.service.BusinessEventNotifierService;
import org.apache.fineract.infrastructure.security.utils.ColumnValidator;
import org.apache.fineract.portfolio.account.service.AccountNumberGenerator;
import org.apache.fineract.portfolio.accounts.constants.AccountsApiConstants;
import org.apache.fineract.portfolio.accounts.service.AccountsCommandsService;
import org.apache.fineract.portfolio.charge.service.ChargeReadPlatformService;
import org.apache.fineract.portfolio.client.service.ClientReadPlatformService;
import org.apache.fineract.portfolio.note.domain.NoteRepository;
import org.apache.fineract.portfolio.savings.domain.SavingsAccountAssembler;
import org.apache.fineract.portfolio.savings.service.SavingsAccountDomainService;
import org.apache.fineract.portfolio.savings.service.SavingsAccountReadPlatformService;
import org.apache.fineract.portfolio.shareaccounts.domain.ShareAccountDividendRepository;
import org.apache.fineract.portfolio.shareaccounts.domain.ShareAccountRepositoryWrapper;
import org.apache.fineract.portfolio.shareaccounts.serialization.ShareAccountDataSerializer;
import org.apache.fineract.portfolio.shareaccounts.service.PurchasedSharesReadPlatformService;
import org.apache.fineract.portfolio.shareaccounts.service.PurchasedSharesReadPlatformServiceImpl;
import org.apache.fineract.portfolio.shareaccounts.service.ShareAccountChargeReadPlatformService;
import org.apache.fineract.portfolio.shareaccounts.service.ShareAccountChargeReadPlatformServiceImpl;
import org.apache.fineract.portfolio.shareaccounts.service.ShareAccountCommandsServiceImpl;
import org.apache.fineract.portfolio.shareaccounts.service.ShareAccountDividendReadPlatformService;
import org.apache.fineract.portfolio.shareaccounts.service.ShareAccountDividendReadPlatformServiceImpl;
import org.apache.fineract.portfolio.shareaccounts.service.ShareAccountReadPlatformService;
import org.apache.fineract.portfolio.shareaccounts.service.ShareAccountReadPlatformServiceImpl;
import org.apache.fineract.portfolio.shareaccounts.service.ShareAccountSchedularService;
import org.apache.fineract.portfolio.shareaccounts.service.ShareAccountSchedularServiceImpl;
import org.apache.fineract.portfolio.shareaccounts.service.ShareAccountWritePlatformService;
import org.apache.fineract.portfolio.shareaccounts.service.ShareAccountWritePlatformServiceJpaRepositoryImpl;
import org.apache.fineract.portfolio.shareproducts.domain.ShareProductRepositoryWrapper;
import org.apache.fineract.portfolio.shareproducts.service.ShareProductDropdownReadPlatformService;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.JdbcTemplate;
@Configuration
public class ShareAccountsConfiguration {
@Bean
@ConditionalOnMissingBean(PurchasedSharesReadPlatformService.class)
public PurchasedSharesReadPlatformService purchasedSharesReadPlatformService(JdbcTemplate jdbcTemplate) {
return new PurchasedSharesReadPlatformServiceImpl(jdbcTemplate);
}
@Bean
@ConditionalOnMissingBean(ShareAccountChargeReadPlatformService.class)
public ShareAccountChargeReadPlatformService shareAccountChargeReadPlatformService(JdbcTemplate jdbcTemplate) {
return new ShareAccountChargeReadPlatformServiceImpl(jdbcTemplate);
}
@Bean(value = "SHAREACCOUNT_COMMANDSERVICE")
@ConditionalOnMissingBean(AccountsCommandsService.class)
public AccountsCommandsService accountsCommandsService(FromJsonHelper fromApiJsonHelper,
ShareAccountDataSerializer shareAccountDataSerializer) {
return new ShareAccountCommandsServiceImpl(fromApiJsonHelper, shareAccountDataSerializer);
}
@Bean
@ConditionalOnMissingBean(ShareAccountDividendReadPlatformService.class)
public ShareAccountDividendReadPlatformService shareAccountDividendReadPlatformService(JdbcTemplate jdbcTemplate,
ColumnValidator columnValidator, PaginationHelper paginationHelper, DatabaseSpecificSQLGenerator sqlGenerator) {
return new ShareAccountDividendReadPlatformServiceImpl(jdbcTemplate, columnValidator, paginationHelper, sqlGenerator);
}
@Bean(value = "share" + AccountsApiConstants.READPLATFORM_NAME)
@ConditionalOnMissingBean(ShareAccountReadPlatformService.class)
public ShareAccountReadPlatformService shareAccountReadPlatformService(ApplicationContext applicationContext,
ChargeReadPlatformService chargeReadPlatformService,
ShareProductDropdownReadPlatformService shareProductDropdownReadPlatformService,
SavingsAccountReadPlatformService savingsAccountReadPlatformService, ClientReadPlatformService clientReadPlatformService,
ShareAccountChargeReadPlatformService shareAccountChargeReadPlatformService,
PurchasedSharesReadPlatformService purchasedSharesReadPlatformService, JdbcTemplate jdbcTemplate,
PaginationHelper paginationHelper, DatabaseSpecificSQLGenerator sqlGenerator) {
return new ShareAccountReadPlatformServiceImpl(applicationContext, chargeReadPlatformService,
shareProductDropdownReadPlatformService, savingsAccountReadPlatformService, clientReadPlatformService,
shareAccountChargeReadPlatformService, purchasedSharesReadPlatformService, jdbcTemplate, paginationHelper, sqlGenerator);
}
@Bean
@ConditionalOnMissingBean(ShareAccountSchedularService.class)
public ShareAccountSchedularService shareAccountSchedularService(ShareAccountDividendRepository shareAccountDividendRepository,
SavingsAccountDomainService savingsAccountDomainService, SavingsAccountAssembler savingsAccountAssembler) {
return new ShareAccountSchedularServiceImpl(shareAccountDividendRepository, savingsAccountDomainService, savingsAccountAssembler);
}
@Bean
@ConditionalOnMissingBean(ShareAccountWritePlatformService.class)
public ShareAccountWritePlatformService shareAccountWritePlatformService(ShareAccountDataSerializer accountDataSerializer,
ShareAccountRepositoryWrapper shareAccountRepository, ShareProductRepositoryWrapper shareProductRepository,
AccountNumberGenerator accountNumberGenerator, AccountNumberFormatRepositoryWrapper accountNumberFormatRepository,
JournalEntryWritePlatformService journalEntryWritePlatformService, NoteRepository noteRepository,
org.springframework.jdbc.core.JdbcTemplate jdbcTemplate,
BusinessEventNotifierService businessEventNotifierService) {
return new ShareAccountWritePlatformServiceJpaRepositoryImpl(accountDataSerializer, shareAccountRepository, shareProductRepository,
accountNumberGenerator, accountNumberFormatRepository, journalEntryWritePlatformService, noteRepository,
jdbcTemplate, businessEventNotifierService);
}
}