feat(share): add Share Collection cashier flow and fix Share Product accounting
- Add Share Collection screen under Cashier menu for cash pre-collection before share account activation; posts Dr Cash Vault, Cr Share Sundry, Cr Income GL entries with duplicate-collection guard - Restore all 4 GL mappings (Assets, Liabilities, Income, Equity) in Share Product accounting step with pill-style savings-product UI - Remove Multiples section from Share Product currency step; default Decimal Places to 2 - GET /cashier/sharecollection/:id endpoint for collection status check Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 4.6
parent
5f7de01ba9
commit
c8e9ab8f84
+101
-86
@@ -7,96 +7,111 @@
|
||||
-->
|
||||
|
||||
<form [formGroup]="shareProductAccountingForm">
|
||||
<!-- Accounting Rule -->
|
||||
<div class="shpa-section">
|
||||
<div class="shpa-section__header">
|
||||
<i class="fas fa-balance-scale"></i>
|
||||
<h3>{{ 'labels.heading.Accounting Rule' | translate }}</h3>
|
||||
<span class="shpa-section__icon">
|
||||
<i class="fas fa-book"></i>
|
||||
</span>
|
||||
<h3>Accounting</h3>
|
||||
</div>
|
||||
<div class="shpa-grid">
|
||||
<div class="shpa-field--full">
|
||||
<mat-radio-group class="shpa-radio-group" formControlName="accountingRule">
|
||||
@for (accountingRule of accountingRuleData; track accountingRule; let i = $index) {
|
||||
<mat-radio-button [value]="i + 1">
|
||||
{{ 'labels.accounting.' + accountingRule | translate }}
|
||||
</mat-radio-button>
|
||||
}
|
||||
</mat-radio-group>
|
||||
|
||||
<div class="shpa-section__body">
|
||||
<!-- Accounting Rule -->
|
||||
<div class="shpa-group">
|
||||
<div class="shpa-group__header">
|
||||
<h4>Accounting Rule</h4>
|
||||
</div>
|
||||
<div class="shpa-radio-group">
|
||||
<mat-radio-button [value]="2" [checked]="true" [disabled]="true">Cash</mat-radio-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Account Sections -->
|
||||
@for (section of accountingSectionConfigs; track section.key) {
|
||||
<div class="shpa-group">
|
||||
<div class="shpa-group__header">
|
||||
<h4>{{ section.title }}</h4>
|
||||
<button type="button" class="shpa-add-btn" (click)="addOtherGlMapping(section.otherMappingsControlName)">
|
||||
+ Add Other
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="shpa-list">
|
||||
@for (row of section.rows; track row.key) {
|
||||
<div class="shpa-row">
|
||||
<div class="shpa-row__meta">
|
||||
<span
|
||||
class="shpa-pill"
|
||||
[class.shpa-pill--green]="section.pillClass === 'green'"
|
||||
[class.shpa-pill--amber]="section.pillClass === 'amber'"
|
||||
[class.shpa-pill--blue]="section.pillClass === 'blue'"
|
||||
>
|
||||
{{ section.pillLabel }}
|
||||
</span>
|
||||
<span class="shpa-row__label">{{ row.label }}</span>
|
||||
</div>
|
||||
<mifosx-gl-account-selector
|
||||
class="shpa-row__input"
|
||||
[inputFormControl]="shareProductAccountingForm.get(row.controlName)"
|
||||
[glAccountList]="row.options"
|
||||
[required]="row.required"
|
||||
[inputLabel]="row.inputLabel"
|
||||
></mifosx-gl-account-selector>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
<div class="shpa-list" [formArrayName]="section.otherMappingsControlName">
|
||||
@for (
|
||||
otherRow of getOtherGlMappingsArray(section.otherMappingsControlName).controls;
|
||||
track $index;
|
||||
let i = $index
|
||||
) {
|
||||
<div class="shpa-other-row-wrap">
|
||||
<div class="shpa-row shpa-row--other" [formGroupName]="i">
|
||||
<div class="shpa-row__meta shpa-row__meta--other">
|
||||
<span
|
||||
class="shpa-pill"
|
||||
[class.shpa-pill--green]="section.pillClass === 'green'"
|
||||
[class.shpa-pill--amber]="section.pillClass === 'amber'"
|
||||
[class.shpa-pill--blue]="section.pillClass === 'blue'"
|
||||
>
|
||||
{{ section.pillLabel }}
|
||||
</span>
|
||||
<input
|
||||
class="shpa-row__name shpa-row__name--other-account"
|
||||
formControlName="accountName"
|
||||
placeholder="Enter account name..."
|
||||
type="text"
|
||||
/>
|
||||
</div>
|
||||
<div class="shpa-row__actions shpa-row__actions--other">
|
||||
<mat-form-field class="shpa-row__other-field" appearance="outline" subscriptSizing="dynamic">
|
||||
<mat-label>GL Code</mat-label>
|
||||
<mat-select
|
||||
[formControl]="getOtherGlMappingControl(section.otherMappingsControlName, i, 'glAccountId')"
|
||||
>
|
||||
@for (glAccount of section.otherAccountOptions; track glAccount.id) {
|
||||
<mat-option [value]="glAccount.id">
|
||||
{{ '(' + glAccount.glCode + ') ' + glAccount.name }}
|
||||
</mat-option>
|
||||
}
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
class="shpa-remove-btn shpa-remove-btn--side"
|
||||
(click)="removeOtherGlMapping(section.otherMappingsControlName, i)"
|
||||
>
|
||||
<i class="fas fa-times"></i>
|
||||
</button>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if (shareProductAccountingForm.value.accountingRule === 2) {
|
||||
<!-- Assets -->
|
||||
<div class="shpa-section">
|
||||
<div class="shpa-section__header">
|
||||
<i class="fas fa-building"></i>
|
||||
<h3>{{ 'labels.heading.Assets' | translate }}</h3>
|
||||
</div>
|
||||
<div class="shpa-grid">
|
||||
<mifosx-gl-account-selector
|
||||
class="shpa-field"
|
||||
[inputFormControl]="shareProductAccountingForm.controls.shareReferenceId"
|
||||
[glAccountList]="assetAccountData"
|
||||
[required]="true"
|
||||
[inputLabel]="'Share reference'"
|
||||
>
|
||||
</mifosx-gl-account-selector>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Liabilities -->
|
||||
<div class="shpa-section">
|
||||
<div class="shpa-section__header">
|
||||
<i class="fas fa-hand-holding-usd"></i>
|
||||
<h3>{{ 'labels.heading.Liabilities' | translate }}</h3>
|
||||
</div>
|
||||
<div class="shpa-grid">
|
||||
<mifosx-gl-account-selector
|
||||
class="shpa-field"
|
||||
[inputFormControl]="shareProductAccountingForm.controls.shareSuspenseId"
|
||||
[glAccountList]="liabilityAccountData"
|
||||
[required]="true"
|
||||
[inputLabel]="'Share suspense control'"
|
||||
>
|
||||
</mifosx-gl-account-selector>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Share Equity -->
|
||||
<div class="shpa-section">
|
||||
<div class="shpa-section__header">
|
||||
<i class="fas fa-chart-pie"></i>
|
||||
<h3>{{ 'labels.heading.Share Equity' | translate }}</h3>
|
||||
</div>
|
||||
<div class="shpa-grid">
|
||||
<mifosx-gl-account-selector
|
||||
class="shpa-field"
|
||||
[inputFormControl]="shareProductAccountingForm.controls.shareEquityId"
|
||||
[glAccountList]="equityAccountData"
|
||||
[required]="true"
|
||||
[inputLabel]="'Equity'"
|
||||
>
|
||||
</mifosx-gl-account-selector>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Income -->
|
||||
<div class="shpa-section">
|
||||
<div class="shpa-section__header">
|
||||
<i class="fas fa-money-bill-wave"></i>
|
||||
<h3>{{ 'labels.heading.Income' | translate }}</h3>
|
||||
</div>
|
||||
<div class="shpa-grid">
|
||||
<mifosx-gl-account-selector
|
||||
class="shpa-field"
|
||||
[inputFormControl]="shareProductAccountingForm.controls.incomeFromFeeAccountId"
|
||||
[glAccountList]="incomeAccountData"
|
||||
[required]="true"
|
||||
[inputLabel]="'Income from fees'"
|
||||
>
|
||||
</mifosx-gl-account-selector>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</form>
|
||||
|
||||
+266
-35
@@ -6,54 +6,73 @@
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
$border: #e2e8f0;
|
||||
$gray-50: #f8f9fa;
|
||||
$gray-200: #e8eaed;
|
||||
$gray-500: #6b7280;
|
||||
$gray-700: #334155;
|
||||
$gray-900: #202124;
|
||||
$primary: #1353a0;
|
||||
|
||||
.shpa-section {
|
||||
border: 1px solid #e2e8f0;
|
||||
border: 1px solid $border;
|
||||
border-radius: 10px;
|
||||
overflow: hidden;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.shpa-section__header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
padding: 12px 24px;
|
||||
background: $gray-50;
|
||||
border-bottom: 1px solid $gray-200;
|
||||
|
||||
h3 {
|
||||
margin: 0;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: $gray-900;
|
||||
}
|
||||
}
|
||||
|
||||
.shpa-section__icon {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
border-radius: 6px;
|
||||
background: #eff6ff;
|
||||
color: #2563eb;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.shpa-section__body {
|
||||
padding: 16px 24px 20px;
|
||||
}
|
||||
|
||||
.shpa-group {
|
||||
& + & {
|
||||
margin-top: 16px;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
&__header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
padding: 12px 24px;
|
||||
background: #f8f9fa;
|
||||
border-bottom: 1px solid #e8eaed;
|
||||
|
||||
i {
|
||||
font-size: 14px;
|
||||
color: #0ea5e9;
|
||||
}
|
||||
|
||||
h3 {
|
||||
margin: 0;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: #202124;
|
||||
}
|
||||
justify-content: space-between;
|
||||
gap: 16px;
|
||||
margin: 0 0 12px;
|
||||
}
|
||||
}
|
||||
|
||||
.shpa-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
gap: 4px 16px;
|
||||
padding: 16px 24px;
|
||||
|
||||
@media (width <= 600px) {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
.shpa-field {
|
||||
width: 100%;
|
||||
|
||||
&--full {
|
||||
grid-column: 1 / -1;
|
||||
h4 {
|
||||
margin: 0 0 12px;
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.08em;
|
||||
text-transform: uppercase;
|
||||
color: #94a3b8;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,3 +81,215 @@
|
||||
gap: 24px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.shpa-add-btn {
|
||||
border: 0;
|
||||
background: transparent;
|
||||
color: #2563eb;
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
padding: 0;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.shpa-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.shpa-list + .shpa-list {
|
||||
margin-top: 12px;
|
||||
}
|
||||
|
||||
.shpa-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 16px;
|
||||
padding: 10px 12px;
|
||||
border: 1px solid #e2e8f0;
|
||||
border-radius: 8px;
|
||||
background: #fff;
|
||||
|
||||
@media (width <= 900px) {
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
}
|
||||
}
|
||||
|
||||
.shpa-row:has(mifosx-gl-account-selector) {
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.shpa-row--other {
|
||||
border-style: dashed;
|
||||
border-color: #cbd5e1;
|
||||
background: #f8fafc;
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 0.95fr) minmax(0, 1.05fr);
|
||||
align-items: center;
|
||||
padding: 12px 16px;
|
||||
|
||||
@media (width <= 1100px) {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
.shpa-other-row-wrap {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1fr) auto;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
|
||||
@media (width <= 1100px) {
|
||||
grid-template-columns: 1fr;
|
||||
gap: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.shpa-row__meta {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
min-width: 0;
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
|
||||
.shpa-row__meta--other {
|
||||
min-width: 0;
|
||||
flex: 1 1 auto;
|
||||
align-self: stretch;
|
||||
}
|
||||
|
||||
.shpa-row__label {
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: $gray-700;
|
||||
}
|
||||
|
||||
.shpa-row__input {
|
||||
flex: 0 0 22rem;
|
||||
max-width: 22rem;
|
||||
min-width: 18rem;
|
||||
|
||||
@media (width <= 900px) {
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
min-width: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.shpa-row__input :is(mifosx-gl-account-selector) {
|
||||
width: 100%;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.shpa-row__input :is(mifosx-gl-account-selector) :is(.layout-column) {
|
||||
width: 100%;
|
||||
margin-right: 0 !important;
|
||||
}
|
||||
|
||||
.shpa-row__input :is(mifosx-gl-account-selector) :is(mat-form-field) {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.shpa-row__actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.shpa-row__actions--other {
|
||||
justify-content: flex-end;
|
||||
align-self: stretch;
|
||||
width: 100%;
|
||||
|
||||
@media (width <= 1100px) {
|
||||
justify-content: flex-start;
|
||||
}
|
||||
}
|
||||
|
||||
.shpa-row__name {
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
max-width: 24rem;
|
||||
height: 40px;
|
||||
border: 1px solid #cbd5e1;
|
||||
border-radius: 6px;
|
||||
padding: 0 12px;
|
||||
font-size: 14px;
|
||||
background: #fff;
|
||||
|
||||
@media (width <= 900px) {
|
||||
max-width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.shpa-row__name--other-account {
|
||||
max-width: 100%;
|
||||
flex: 1 1 auto;
|
||||
height: 48px;
|
||||
}
|
||||
|
||||
.shpa-row__other-field {
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.shpa-row__other-field :is(mat-form-field) {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.shpa-row__other-field :is(.mat-mdc-form-field-required-marker) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.shpa-remove-btn {
|
||||
border: 0;
|
||||
background: transparent;
|
||||
color: #ef4444;
|
||||
font-size: 18px;
|
||||
line-height: 1;
|
||||
cursor: pointer;
|
||||
padding: 0 4px;
|
||||
}
|
||||
|
||||
.shpa-remove-btn--side {
|
||||
place-self: center end;
|
||||
margin-right: 4px;
|
||||
|
||||
@media (width <= 1100px) {
|
||||
justify-self: start;
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.shpa-pill {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 2px 8px;
|
||||
border-radius: 999px;
|
||||
font-size: 10px;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.04em;
|
||||
text-transform: uppercase;
|
||||
white-space: nowrap;
|
||||
|
||||
&--green {
|
||||
background: #dcfce7;
|
||||
color: #16a34a;
|
||||
}
|
||||
|
||||
&--amber {
|
||||
background: #fef3c7;
|
||||
color: #d97706;
|
||||
}
|
||||
|
||||
&--blue {
|
||||
background: #dbeafe;
|
||||
color: #2563eb;
|
||||
}
|
||||
}
|
||||
|
||||
+176
-38
@@ -8,15 +8,44 @@
|
||||
|
||||
import { Component, OnInit, Input, inject } from '@angular/core';
|
||||
import {
|
||||
FormsModule,
|
||||
UntypedFormGroup,
|
||||
UntypedFormBuilder,
|
||||
Validators,
|
||||
UntypedFormArray,
|
||||
UntypedFormControl,
|
||||
Validators,
|
||||
ReactiveFormsModule
|
||||
} from '@angular/forms';
|
||||
import { MatRadioGroup, MatRadioButton } from '@angular/material/radio';
|
||||
import { GlAccountSelectorComponent } from '../../../../shared/accounting/gl-account-selector/gl-account-selector.component';
|
||||
import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
|
||||
import { GLAccount } from 'app/shared/models/general.model';
|
||||
|
||||
type ShareAccountingSectionKey = 'assets' | 'liabilities' | 'income' | 'equity';
|
||||
|
||||
type ShareAccountingRowConfig = {
|
||||
key: string;
|
||||
label: string;
|
||||
inputLabel: string;
|
||||
controlName: string;
|
||||
options: GLAccount[];
|
||||
required?: boolean;
|
||||
};
|
||||
|
||||
type ShareAccountingSectionConfig = {
|
||||
key: ShareAccountingSectionKey;
|
||||
title: string;
|
||||
pillLabel: string;
|
||||
pillClass: 'green' | 'amber' | 'blue';
|
||||
rows: ShareAccountingRowConfig[];
|
||||
otherMappingsControlName: string;
|
||||
otherAccountOptions: GLAccount[];
|
||||
};
|
||||
|
||||
type OtherGlMappingValue = {
|
||||
accountName: string;
|
||||
glAccountId: number | null;
|
||||
};
|
||||
|
||||
@Component({
|
||||
selector: 'mifosx-share-product-accounting-step',
|
||||
@@ -24,6 +53,8 @@ import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
|
||||
styleUrls: ['./share-product-accounting-step.component.scss'],
|
||||
imports: [
|
||||
...STANDALONE_SHARED_IMPORTS,
|
||||
FormsModule,
|
||||
ReactiveFormsModule,
|
||||
MatRadioGroup,
|
||||
MatRadioButton,
|
||||
GlAccountSelectorComponent
|
||||
@@ -37,65 +68,172 @@ export class ShareProductAccountingStepComponent implements OnInit {
|
||||
@Input() shareProductFormValid: boolean;
|
||||
|
||||
shareProductAccountingForm: UntypedFormGroup;
|
||||
accountingSectionConfigs: ShareAccountingSectionConfig[] = [];
|
||||
|
||||
assetAccountData: any;
|
||||
incomeAccountData: any;
|
||||
equityAccountData: any;
|
||||
liabilityAccountData: any;
|
||||
assetAccountData: GLAccount[] = [];
|
||||
liabilityAccountData: GLAccount[] = [];
|
||||
incomeAccountData: GLAccount[] = [];
|
||||
equityAccountData: GLAccount[] = [];
|
||||
|
||||
constructor() {
|
||||
this.createShareProductAccountingForm();
|
||||
this.setConditionalControls();
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.assetAccountData = this.shareProductsTemplate.accountingMappingOptions.assetAccountOptions || [];
|
||||
this.incomeAccountData = this.shareProductsTemplate.accountingMappingOptions.incomeAccountOptions || [];
|
||||
this.equityAccountData = this.shareProductsTemplate.accountingMappingOptions.equityAccountOptions || [];
|
||||
this.liabilityAccountData = this.shareProductsTemplate.accountingMappingOptions.liabilityAccountOptions || [];
|
||||
const opts = this.shareProductsTemplate.accountingMappingOptions || {};
|
||||
this.assetAccountData = opts.assetAccountOptions || [];
|
||||
this.liabilityAccountData = opts.liabilityAccountOptions || [];
|
||||
this.incomeAccountData = opts.incomeAccountOptions || [];
|
||||
this.equityAccountData = opts.equityAccountOptions || [];
|
||||
|
||||
if (this.shareProductsTemplate.accountingRule) {
|
||||
this.buildAccountingSectionConfigs();
|
||||
|
||||
if (this.shareProductsTemplate.accountingMappings) {
|
||||
const m = this.shareProductsTemplate.accountingMappings;
|
||||
this.shareProductAccountingForm.patchValue({
|
||||
accountingRule: this.shareProductsTemplate.accountingRule.id
|
||||
shareReferenceId: m.shareReferenceId?.id || '',
|
||||
shareSuspenseId: m.shareSuspenseId?.id || '',
|
||||
incomeFromFeeAccountId: m.incomeFromFeeAccountId?.id || '',
|
||||
shareEquityId: m.shareEquityId?.id || ''
|
||||
});
|
||||
|
||||
if (this.shareProductsTemplate.accountingRule.id === 2) {
|
||||
this.shareProductAccountingForm.patchValue({
|
||||
shareReferenceId: this.shareProductsTemplate.accountingMappings.shareReferenceId.id,
|
||||
shareSuspenseId: this.shareProductsTemplate.accountingMappings.shareSuspenseId.id,
|
||||
shareEquityId: this.shareProductsTemplate.accountingMappings.shareEquityId.id,
|
||||
incomeFromFeeAccountId: this.shareProductsTemplate.accountingMappings.incomeFromFeeAccountId.id
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
createShareProductAccountingForm() {
|
||||
this.shareProductAccountingForm = this.formBuilder.group({
|
||||
accountingRule: [1]
|
||||
accountingRule: [2],
|
||||
shareReferenceId: [
|
||||
'',
|
||||
Validators.required
|
||||
],
|
||||
shareSuspenseId: [
|
||||
'',
|
||||
Validators.required
|
||||
],
|
||||
incomeFromFeeAccountId: [
|
||||
'',
|
||||
Validators.required
|
||||
],
|
||||
shareEquityId: [
|
||||
'',
|
||||
Validators.required
|
||||
],
|
||||
assetsOtherGlMappings: this.formBuilder.array([]),
|
||||
liabilitiesOtherGlMappings: this.formBuilder.array([]),
|
||||
incomeOtherGlMappings: this.formBuilder.array([]),
|
||||
equityOtherGlMappings: this.formBuilder.array([])
|
||||
});
|
||||
}
|
||||
|
||||
setConditionalControls() {
|
||||
this.shareProductAccountingForm.get('accountingRule').valueChanges.subscribe((accountingRule: any) => {
|
||||
if (accountingRule === 2) {
|
||||
this.shareProductAccountingForm.addControl('shareReferenceId', new UntypedFormControl('', Validators.required));
|
||||
this.shareProductAccountingForm.addControl('shareSuspenseId', new UntypedFormControl('', Validators.required));
|
||||
this.shareProductAccountingForm.addControl('shareEquityId', new UntypedFormControl('', Validators.required));
|
||||
this.shareProductAccountingForm.addControl(
|
||||
'incomeFromFeeAccountId',
|
||||
new UntypedFormControl('', Validators.required)
|
||||
);
|
||||
} else {
|
||||
this.shareProductAccountingForm.removeControl('shareReferenceId');
|
||||
this.shareProductAccountingForm.removeControl('shareSuspenseId');
|
||||
this.shareProductAccountingForm.removeControl('shareEquityId');
|
||||
this.shareProductAccountingForm.removeControl('incomeFromFeeAccountId');
|
||||
buildAccountingSectionConfigs() {
|
||||
this.accountingSectionConfigs = [
|
||||
{
|
||||
key: 'assets',
|
||||
title: 'Assets',
|
||||
pillLabel: 'Asset',
|
||||
pillClass: 'green',
|
||||
rows: [
|
||||
{
|
||||
key: 'shareReferenceId',
|
||||
label: 'Share Reference (Cash Vault)',
|
||||
inputLabel: 'Share reference',
|
||||
controlName: 'shareReferenceId',
|
||||
options: this.assetAccountData,
|
||||
required: true
|
||||
}
|
||||
],
|
||||
otherMappingsControlName: 'assetsOtherGlMappings',
|
||||
otherAccountOptions: this.assetAccountData
|
||||
},
|
||||
{
|
||||
key: 'liabilities',
|
||||
title: 'Liabilities',
|
||||
pillLabel: 'Liability',
|
||||
pillClass: 'green',
|
||||
rows: [
|
||||
{
|
||||
key: 'shareSuspenseId',
|
||||
label: 'Share Sundry (Suspense)',
|
||||
inputLabel: 'Share sundry / suspense',
|
||||
controlName: 'shareSuspenseId',
|
||||
options: this.liabilityAccountData,
|
||||
required: true
|
||||
}
|
||||
],
|
||||
otherMappingsControlName: 'liabilitiesOtherGlMappings',
|
||||
otherAccountOptions: this.liabilityAccountData
|
||||
},
|
||||
{
|
||||
key: 'income',
|
||||
title: 'Income',
|
||||
pillLabel: 'Income',
|
||||
pillClass: 'blue',
|
||||
rows: [
|
||||
{
|
||||
key: 'incomeFromFeeAccountId',
|
||||
label: 'Income From Fees',
|
||||
inputLabel: 'Income from fees',
|
||||
controlName: 'incomeFromFeeAccountId',
|
||||
options: this.incomeAccountData,
|
||||
required: true
|
||||
}
|
||||
],
|
||||
otherMappingsControlName: 'incomeOtherGlMappings',
|
||||
otherAccountOptions: this.incomeAccountData
|
||||
},
|
||||
{
|
||||
key: 'equity',
|
||||
title: 'Share Equity',
|
||||
pillLabel: 'Equity',
|
||||
pillClass: 'blue',
|
||||
rows: [
|
||||
{
|
||||
key: 'shareEquityId',
|
||||
label: 'Share Equity',
|
||||
inputLabel: 'Share equity',
|
||||
controlName: 'shareEquityId',
|
||||
options: this.equityAccountData,
|
||||
required: true
|
||||
}
|
||||
],
|
||||
otherMappingsControlName: 'equityOtherGlMappings',
|
||||
otherAccountOptions: this.equityAccountData
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
getOtherGlMappingsArray(controlName: string): UntypedFormArray {
|
||||
return this.shareProductAccountingForm.get(controlName) as UntypedFormArray;
|
||||
}
|
||||
|
||||
addOtherGlMapping(controlName: string) {
|
||||
this.getOtherGlMappingsArray(controlName).push(this.createOtherGlMappingGroup());
|
||||
}
|
||||
|
||||
removeOtherGlMapping(controlName: string, index: number) {
|
||||
this.getOtherGlMappingsArray(controlName).removeAt(index);
|
||||
}
|
||||
|
||||
getOtherGlMappingControl(controlName: string, index: number, fieldName: string): UntypedFormControl {
|
||||
return this.getOtherGlMappingsArray(controlName).at(index).get(fieldName) as UntypedFormControl;
|
||||
}
|
||||
|
||||
private createOtherGlMappingGroup(val: Partial<OtherGlMappingValue> = {}) {
|
||||
return this.formBuilder.group({
|
||||
accountName: [
|
||||
val.accountName || '',
|
||||
Validators.required
|
||||
],
|
||||
glAccountId: [
|
||||
val.glAccountId || '',
|
||||
Validators.required
|
||||
]
|
||||
});
|
||||
}
|
||||
|
||||
get shareProductAccounting() {
|
||||
return this.shareProductAccountingForm.value;
|
||||
const { assetsOtherGlMappings, liabilitiesOtherGlMappings, incomeOtherGlMappings, equityOtherGlMappings, ...rest } =
|
||||
this.shareProductAccountingForm.value;
|
||||
return rest;
|
||||
}
|
||||
}
|
||||
|
||||
-31
@@ -49,35 +49,4 @@
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="shpcr-section">
|
||||
<div class="shpcr-section__header">
|
||||
<i class="fas fa-calculator"></i>
|
||||
<h3>{{ 'labels.inputs.Multiples' | translate }}</h3>
|
||||
</div>
|
||||
<div class="shpcr-grid">
|
||||
<div class="shpcr-field--full shpcr-checkbox-row">
|
||||
<mat-checkbox labelPosition="before" formControlName="setMultiples">
|
||||
{{ 'labels.inputs.Set the share installment in multiples of' | translate }}
|
||||
</mat-checkbox>
|
||||
</div>
|
||||
|
||||
@if (shareProductCurrencyForm.value.setMultiples) {
|
||||
<mat-form-field appearance="outline" class="shpcr-field">
|
||||
<mat-label>{{ 'labels.inputs.Currency in multiples of' | translate }}</mat-label>
|
||||
<input
|
||||
type="number"
|
||||
min="1"
|
||||
matInput
|
||||
matTooltip="{{ 'tooltips.Amount to be rounded off' | translate }}"
|
||||
formControlName="inMultiplesOf"
|
||||
/>
|
||||
<mat-error>
|
||||
{{ 'labels.inputs.Currency in multiples of' | translate }} {{ 'labels.commons.is' | translate }}
|
||||
<strong>{{ 'labels.commons.required' | translate }}</strong>
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
+6
-48
@@ -6,11 +6,9 @@
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
import { Component, OnInit, Input, inject, DestroyRef } from '@angular/core';
|
||||
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
||||
import { Component, OnInit, Input, inject } from '@angular/core';
|
||||
import { UntypedFormGroup, UntypedFormBuilder, Validators, ReactiveFormsModule } from '@angular/forms';
|
||||
import { MatTooltip } from '@angular/material/tooltip';
|
||||
import { MatCheckbox } from '@angular/material/checkbox';
|
||||
import { FaIconComponent } from '@fortawesome/angular-fontawesome';
|
||||
import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
|
||||
|
||||
@@ -21,13 +19,11 @@ import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
|
||||
imports: [
|
||||
...STANDALONE_SHARED_IMPORTS,
|
||||
MatTooltip,
|
||||
FaIconComponent,
|
||||
MatCheckbox
|
||||
FaIconComponent
|
||||
]
|
||||
})
|
||||
export class ShareProductCurrencyStepComponent implements OnInit {
|
||||
private formBuilder = inject(UntypedFormBuilder);
|
||||
private destroyRef = inject(DestroyRef);
|
||||
|
||||
@Input() shareProductsTemplate: any;
|
||||
|
||||
@@ -44,12 +40,8 @@ export class ShareProductCurrencyStepComponent implements OnInit {
|
||||
|
||||
this.shareProductCurrencyForm.patchValue({
|
||||
currencyCode: this.shareProductsTemplate.currency?.code || this.currencyData[0].code,
|
||||
digitsAfterDecimal: this.shareProductsTemplate.digitsAfterDecimal ?? '',
|
||||
setMultiples: !!this.shareProductsTemplate.inMultiplesOf,
|
||||
inMultiplesOf: this.shareProductsTemplate.inMultiplesOf ?? ''
|
||||
digitsAfterDecimal: this.shareProductsTemplate.digitsAfterDecimal ?? 2
|
||||
});
|
||||
|
||||
this.setupConditionalValidation();
|
||||
}
|
||||
|
||||
createShareProductCurrencyForm() {
|
||||
@@ -59,54 +51,20 @@ export class ShareProductCurrencyStepComponent implements OnInit {
|
||||
Validators.required
|
||||
],
|
||||
digitsAfterDecimal: [
|
||||
'',
|
||||
2,
|
||||
[
|
||||
Validators.required,
|
||||
Validators.min(0)
|
||||
]
|
||||
],
|
||||
setMultiples: [false],
|
||||
inMultiplesOf: ['']
|
||||
});
|
||||
}
|
||||
|
||||
setupConditionalValidation() {
|
||||
const inMultiplesOfControl = this.shareProductCurrencyForm.get('inMultiplesOf');
|
||||
const setMultiplesControl = this.shareProductCurrencyForm.get('setMultiples');
|
||||
|
||||
const applyInMultiplesValidators = (checked: boolean) => {
|
||||
if (checked) {
|
||||
inMultiplesOfControl?.setValidators([
|
||||
Validators.required,
|
||||
Validators.min(1)
|
||||
]);
|
||||
} else {
|
||||
inMultiplesOfControl?.clearValidators();
|
||||
inMultiplesOfControl?.setValue('');
|
||||
}
|
||||
inMultiplesOfControl?.updateValueAndValidity();
|
||||
};
|
||||
|
||||
// Apply validators based on initial value
|
||||
applyInMultiplesValidators(setMultiplesControl?.value);
|
||||
|
||||
// Listen for changes
|
||||
setMultiplesControl?.valueChanges.pipe(takeUntilDestroyed(this.destroyRef)).subscribe((checked) => {
|
||||
applyInMultiplesValidators(checked);
|
||||
]
|
||||
});
|
||||
}
|
||||
|
||||
get shareProductCurrency() {
|
||||
const formValue = this.shareProductCurrencyForm.value;
|
||||
const result: any = {
|
||||
return {
|
||||
currencyCode: formValue.currencyCode,
|
||||
digitsAfterDecimal: formValue.digitsAfterDecimal
|
||||
};
|
||||
|
||||
if (formValue.inMultiplesOf !== '' && formValue.inMultiplesOf !== null && formValue.inMultiplesOf !== undefined) {
|
||||
result.inMultiplesOf = formValue.inMultiplesOf;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user