fix(savings-product): move savings interest paid to expenses mapping

This commit is contained in:
2026-10-01 14:01:24 +05:30
parent 4dee1db360
commit 1ffac4b1e5
2 changed files with 28 additions and 24 deletions
@@ -88,7 +88,7 @@ export class SavingProductAccountingStepComponent implements OnInit {
liabilityAccountOptions: GLAccount[] = []; liabilityAccountOptions: GLAccount[] = [];
expenseAccountOptions: GLAccount[] = []; expenseAccountOptions: GLAccount[] = [];
incomeAccountOptions: GLAccount[] = []; incomeAccountOptions: GLAccount[] = [];
/** Savings Interest Paid nets to nil each posting, so it may be a liability clearing GL or an expense GL. */ /** Savings Interest Paid sits under Expenses and offers expense GLs; it nets to nil at each posting. */
paidAccountOptions: GLAccount[] = []; paidAccountOptions: GLAccount[] = [];
assetAccountOptions: GLAccount[] = []; assetAccountOptions: GLAccount[] = [];
paymentTypeOptions: any[] = []; paymentTypeOptions: any[] = [];
@@ -116,7 +116,7 @@ export class SavingProductAccountingStepComponent implements OnInit {
this.liabilityAccountOptions = accountingMappingOptions.liabilityAccountOptions || []; this.liabilityAccountOptions = accountingMappingOptions.liabilityAccountOptions || [];
this.expenseAccountOptions = accountingMappingOptions.expenseAccountOptions || []; this.expenseAccountOptions = accountingMappingOptions.expenseAccountOptions || [];
this.incomeAccountOptions = accountingMappingOptions.incomeAccountOptions || []; this.incomeAccountOptions = accountingMappingOptions.incomeAccountOptions || [];
this.paidAccountOptions = this.liabilityAccountOptions.concat(this.expenseAccountOptions); this.paidAccountOptions = this.expenseAccountOptions;
this.paymentTypeOptions = this.savingProductsTemplate?.paymentTypeOptions || []; this.paymentTypeOptions = this.savingProductsTemplate?.paymentTypeOptions || [];
this.fundSourceAccountOptions = this.assetAccountOptions.concat(this.liabilityAccountOptions); this.fundSourceAccountOptions = this.assetAccountOptions.concat(this.liabilityAccountOptions);
this.buildAccountingSectionConfigs(); this.buildAccountingSectionConfigs();
@@ -204,9 +204,9 @@ export class SavingProductAccountingStepComponent implements OnInit {
// Backend serializes as 'otherGlMappingsMap'; fall back to 'otherGlMappings' for compatibility // Backend serializes as 'otherGlMappingsMap'; fall back to 'otherGlMappings' for compatibility
const glMappings = mappings.otherGlMappingsMap || mappings.otherGlMappings; const glMappings = mappings.otherGlMappingsMap || mappings.otherGlMappings;
this.setOtherGlMappings('assetsOtherGlMappings', glMappings?.assets || [], this.assetAccountOptions); this.setOtherGlMappings('assetsOtherGlMappings', glMappings?.assets || [], this.assetAccountOptions);
// Payable and Paid are liability rows, saved under liabilities. A product saved while they sat // Payable is a liability row, saved under liabilities; Paid an expense row, saved under
// under expenses still carries them there, so both buckets are read and the named entries are // expenses. Products saved before either move carry them in the other bucket, so both buckets
// stripped out of either one's "Other" rows. // are read and the named entries are stripped out of either one's "Other" rows.
const fromLiabilities = this.extractNamedGlMapping( const fromLiabilities = this.extractNamedGlMapping(
this.extractNamedGlMapping( this.extractNamedGlMapping(
glMappings?.liabilities || [], glMappings?.liabilities || [],
@@ -219,12 +219,13 @@ export class SavingProductAccountingStepComponent implements OnInit {
this.paidAccountOptions this.paidAccountOptions
); );
this.setOtherGlMappings('liabilitiesOtherGlMappings', fromLiabilities, this.liabilityAccountOptions); this.setOtherGlMappings('liabilitiesOtherGlMappings', fromLiabilities, this.liabilityAccountOptions);
const fromExpenses = [ const fromExpenses = this.extractNamedGlMapping(
SAVINGS_INTEREST_PAYABLE_ACCOUNT_NAME, (glMappings?.expenses || []).filter(
SAVINGS_INTEREST_PAID_ACCOUNT_NAME (mapping: any) => mapping?.accountName !== SAVINGS_INTEREST_PAYABLE_ACCOUNT_NAME
].reduce( ),
(remaining, name) => remaining.filter((mapping: any) => mapping?.accountName !== name), SAVINGS_INTEREST_PAID_ACCOUNT_NAME,
glMappings?.expenses || [] 'savingsInterestPaidAccountId',
this.paidAccountOptions
); );
this.setOtherGlMappings('expensesOtherGlMappings', fromExpenses, this.expenseAccountOptions); this.setOtherGlMappings('expensesOtherGlMappings', fromExpenses, this.expenseAccountOptions);
this.setOtherGlMappings('incomeOtherGlMappings', glMappings?.income || [], this.incomeAccountOptions); this.setOtherGlMappings('incomeOtherGlMappings', glMappings?.income || [], this.incomeAccountOptions);
@@ -340,14 +341,18 @@ export class SavingProductAccountingStepComponent implements OnInit {
SAVINGS_INTEREST_PAYABLE_ACCOUNT_NAME, SAVINGS_INTEREST_PAYABLE_ACCOUNT_NAME,
this.liabilityAccountOptions this.liabilityAccountOptions
), ),
...mapOtherGlMappings('liabilitiesOtherGlMappings', this.liabilityAccountOptions)
],
// The backend finds Savings Interest Paid by name in any bucket; it sits under expenses
// because that is where the screen shows it.
expenses: [
...namedAsOtherGlMapping( ...namedAsOtherGlMapping(
'savingsInterestPaidAccountId', 'savingsInterestPaidAccountId',
SAVINGS_INTEREST_PAID_ACCOUNT_NAME, SAVINGS_INTEREST_PAID_ACCOUNT_NAME,
this.paidAccountOptions this.paidAccountOptions
), ),
...mapOtherGlMappings('liabilitiesOtherGlMappings', this.liabilityAccountOptions) ...mapOtherGlMappings('expensesOtherGlMappings', this.expenseAccountOptions)
], ],
expenses: mapOtherGlMappings('expensesOtherGlMappings', this.expenseAccountOptions),
income: mapOtherGlMappings('incomeOtherGlMappings', this.incomeAccountOptions) income: mapOtherGlMappings('incomeOtherGlMappings', this.incomeAccountOptions)
}; };
@@ -537,16 +542,6 @@ export class SavingProductAccountingStepComponent implements OnInit {
options: this.liabilityAccountOptions, options: this.liabilityAccountOptions,
required: true required: true
}, },
{
// A pass-through between the payable and the members that closes each posting at nil, so a
// liability clearing GL and an expense GL both work; both kinds are offered.
key: 'savingsInterestPaidAccountId',
label: 'Savings Interest Paid',
inputLabel: 'Savings interest paid',
controlName: 'savingsInterestPaidAccountId',
options: this.paidAccountOptions,
required: true
},
{ {
key: 'interestPayableAccountId', key: 'interestPayableAccountId',
label: 'Interest Payable Account', label: 'Interest Payable Account',
@@ -582,6 +577,15 @@ export class SavingProductAccountingStepComponent implements OnInit {
options: this.expenseAccountOptions, options: this.expenseAccountOptions,
required: true required: true
}, },
{
// A pass-through between the payable and the members that closes each posting at nil.
key: 'savingsInterestPaidAccountId',
label: 'Savings Interest Paid',
inputLabel: 'Savings interest paid',
controlName: 'savingsInterestPaidAccountId',
options: this.paidAccountOptions,
required: true
},
{ {
// Cash has no paise, so a closing balance of 9,883.79 is handed over as 9,883 and the // Cash has no paise, so a closing balance of 9,883.79 is handed over as 9,883 and the
// 79 paise land here rather than on the member's own ledger. // 79 paise land here rather than on the member's own ledger.
+1 -1
View File
@@ -10,7 +10,7 @@
export default { export default {
'mifos_x': { 'mifos_x': {
'version': '261001', 'version': '261001',
'hash': '4f02f24a' 'hash': 'b6994c0b'
}, },
'allow_switching_backend_instance': true 'allow_switching_backend_instance': true
}; };