Skip to content

Commit 218a21c

Browse files
authored
refactor: make outstanding amounts more granular (#1510)
* refactor: rename 'total_outstanding' amounts * refactor: rename 'outstanding_from_due' & expand disbursed outstanding functions * feat: introduce interest payment outstanding projection * chore: make overdue_duration optional * chore: implement 'defaults_at' * fix: unstable let expression See rust-lang/rust#53667 for more * test: fix maturity offset with 1 interest accrual * test: add interest_outstanding projection unit tests * chore: add interest_due_duration in gql layer * chore: add interestDueDuration defaults to frontend * chore: ignore 'DAYS' variant for 'durationPeriod' select
1 parent 1e9b49a commit 218a21c

File tree

21 files changed

+594
-91
lines changed

21 files changed

+594
-91
lines changed

apps/admin-panel/app/credit-facilities/create.tsx

+11-5
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,10 @@ export const CreateCreditFacilityDialog: React.FC<CreateCreditFacilityDialogProp
234234
units: parseInt(durationUnits),
235235
period: durationPeriod as Period,
236236
},
237+
interestDueDuration: {
238+
units: parseInt("0"),
239+
period: Period.Days,
240+
},
237241
},
238242
},
239243
},
@@ -463,11 +467,13 @@ export const CreateCreditFacilityDialog: React.FC<CreateCreditFacilityDialogProp
463467
<SelectValue placeholder={t("form.placeholders.selectPeriod")} />
464468
</SelectTrigger>
465469
<SelectContent>
466-
{Object.values(Period).map((period) => (
467-
<SelectItem key={period} value={period}>
468-
{formatPeriod(period)}
469-
</SelectItem>
470-
))}
470+
{Object.values(Period)
471+
.filter((period) => period !== Period.Days)
472+
.map((period) => (
473+
<SelectItem key={period} value={period}>
474+
{formatPeriod(period)}
475+
</SelectItem>
476+
))}
471477
</SelectContent>
472478
</Select>
473479
</div>

apps/admin-panel/app/terms-templates/[terms-template-id]/update.tsx

+11-5
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,10 @@ export const UpdateTermsTemplateDialog: React.FC<UpdateTermsTemplateDialogProps>
115115
period: formValues.durationPeriod as Period,
116116
units: parseInt(formValues.durationUnits),
117117
},
118+
interestDueDuration: {
119+
period: Period.Days,
120+
units: parseInt("0"),
121+
},
118122
liquidationCvl: formValues.liquidationCvl,
119123
marginCallCvl: formValues.marginCallCvl,
120124
initialCvl: formValues.initialCvl,
@@ -213,11 +217,13 @@ export const UpdateTermsTemplateDialog: React.FC<UpdateTermsTemplateDialogProps>
213217
<SelectValue placeholder={t("placeholders.durationPeriod")} />
214218
</SelectTrigger>
215219
<SelectContent>
216-
{Object.values(Period).map((period) => (
217-
<SelectItem key={period} value={period}>
218-
{formatPeriod(period)}
219-
</SelectItem>
220-
))}
220+
{Object.values(Period)
221+
.filter((period) => period !== Period.Days)
222+
.map((period) => (
223+
<SelectItem key={period} value={period}>
224+
{formatPeriod(period)}
225+
</SelectItem>
226+
))}
221227
</SelectContent>
222228
</Select>
223229
</div>

apps/admin-panel/app/terms-templates/create.tsx

+11-5
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,10 @@ export const CreateTermsTemplateDialog: React.FC<CreateTermsTemplateDialogProps>
113113
period: formValues.durationPeriod as Period,
114114
units: parseInt(formValues.durationUnits),
115115
},
116+
interestDueDuration: {
117+
period: Period.Days,
118+
units: parseInt("0"),
119+
},
116120
liquidationCvl: formValues.liquidationCvl,
117121
marginCallCvl: formValues.marginCallCvl,
118122
initialCvl: formValues.initialCvl,
@@ -226,11 +230,13 @@ export const CreateTermsTemplateDialog: React.FC<CreateTermsTemplateDialogProps>
226230
<SelectValue placeholder={t("placeholders.durationPeriod")} />
227231
</SelectTrigger>
228232
<SelectContent>
229-
{Object.values(Period).map((period) => (
230-
<SelectItem key={period} value={period}>
231-
{formatPeriod(period)}
232-
</SelectItem>
233-
))}
233+
{Object.values(Period)
234+
.filter((period) => period !== Period.Days)
235+
.map((period) => (
236+
<SelectItem key={period} value={period}>
237+
{formatPeriod(period)}
238+
</SelectItem>
239+
))}
234240
</SelectContent>
235241
</Select>
236242
</div>

