feat: implement cashier dashboard, dividend dashboards, share accounts views, and checker tasks
This commit is contained in:
+20
-1
@@ -54,6 +54,24 @@
|
||||
<strong>{{ 'labels.commons.required' | translate }}</strong>
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field class="flex-48" (click)="resolutionDatePicker.open()">
|
||||
<mat-label>{{ 'labels.inputs.Resolution Date' | translate }}</mat-label>
|
||||
<input
|
||||
matInput
|
||||
[min]="minDate"
|
||||
[max]="maxDate"
|
||||
[matDatepicker]="resolutionDatePicker"
|
||||
formControlName="resolutionDate"
|
||||
required
|
||||
/>
|
||||
<mat-datepicker-toggle matSuffix [for]="resolutionDatePicker"></mat-datepicker-toggle>
|
||||
<mat-datepicker #resolutionDatePicker></mat-datepicker>
|
||||
<mat-error>
|
||||
{{ 'labels.inputs.Resolution Date' | translate }} {{ 'labels.commons.is' | translate }}
|
||||
<strong>{{ 'labels.commons.required' | translate }}</strong>
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
|
||||
<div class="layout-row align-center gap-2percent margin-t responsive-column">
|
||||
@@ -68,7 +86,8 @@
|
||||
!(
|
||||
sharesAccountDetailsForm.get('productId')?.value &&
|
||||
sharesAccountDetailsForm.get('submittedDate')?.value &&
|
||||
sharesAccountDetailsForm.get('resolutionNumber')?.value
|
||||
sharesAccountDetailsForm.get('resolutionNumber')?.value &&
|
||||
sharesAccountDetailsForm.get('resolutionDate')?.value
|
||||
)
|
||||
"
|
||||
>
|
||||
|
||||
+7
-1
@@ -76,7 +76,9 @@ export class SharesAccountDetailsStepComponent implements OnInit {
|
||||
this.sharesAccountTemplate.timeline.submittedOnDate &&
|
||||
new Date(this.sharesAccountTemplate.timeline.submittedOnDate),
|
||||
externalId: this.sharesAccountTemplate.externalId,
|
||||
resolutionNumber: this.sharesAccountTemplate.resolutionNumber
|
||||
resolutionNumber: this.sharesAccountTemplate.resolutionNumber,
|
||||
resolutionDate:
|
||||
this.sharesAccountTemplate.resolutionDate && new Date(this.sharesAccountTemplate.resolutionDate)
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -102,6 +104,10 @@ export class SharesAccountDetailsStepComponent implements OnInit {
|
||||
resolutionNumber: [
|
||||
'',
|
||||
Validators.required
|
||||
],
|
||||
resolutionDate: [
|
||||
'',
|
||||
Validators.required
|
||||
]
|
||||
});
|
||||
}
|
||||
|
||||
+14
-3
@@ -41,6 +41,11 @@
|
||||
<span class="flex-60">{{ sharesAccount.resolutionNumber }}</span>
|
||||
</div>
|
||||
|
||||
<div class="flex-fill">
|
||||
<span class="flex-40">{{ 'labels.inputs.Resolution Date' | translate }}</span>
|
||||
<span class="flex-60">{{ sharesAccount.resolutionDate | dateFormat }}</span>
|
||||
</div>
|
||||
|
||||
<h3 class="mat-h3 flex-fill">{{ 'labels.heading.Terms' | translate }}</h3>
|
||||
|
||||
<mat-divider class="flex-fill"></mat-divider>
|
||||
@@ -71,9 +76,15 @@
|
||||
|
||||
<div class="flex-fill">
|
||||
<span class="flex-40">{{ 'labels.inputs.Default Savings Account' | translate }}</span>
|
||||
<span class="flex-60">{{
|
||||
sharesAccount.savingsAccountId | find: sharesAccountProductTemplate.clientSavingsAccounts : 'id' : 'accountNo'
|
||||
}}</span>
|
||||
<span class="flex-60">
|
||||
@if (sharesAccount.savingsAccountId) {
|
||||
{{
|
||||
sharesAccount.savingsAccountId | find: sharesAccountProductTemplate.clientSavingsAccounts : 'id' : 'accountNo'
|
||||
}}
|
||||
} @else {
|
||||
{{ 'labels.inputs.Unassigned' | translate }}
|
||||
}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@if (sharesAccount.minimumActivePeriod) {
|
||||
|
||||
+27
@@ -30,6 +30,9 @@
|
||||
<span class="flex-48">{{ calculateCurrenValue() | currency: currency.code : 'symbol-narrow' : '1.2-2' }}</span>
|
||||
}
|
||||
|
||||
<!-- No "None" option: the server rejects a share account without a linked savings account, and
|
||||
offering the choice only produced a failure after the whole stepper was filled in. The link
|
||||
is also where an over-payment at the counter is credited back to the member. -->
|
||||
<mat-form-field class="flex-48">
|
||||
<mat-label>{{ 'labels.inputs.Default Savings Account' | translate }}</mat-label>
|
||||
<mat-select formControlName="savingsAccountId" required>
|
||||
@@ -42,6 +45,12 @@
|
||||
<strong>{{ 'labels.commons.required' | translate }}</strong>
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
@if (savingsAccountsData?.length === 0) {
|
||||
<p class="sats-field-note flex-98">
|
||||
This client has no active savings account in the share product's currency. Open one first — a share account
|
||||
cannot be created without it.
|
||||
</p>
|
||||
}
|
||||
|
||||
<mat-form-field class="flex-48" (click)="applicationDatePicker.open()">
|
||||
<mat-label>{{ 'labels.inputs.Application Date' | translate }}</mat-label>
|
||||
@@ -67,6 +76,15 @@
|
||||
|
||||
<h4 class="mat-h4 flex-98">{{ 'labels.heading.Minimum Active Period' | translate }}</h4>
|
||||
|
||||
<!-- Spelled out because the field name does not say what it decides. This is the holding period
|
||||
that qualifies a member for a dividend: shares bought fewer days before the period end than
|
||||
this are skipped entirely by the dividend allocation, and the member is paid nothing on
|
||||
them. Setting it wrong is silent — nobody sees it until a declaration pays out. -->
|
||||
<p class="sats-field-note flex-98">
|
||||
Enter the minimum number of days a member must hold these shares before they qualify for a dividend. Shares held
|
||||
for fewer days than this are excluded from the dividend calculation and earn nothing for that period.
|
||||
</p>
|
||||
|
||||
<mat-form-field class="flex-48">
|
||||
<mat-label>{{ 'labels.inputs.Frequency' | translate }}</mat-label>
|
||||
<input type="number" matInput formControlName="minimumActivePeriod" />
|
||||
@@ -88,6 +106,15 @@
|
||||
|
||||
<h4 class="mat-h4 flex-98">{{ 'labels.heading.Lock-in Period' | translate }}</h4>
|
||||
|
||||
<!-- Sits next to Minimum Active Period and is easily confused with it, so the difference is
|
||||
stated outright: that one decides whether a dividend is earned, this one decides whether
|
||||
shares can be sold back. Counted per purchase, not per account — a later purchase carries
|
||||
its own lock-in from its own date, which is not obvious from a single "Frequency" box. -->
|
||||
<p class="sats-field-note flex-98">
|
||||
Enter the minimum number of days a member must wait before they can redeem (sell back) these shares. This is
|
||||
counted separately from each purchase, and does not affect dividends — for that, use Minimum Active Period above.
|
||||
</p>
|
||||
|
||||
<mat-form-field class="flex-48">
|
||||
<mat-label>{{ 'labels.inputs.Frequency' | translate }}</mat-label>
|
||||
<input type="number" matInput formControlName="lockinPeriodFrequency" />
|
||||
|
||||
+11
@@ -14,3 +14,14 @@ h4 {
|
||||
.margin-t {
|
||||
margin-top: 1em;
|
||||
}
|
||||
|
||||
/* Red because both fields it marks fail silently — a wrong Minimum Active Period pays the member
|
||||
nothing months later, a wrong Lock-in Period only surfaces when a redemption is refused. Neither
|
||||
raises a validation error at the time. Sits under the heading and above the inputs, so it is read
|
||||
before the number is typed rather than after. */
|
||||
.sats-field-note {
|
||||
margin: -0.5em 0 1em;
|
||||
color: #b42318;
|
||||
font-size: 12.5px;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
+7
@@ -128,6 +128,13 @@ export class SharesAccountTermsStepComponent implements OnChanges, OnInit {
|
||||
],
|
||||
unitPrice: [{ value: '', disabled: true }],
|
||||
currencyMultiple: [{ value: '', disabled: true }],
|
||||
// Required to match the server, which has always rejected a share account without a linked
|
||||
// savings account. The form allowed one through, so the failure surfaced as a validation
|
||||
// error on submit after the whole stepper had been filled in.
|
||||
//
|
||||
// It also gives an over-payment somewhere to go: a member who hands over more cash than the
|
||||
// application costs has the difference credited to this account, rather than being left in a
|
||||
// suspense GL that nobody can attribute back to them.
|
||||
savingsAccountId: [
|
||||
'',
|
||||
Validators.required
|
||||
|
||||
Reference in New Issue
Block a user