refactor: replace economic code fields with individual and legal variants, add equal length validation

This commit is contained in:
2026-06-03 17:48:54 +03:30
parent 8d6fa8860b
commit 694b2ec946
23 changed files with 272 additions and 101 deletions
@@ -68,6 +68,13 @@ export class FormErrorsService {
const info = errors['invalidUsername']; const info = errors['invalidUsername'];
out.push({ key: 'invalidUsername', message: info.message ?? `${label} معتبر نیست.` }); out.push({ key: 'invalidUsername', message: info.message ?? `${label} معتبر نیست.` });
} }
if (errors['equalLength']) {
const info = errors['equalLength'];
out.push({
key: 'equalLength',
message: `تعداد کاراکترهای ${label} باید برابر با ${info.length} باشد.`,
});
}
if (errors['invalidFiscalId']) { if (errors['invalidFiscalId']) {
const info = errors['invalidFiscalId']; const info = errors['invalidFiscalId'];
@@ -0,0 +1,19 @@
import { AbstractControl, ValidationErrors, ValidatorFn } from '@angular/forms';
export function equalLengthValidator(length: number): ValidatorFn {
return (control: AbstractControl): ValidationErrors | null => {
const v = control.value;
if (v === null || v === undefined || v === '') {
return null;
}
const normalized = String(v);
return normalized.length !== length
? {
equalLength: {
length,
},
}
: null;
};
}
+1
View File
@@ -1,3 +1,4 @@
export * from './equalLength.validator';
export * from './fiscal-id.validator'; export * from './fiscal-id.validator';
export * from './greater.validator'; export * from './greater.validator';
export * from './iban.validator'; export * from './iban.validator';
@@ -4,11 +4,10 @@
[modal]="true" [modal]="true"
[style]="{ width: '500px' }" [style]="{ width: '500px' }"
[closable]="true" [closable]="true"
(onHide)="close()" (onHide)="close()">
>
<form [formGroup]="form" (submit)="submit()" class="flex flex-col gap-4"> <form [formGroup]="form" (submit)="submit()" class="flex flex-col gap-4">
<field-name [control]="form.controls.name" /> <field-name [control]="form.controls.name" />
<field-economic-code [control]="form.controls.economic_code" /> <field-individual-economic-code [control]="form.controls.economic_code" />
<field-fiscal-id [control]="form.controls.fiscal_id" /> <field-fiscal-id [control]="form.controls.fiscal_id" />
<field-partner-token [control]="form.controls.partner_token" /> <field-partner-token [control]="form.controls.partner_token" />
<field-invoice-number-sequence [control]="form.controls.invoice_number_sequence" [min]="1" /> <field-invoice-number-sequence [control]="form.controls.invoice_number_sequence" [min]="1" />
@@ -1,7 +1,7 @@
import { AbstractFormDialog } from '@/shared/abstractClasses'; import { AbstractFormDialog } from '@/shared/abstractClasses';
import { import {
EconomicCodeComponent,
FiscalIdComponent, FiscalIdComponent,
IndividualEconomicCodeComponent,
NameComponent, NameComponent,
PartnerTokenComponent, PartnerTokenComponent,
} from '@/shared/components'; } from '@/shared/components';
@@ -22,7 +22,7 @@ import { BusinessActivitiesService } from '../services/main.service';
SharedDialogComponent, SharedDialogComponent,
FormFooterActionsComponent, FormFooterActionsComponent,
NameComponent, NameComponent,
EconomicCodeComponent, IndividualEconomicCodeComponent,
PartnerTokenComponent, PartnerTokenComponent,
InvoiceNumberSequenceComponent, InvoiceNumberSequenceComponent,
FiscalIdComponent, FiscalIdComponent,
@@ -37,11 +37,11 @@ export class ConsumerBusinessActivityFormComponent extends AbstractFormDialog<
form = this.fb.group({ form = this.fb.group({
name: fieldControl.name(this.initialValues?.name || ''), name: fieldControl.name(this.initialValues?.name || ''),
economic_code: fieldControl.economic_code(this.initialValues?.economic_code || ''), economic_code: fieldControl.individual_economic_code(this.initialValues?.economic_code || ''),
fiscal_id: fieldControl.fiscal_id(this.initialValues?.fiscal_id || ''), fiscal_id: fieldControl.fiscal_id(this.initialValues?.fiscal_id || ''),
partner_token: fieldControl.partner_token(this.initialValues?.partner_token || ''), partner_token: fieldControl.partner_token(this.initialValues?.partner_token || ''),
invoice_number_sequence: fieldControl.invoice_number_sequence( invoice_number_sequence: fieldControl.invoice_number_sequence(
this.initialValues?.invoice_number_sequence || 1, this.initialValues?.invoice_number_sequence || 1
), ),
}); });
@@ -1,6 +1,6 @@
<form [formGroup]="form" (submit)="submit()" class="flex flex-col gap-4"> <form [formGroup]="form" (submit)="submit()" class="flex flex-col gap-4">
<field-name [control]="form.controls.name" /> <field-name [control]="form.controls.name" />
<field-economic-code [control]="form.controls.economic_code" /> <field-individual-economic-code [control]="form.controls.economic_code" />
<field-fiscal-id [control]="form.controls.fiscal_id" /> <field-fiscal-id [control]="form.controls.fiscal_id" />
<field-invoice-number-sequence [control]="form.controls.invoice_number_sequence" /> <field-invoice-number-sequence [control]="form.controls.invoice_number_sequence" />
<field-partner-token [control]="form.controls.partner_token" /> <field-partner-token [control]="form.controls.partner_token" />
@@ -1,8 +1,8 @@
import { AbstractForm } from '@/shared/abstractClasses'; import { AbstractForm } from '@/shared/abstractClasses';
import { import {
EconomicCodeComponent,
FiscalIdComponent, FiscalIdComponent,
GuildIdComponent, GuildIdComponent,
IndividualEconomicCodeComponent,
LicenseStartsAtComponent, LicenseStartsAtComponent,
NameComponent, NameComponent,
PartnerTokenComponent, PartnerTokenComponent,
@@ -22,7 +22,7 @@ import { PartnerConsumerBusinessActivitiesService } from '../../services/busines
imports: [ imports: [
ReactiveFormsModule, ReactiveFormsModule,
NameComponent, NameComponent,
EconomicCodeComponent, IndividualEconomicCodeComponent,
FiscalIdComponent, FiscalIdComponent,
PartnerTokenComponent, PartnerTokenComponent,
GuildIdComponent, GuildIdComponent,
@@ -44,10 +44,10 @@ export class ConsumerBusinessActivitiesFormComponent extends AbstractForm<
initForm = () => { initForm = () => {
const form = this.fb.group({ const form = this.fb.group({
name: fieldControl.name(this.initialValues?.name || ''), name: fieldControl.name(this.initialValues?.name || ''),
economic_code: fieldControl.economic_code(this.initialValues?.economic_code || ''), economic_code: fieldControl.individual_economic_code(this.initialValues?.economic_code || ''),
fiscal_id: fieldControl.fiscal_id(this.initialValues?.fiscal_id || ''), fiscal_id: fieldControl.fiscal_id(this.initialValues?.fiscal_id || ''),
invoice_number_sequence: fieldControl.invoice_number_sequence( invoice_number_sequence: fieldControl.invoice_number_sequence(
parseInt(this.initialValues?.invoice_number_sequence || '1'), parseInt(this.initialValues?.invoice_number_sequence || '1')
), ),
partner_token: fieldControl.partner_token(this.initialValues?.partner_token || ''), partner_token: fieldControl.partner_token(this.initialValues?.partner_token || ''),
guild_id: fieldControl.guild_id(this.initialValues?.guild?.id || ''), guild_id: fieldControl.guild_id(this.initialValues?.guild?.id || ''),
@@ -63,7 +63,7 @@ export class ConsumerBusinessActivitiesFormComponent extends AbstractForm<
this.fb.control<string>('', { this.fb.control<string>('', {
nonNullable: true, nonNullable: true,
validators: fieldControl.license_starts_at('')[1], validators: fieldControl.license_starts_at('')[1],
}), })
); );
} }
@@ -5,12 +5,11 @@
<ng-template #topbarCenter> <ng-template #topbarCenter>
@if (posInfo()) { @if (posInfo()) {
<a [routerLink]="homeRouteLink" class="flex flex-col items-center justify-center gap-2"> <a [routerLink]="homeRouteLink" class="flex flex-col items-center justify-center gap-2">
<div class="w-8 h-8"> <div class="h-8 w-8">
<img <img
[src]="posInfo()?.partner?.logo_url ?? placeholderLogo" [src]="posInfo()?.partner?.logo_url ?? placeholderLogo"
alt="Logo" alt="Logo"
class="w-full h-full object-cover rounded-md bg-surface-card" class="bg-surface-card h-full w-full rounded-md object-cover" />
/>
</div> </div>
<span class="text-base font-semibold">{{ posInfo()?.partner?.name }}</span> <span class="text-base font-semibold">{{ posInfo()?.partner?.name }}</span>
</a> </a>
@@ -19,7 +18,7 @@
<ng-template #topbarEnd> <ng-template #topbarEnd>
<div class=""> <div class="">
<p-menu #menu [model]="profileMenuItems" [popup]="true" /> <p-menu #menu [model]="profileMenuItems" [popup]="true" appendTo="body" />
<p-button (click)="menu.toggle($event)" icon="pi pi-user" outlined size="large" /> <p-button (click)="menu.toggle($event)" icon="pi pi-user" outlined size="large" />
</div> </div>
</ng-template> </ng-template>
@@ -57,11 +56,11 @@
<pos-choose-cards class="h-full" (onChoose)="onChoosePos()" /> <pos-choose-cards class="h-full" (onChoose)="onChoosePos()" />
} }
@default { @default {
<div class="h-full w-full flex items-center justify-center"> <div class="flex h-full w-full items-center justify-center">
<p-card class="border border-surface-border"> <p-card class="border-surface-border border">
<div class="flex flex-col gap-2 items-center justify-between shrink-0"> <div class="flex shrink-0 flex-col items-center justify-between gap-2">
<i class="pi pi-exclamation-triangle text-6xl! mb-3 text-error" aria-hidden="true"></i> <i class="pi pi-exclamation-triangle text-error mb-3 text-6xl!" aria-hidden="true"></i>
<span class="text-xl font-semibold mb-2 text-error block"> عدم دسترسی </span> <span class="text-error mb-2 block text-xl font-semibold"> عدم دسترسی </span>
<p class="inline-block text-base">متاسفانه امکان دسترسی برای کاربر شما فراهم نیست</p> <p class="inline-block text-base">متاسفانه امکان دسترسی برای کاربر شما فراهم نیست</p>
<button pButton outlined severity="danger" class="mt-5 w-32" (click)="logout()">خروج</button> <button pButton outlined severity="danger" class="mt-5 w-32" (click)="logout()">خروج</button>
</div> </div>
@@ -1,9 +1,10 @@
<form [formGroup]="form" (submit)="submit()"> <form [formGroup]="form" (submit)="submit()">
<p-message severity="info"> وارد کردن شماره اقتصادی و یا کد ملی به همراه کد پستی الزامی است. </p-message>
<field-first-name [control]="form.controls.first_name" /> <field-first-name [control]="form.controls.first_name" />
<field-last-name [control]="form.controls.last_name" /> <field-last-name [control]="form.controls.last_name" />
<field-mobile-number [control]="form.controls.mobile_number" /> <field-individual-economic-code [control]="form.controls.economic_code" />
<field-national-id [control]="form.controls.national_id" /> <field-national-id [control]="form.controls.national_id" />
<field-economic-code [control]="form.controls.economic_code" />
<field-postal-code [control]="form.controls.postal_code" /> <field-postal-code [control]="form.controls.postal_code" />
<app-form-footer-actions submitLabel="ثبت مشتری و ادامه" (onCancel)="close()" (onSubmit)="submit()" /> <app-form-footer-actions submitLabel="ثبت مشتری و ادامه" (onCancel)="close()" (onSubmit)="submit()" />
@@ -1,9 +1,8 @@
import { AbstractForm } from '@/shared/abstractClasses'; import { AbstractForm } from '@/shared/abstractClasses';
import { import {
EconomicCodeComponent,
FirstNameComponent, FirstNameComponent,
IndividualEconomicCodeComponent,
LastNameComponent, LastNameComponent,
MobileNumberComponent,
NationalIdComponent, NationalIdComponent,
PostalCodeComponent, PostalCodeComponent,
} from '@/shared/components'; } from '@/shared/components';
@@ -11,7 +10,8 @@ import { FormFooterActionsComponent } from '@/shared/components/formFooterAction
import { fieldControl } from '@/shared/constants'; import { fieldControl } from '@/shared/constants';
import { CustomerType } from '@/shared/localEnum/constants/customerTypes'; import { CustomerType } from '@/shared/localEnum/constants/customerTypes';
import { Component, computed, inject } from '@angular/core'; import { Component, computed, inject } from '@angular/core';
import { ReactiveFormsModule } from '@angular/forms'; import { AbstractControl, ReactiveFormsModule, ValidationErrors } from '@angular/forms';
import { Message } from 'primeng/message';
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { IIndividualCustomer } from '../../../models/customer'; import { IIndividualCustomer } from '../../../models/customer';
import { PosLandingStore } from '../../../store/main.store'; import { PosLandingStore } from '../../../store/main.store';
@@ -24,10 +24,10 @@ import { PosLandingStore } from '../../../store/main.store';
FormFooterActionsComponent, FormFooterActionsComponent,
FirstNameComponent, FirstNameComponent,
LastNameComponent, LastNameComponent,
NationalIdComponent, IndividualEconomicCodeComponent,
EconomicCodeComponent,
MobileNumberComponent,
PostalCodeComponent, PostalCodeComponent,
Message,
NationalIdComponent,
], ],
}) })
export class CustomerIndividualFormComponent extends AbstractForm< export class CustomerIndividualFormComponent extends AbstractForm<
@@ -38,14 +38,56 @@ export class CustomerIndividualFormComponent extends AbstractForm<
costumerInfo = computed(() => this.store.customer().info?.customer_individual); costumerInfo = computed(() => this.store.customer().info?.customer_individual);
override showSuccessMessage = false; override showSuccessMessage = false;
form = this.fb.group({ private readonly identityOrEconomicCodeValidator = (
first_name: fieldControl.first_name(this.costumerInfo()?.first_name || ''), control: AbstractControl
last_name: fieldControl.last_name(this.costumerInfo()?.last_name || ''), ): ValidationErrors | null => {
national_id: fieldControl.national_id(this.costumerInfo()?.national_id || ''), const economicCode = control.get('economic_code')?.value;
postal_code: fieldControl.postal_code(this.costumerInfo()?.postal_code || ''), const nationalId = control.get('national_id')?.value;
economic_code: fieldControl.economic_code(this.costumerInfo()?.economic_code || ''), const postalCode = control.get('postal_code')?.value;
mobile_number: fieldControl.mobile_number(this.costumerInfo()?.mobile_number || ''),
const hasEconomicCode = !!String(economicCode ?? '').trim();
const hasNationalId = !!String(nationalId ?? '').trim();
const hasPostalCode = !!String(postalCode ?? '').trim();
const isValid = hasEconomicCode || (hasNationalId && hasPostalCode);
if (!isValid) {
return { identityOrEconomicCodeRequired: true };
}
return null;
};
form = this.fb.group(
{
first_name: fieldControl.first_name(this.costumerInfo()?.first_name || '', false),
last_name: fieldControl.last_name(this.costumerInfo()?.last_name || '', false),
economic_code: fieldControl.individual_economic_code(
this.costumerInfo()?.economic_code || '',
false
),
national_id: fieldControl.national_id(this.costumerInfo()?.national_id || '', false),
postal_code: fieldControl.postal_code(this.costumerInfo()?.postal_code || '', false),
},
{ validators: this.identityOrEconomicCodeValidator }
);
override submit() {
this.form.markAllAsTouched();
this.form.updateValueAndValidity({ onlySelf: false, emitEvent: false });
if (this.form.hasError('identityOrEconomicCodeRequired')) {
this.toastService.warn({
text: 'وارد کردن شماره اقتصادی و یا کد ملی به همراه کد پستی الزامی است.',
}); });
}
if (this.form.invalid) {
return;
}
super.submit();
}
override submitForm() { override submitForm() {
return new Observable<IIndividualCustomer>((observer) => { return new Observable<IIndividualCustomer>((observer) => {
@@ -54,7 +96,10 @@ export class CustomerIndividualFormComponent extends AbstractForm<
customer_id: '', customer_id: '',
type: CustomerType.INDIVIDUAL, type: CustomerType.INDIVIDUAL,
info: { info: {
customer_individual: info, customer_individual: {
...info,
economic_code: info.economic_code ? info.economic_code.toString() : undefined,
},
}, },
}); });
return observer.next(info); return observer.next(info);
@@ -1,8 +1,11 @@
<form [formGroup]="form" (submit)="submit()"> <form [formGroup]="form" (submit)="submit()">
<app-input [control]="form.controls.company_name" name="company_name" label="نام مجموعه" /> <p-message severity="info" icon="pi pi-info-circle">
<app-input [control]="form.controls.registration_number" name="registration_number" label="شماره ثبت" /> وارد کردن شماره اقتصادی و یا شناسه‌ ملی به همراه کد پستی الزامی است.
<app-input [control]="form.controls.economic_code" name="economic_code" label="کد اقتصادی" maxlength="11" /> </p-message>
<app-input [control]="form.controls.postal_code" name="postal_code" label="کد پستی" type="postalCode" /> <field-name [control]="form.controls.name" name="name" label="نام مجموعه" />
<field-legal-economic-code [control]="form.controls.economic_code" />
<field-registration-number [control]="form.controls.registration_number" />
<field-postal-code [control]="form.controls.postal_code" />
<app-form-footer-actions submitLabel="ثبت مشتری و ادامه" (onCancel)="close()" (onSubmit)="submit()" /> <app-form-footer-actions submitLabel="ثبت مشتری و ادامه" (onCancel)="close()" (onSubmit)="submit()" />
</form> </form>
@@ -1,10 +1,16 @@
import { postalCodeValidator } from '@/core/validators';
import { AbstractForm } from '@/shared/abstractClasses'; import { AbstractForm } from '@/shared/abstractClasses';
import { InputComponent } from '@/shared/components'; import {
LegalEconomicCodeComponent,
NameComponent,
PostalCodeComponent,
RegistrationNumberComponent,
} from '@/shared/components';
import { FormFooterActionsComponent } from '@/shared/components/formFooterActions/form-footer-actions.component'; import { FormFooterActionsComponent } from '@/shared/components/formFooterActions/form-footer-actions.component';
import { fieldControl } from '@/shared/constants';
import { CustomerType } from '@/shared/localEnum/constants/customerTypes'; import { CustomerType } from '@/shared/localEnum/constants/customerTypes';
import { Component, inject } from '@angular/core'; import { Component, inject } from '@angular/core';
import { ReactiveFormsModule, Validators } from '@angular/forms'; import { AbstractControl, ReactiveFormsModule, ValidationErrors } from '@angular/forms';
import { Message } from 'primeng/message';
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { ILegalCustomer } from '../../../models/customer'; import { ILegalCustomer } from '../../../models/customer';
import { PosLandingStore } from '../../../store/main.store'; import { PosLandingStore } from '../../../store/main.store';
@@ -12,31 +18,76 @@ import { PosLandingStore } from '../../../store/main.store';
@Component({ @Component({
selector: 'pos-customer-legal-form', selector: 'pos-customer-legal-form',
templateUrl: './form.component.html', templateUrl: './form.component.html',
imports: [ReactiveFormsModule, InputComponent, FormFooterActionsComponent], imports: [
ReactiveFormsModule,
FormFooterActionsComponent,
LegalEconomicCodeComponent,
NameComponent,
RegistrationNumberComponent,
PostalCodeComponent,
Message,
],
}) })
export class CustomerLegalFormComponent extends AbstractForm<ILegalCustomer, ILegalCustomer> { export class CustomerLegalFormComponent extends AbstractForm<ILegalCustomer, ILegalCustomer> {
private readonly store = inject(PosLandingStore); private readonly store = inject(PosLandingStore);
override showSuccessMessage = false; override showSuccessMessage = false;
form = this.fb.group({ private readonly registrationOrEconomicCodeValidator = (
company_name: [ control: AbstractControl
this.store.customer().info?.customer_legal?.company_name || '', ): ValidationErrors | null => {
[Validators.required], const economicCode = control.get('economic_code')?.value;
], const registrationNumber = control.get('registration_number')?.value;
registration_number: [ const postalCode = control.get('postal_code')?.value;
this.store.customer().info?.customer_legal?.registration_number || '',
[Validators.required], const hasEconomicCode = !!String(economicCode ?? '').trim();
], const hasRegistrationNumber = !!String(registrationNumber ?? '').trim();
economic_code: [ const hasPostalCode = !!String(postalCode ?? '').trim();
const isValid = hasEconomicCode || (hasRegistrationNumber && hasPostalCode);
if (!isValid) {
return { registrationOrEconomicCodeRequired: true };
}
return null;
};
form = this.fb.group(
{
name: fieldControl.name(this.store.customer().info?.customer_legal?.name || ''),
economic_code: fieldControl.legal_economic_code(
this.store.customer().info?.customer_legal?.economic_code || '', this.store.customer().info?.customer_legal?.economic_code || '',
[Validators.required], false
], ),
postal_code: [ registration_number: fieldControl.registration_number(
this.store.customer().info?.customer_legal?.registration_number || '',
false
),
postal_code: fieldControl.postal_code(
this.store.customer().info?.customer_legal?.postal_code || '', this.store.customer().info?.customer_legal?.postal_code || '',
[postalCodeValidator()], false
], ),
},
{ validators: this.registrationOrEconomicCodeValidator }
);
override submit() {
this.form.markAllAsTouched();
this.form.updateValueAndValidity({ onlySelf: false, emitEvent: false });
if (this.form.hasError('registrationOrEconomicCodeRequired')) {
this.toastService.warn({
text: 'وارد کردن شماره اقتصادی و یا شناسه‌ ملی به همراه کد پستی الزامی است.',
}); });
}
if (this.form.invalid) {
return;
}
super.submit();
}
override submitForm() { override submitForm() {
return new Observable<ILegalCustomer>((observer) => { return new Observable<ILegalCustomer>((observer) => {
@@ -45,7 +96,10 @@ export class CustomerLegalFormComponent extends AbstractForm<ILegalCustomer, ILe
customer_id: '', customer_id: '',
type: CustomerType.LEGAL, type: CustomerType.LEGAL,
info: { info: {
customer_legal: info, customer_legal: {
...info,
economic_code: info.economic_code ? info.economic_code.toString() : undefined,
},
}, },
}); });
return observer.next(info); return observer.next(info);
@@ -9,13 +9,13 @@ import { PosOrderSectionComponent } from './order-section.component';
imports: [SharedLightBottomsheetComponent, PosOrderSectionComponent], imports: [SharedLightBottomsheetComponent, PosOrderSectionComponent],
}) })
export class PosOrderSectionDialogComponent extends AbstractDialog { export class PosOrderSectionDialogComponent extends AbstractDialog {
@Output() onCloseInvoiceBottomSheet = new EventEmitter(); @Output() onAddMoreGoods = new EventEmitter();
@Output() onSubmitOrder = new EventEmitter(); @Output() onSubmit = new EventEmitter();
closeInvoiceBottomSheet = () => { closeInvoiceBottomSheet = () => {
this.onCloseInvoiceBottomSheet.emit(); this.onAddMoreGoods.emit();
}; };
submitOrder = () => { submitOrder = () => {
this.onSubmitOrder.emit(); this.onSubmit.emit();
}; };
} }
@@ -64,7 +64,7 @@ export class PosOrderSectionComponent {
customerNameToShow = `${info?.customer_individual ? info?.customer_individual.first_name + ' ' + info?.customer_individual.last_name : '-----'} (نوع اول)`; customerNameToShow = `${info?.customer_individual ? info?.customer_individual.first_name + ' ' + info?.customer_individual.last_name : '-----'} (نوع اول)`;
break; break;
case 'LEGAL': case 'LEGAL':
customerNameToShow = `${info?.customer_legal?.company_name || '-----'} (نوع اول)`; customerNameToShow = `${info?.customer_legal?.name || '-----'} (نوع اول)`;
break; break;
} }
@@ -85,6 +85,8 @@ export class PosOrderSectionComponent {
} }
submitAndPay() { submitAndPay() {
console.log('submitAndPay');
this.onSubmit.emit(); this.onSubmit.emit();
} }
@@ -1,17 +1,16 @@
export interface IIndividualCustomer { export interface IIndividualCustomer {
first_name: string; first_name?: string;
last_name: string; last_name?: string;
national_id: string; national_id?: string;
postal_code: string; postal_code?: string;
mobile_number: string; economic_code?: string;
economic_code: string;
is_favorite?: boolean; is_favorite?: boolean;
} }
export interface ILegalCustomer { export interface ILegalCustomer {
company_name: string; name?: string;
economic_code: string; economic_code?: string;
registration_number: string; registration_number?: string;
postal_code: string; postal_code?: string;
is_favorite?: boolean; is_favorite?: boolean;
} }
@@ -4,11 +4,10 @@
[modal]="true" [modal]="true"
[style]="{ width: '500px' }" [style]="{ width: '500px' }"
[closable]="true" [closable]="true"
(onHide)="close()" (onHide)="close()">
>
<form [formGroup]="form" (submit)="submit()" class="flex flex-col gap-4"> <form [formGroup]="form" (submit)="submit()" class="flex flex-col gap-4">
<field-name [control]="form.controls.name" /> <field-name [control]="form.controls.name" />
<field-economic-code [control]="form.controls.economic_code" /> <field-individual-economic-code [control]="form.controls.economic_code" />
<field-fiscal-id [control]="form.controls.fiscal_id" /> <field-fiscal-id [control]="form.controls.fiscal_id" />
<field-partner-token [control]="form.controls.partner_token" /> <field-partner-token [control]="form.controls.partner_token" />
<field-guild-id [control]="form.controls.guild_id" /> <field-guild-id [control]="form.controls.guild_id" />
@@ -1,9 +1,9 @@
// import { CatalogRolesComponent } from '@/shared/catalog/roles'; // import { CatalogRolesComponent } from '@/shared/catalog/roles';
import { AbstractFormDialog } from '@/shared/abstractClasses'; import { AbstractFormDialog } from '@/shared/abstractClasses';
import { import {
EconomicCodeComponent,
FiscalIdComponent, FiscalIdComponent,
GuildIdComponent, GuildIdComponent,
IndividualEconomicCodeComponent,
NameComponent, NameComponent,
PartnerTokenComponent, PartnerTokenComponent,
} from '@/shared/components'; } from '@/shared/components';
@@ -22,7 +22,7 @@ import { AdminConsumerBusinessActivitiesService } from '../../services/businessA
ReactiveFormsModule, ReactiveFormsModule,
SharedDialogComponent, SharedDialogComponent,
NameComponent, NameComponent,
EconomicCodeComponent, IndividualEconomicCodeComponent,
FiscalIdComponent, FiscalIdComponent,
PartnerTokenComponent, PartnerTokenComponent,
GuildIdComponent, GuildIdComponent,
@@ -41,7 +41,7 @@ export class ConsumerBusinessActivitiesFormComponent extends AbstractFormDialog<
initForm = () => { initForm = () => {
return this.fb.group({ return this.fb.group({
name: fieldControl.name(this.initialValues?.name || ''), name: fieldControl.name(this.initialValues?.name || ''),
economic_code: fieldControl.economic_code(this.initialValues?.economic_code || ''), economic_code: fieldControl.individual_economic_code(this.initialValues?.economic_code || ''),
fiscal_id: fieldControl.fiscal_id(this.initialValues?.fiscal_id || ''), fiscal_id: fieldControl.fiscal_id(this.initialValues?.fiscal_id || ''),
partner_token: fieldControl.partner_token(this.initialValues?.partner_token || ''), partner_token: fieldControl.partner_token(this.initialValues?.partner_token || ''),
// @ts-ignore // @ts-ignore
+2 -1
View File
@@ -4,13 +4,14 @@ export * from './code.component';
export * from './company_name.component'; export * from './company_name.component';
export * from './description.component'; export * from './description.component';
export * from './device_id.component'; export * from './device_id.component';
export * from './economic_code.component';
export * from './email.component'; export * from './email.component';
export * from './first_name.component'; export * from './first_name.component';
export * from './fiscal_id.component'; export * from './fiscal_id.component';
export * from './guild_id.component'; export * from './guild_id.component';
export * from './individual_economic_code.component';
export * from './invoice_templates.component'; export * from './invoice_templates.component';
export * from './last_name.component'; export * from './last_name.component';
export * from './legal_economic_code.component';
export * from './legal_name.component'; export * from './legal_name.component';
export * from './license_starts_at.component'; export * from './license_starts_at.component';
export * from './mobile.component'; export * from './mobile.component';
@@ -0,0 +1,19 @@
import { Component, Input } from '@angular/core';
import { FormControl, ReactiveFormsModule } from '@angular/forms';
import { InputComponent } from '../input/input.component';
@Component({
selector: 'field-individual-economic-code',
template: `<app-input
label="شماره اقتصادی"
[control]="control"
[name]="name"
type="number"
[length]="14"
/>`,
imports: [ReactiveFormsModule, InputComponent],
})
export class IndividualEconomicCodeComponent {
@Input({ required: true }) control = new FormControl<string>('');
@Input() name = 'individual_economic_code';
}
@@ -3,11 +3,17 @@ import { FormControl, ReactiveFormsModule } from '@angular/forms';
import { InputComponent } from '../input/input.component'; import { InputComponent } from '../input/input.component';
@Component({ @Component({
selector: 'field-economic-code', selector: 'field-legal-economic-code',
template: `<app-input label="کد اقتصادی" [control]="control" [name]="name" type="number" />`, template: `<app-input
label="شماره اقتصادی"
[control]="control"
[name]="name"
type="number"
[length]="11"
/>`,
imports: [ReactiveFormsModule, InputComponent], imports: [ReactiveFormsModule, InputComponent],
}) })
export class EconomicCodeComponent { export class LegalEconomicCodeComponent {
@Input({ required: true }) control = new FormControl<string>(''); @Input({ required: true }) control = new FormControl<string>('');
@Input() name = 'economic_code'; @Input() name = 'legal_economic_code';
} }
@@ -6,6 +6,7 @@ import { PosInfoStore } from '@/domains/pos/store';
import { import {
CatalogInvoiceTypeTagComponent, CatalogInvoiceTypeTagComponent,
CatalogTaxProviderStatusTagComponent, CatalogTaxProviderStatusTagComponent,
TspProviderResponseStatus,
} from '@/shared/catalog'; } from '@/shared/catalog';
import { CatalogInvoiceSettlementTypeTagComponent } from '@/shared/catalog/invoiceSettlementType'; import { CatalogInvoiceSettlementTypeTagComponent } from '@/shared/catalog/invoiceSettlementType';
import { import {
@@ -87,6 +88,7 @@ export class SharedSaleInvoiceSingleViewComponent {
@Output() onRefresh = new EventEmitter<void>(); @Output() onRefresh = new EventEmitter<void>();
@Output() onSendToTsp = new EventEmitter<Observable<any>>(); @Output() onSendToTsp = new EventEmitter<Observable<any>>();
@Output() onResendToTsp = new EventEmitter<Observable<any>>();
@Output() onInquiry = new EventEmitter<Observable<any>>(); @Output() onInquiry = new EventEmitter<Observable<any>>();
@ViewChild('totalAmount', { static: false }) totalAmount!: TemplateRef<any>; @ViewChild('totalAmount', { static: false }) totalAmount!: TemplateRef<any>;
@@ -102,6 +104,7 @@ export class SharedSaleInvoiceSingleViewComponent {
this.showBackFromSale = this.showBackFromSale.bind(this); this.showBackFromSale = this.showBackFromSale.bind(this);
this.printInvoice = this.printInvoice.bind(this); this.printInvoice = this.printInvoice.bind(this);
this.send = this.send.bind(this); this.send = this.send.bind(this);
this.resend = this.resend.bind(this);
this.inquiry = this.inquiry.bind(this); this.inquiry = this.inquiry.bind(this);
} }
@@ -116,6 +119,9 @@ export class SharedSaleInvoiceSingleViewComponent {
send() { send() {
this.onSendToTsp.emit(); this.onSendToTsp.emit();
} }
resend() {
this.onResendToTsp.emit();
}
async showRevokeConfirmation() { async showRevokeConfirmation() {
await this.confirmationService.ask({ await this.confirmationService.ask({
header: `ابطال صورت‌حساب‌ شماره ${this.invoice!.invoice_number}`, header: `ابطال صورت‌حساب‌ شماره ${this.invoice!.invoice_number}`,
@@ -146,40 +152,46 @@ export class SharedSaleInvoiceSingleViewComponent {
if (changes['invoice'] && this.invoice) { if (changes['invoice'] && this.invoice) {
const invoiceStatus = this.invoice.status.value; const invoiceStatus = this.invoice.status.value;
const actions: MenuItem[] = [ const actions: MenuItem[] = [
{
label: 'ارسال مجدد صورت‌حساب',
icon: 'pi pi-send',
neededStatus: TspProviderResponseStatus.SEND_FAILURE,
command: this.resend,
},
{ {
label: 'ارسال صورت‌حساب', label: 'ارسال صورت‌حساب',
icon: 'pi pi-send', icon: 'pi pi-send',
neededStatus: 'NOT_SEND', neededStatus: TspProviderResponseStatus.NOT_SEND,
command: this.send, command: this.send,
}, },
{ {
label: 'استعلام وضعیت', label: 'استعلام وضعیت',
icon: 'pi pi-info', icon: 'pi pi-info',
neededStatus: 'FISCAL_QUEUED', neededStatus: TspProviderResponseStatus.FISCAL_QUEUED,
command: this.inquiry, command: this.inquiry,
}, },
{ {
label: 'دلایل خطا', label: 'دلایل خطا',
icon: 'pi pi-question', icon: 'pi pi-question',
neededStatus: 'FAILURE', neededStatus: TspProviderResponseStatus.FAILURE,
command: this.showErrors, command: this.showErrors,
}, },
{ {
label: 'ابطال', label: 'ابطال',
icon: 'pi pi-eraser', icon: 'pi pi-eraser',
neededStatus: 'SUCCESS', neededStatus: TspProviderResponseStatus.SUCCESS,
command: this.showRevokeConfirmation, command: this.showRevokeConfirmation,
}, },
{ {
label: 'اصلاح', label: 'اصلاح',
icon: 'pi pi-file-edit', icon: 'pi pi-file-edit',
neededStatus: 'SUCCESS', neededStatus: TspProviderResponseStatus.SUCCESS,
command: this.showCorrection, command: this.showCorrection,
}, },
{ {
label: 'برگشت از فروش', label: 'برگشت از فروش',
icon: 'pi pi-cart-minus', icon: 'pi pi-cart-minus',
neededStatus: 'SUCCESS', neededStatus: TspProviderResponseStatus.SUCCESS,
command: this.showBackFromSale, command: this.showBackFromSale,
}, },
{ {
+7 -2
View File
@@ -1,4 +1,5 @@
import { import {
equalLengthValidator,
fiscalIdValidator, fiscalIdValidator,
mobileValidator, mobileValidator,
password, password,
@@ -70,9 +71,13 @@ export const fieldControl = {
value, value,
isRequired ? [...required(), usernameValidator()] : [usernameValidator()], isRequired ? [...required(), usernameValidator()] : [usernameValidator()],
], ],
economic_code: (value = '', isRequired = true): ControlConfig => [ legal_economic_code: (value = '', isRequired = true): ControlConfig => [
value, value,
isRequired ? required() : [], isRequired ? [...required(), equalLengthValidator(11)] : [equalLengthValidator(11)],
],
individual_economic_code: (value = '', isRequired = true): ControlConfig => [
value,
isRequired ? [...required(), equalLengthValidator(14)] : [equalLengthValidator(14)],
], ],
fiscal_id: (value = '', isRequired = true): ControlConfig => [ fiscal_id: (value = '', isRequired = true): ControlConfig => [
value, value,
@@ -43,7 +43,7 @@
[ngClass]="{ [ngClass]="{
'bg-surface-ground font-bold': day.isCurrentDay, 'bg-surface-ground font-bold': day.isCurrentDay,
'bg-primary! text-primary-contrast!': isSelected(day), 'bg-primary! text-primary-contrast!': isSelected(day),
'text-muted-color! font-normal': day.isDisabled, 'text-muted-color! font-light': day.isDisabled,
}" }"
(click)="selectDate(day)"> (click)="selectDate(day)">
{{ day.day }} {{ day.day }}