apps/admin-panel/cypress/e2e/credit-facilities.cy.ts

+4
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,10 @@ describe("credit facility", () => {
2929
units: 12 * 9999999,
3030
period: Period.Months,
3131
},
32+
interestDueDuration: {
33+
units: 0,
34+
period: Period.Days,
35+
},
3236
}).then((id) => {
3337
cy.log(`Created terms template with ID: ${id}`)
3438
})

apps/admin-panel/lib/graphql/generated/index.ts

+4
Original file line numberDiff line numberDiff line change
@@ -1307,6 +1307,7 @@ export type PaymentEntry = {
13071307
};
13081308

13091309
export enum Period {
1310+
Days = 'DAYS',
13101311
Months = 'MONTHS'
13111312
}
13121313

@@ -1670,6 +1671,7 @@ export type TermsInput = {
16701671
duration: DurationInput;
16711672
incurrenceInterval: InterestInterval;
16721673
initialCvl: Scalars['CVLPct']['input'];
1674+
interestDueDuration: DurationInput;
16731675
liquidationCvl: Scalars['CVLPct']['input'];
16741676
marginCallCvl: Scalars['CVLPct']['input'];
16751677
oneTimeFeeRate: Scalars['OneTimeFeeRatePct']['input'];
@@ -1691,6 +1693,7 @@ export type TermsTemplateCreateInput = {
16911693
duration: DurationInput;
16921694
incurrenceInterval: InterestInterval;
16931695
initialCvl: Scalars['CVLPct']['input'];
1696+
interestDueDuration: DurationInput;
16941697
liquidationCvl: Scalars['CVLPct']['input'];
16951698
marginCallCvl: Scalars['CVLPct']['input'];
16961699
name: Scalars['String']['input'];
@@ -1709,6 +1712,7 @@ export type TermsTemplateUpdateInput = {
17091712
id: Scalars['UUID']['input'];
17101713
incurrenceInterval: InterestInterval;
17111714
initialCvl: Scalars['CVLPct']['input'];
1715+
interestDueDuration: DurationInput;
17121716
liquidationCvl: Scalars['CVLPct']['input'];
17131717
marginCallCvl: Scalars['CVLPct']['input'];
17141718
oneTimeFeeRate: Scalars['OneTimeFeeRatePct']['input'];

apps/admin-panel/lib/graphql/generated/mocks.ts

+4-1
Original file line numberDiff line numberDiff line change
@@ -1256,7 +1256,7 @@ export const mockDurationInput = (overrides?: Partial<DurationInput>, _relations
12561256
const relationshipsToOmit: Set<string> = new Set(_relationshipsToOmit);
12571257
relationshipsToOmit.add('DurationInput');
12581258
return {
1259-
period: overrides && overrides.hasOwnProperty('period') ? overrides.period! : Period.Months,
1259+
period: overrides && overrides.hasOwnProperty('period') ? overrides.period! : Period.Days,
12601260
units: overrides && overrides.hasOwnProperty('units') ? overrides.units! : faker.number.int({ min: 0, max: 9999 }),
12611261
};
12621262
};
@@ -1733,6 +1733,7 @@ export const mockTermsInput = (overrides?: Partial<TermsInput>, _relationshipsTo
17331733
duration: overrides && overrides.hasOwnProperty('duration') ? overrides.duration! : relationshipsToOmit.has('DurationInput') ? {} as DurationInput : mockDurationInput({}, relationshipsToOmit),
17341734
incurrenceInterval: overrides && overrides.hasOwnProperty('incurrenceInterval') ? overrides.incurrenceInterval! : InterestInterval.EndOfDay,
17351735
initialCvl: overrides && overrides.hasOwnProperty('initialCvl') ? overrides.initialCvl! : generateMockValue.int(),
1736+
interestDueDuration: overrides && overrides.hasOwnProperty('interestDueDuration') ? overrides.interestDueDuration! : relationshipsToOmit.has('DurationInput') ? {} as DurationInput : mockDurationInput({}, relationshipsToOmit),
17361737
liquidationCvl: overrides && overrides.hasOwnProperty('liquidationCvl') ? overrides.liquidationCvl! : generateMockValue.int(),
17371738
marginCallCvl: overrides && overrides.hasOwnProperty('marginCallCvl') ? overrides.marginCallCvl! : generateMockValue.int(),
17381739
oneTimeFeeRate: overrides && overrides.hasOwnProperty('oneTimeFeeRate') ? overrides.oneTimeFeeRate! : faker.lorem.word(),
@@ -1762,6 +1763,7 @@ export const mockTermsTemplateCreateInput = (overrides?: Partial<TermsTemplateCr
17621763
duration: overrides && overrides.hasOwnProperty('duration') ? overrides.duration! : relationshipsToOmit.has('DurationInput') ? {} as DurationInput : mockDurationInput({}, relationshipsToOmit),
17631764
incurrenceInterval: overrides && overrides.hasOwnProperty('incurrenceInterval') ? overrides.incurrenceInterval! : InterestInterval.EndOfDay,
17641765
initialCvl: overrides && overrides.hasOwnProperty('initialCvl') ? overrides.initialCvl! : generateMockValue.int(),
1766+
interestDueDuration: overrides && overrides.hasOwnProperty('interestDueDuration') ? overrides.interestDueDuration! : relationshipsToOmit.has('DurationInput') ? {} as DurationInput : mockDurationInput({}, relationshipsToOmit),
17651767
liquidationCvl: overrides && overrides.hasOwnProperty('liquidationCvl') ? overrides.liquidationCvl! : generateMockValue.int(),
17661768
marginCallCvl: overrides && overrides.hasOwnProperty('marginCallCvl') ? overrides.marginCallCvl! : generateMockValue.int(),
17671769
name: overrides && overrides.hasOwnProperty('name') ? overrides.name! : generateMockValue.name(),
@@ -1788,6 +1790,7 @@ export const mockTermsTemplateUpdateInput = (overrides?: Partial<TermsTemplateUp
17881790
id: overrides && overrides.hasOwnProperty('id') ? overrides.id! : generateMockValue.uuid(),
17891791
incurrenceInterval: overrides && overrides.hasOwnProperty('incurrenceInterval') ? overrides.incurrenceInterval! : InterestInterval.EndOfDay,
17901792
initialCvl: overrides && overrides.hasOwnProperty('initialCvl') ? overrides.initialCvl! : generateMockValue.int(),
1793+
interestDueDuration: overrides && overrides.hasOwnProperty('interestDueDuration') ? overrides.interestDueDuration! : relationshipsToOmit.has('DurationInput') ? {} as DurationInput : mockDurationInput({}, relationshipsToOmit),
17911794
liquidationCvl: overrides && overrides.hasOwnProperty('liquidationCvl') ? overrides.liquidationCvl! : generateMockValue.int(),
17921795
marginCallCvl: overrides && overrides.hasOwnProperty('marginCallCvl') ? overrides.marginCallCvl! : generateMockValue.int(),
17931796
oneTimeFeeRate: overrides && overrides.hasOwnProperty('oneTimeFeeRate') ? overrides.oneTimeFeeRate! : faker.lorem.word(),

bats/credit-facility.bats

+1
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,7 @@ ymd() {
126126
incurrenceInterval: "END_OF_DAY",
127127
oneTimeFeeRate: "5",
128128
duration: { period: "MONTHS", units: 3 },
129+
interestDueDuration: { period: "DAYS", units: 0 },
129130
liquidationCvl: "105",
130131
marginCallCvl: "125",
131132
initialCvl: "140"

bats/terms_template.bats

+2
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ teardown_file() {
2626
period: "MONTHS",
2727
units: 12
2828
},
29+
interestDueDuration: { period: "DAYS", units: 0 },
2930
liquidationCvl: 80,
3031
marginCallCvl: 90,
3132
initialCvl: 100
@@ -58,6 +59,7 @@ teardown_file() {
5859
period: "MONTHS",
5960
units: 24
6061
},
62+
interestDueDuration: { period: "DAYS", units: 0 },
6163
liquidationCvl: 75,
6264
marginCallCvl: 85,
6365
initialCvl: 95

0 commit comments

Comments
 (0)