complete fd account module

This commit is contained in:
Nitesh Yadav
2026-05-07 17:31:25 +05:30
parent a12ba20de4
commit 19c0e98e96
9 changed files with 406 additions and 54 deletions
@@ -70,7 +70,7 @@ export class CreateFixedDepositAccountComponent {
/** Fixed Deposit Account Product Template */
fixedDepositsAccountProductTemplate: any;
/** Tenure selected from the interest rate chart step */
selectedTenure: { depositPeriod: number; depositPeriodFrequencyId: number } | null = null;
selectedTenure: { depositPeriod: number; depositPeriodFrequencyId: number; depositAmount?: number } | null = null;
/**
* Fetches FD account template from `resolve`
@@ -94,7 +94,7 @@ export class CreateFixedDepositAccountComponent {
this.fixedDepositsAccountProductTemplate = $event;
}
onTenureSelected($event: { depositPeriod: number; depositPeriodFrequencyId: number }) {
onTenureSelected($event: { depositPeriod: number; depositPeriodFrequencyId: number; depositAmount?: number }) {
this.selectedTenure = $event;
}
@@ -129,12 +129,59 @@ export class CreateFixedDepositAccountComponent {
const locale = this.settingsService.language.code;
const dateFormat = this.settingsService.dateFormat;
const monthDayFormat = 'dd MMMM';
const pt = this.fixedDepositsAccountProductTemplate;
// Terms — pulled from product template
const terms: any = {};
if (pt?.nominalAnnualInterestRate != null) terms.nominalAnnualInterestRate = pt.nominalAnnualInterestRate;
if (pt?.interestCompoundingPeriodType?.id != null)
terms.interestCompoundingPeriodType = pt.interestCompoundingPeriodType.id;
if (pt?.interestPostingPeriodType?.id != null) terms.interestPostingPeriodType = pt.interestPostingPeriodType.id;
if (pt?.interestCalculationType?.id != null) terms.interestCalculationType = pt.interestCalculationType.id;
if (pt?.interestCalculationDaysInYearType?.id != null)
terms.interestCalculationDaysInYearType = pt.interestCalculationDaysInYearType.id;
// Deposit amount: Prefer user input from Rate Calculator, fallback to product template
if (this.selectedTenure?.depositAmount != null) {
terms.depositAmount = this.selectedTenure.depositAmount;
} else if (pt?.depositAmount != null) {
terms.depositAmount = pt.depositAmount;
}
if (pt?.minDepositAmount != null) terms.minDepositAmount = pt.minDepositAmount;
if (pt?.maxDepositAmount != null) terms.maxDepositAmount = pt.maxDepositAmount;
// Settings — pulled from product template
const settings: any = {};
if (pt?.lockinPeriodFrequency != null) settings.lockinPeriodFrequency = pt.lockinPeriodFrequency;
if (pt?.lockinPeriodFrequencyType?.id != null) settings.lockinPeriodFrequencyType = pt.lockinPeriodFrequencyType.id;
if (pt?.minDepositTerm != null) settings.minDepositTerm = pt.minDepositTerm;
if (pt?.minDepositTermType?.id != null) settings.minDepositTermTypeId = pt.minDepositTermType.id;
if (pt?.inMultiplesOfDepositTerm != null) settings.inMultiplesOfDepositTerm = pt.inMultiplesOfDepositTerm;
if (pt?.inMultiplesOfDepositTermType?.id != null)
settings.inMultiplesOfDepositTermTypeId = pt.inMultiplesOfDepositTermType.id;
if (pt?.maxDepositTerm != null) settings.maxDepositTerm = pt.maxDepositTerm;
if (pt?.maxDepositTermType?.id != null) settings.maxDepositTermTypeId = pt.maxDepositTermType.id;
if (pt?.preClosurePenalApplicable != null) settings.preClosurePenalApplicable = pt.preClosurePenalApplicable;
if (pt?.preClosurePenalInterest != null) settings.preClosurePenalInterest = pt.preClosurePenalInterest;
if (pt?.preClosurePenalInterestOnType?.id != null)
settings.preClosurePenalInterestOnTypeId = pt.preClosurePenalInterestOnType.id;
if (pt?.maturityInstructionId != null) settings.maturityInstructionId = pt.maturityInstructionId;
if (pt?.transferInterestToSavings != null) settings.transferInterestToSavings = pt.transferInterestToSavings;
if (pt?.withHoldTax != null) settings.withHoldTax = pt.withHoldTax;
// Charges — from product template if not overridden in details
const rawCharges: any[] =
(this.fixedDepositAccount.charges?.length ? this.fixedDepositAccount.charges : pt?.charges) ?? [];
const fixedDepositAccount = {
...this.fixedDepositAccount,
...terms,
...settings,
...(this.selectedTenure ?? {}),
clientId: this.fixedDepositsAccountTemplate.clientId,
charges: this.fixedDepositAccount.charges.map((charge: any) => ({
chargeId: charge.id,
charges: rawCharges.map((charge: any) => ({
chargeId: charge.chargeId ?? charge.id,
amount: charge.amount,
dueDate: charge.dueDate && this.dateUtils.formatDate(charge.dueDate, dateFormat),
feeOnMonthDay:
@@ -142,7 +189,7 @@ export class CreateFixedDepositAccountComponent {
feeInterval: charge.feeInterval
})),
submittedOnDate: this.dateUtils.formatDate(this.fixedDepositAccount.submittedOnDate, dateFormat),
charts: [{ chartSlabs: this.fixedDepositsAccountProductTemplate.accountChart?.chartSlabs ?? [] }],
charts: [{ chartSlabs: pt?.accountChart?.chartSlabs ?? [] }],
dateFormat,
monthDayFormat,
locale
@@ -55,11 +55,9 @@
<h4>Nomination Facility</h4>
<p>Allow account holders to designate individuals to receive funds in the event of account holder's death.</p>
</div>
<span class="facc-yn" [class.facc-yn--yes]="fixedDepositsAccountProductTemplate?.nominationFacility">
{{ fixedDepositsAccountProductTemplate?.nominationFacility ? 'Yes' : 'No' }}
</span>
<mat-slide-toggle [(ngModel)]="nominationFacility" color="primary"></mat-slide-toggle>
</div>
@if (fixedDepositsAccountProductTemplate?.nominationFacility) {
@if (nominationFacility) {
<mifosx-nominee-form (nomineeChange)="onNomineeChange($event)"></mifosx-nominee-form>
}
</div>
@@ -31,6 +31,7 @@ export class FixedDepositAccountCurrencyStepComponent implements OnChanges {
nomineeData: any = null;
autoDebitFacility = false;
nominationFacility = false;
// Editable feature toggles (pre-filled from product, overridable by user)
@@ -38,6 +39,7 @@ export class FixedDepositAccountCurrencyStepComponent implements OnChanges {
if (changes['fixedDepositsAccountProductTemplate']?.currentValue) {
const pt = changes['fixedDepositsAccountProductTemplate'].currentValue;
this.autoDebitFacility = !!pt.autoDebitFacility;
this.nominationFacility = !!pt.nominationFacility;
}
}
@@ -49,6 +51,7 @@ export class FixedDepositAccountCurrencyStepComponent implements OnChanges {
get currencyStepData() {
return {
autoDebitFacility: this.autoDebitFacility,
nominationFacility: this.nominationFacility,
...(this.nomineeData ? { nominee: this.nomineeData } : {})
};
}
@@ -145,24 +145,23 @@
}
</div>
<!-- Principal Amount (SPECIAL only) -->
@if (isSpecial) {
<div class="fdrc-calc-field">
<label class="fdrc-calc-label"
>Principal Amount <span class="fdrc-calc-optional">(optional — for monthly payout estimate)</span></label
>
<div class="fdrc-amount-row">
<span class="fdrc-amount-prefix">₹</span>
<input
type="number"
class="fdrc-tenure-input fdrc-tenure-input--amount"
[(ngModel)]="principalAmount"
[min]="1"
placeholder="e.g. 100000"
/>
</div>
<!-- Principal Amount (all FD types) -->
<div class="fdrc-calc-field">
<label class="fdrc-calc-label">
Deposit Amount
<span class="fdrc-calc-optional">(optional — to calculate maturity amount)</span>
</label>
<div class="fdrc-amount-row">
<span class="fdrc-amount-prefix">₹</span>
<input
type="number"
class="fdrc-tenure-input fdrc-tenure-input--amount"
[(ngModel)]="principalAmount"
[min]="1"
placeholder="e.g. 100000"
/>
</div>
}
</div>
<div class="fdrc-calc-actions">
<button type="button" class="fdrc-calc-btn" (click)="calculateRate()">
@@ -237,25 +236,57 @@
}
</div>
<!-- Monthly Payout (SPECIAL) -->
@if (isSpecial && matchResult.estimatedMonthlyPayout != null) {
<div class="fdrc-payout-card">
<i class="fas fa-wallet"></i>
<div class="fdrc-payout-card__content">
<span class="fdrc-payout-card__label">Estimated Monthly Payout</span>
<span class="fdrc-payout-card__amount"
>₹ {{ matchResult.estimatedMonthlyPayout | number: '1.2-2' }}</span
>
<span class="fdrc-payout-card__formula"
>= ₹{{ principalAmount | number }} × {{ matchResult.appliedRate }}% ÷ 12</span
>
<!-- Maturity Summary (all FD types) -->
@if (matchResult.maturityAmount != null) {
<div class="fdrc-maturity-card">
<div class="fdrc-maturity-card__header">
<i class="fas fa-coins"></i>
<span>Maturity Summary</span>
</div>
<div class="fdrc-maturity-card__body">
<div class="fdrc-maturity-row">
<span class="fdrc-maturity-row__label">Deposit Amount</span>
<span class="fdrc-maturity-row__value">₹ {{ principalAmount | number: '1.2-2' }}</span>
</div>
<div class="fdrc-maturity-row">
<span class="fdrc-maturity-row__label">Tenure</span>
<span class="fdrc-maturity-row__value">{{ tenureValue }} {{ tenurePeriodUnit }}</span>
</div>
<div class="fdrc-maturity-row">
<span class="fdrc-maturity-row__label">Interest Rate</span>
<span class="fdrc-maturity-row__value">{{ matchResult.appliedRate | number: '1.2-2' }}% p.a.</span>
</div>
<div class="fdrc-maturity-row fdrc-maturity-row--interest">
<span class="fdrc-maturity-row__label">Interest Earned</span>
<span class="fdrc-maturity-row__value">₹ {{ matchResult.interestEarned | number: '1.2-2' }}</span>
</div>
@if (isSpecial && matchResult.estimatedMonthlyPayout != null) {
<div class="fdrc-maturity-row">
<span class="fdrc-maturity-row__label">Monthly Payout</span>
<span class="fdrc-maturity-row__value fdrc-maturity-row__value--highlight">
₹ {{ matchResult.estimatedMonthlyPayout | number: '1.2-2' }} / month
</span>
</div>
}
<div class="fdrc-maturity-row fdrc-maturity-row--total">
<span class="fdrc-maturity-row__label">
@if (isSpecial) {
Principal at Maturity
} @else {
Maturity Amount
}
</span>
<span class="fdrc-maturity-row__value fdrc-maturity-row__value--big">
₹ {{ (isSpecial ? principalAmount : matchResult.maturityAmount) | number: '1.2-2' }}
</span>
</div>
</div>
</div>
}
@if (isSpecial && !matchResult.estimatedMonthlyPayout) {
@if (!matchResult.maturityAmount) {
<div class="fdrc-payout-hint">
<i class="fas fa-lightbulb"></i>
Enter a principal amount above to see estimated monthly interest payout.
Enter a deposit amount above to see the maturity calculation.
</div>
}
</div>
@@ -869,3 +869,79 @@ $red-light: #fee2e2;
margin-top: 8px;
flex-wrap: wrap;
}
// ── Maturity Card ─────────────────────────────────────────────────────────
.fdrc-maturity-card {
margin-top: 14px;
border: 1.5px solid #86efac;
border-radius: 10px;
overflow: hidden;
&__header {
display: flex;
align-items: center;
gap: 8px;
background: #dcfce7;
padding: 8px 14px;
font-size: 12px;
font-weight: 700;
color: #166534;
text-transform: uppercase;
letter-spacing: 0.05em;
i {
font-size: 13px;
}
}
&__body {
background: #f0fdf4;
}
}
.fdrc-maturity-row {
display: flex;
justify-content: space-between;
align-items: center;
padding: 8px 14px;
border-bottom: 1px solid #d1fae5;
font-size: 13px;
&:last-child {
border-bottom: none;
}
&__label {
color: #374151;
font-weight: 500;
}
&__value {
font-weight: 600;
color: #1e293b;
}
&--interest {
.fdrc-maturity-row__value {
color: #16a34a;
}
}
&--total {
background: #dcfce7;
border-top: 1.5px solid #86efac;
padding: 10px 14px;
}
&__value--big {
font-size: 17px;
font-weight: 800;
color: #15803d;
}
&__value--highlight {
color: #0369a1;
font-weight: 700;
}
}
@@ -33,6 +33,9 @@ export interface SlabMatchResult {
slabIndex: number;
appliedRate: number;
estimatedMonthlyPayout?: number;
maturityAmount?: number;
interestEarned?: number;
tenureDays?: number;
}
@Component({
@@ -69,7 +72,11 @@ export class FixedDepositAccountInterestRateChartStepComponent implements OnChan
@Input() fixedDepositsAccountTemplate: any;
@Input() fixedDepositsAccountProductTemplate: any;
@Output() tenureSelected = new EventEmitter<{ depositPeriod: number; depositPeriodFrequencyId: number }>();
@Output() tenureSelected = new EventEmitter<{
depositPeriod: number;
depositPeriodFrequencyId: number;
depositAmount?: number;
}>();
interestRateChartData: any[] = [];
@@ -99,6 +106,10 @@ export class FixedDepositAccountInterestRateChartStepComponent implements OnChan
tenurePeriodUnit: FdPeriodUnit = 'Days';
principalAmount: number | null = null;
get showPrincipalInput(): boolean {
return true; // shown for all FD types
}
matchResult: SlabMatchResult | null = null;
validationWarning: string | null = null;
@@ -219,19 +230,38 @@ export class FixedDepositAccountInterestRateChartStepComponent implements OnChan
const result: SlabMatchResult = {
slab: matchedSlab,
slabIndex: matchedIndex,
appliedRate: rate
appliedRate: rate,
tenureDays: enteredDays
};
if (this.isSpecial && this.principalAmount && this.principalAmount > 0) {
result.estimatedMonthlyPayout = (this.principalAmount * rate) / 1200;
if (this.principalAmount && this.principalAmount > 0) {
const p = this.principalAmount;
const r = rate / 100;
const years = enteredDays / 365;
const n = 4; // Quarterly compounding
if (this.isSpecial) {
// Monthly payout: simple interest paid monthly, principal returned at maturity
result.estimatedMonthlyPayout = (p * rate) / 1200;
result.interestEarned = result.estimatedMonthlyPayout * (enteredDays / 30);
result.maturityAmount = p; // principal returned at maturity
} else {
// Compound interest (Standard Quarterly)
result.maturityAmount = p * Math.pow(1 + r / n, n * years);
result.interestEarned = result.maturityAmount - p;
}
}
this.matchResult = result;
// Emit selected tenure to parent for backend submission
// Emit selected tenure and principal amount to parent for backend submission
const frequencyId = this.resolveFrequencyId(this.tenurePeriodUnit);
if (frequencyId !== null) {
this.tenureSelected.emit({ depositPeriod: this.tenureValue, depositPeriodFrequencyId: frequencyId });
this.tenureSelected.emit({
depositPeriod: this.tenureValue,
depositPeriodFrequencyId: frequencyId,
depositAmount: this.principalAmount || undefined
});
}
}
@@ -250,6 +280,7 @@ export class FixedDepositAccountInterestRateChartStepComponent implements OnChan
this.principalAmount = null;
this.matchResult = null;
this.validationWarning = null;
this.tenurePeriodUnit = this.allowedPeriodUnits[0];
}
onPeriodUnitChange() {
@@ -408,6 +408,45 @@
</div>
}
<!-- ══ 4. Maturity Summary ═══════════════════════════════════ -->
@if (maturitySummary) {
<div class="preview-section">
<div class="preview-section__header">
<i class="fas fa-piggy-bank preview-section__icon"></i>
<span class="preview-section__title">Maturity Summary</span>
<span class="preview-section__badge preview-section__badge--active">Projected</span>
</div>
<div class="preview-body">
<div class="preview-grid preview-grid--three">
<div class="preview-field">
<span class="preview-label">Deposit Amount</span>
<span class="preview-value">{{ maturitySummary.depositAmount | formatNumber }} {{ currencyName }}</span>
</div>
<div class="preview-field">
<span class="preview-label">Applicable Interest Rate</span>
<span class="preview-value">{{ maturitySummary.interestRate | formatNumber }}%</span>
</div>
<div class="preview-field">
<span class="preview-label">Tenure</span>
<span class="preview-value">{{ maturitySummary.tenureLabel }}</span>
</div>
<div class="preview-field">
<span class="preview-label">Estimated Interest Earned</span>
<span class="preview-value" style="color: var(--theme-success, #28a745); font-weight: 500">
+ {{ maturitySummary.totalInterest | formatNumber }} {{ currencyName }}
</span>
</div>
<div class="preview-field" style="grid-column: span 2">
<span class="preview-label">Projected Maturity Amount</span>
<span class="preview-value preview-mono" style="font-weight: 600; font-size: 1.25rem">
{{ maturitySummary.maturityAmount | formatNumber }} {{ currencyName }}
</span>
</div>
</div>
</div>
</div>
}
<!-- ══ 5. Interest Rate Chart ═══════════════════════════════════ -->
<div class="preview-section">
<div class="preview-section__header">
@@ -77,7 +77,8 @@ export class FixedDepositAccountPreviewStepComponent implements OnChanges {
/** Fixed Deposits Account Data */
@Input() fixedDepositAccountData: any;
/** Tenure selected from the interest rate chart step */
@Input() selectedTenure: { depositPeriod: number; depositPeriodFrequencyId: number } | null = null;
@Input() selectedTenure: { depositPeriod: number; depositPeriodFrequencyId: number; depositAmount?: number } | null =
null;
/** Charges Displayed Columns */
chargesDisplayedColumns: string[] = [
@@ -146,10 +147,27 @@ export class FixedDepositAccountPreviewStepComponent implements OnChanges {
}
get matchedSlabIndex(): number {
if (!this.selectedTenure || !this.interestRateChartData?.length) return -1;
const { depositPeriod, depositPeriodFrequencyId } = this.selectedTenure;
if (!this.interestRateChartData?.length) return -1;
let depositPeriod: number;
let depositPeriodFrequencyId: number;
if (this.selectedTenure) {
depositPeriod = this.selectedTenure.depositPeriod;
depositPeriodFrequencyId = this.selectedTenure.depositPeriodFrequencyId;
} else if (
this.fixedDepositsAccountProductTemplate?.minDepositTerm != null &&
this.fixedDepositsAccountProductTemplate?.minDepositTermType?.id != null
) {
depositPeriod = this.fixedDepositsAccountProductTemplate.minDepositTerm;
depositPeriodFrequencyId = this.fixedDepositsAccountProductTemplate.minDepositTermType.id;
} else {
return -1;
}
// Convert selected tenure to days for comparison
const toDays = (v: number, id: number) => {
if (id === 1) return v * 7; // Weeks
if (id === 2) return v * 30; // Months
if (id === 3) return v * 365; // Years
return v; // Days (id=0)
@@ -158,7 +176,13 @@ export class FixedDepositAccountPreviewStepComponent implements OnChanges {
return this.interestRateChartData.findIndex((slab: any) => {
const periodTypeValue = (slab.periodType?.value || '').toLowerCase();
const slabId = periodTypeValue.includes('month') ? 2 : periodTypeValue.includes('year') ? 3 : 0;
const slabId = periodTypeValue.includes('month')
? 2
: periodTypeValue.includes('year')
? 3
: periodTypeValue.includes('week')
? 1
: 0;
const fromDays = toDays(slab.fromPeriod ?? 0, slabId);
const toDaysVal = slab.toPeriod != null ? toDays(slab.toPeriod, slabId) : Infinity;
return enteredDays >= fromDays && enteredDays <= toDaysVal;
@@ -166,9 +190,111 @@ export class FixedDepositAccountPreviewStepComponent implements OnChanges {
}
get selectedTenureLabel(): string {
if (!this.selectedTenure) return '';
const { depositPeriod, depositPeriodFrequencyId } = this.selectedTenure;
const unitMap: Record<number, string> = { 0: 'Days', 2: 'Months', 3: 'Years' };
let depositPeriod: number;
let depositPeriodFrequencyId: number;
if (this.selectedTenure) {
depositPeriod = this.selectedTenure.depositPeriod;
depositPeriodFrequencyId = this.selectedTenure.depositPeriodFrequencyId;
} else if (
this.fixedDepositsAccountProductTemplate?.minDepositTerm != null &&
this.fixedDepositsAccountProductTemplate?.minDepositTermType?.id != null
) {
depositPeriod = this.fixedDepositsAccountProductTemplate.minDepositTerm;
depositPeriodFrequencyId = this.fixedDepositsAccountProductTemplate.minDepositTermType.id;
} else {
return '';
}
const unitMap: Record<number, string> = { 0: 'Days', 1: 'Weeks', 2: 'Months', 3: 'Years' };
return `${depositPeriod} ${unitMap[depositPeriodFrequencyId] ?? ''}`;
}
get maturitySummary(): {
depositAmount: number;
maturityAmount: number;
totalInterest: number;
interestRate: number;
tenureLabel: string;
} {
let depositPeriod = 0;
let depositPeriodFrequencyId = 0;
if (this.selectedTenure) {
depositPeriod = this.selectedTenure.depositPeriod;
depositPeriodFrequencyId = this.selectedTenure.depositPeriodFrequencyId;
} else if (
this.fixedDepositsAccountProductTemplate?.minDepositTerm != null &&
this.fixedDepositsAccountProductTemplate?.minDepositTermType?.id != null
) {
depositPeriod = this.fixedDepositsAccountProductTemplate.minDepositTerm;
depositPeriodFrequencyId = this.fixedDepositsAccountProductTemplate.minDepositTermType.id;
}
const depositAmount =
this.selectedTenure?.depositAmount ??
this.fixedDepositAccountData?.depositAmount ??
this.fixedDepositsAccountProductTemplate?.depositAmount ??
0;
const chartRate =
this.matchedSlabIndex >= 0 ? this.interestRateChartData[this.matchedSlabIndex]?.annualInterestRate : null;
const interestRate = chartRate ?? this.fixedDepositsAccountProductTemplate?.nominalAnnualInterestRate ?? 0;
const r = interestRate / 100;
// Tenure in years (t)
let t = 0;
const daysInYear =
this.fixedDepositsAccountProductTemplate?.interestCalculationDaysInYearType?.id === 360 ? 360 : 365;
if (depositPeriodFrequencyId === 0) {
// Days
t = depositPeriod / daysInYear;
} else if (depositPeriodFrequencyId === 1) {
// Weeks
t = (depositPeriod * 7) / daysInYear;
} else if (depositPeriodFrequencyId === 2) {
// Months
t = depositPeriod / 12;
} else if (depositPeriodFrequencyId === 3) {
// Years
t = depositPeriod;
}
// Compounding frequency (n)
const compPeriodValue = (
this.fixedDepositsAccountProductTemplate?.interestCompoundingPeriodType?.value || ''
).toLowerCase();
let n = 1;
if (compPeriodValue.includes('daily')) n = daysInYear;
else if (compPeriodValue.includes('monthly')) n = 12;
else if (compPeriodValue.includes('quarterly')) n = 4;
else if (compPeriodValue.includes('half')) n = 2;
else if (compPeriodValue.includes('biannual')) n = 2;
else if (compPeriodValue.includes('annual')) n = 1;
// Interest Calculation Type
const calcTypeValue = (
this.fixedDepositsAccountProductTemplate?.interestCalculationType?.value || ''
).toLowerCase();
const isSimple = calcTypeValue.includes('simple');
let maturityAmount = 0;
if (isSimple) {
maturityAmount = depositAmount + depositAmount * r * t;
} else {
// Compound interest: A = P(1 + r/n)^(nt)
maturityAmount = depositAmount * Math.pow(1 + r / n, n * t);
}
const totalInterest = maturityAmount - depositAmount;
return {
depositAmount,
maturityAmount,
totalInterest,
interestRate,
tenureLabel: this.selectedTenureLabel
};
}
}
+2 -1
View File
@@ -5,12 +5,13 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
// IMPORTANT: THIS FILE IS AUTO GENERATED! DO NOT MANUALLY EDIT OR CHECKIN!
/* tslint:disable */
export default {
'mifos_x': {
'version': '260507',
'hash': 'b0cffd38'
'hash': 'a12ba20d'
},
'allow_switching_backend_instance': true
};