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:
Jaanu423
2026-06-04 13:20:53 +05:30
co-authored by Claude Sonnet 4.6
parent 5f7de01ba9
commit c8e9ab8f84
12 changed files with 1613 additions and 239 deletions
@@ -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>
@@ -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;
}
}
@@ -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;
}
}
@@ -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,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;
}
}