feat: implement fiscal ID validation; replace fiscal code validator and update related components
This commit is contained in:
@@ -68,6 +68,11 @@ export class FormErrorsService {
|
||||
const info = errors['invalidUsername'];
|
||||
out.push({ key: 'invalidUsername', message: info.message ?? `${label} معتبر نیست.` });
|
||||
}
|
||||
|
||||
if (errors['invalidFiscalId']) {
|
||||
const info = errors['invalidFiscalId'];
|
||||
out.push({ key: 'invalidFiscalId', message: info.message ?? `${label} معتبر نیست.` });
|
||||
}
|
||||
// fallback: include any other error keys
|
||||
Object.keys(errors).forEach((k) => {
|
||||
if (
|
||||
@@ -80,6 +85,7 @@ export class FormErrorsService {
|
||||
'max',
|
||||
'email',
|
||||
'pattern',
|
||||
'invalidFiscalId',
|
||||
].indexOf(k) === -1
|
||||
) {
|
||||
try {
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
import { ValidatorFn } from '@angular/forms';
|
||||
|
||||
export function fiscalCodeValidator(): ValidatorFn {
|
||||
return (control) => {
|
||||
if (control.value === null || control.value === undefined || control.value === '') {
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
return control.value.length === 11 && /^[0-9]{11}$/.test(control.value)
|
||||
? null
|
||||
: { fiscalCode: 'معتبر نیست' };
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
import { ValidatorFn } from '@angular/forms';
|
||||
|
||||
export function fiscalIdValidator(): ValidatorFn {
|
||||
return (control) => {
|
||||
if (control.value === null || control.value === undefined || control.value === '') {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (control.value.length < 6) {
|
||||
return {
|
||||
minlength: {
|
||||
requiredLength: 6,
|
||||
actualLength: control.value.length,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
const pattern = /^[a-zA-Z0-9]*$/;
|
||||
|
||||
if (!pattern.test(control.value)) {
|
||||
return {
|
||||
invalidFiscalId: {
|
||||
value: control.value,
|
||||
message: 'شناسه مالی فقط میتواند شامل حروف انگلیسی و اعداد باشد',
|
||||
},
|
||||
};
|
||||
}
|
||||
return null;
|
||||
};
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
export * from './fiscal-code.validator';
|
||||
export * from './fiscal-id.validator';
|
||||
export * from './greater.validator';
|
||||
export * from './iban.validator';
|
||||
export * from './mobile.validator';
|
||||
|
||||
@@ -1,16 +1,21 @@
|
||||
import { ValidatorFn } from '@angular/forms';
|
||||
|
||||
// Password must be minimum 8 characters, include at least one uppercase,
|
||||
// one lowercase, one number and one special character
|
||||
// export const PASSWORD_PATTERN = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[^A-Za-z0-9]).{8,}$/;
|
||||
export const PASSWORD_PATTERN = /^[0-9]{6,}$/;
|
||||
// const PASSWORD_PATTERN = /^[a-zA-Z0-9_-]*$/;
|
||||
|
||||
// Validator factory named `password` as requested. Returns `null` for empty
|
||||
// values so `Validators.required` can be used alongside it when needed.
|
||||
export function password(): ValidatorFn {
|
||||
return (control) => {
|
||||
const value = control?.value;
|
||||
if (value === null || value === undefined || String(value).length === 0) return null;
|
||||
return PASSWORD_PATTERN.test(String(value)) ? null : { password: 'معتبر نیست' };
|
||||
|
||||
if (value.length < 6) {
|
||||
return {
|
||||
minlength: {
|
||||
requiredLength: 6,
|
||||
actualLength: value.length,
|
||||
},
|
||||
};
|
||||
}
|
||||
return null;
|
||||
// return PASSWORD_PATTERN.test(String(value)) ? null : { password: 'معتبر نیست' };
|
||||
};
|
||||
}
|
||||
|
||||
@@ -9,28 +9,28 @@ export const CONSUMER_MENU_ITEMS = [
|
||||
},
|
||||
{
|
||||
label: 'فعالیت اقتصادی',
|
||||
icon: 'pi pi-fw pi-home',
|
||||
icon: 'pi pi-fw pi-shop',
|
||||
routerLink: ['/consumer/business_activities'],
|
||||
},
|
||||
{
|
||||
label: 'پایانههای فروش',
|
||||
icon: 'pi pi-fw pi-home',
|
||||
label: 'پایانهی فروش',
|
||||
icon: 'pi pi-fw pi-tablet',
|
||||
routerLink: ['/consumer/poses'],
|
||||
},
|
||||
{
|
||||
label: 'فاکتورها',
|
||||
icon: 'pi pi-fw pi-home',
|
||||
icon: 'pi pi-fw pi-receipt',
|
||||
routerLink: ['/consumer/sale_invoices'],
|
||||
},
|
||||
{
|
||||
label: 'مشتریها',
|
||||
icon: 'pi pi-fw pi-home',
|
||||
icon: 'pi pi-fw pi-users',
|
||||
routerLink: ['/consumer/customers'],
|
||||
},
|
||||
|
||||
{
|
||||
label: 'حسابهای کاربری',
|
||||
icon: 'pi pi-fw pi-home',
|
||||
icon: 'pi pi-fw pi-user',
|
||||
routerLink: ['/consumer/accounts'],
|
||||
},
|
||||
],
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
<form [formGroup]="form" (submit)="submit()" class="flex flex-col gap-4">
|
||||
<field-name [control]="form.controls.name" />
|
||||
<field-economic-code [control]="form.controls.economic_code" />
|
||||
<field-fiscal-code [control]="form.controls.fiscal_id" />
|
||||
<field-fiscal-id [control]="form.controls.fiscal_id" />
|
||||
<field-partner-token [control]="form.controls.partner_token" />
|
||||
<field-invoice-number-sequence [control]="form.controls.invoice_number_sequence" [min]="1" />
|
||||
<app-form-footer-actions [submitLabel]="'ذخیره'" [loading]="submitLoading()" (onCancel)="close()" />
|
||||
|
||||
@@ -9,17 +9,17 @@ export const PARTNER_MENU_ITEMS = [
|
||||
},
|
||||
{
|
||||
label: 'حسابهای کاربری',
|
||||
icon: 'pi pi-fw pi-home',
|
||||
icon: 'pi pi-fw pi-user',
|
||||
routerLink: ['/partner/accounts'],
|
||||
},
|
||||
{
|
||||
label: 'مشتریها',
|
||||
icon: 'pi pi-fw pi-home',
|
||||
icon: 'pi pi-fw pi-users',
|
||||
routerLink: ['/partner/consumers'],
|
||||
},
|
||||
{
|
||||
label: 'لایسنسها',
|
||||
icon: 'pi pi-fw pi-home',
|
||||
icon: 'pi pi-fw pi-key',
|
||||
routerLink: ['/partner/licenses'],
|
||||
},
|
||||
],
|
||||
|
||||
+2
-1
@@ -1,7 +1,8 @@
|
||||
<form [formGroup]="form" (submit)="submit()" class="flex flex-col gap-4">
|
||||
<field-name [control]="form.controls.name" />
|
||||
<field-economic-code [control]="form.controls.economic_code" />
|
||||
<field-fiscal-code [control]="form.controls.fiscal_id" />
|
||||
<field-fiscal-id [control]="form.controls.fiscal_id" />
|
||||
<field-invoice-number-sequence [control]="form.controls.invoice_number_sequence" />
|
||||
<field-partner-token [control]="form.controls.partner_token" />
|
||||
<field-guild-id [control]="form.controls.guild_id" />
|
||||
|
||||
|
||||
+5
@@ -7,6 +7,7 @@ import {
|
||||
NameComponent,
|
||||
PartnerTokenComponent,
|
||||
} from '@/shared/components';
|
||||
import { InvoiceNumberSequenceComponent } from '@/shared/components/fields/invoice_number_sequence.component';
|
||||
import { FormFooterActionsComponent } from '@/shared/components/formFooterActions/form-footer-actions.component';
|
||||
import { fieldControl } from '@/shared/constants/fields';
|
||||
import { Component, inject, Input } from '@angular/core';
|
||||
@@ -28,6 +29,7 @@ import { PartnerConsumerBusinessActivitiesService } from '../../services/busines
|
||||
LicenseStartsAtComponent,
|
||||
FormFooterActionsComponent,
|
||||
Divider,
|
||||
InvoiceNumberSequenceComponent,
|
||||
],
|
||||
})
|
||||
export class ConsumerBusinessActivitiesFormComponent extends AbstractForm<
|
||||
@@ -44,6 +46,9 @@ export class ConsumerBusinessActivitiesFormComponent extends AbstractForm<
|
||||
name: fieldControl.name(this.initialValues?.name || ''),
|
||||
economic_code: fieldControl.economic_code(this.initialValues?.economic_code || ''),
|
||||
fiscal_id: fieldControl.fiscal_id(this.initialValues?.fiscal_id || ''),
|
||||
invoice_number_sequence: fieldControl.invoice_number_sequence(
|
||||
parseInt(this.initialValues?.invoice_number_sequence || '1'),
|
||||
),
|
||||
partner_token: fieldControl.partner_token(this.initialValues?.partner_token || ''),
|
||||
guild_id: fieldControl.guild_id(this.initialValues?.guild?.id || ''),
|
||||
license_starts_at: [''],
|
||||
|
||||
@@ -7,6 +7,7 @@ export interface IBusinessActivityRawResponse {
|
||||
fiscal_id: string;
|
||||
partner_token: string;
|
||||
guild: ISummary;
|
||||
invoice_number_sequence: string;
|
||||
created_at: string;
|
||||
license_info: LicenseInfo;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<div class="form-group">
|
||||
<form [formGroup]="form">
|
||||
<app-input [control]="form.controls.unit_price" name="unit_price" label="قیمت پایه هر گرم" type="price" />
|
||||
<app-input [control]="form.controls.unit_price" name="unit_price" label="مبلغ واحد" type="price" />
|
||||
<app-input [control]="form.controls.quantity" name="quantity" label="مقدار" type="number" suffix="گرم" />
|
||||
<app-enum-select [control]="form.controls.payload.controls.karat" type="goldKarat" name="karat" />
|
||||
|
||||
|
||||
@@ -31,16 +31,16 @@ export class PosGoldPayloadFormComponent extends AbstractForm<
|
||||
IPosOrderItem<IGoldPayload>,
|
||||
IPosOrderItem<IGoldPayload>
|
||||
> {
|
||||
// override defaultValues: Partial<IPosOrderItem<IGoldPayload>> = {
|
||||
// unit_price: 200_000_000,
|
||||
// quantity: 2,
|
||||
// payload: {
|
||||
// // commission: 10_000,
|
||||
// // wages: 10_000,
|
||||
// // profit: 10_000,
|
||||
// karat: 'KARAT_18' as TGoldKarat,
|
||||
// } as any,
|
||||
// };
|
||||
override defaultValues: Partial<IPosOrderItem<IGoldPayload>> = {
|
||||
// unit_price: 200_000_000,
|
||||
// quantity: 2,
|
||||
payload: {
|
||||
// commission: 10_000,
|
||||
// wages: 10_000,
|
||||
// profit: 10_000,
|
||||
karat: 'KARAT_18' as TGoldKarat,
|
||||
} as any,
|
||||
};
|
||||
|
||||
@Input({ required: true }) vatPercentage!: number;
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
</form>
|
||||
|
||||
<div class="form-group">
|
||||
<uikit-field label="تعداد مراحل پرداخت با پایانه" name="pay_by_terminal_steps">
|
||||
<uikit-field label="تعداد مراحل پرداخت با پوز" name="pay_by_terminal_steps">
|
||||
<p-select
|
||||
[options]="payByTerminalSteps()"
|
||||
[ngModel]="selectedPayByTerminalStep()"
|
||||
@@ -30,7 +30,7 @@
|
||||
<app-input
|
||||
[control]="terminalControl"
|
||||
[name]="'terminal_' + $index"
|
||||
[label]="$index === 0 ? 'پرداخت با پایانه' : 'پرداخت با پایانه - مرحله ' + ($index + 1)"
|
||||
[label]="$index === 0 ? 'پرداخت با پوز' : 'پرداخت با پوز - مرحله ' + ($index + 1)"
|
||||
type="price"
|
||||
[max]="terminalsMax()[$index]"
|
||||
[disabled]="payByTerminalSteps()[$index].payed"
|
||||
@@ -43,24 +43,30 @@
|
||||
[disabled]="!remainedAmount() || payByTerminalSteps()[$index].payed"
|
||||
(click)="fillTerminalRemained($index)"
|
||||
>
|
||||
تمامی بدهی باقیمانده
|
||||
باقیمانده مبلغ
|
||||
</button>
|
||||
</ng-template>
|
||||
</app-input>
|
||||
}
|
||||
</div>
|
||||
<form [formGroup]="form" (submit)="submit()">
|
||||
<app-input [control]="form.controls.cash" name="cash" label="نقدی" type="price" [max]="cashMax()">
|
||||
<app-input
|
||||
[control]="form.controls.cash"
|
||||
name="cash"
|
||||
label="نقدی/ کارتخوان دیگر/ کارت به کارت"
|
||||
type="price"
|
||||
[max]="cashMax()"
|
||||
>
|
||||
<ng-template #suffixTemp>
|
||||
<button pButton size="small" type="button" [disabled]="!remainedAmount()" (click)="fillRemained('CASH')">
|
||||
تمامی بدهی باقیمانده
|
||||
باقیمانده مبلغ
|
||||
</button>
|
||||
</ng-template>
|
||||
</app-input>
|
||||
<app-input [control]="form.controls.set_off" name="setOff" label="تهاتر" type="price" [max]="setOffMax()">
|
||||
<ng-template #suffixTemp>
|
||||
<button pButton size="small" type="button" [disabled]="!remainedAmount()" (click)="fillRemained('SET_OFF')">
|
||||
تمامی بدهی باقیمانده
|
||||
باقیمانده مبلغ
|
||||
</button>
|
||||
</ng-template>
|
||||
</app-input>
|
||||
|
||||
@@ -13,5 +13,6 @@ export class LayoutComponent {
|
||||
|
||||
ngOnInit() {
|
||||
this.layoutService.setMenuItems(SUPER_ADMIN_MENU_ITEMS);
|
||||
this.layoutService.setPanelInfo({ title: 'پنل مدیریتی ' });
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -9,7 +9,7 @@
|
||||
<form [formGroup]="form" (submit)="submit()" class="flex flex-col gap-4">
|
||||
<field-name [control]="form.controls.name" />
|
||||
<field-economic-code [control]="form.controls.economic_code" />
|
||||
<field-fiscal-code [control]="form.controls.fiscal_id" />
|
||||
<field-fiscal-id [control]="form.controls.fiscal_id" />
|
||||
<field-partner-token [control]="form.controls.partner_token" />
|
||||
<field-guild-id [control]="form.controls.guild_id" />
|
||||
<app-form-footer-actions [submitLabel]="'ذخیره'" [loading]="submitLoading()" (onCancel)="close()" />
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
</ng-template>
|
||||
<div class="flex flex-col gap-4">
|
||||
<div class="listKeyValue">
|
||||
<app-key-value label="عنوان شریک تجاری" [value]="partner()?.name" />
|
||||
<app-key-value label="کد شریک تجاری" [value]="partner()?.code" />
|
||||
<app-key-value label="عنوان" [value]="partner()?.name" />
|
||||
<app-key-value label="کد" [value]="partner()?.code" />
|
||||
|
||||
<div class="col-span-3 grid grid-cols-3 gap-4 mt-6">
|
||||
<partner-license-info-template
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<p-select
|
||||
[loading]="loading()"
|
||||
[options]="items()"
|
||||
optionLabel="name"
|
||||
optionLabel="fullname"
|
||||
[optionValue]="selectOptionValue"
|
||||
placeholder="انتخاب شناسه کالا"
|
||||
[formControl]="control"
|
||||
|
||||
@@ -3,11 +3,12 @@ import { FormControl, ReactiveFormsModule } from '@angular/forms';
|
||||
import { InputComponent } from '../input/input.component';
|
||||
|
||||
@Component({
|
||||
selector: 'field-fiscal-code',
|
||||
selector: 'field-fiscal-id',
|
||||
template: `<app-input
|
||||
label="کد مالیاتی"
|
||||
label="شناسه یکتا"
|
||||
[control]="control"
|
||||
[name]="name"
|
||||
[length]="6"
|
||||
[isLtrInput]="true"
|
||||
/>`,
|
||||
imports: [ReactiveFormsModule, InputComponent],
|
||||
|
||||
@@ -10,6 +10,7 @@ import { InputComponent } from '../input/input.component';
|
||||
[name]="name"
|
||||
type="number"
|
||||
[min]="min"
|
||||
[maxLength]="9"
|
||||
/>`,
|
||||
imports: [ReactiveFormsModule, InputComponent],
|
||||
})
|
||||
|
||||
@@ -4,12 +4,7 @@ import { InputComponent } from '../input/input.component';
|
||||
|
||||
@Component({
|
||||
selector: 'field-partner-token',
|
||||
template: `<app-input
|
||||
label="توکن پارتنر"
|
||||
[control]="control"
|
||||
[name]="name"
|
||||
[isLtrInput]="true"
|
||||
/>`,
|
||||
template: `<app-input label="توکن" [control]="control" [name]="name" [isLtrInput]="true" />`,
|
||||
imports: [ReactiveFormsModule, InputComponent],
|
||||
})
|
||||
export class PartnerTokenComponent {
|
||||
|
||||
@@ -4,7 +4,7 @@ import { InputComponent } from '../input/input.component';
|
||||
|
||||
@Component({
|
||||
selector: 'field-terminal',
|
||||
template: `<app-input label="پرداخت با پایانه" [control]="control" [name]="name" type="price" />`,
|
||||
template: `<app-input label="پرداخت با پوز" [control]="control" [name]="name" type="price" />`,
|
||||
imports: [ReactiveFormsModule, InputComponent],
|
||||
})
|
||||
export class TerminalComponent {
|
||||
|
||||
@@ -66,6 +66,7 @@ export class InputComponent {
|
||||
@Input() isLtrInput = false;
|
||||
@Input() suffix: string = '';
|
||||
@Input() maxLength?: number;
|
||||
@Input() length?: number;
|
||||
@Input() min?: number;
|
||||
@Input() max?: number;
|
||||
@Input() fixed?: number;
|
||||
@@ -132,6 +133,8 @@ export class InputComponent {
|
||||
}
|
||||
|
||||
get preparedMaxLength(): number | null {
|
||||
const length = this.length || this.maxLength;
|
||||
if (length) return length;
|
||||
switch (this.type) {
|
||||
case 'mobile':
|
||||
return 11;
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
<app-key-value label="مجموع قابل پرداخت" [value]="invoice.total_amount" type="price" />
|
||||
@for (payment of invoice.payments; track $index) {
|
||||
<app-key-value
|
||||
[label]="`${payment.payment_method === 'SET_OFF' ? 'تهاتر' : payment.payment_method === 'TERMINAL' ? 'پایانه' : 'نقدی'}`"
|
||||
[label]="`${payment.payment_method === 'SET_OFF' ? 'تهاتر' : payment.payment_method === 'TERMINAL' ? 'پوز' : 'نقدی/ کارتخوان دیگر/ کارت به کارت'}`"
|
||||
[value]="payment.amount"
|
||||
type="price"
|
||||
/>
|
||||
|
||||
@@ -11,8 +11,7 @@ import { Password } from 'primeng/password';
|
||||
export class SharedPasswordInputComponent {
|
||||
@Input({ required: true }) passwordControl = new FormControl<string>('');
|
||||
@Input({ required: true }) confirmPasswordControl = new FormControl<string>('');
|
||||
@Input() hint: string =
|
||||
'رمز باید حداقل ۸ کاراکتر بوده و شامل حداقل یک حرف بزرگ، یک حرف کوچک، یک عدد و یک کاراکتر ویژه باشد.';
|
||||
@Input() hint: string = 'رمز باید حداقل ۶ کاراکتر باشد.';
|
||||
@Input() fluid: boolean = true;
|
||||
@Input() size: 'small' | 'large' = 'large';
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import {
|
||||
fiscalCodeValidator,
|
||||
fiscalIdValidator,
|
||||
mobileValidator,
|
||||
password,
|
||||
postalCodeValidator,
|
||||
@@ -30,15 +30,10 @@ export const fieldControl = {
|
||||
value,
|
||||
isRequired ? required() : [],
|
||||
],
|
||||
firstName: (value = '', isRequired = true): ControlConfig => [
|
||||
value,
|
||||
isRequired ? required() : [],
|
||||
],
|
||||
last_name: (value = '', isRequired = true): ControlConfig => [
|
||||
value,
|
||||
isRequired ? required() : [],
|
||||
],
|
||||
lastName: (value = '', isRequired = true): ControlConfig => [value, isRequired ? required() : []],
|
||||
legal_name: (value = '', isRequired = true): ControlConfig => [
|
||||
value,
|
||||
isRequired ? required() : [],
|
||||
@@ -55,10 +50,6 @@ export const fieldControl = {
|
||||
value,
|
||||
isRequired ? [Validators.required, mobileValidator()] : [mobileValidator()],
|
||||
],
|
||||
mobile: (value = '', isRequired = true): ControlConfig => [
|
||||
value,
|
||||
isRequired ? [Validators.required, mobileValidator()] : [mobileValidator()],
|
||||
],
|
||||
email: (value = '', isRequired = false): ControlConfig => [
|
||||
value,
|
||||
isRequired ? [Validators.required, Validators.email] : [Validators.email],
|
||||
@@ -85,7 +76,7 @@ export const fieldControl = {
|
||||
],
|
||||
fiscal_id: (value = '', isRequired = true): ControlConfig => [
|
||||
value,
|
||||
isRequired ? [Validators.required, fiscalCodeValidator()] : [fiscalCodeValidator()],
|
||||
isRequired ? [Validators.required, fiscalIdValidator()] : [fiscalIdValidator()],
|
||||
],
|
||||
partner_token: (value = '', isRequired = true): ControlConfig => [
|
||||
value,
|
||||
|
||||
Reference in New Issue
Block a user