diff --git a/fineract-core/src/main/java/org/apache/fineract/portfolio/dds/accounttype/data/DdsAccountTypeData.java b/fineract-core/src/main/java/org/apache/fineract/portfolio/dds/accounttype/data/DdsAccountTypeData.java
index 07012545c..5f7c5ed3e 100644
--- a/fineract-core/src/main/java/org/apache/fineract/portfolio/dds/accounttype/data/DdsAccountTypeData.java
+++ b/fineract-core/src/main/java/org/apache/fineract/portfolio/dds/accounttype/data/DdsAccountTypeData.java
@@ -65,6 +65,12 @@ public final class DdsAccountTypeData implements Serializable {
private final Long commissionExpenseGlId;
private final String commissionExpenseGlCode;
private final String commissionExpenseGlName;
+ private final Long prematureIncomeGlId;
+ private final String prematureIncomeGlCode;
+ private final String prematureIncomeGlName;
+ private final Long tdsPayableGlId;
+ private final String tdsPayableGlCode;
+ private final String tdsPayableGlName;
private final LocalDateTime createdDate;
private final LocalDateTime lastModifiedDate;
}
diff --git a/fineract-core/src/main/java/org/apache/fineract/portfolio/dds/accounttype/domain/DdsAccountType.java b/fineract-core/src/main/java/org/apache/fineract/portfolio/dds/accounttype/domain/DdsAccountType.java
index 0c47f1e3b..bcfb22d8a 100644
--- a/fineract-core/src/main/java/org/apache/fineract/portfolio/dds/accounttype/domain/DdsAccountType.java
+++ b/fineract-core/src/main/java/org/apache/fineract/portfolio/dds/accounttype/domain/DdsAccountType.java
@@ -133,6 +133,17 @@ public class DdsAccountType extends AbstractAuditableCustom {
@Column(name = "commission_expense_gl_id")
private Long commissionExpenseGlId;
+ /**
+ * What an early closure earns the bank: the interest a customer gives back, and the penalty taken off
+ * the agent's commission.
+ */
+ @Column(name = "premature_income_gl_id")
+ private Long prematureIncomeGlId;
+
+ /** Where TDS withheld from the agents' commission on this product (Section 194H) is owed until paid over. */
+ @Column(name = "tds_payable_gl_id")
+ private Long tdsPayableGlId;
+
protected DdsAccountType() {
//
}
@@ -152,6 +163,8 @@ public class DdsAccountType extends AbstractAuditableCustom {
this.depositLiabilityGlId = command.longValueOfParameterNamed("depositLiabilityGlId");
this.interestExpenseGlId = command.longValueOfParameterNamed("interestExpenseGlId");
this.commissionExpenseGlId = command.longValueOfParameterNamed("commissionExpenseGlId");
+ this.prematureIncomeGlId = command.longValueOfParameterNamed("prematureIncomeGlId");
+ this.tdsPayableGlId = command.longValueOfParameterNamed("tdsPayableGlId");
// A newly created account type is available for use straight away.
this.active = !command.parameterExists("active") || command.booleanPrimitiveValueOfParameterNamed("active");
}
@@ -275,6 +288,15 @@ public class DdsAccountType extends AbstractAuditableCustom {
this.commissionExpenseGlId = command.longValueOfParameterNamed("commissionExpenseGlId");
actualChanges.put("commissionExpenseGlId", this.commissionExpenseGlId);
}
+ if (command.parameterExists("prematureIncomeGlId")
+ && command.isChangeInLongParameterNamed("prematureIncomeGlId", this.prematureIncomeGlId)) {
+ this.prematureIncomeGlId = command.longValueOfParameterNamed("prematureIncomeGlId");
+ actualChanges.put("prematureIncomeGlId", this.prematureIncomeGlId);
+ }
+ if (command.parameterExists("tdsPayableGlId") && command.isChangeInLongParameterNamed("tdsPayableGlId", this.tdsPayableGlId)) {
+ this.tdsPayableGlId = command.longValueOfParameterNamed("tdsPayableGlId");
+ actualChanges.put("tdsPayableGlId", this.tdsPayableGlId);
+ }
if (command.isChangeInBooleanParameterNamed("active", this.active)) {
final boolean newValue = command.booleanPrimitiveValueOfParameterNamed("active");
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/dds/accounting/DdsLedger.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/dds/accounting/DdsLedger.java
index 6909bab99..6a4a5f121 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/dds/accounting/DdsLedger.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/dds/accounting/DdsLedger.java
@@ -238,6 +238,89 @@ public class DdsLedger {
ddsPair(party, date, amount, depositLiabilityOf(party), interestExpenseOf(party), description);
}
+ /**
+ * Interest a customer gives back on closing early, as the bank's income: the interest was paid and
+ * stays an expense, and what comes back is earned for the closure.
+ *
+ *
+ * DR DDS Customer Deposits the customer's balance comes down
+ * CR Premature Closure Income the product's premature-closure income
+ *
+ */
+ public void recoverPrematureInterest(final Party party, final LocalDate date, final BigDecimal amount, final String description) {
+ if (party.getSavingsAccountId() != null) {
+ final SavingsAccount account = load(party.getSavingsAccountId());
+ final SavingsAccountTransaction withdrawal = withdraw(account, date, amount);
+ postPair(account.office(), account.getCurrency().getCode(), date, amount, controlAccountOf(account), prematureIncomeOf(party),
+ description, withdrawal.getId(), account.getId());
+ return;
+ }
+ ddsPair(party, date, amount, depositLiabilityOf(party), prematureIncomeOf(party), description);
+ }
+
+ /**
+ * Interest an agent funded on an early closure, now paid for out of their commission. The bank booked
+ * it as interest on the closing day; the commission it came off was booked net. So it moves across:
+ *
+ *
+ * DR Agent Commission Expense
+ * CR Interest Expense
+ *
+ */
+ public void bookAgentInterestTopUpRecovery(final Party party, final LocalDate date, final BigDecimal amount,
+ final String description) {
+ ddsPair(party, date, amount, commissionExpenseOf(party), interestExpenseOf(party), description);
+ }
+
+ private static final String TDS_BASIS_SQL = "select upper(trim(p.sub_type)) as \"product\", t.commission_expense_gl_id as \"commissionGlId\", "
+ + "t.tds_payable_gl_id as \"tdsGlId\", sum(pt.amount) as \"volume\" from m_dds_party_transaction pt "
+ + "join m_dds_party p on p.id = pt.party_id left join m_dds_account_type t on t.account_type_code = upper(trim(p.sub_type)) "
+ + "where pt.agent_id = ? and pt.transaction_type = 'PARTY_TRAN' and pt.direction = 'CREDIT' and pt.commission_paid_batch_run_id = ? "
+ + "group by upper(trim(p.sub_type)), t.commission_expense_gl_id, t.tds_payable_gl_id order by upper(trim(p.sub_type))";
+
+ /**
+ * TDS withheld from an agent's commission (Section 194H), booked when the commission it was withheld
+ * from is paid. The agent is paid net; the TDS is commission the bank owes the tax department instead,
+ * so it is commission expense like the rest, credited to TDS Payable - shared over the products by
+ * what was collected for each, as the commission is.
+ *
+ *
+ * DR Agent Commission Expense
+ * CR TDS Payable
+ *
+ */
+ public void bookCommissionTds(final Office office, final Long agentId, final Long payingRunId, final LocalDate date,
+ final BigDecimal tds, final String description) {
+ if (tds == null || tds.signum() <= 0) {
+ return;
+ }
+ final List