Compare commits
2 Commits
c135e1a85f
...
c5e1fab09b
| Author | SHA1 | Date | |
|---|---|---|---|
| c5e1fab09b | |||
| 2e1ad77946 |
@@ -68,6 +68,11 @@ 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['invalidFiscalId']) {
|
||||||
|
const info = errors['invalidFiscalId'];
|
||||||
|
out.push({ key: 'invalidFiscalId', message: info.message ?? `${label} معتبر نیست.` });
|
||||||
|
}
|
||||||
// fallback: include any other error keys
|
// fallback: include any other error keys
|
||||||
Object.keys(errors).forEach((k) => {
|
Object.keys(errors).forEach((k) => {
|
||||||
if (
|
if (
|
||||||
@@ -80,6 +85,7 @@ export class FormErrorsService {
|
|||||||
'max',
|
'max',
|
||||||
'email',
|
'email',
|
||||||
'pattern',
|
'pattern',
|
||||||
|
'invalidFiscalId',
|
||||||
].indexOf(k) === -1
|
].indexOf(k) === -1
|
||||||
) {
|
) {
|
||||||
try {
|
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 './greater.validator';
|
||||||
export * from './iban.validator';
|
export * from './iban.validator';
|
||||||
export * from './mobile.validator';
|
export * from './mobile.validator';
|
||||||
|
|||||||
@@ -1,16 +1,21 @@
|
|||||||
import { ValidatorFn } from '@angular/forms';
|
import { ValidatorFn } from '@angular/forms';
|
||||||
|
|
||||||
// Password must be minimum 8 characters, include at least one uppercase,
|
// const PASSWORD_PATTERN = /^[a-zA-Z0-9_-]*$/;
|
||||||
// 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,}$/;
|
|
||||||
|
|
||||||
// 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 {
|
export function password(): ValidatorFn {
|
||||||
return (control) => {
|
return (control) => {
|
||||||
const value = control?.value;
|
const value = control?.value;
|
||||||
if (value === null || value === undefined || String(value).length === 0) return null;
|
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: 'فعالیت اقتصادی',
|
label: 'فعالیت اقتصادی',
|
||||||
icon: 'pi pi-fw pi-home',
|
icon: 'pi pi-fw pi-shop',
|
||||||
routerLink: ['/consumer/business_activities'],
|
routerLink: ['/consumer/business_activities'],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'پایانههای فروش',
|
label: 'پایانهی فروش',
|
||||||
icon: 'pi pi-fw pi-home',
|
icon: 'pi pi-fw pi-tablet',
|
||||||
routerLink: ['/consumer/poses'],
|
routerLink: ['/consumer/poses'],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'فاکتورها',
|
label: 'فاکتورها',
|
||||||
icon: 'pi pi-fw pi-home',
|
icon: 'pi pi-fw pi-receipt',
|
||||||
routerLink: ['/consumer/sale_invoices'],
|
routerLink: ['/consumer/sale_invoices'],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'مشتریها',
|
label: 'مشتریها',
|
||||||
icon: 'pi pi-fw pi-home',
|
icon: 'pi pi-fw pi-users',
|
||||||
routerLink: ['/consumer/customers'],
|
routerLink: ['/consumer/customers'],
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
label: 'حسابهای کاربری',
|
label: 'حسابهای کاربری',
|
||||||
icon: 'pi pi-fw pi-home',
|
icon: 'pi pi-fw pi-user',
|
||||||
routerLink: ['/consumer/accounts'],
|
routerLink: ['/consumer/accounts'],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
<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-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-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" />
|
||||||
<app-form-footer-actions [submitLabel]="'ذخیره'" [loading]="submitLoading()" (onCancel)="close()" />
|
<app-form-footer-actions [submitLabel]="'ذخیره'" [loading]="submitLoading()" (onCancel)="close()" />
|
||||||
|
|||||||
@@ -9,17 +9,17 @@ export const PARTNER_MENU_ITEMS = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'حسابهای کاربری',
|
label: 'حسابهای کاربری',
|
||||||
icon: 'pi pi-fw pi-home',
|
icon: 'pi pi-fw pi-user',
|
||||||
routerLink: ['/partner/accounts'],
|
routerLink: ['/partner/accounts'],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'مشتریها',
|
label: 'مشتریها',
|
||||||
icon: 'pi pi-fw pi-home',
|
icon: 'pi pi-fw pi-users',
|
||||||
routerLink: ['/partner/consumers'],
|
routerLink: ['/partner/consumers'],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'لایسنسها',
|
label: 'لایسنسها',
|
||||||
icon: 'pi pi-fw pi-home',
|
icon: 'pi pi-fw pi-key',
|
||||||
routerLink: ['/partner/licenses'],
|
routerLink: ['/partner/licenses'],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|||||||
+2
-1
@@ -1,7 +1,8 @@
|
|||||||
<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-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-partner-token [control]="form.controls.partner_token" />
|
||||||
<field-guild-id [control]="form.controls.guild_id" />
|
<field-guild-id [control]="form.controls.guild_id" />
|
||||||
|
|
||||||
|
|||||||
+5
@@ -7,6 +7,7 @@ import {
|
|||||||
NameComponent,
|
NameComponent,
|
||||||
PartnerTokenComponent,
|
PartnerTokenComponent,
|
||||||
} from '@/shared/components';
|
} from '@/shared/components';
|
||||||
|
import { InvoiceNumberSequenceComponent } from '@/shared/components/fields/invoice_number_sequence.component';
|
||||||
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/fields';
|
import { fieldControl } from '@/shared/constants/fields';
|
||||||
import { Component, inject, Input } from '@angular/core';
|
import { Component, inject, Input } from '@angular/core';
|
||||||
@@ -28,6 +29,7 @@ import { PartnerConsumerBusinessActivitiesService } from '../../services/busines
|
|||||||
LicenseStartsAtComponent,
|
LicenseStartsAtComponent,
|
||||||
FormFooterActionsComponent,
|
FormFooterActionsComponent,
|
||||||
Divider,
|
Divider,
|
||||||
|
InvoiceNumberSequenceComponent,
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
export class ConsumerBusinessActivitiesFormComponent extends AbstractForm<
|
export class ConsumerBusinessActivitiesFormComponent extends AbstractForm<
|
||||||
@@ -44,6 +46,9 @@ export class ConsumerBusinessActivitiesFormComponent extends AbstractForm<
|
|||||||
name: fieldControl.name(this.initialValues?.name || ''),
|
name: fieldControl.name(this.initialValues?.name || ''),
|
||||||
economic_code: fieldControl.economic_code(this.initialValues?.economic_code || ''),
|
economic_code: fieldControl.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(
|
||||||
|
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 || ''),
|
||||||
license_starts_at: [''],
|
license_starts_at: [''],
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ export interface IBusinessActivityRawResponse {
|
|||||||
fiscal_id: string;
|
fiscal_id: string;
|
||||||
partner_token: string;
|
partner_token: string;
|
||||||
guild: ISummary;
|
guild: ISummary;
|
||||||
|
invoice_number_sequence: string;
|
||||||
created_at: string;
|
created_at: string;
|
||||||
license_info: LicenseInfo;
|
license_info: LicenseInfo;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ export interface IGoodRawResponse {
|
|||||||
is_default_guild_good: boolean;
|
is_default_guild_good: boolean;
|
||||||
created_at: string;
|
created_at: string;
|
||||||
updated_at: string;
|
updated_at: string;
|
||||||
|
is_favorite: boolean;
|
||||||
}
|
}
|
||||||
export interface IGoodResponse extends IGoodRawResponse {}
|
export interface IGoodResponse extends IGoodRawResponse {}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
<p-button
|
||||||
|
type="button"
|
||||||
|
[icon]="`pi pi-${isFavorite ? 'star-fill' : 'star'}`"
|
||||||
|
class="shrink-0"
|
||||||
|
[size]="size"
|
||||||
|
[loading]="loading()"
|
||||||
|
[severity]="isFavorite ? 'warn' : 'secondary'"
|
||||||
|
(click)="toggleFavorite(id, isFavorite)"
|
||||||
|
></p-button>
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
import { Component, inject, Input, signal } from '@angular/core';
|
||||||
|
import { Button } from 'primeng/button';
|
||||||
|
import { finalize, Observable } from 'rxjs';
|
||||||
|
import { PosGoodFavoriteService } from '../services/favorite.service';
|
||||||
|
import { PosLandingStore } from '../store/main.store';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'pos-good-favorite-cta',
|
||||||
|
templateUrl: 'favorite-CTA.component.html',
|
||||||
|
imports: [Button],
|
||||||
|
})
|
||||||
|
export class FavoriteCTAComponent {
|
||||||
|
private readonly favoriteService = inject(PosGoodFavoriteService);
|
||||||
|
private readonly store = inject(PosLandingStore);
|
||||||
|
|
||||||
|
@Input({ required: true }) id!: string;
|
||||||
|
@Input({ required: true }) isFavorite!: boolean;
|
||||||
|
@Input() size?: 'small' | 'large';
|
||||||
|
|
||||||
|
loading = signal(false);
|
||||||
|
|
||||||
|
toggleFavorite(goodId: string, currentState: boolean) {
|
||||||
|
if (this.loading()) return;
|
||||||
|
if (currentState) {
|
||||||
|
this.detachFavorite(goodId);
|
||||||
|
} else {
|
||||||
|
this.attachFavorite(goodId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
attachFavorite(goodId: string) {
|
||||||
|
this.handleFavoriteRequest(this.favoriteService.attach(goodId), goodId, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
detachFavorite(goodId: string) {
|
||||||
|
this.handleFavoriteRequest(this.favoriteService.detach(goodId), goodId, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private handleFavoriteRequest(
|
||||||
|
request$: Observable<unknown>,
|
||||||
|
goodId: string,
|
||||||
|
isFavorite: boolean,
|
||||||
|
) {
|
||||||
|
this.loading.set(true);
|
||||||
|
request$
|
||||||
|
.pipe(
|
||||||
|
finalize(() => {
|
||||||
|
this.loading.set(false);
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.subscribe(() => {
|
||||||
|
this.store.toggleGoodFavorite(goodId, isFavorite);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,7 +7,7 @@
|
|||||||
}
|
}
|
||||||
} @else {
|
} @else {
|
||||||
@for (good of visibleGoods(); track good.id) {
|
@for (good of visibleGoods(); track good.id) {
|
||||||
<div class="flex-1 bg-surface-card rounded-lg p-1 shadow-xs flex flex-col" (click)="addProduct(good)">
|
<div class="flex-1 bg-surface-card rounded-lg p-1 shadow-xs flex flex-col">
|
||||||
<img
|
<img
|
||||||
[src]="good.image_url || goodPlaceholder"
|
[src]="good.image_url || goodPlaceholder"
|
||||||
loading="lazy"
|
loading="lazy"
|
||||||
@@ -21,9 +21,9 @@
|
|||||||
<small class="text-xs text-muted-color">*</small>
|
<small class="text-xs text-muted-color">*</small>
|
||||||
}
|
}
|
||||||
</span>
|
</span>
|
||||||
<div class="flex items-center justify-between mt-1 w-full px-2 my-2 shrink-0">
|
<div class="flex items-center justify-between mt-1 w-full px-2 my-2 shrink-0 gap-1">
|
||||||
<!-- <span [appPriceMask]="good.salePrice" class="text-base font-bold"></span> -->
|
<button pButton type="button" label="انتخاب" class="grow w-full" (click)="addProduct(good)"></button>
|
||||||
<button pButton type="button" icon="pi pi-plus" class="w-full!">انتخاب</button>
|
<pos-good-favorite-cta [isFavorite]="good.is_favorite" [id]="good.id" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -13,26 +13,30 @@ import { ButtonDirective } from 'primeng/button';
|
|||||||
import { Skeleton } from 'primeng/skeleton';
|
import { Skeleton } from 'primeng/skeleton';
|
||||||
import images from 'src/assets/images';
|
import images from 'src/assets/images';
|
||||||
import { PosLandingStore } from '../store/main.store';
|
import { PosLandingStore } from '../store/main.store';
|
||||||
|
import { FavoriteCTAComponent } from './favorite-CTA.component';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'pos-good-grid-view',
|
selector: 'pos-good-grid-view',
|
||||||
templateUrl: './grid-view.component.html',
|
templateUrl: './grid-view.component.html',
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
imports: [ButtonDirective, Skeleton],
|
imports: [ButtonDirective, Skeleton, FavoriteCTAComponent],
|
||||||
})
|
})
|
||||||
export class PosGoodsGridViewComponent {
|
export class PosGoodsGridViewComponent {
|
||||||
private readonly store = inject(PosLandingStore);
|
|
||||||
@Output() onAdd = new EventEmitter<IGoodResponse>();
|
@Output() onAdd = new EventEmitter<IGoodResponse>();
|
||||||
|
|
||||||
goods = computed(() => this.store.filteredGoods());
|
private readonly store = inject(PosLandingStore);
|
||||||
private readonly pageSize = 80;
|
private readonly pageSize = 80;
|
||||||
readonly renderLimit = signal(this.pageSize);
|
readonly renderLimit = signal(this.pageSize);
|
||||||
|
|
||||||
|
goodPlaceholder = images.placeholders.default;
|
||||||
|
|
||||||
|
onFavItemsLoading = signal<string[]>([]);
|
||||||
|
|
||||||
|
goods = computed(() => this.store.filteredGoods());
|
||||||
visibleGoods = computed(() => this.goods()?.slice(0, this.renderLimit()));
|
visibleGoods = computed(() => this.goods()?.slice(0, this.renderLimit()));
|
||||||
hasMore = computed(() => (this.goods()?.length || 0) > (this.visibleGoods()?.length || 0));
|
hasMore = computed(() => (this.goods()?.length || 0) > (this.visibleGoods()?.length || 0));
|
||||||
loading = computed(() => this.store.getGoodsLoading());
|
loading = computed(() => this.store.getGoodsLoading());
|
||||||
|
|
||||||
goodPlaceholder = images.placeholders.default;
|
|
||||||
|
|
||||||
addProduct(good: IGoodResponse) {
|
addProduct(good: IGoodResponse) {
|
||||||
this.onAdd.emit(good);
|
this.onAdd.emit(good);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
}
|
}
|
||||||
} @else {
|
} @else {
|
||||||
@for (good of visibleGoods(); track good.id) {
|
@for (good of visibleGoods(); track good.id) {
|
||||||
<div class="flex items-center bg-surface-card rounded-lg p-1 shadow-xs gap-2">
|
<div class="flex items-center bg-surface-card rounded-lg py-1 px-3 shadow-xs gap-2">
|
||||||
<div class="grow flex items-center gap-2">
|
<div class="grow flex items-center gap-2">
|
||||||
<img
|
<img
|
||||||
[src]="good.image_url || goodPlaceholder"
|
[src]="good.image_url || goodPlaceholder"
|
||||||
@@ -16,7 +16,7 @@
|
|||||||
class="w-12 h-12 object-cover rounded-md"
|
class="w-12 h-12 object-cover rounded-md"
|
||||||
/>
|
/>
|
||||||
<div class="flex flex-col gap-2">
|
<div class="flex flex-col gap-2">
|
||||||
<span class="text-lg font-bold">
|
<span class="text-base font-bold">
|
||||||
{{ good.name }}
|
{{ good.name }}
|
||||||
@if (!good.is_default_guild_good) {
|
@if (!good.is_default_guild_good) {
|
||||||
<small class="text-xs text-muted-color">*</small>
|
<small class="text-xs text-muted-color">*</small>
|
||||||
@@ -27,8 +27,9 @@
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="shrink-0 flex items-center justify-between gap-3">
|
<div class="shrink-0 flex items-center justify-between gap-1">
|
||||||
<button pButton type="button" icon="pi pi-plus" size="small" (click)="addGood(good)">انتخاب</button>
|
<pos-good-favorite-cta [isFavorite]="good.is_favorite" [id]="good.id" size="small" />
|
||||||
|
<button pButton type="button" label="انتخاب" class="w-28" size="small" (click)="addGood(good)"></button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,26 +13,29 @@ import { ButtonDirective } from 'primeng/button';
|
|||||||
import { Skeleton } from 'primeng/skeleton';
|
import { Skeleton } from 'primeng/skeleton';
|
||||||
import images from 'src/assets/images';
|
import images from 'src/assets/images';
|
||||||
import { PosLandingStore } from '../store/main.store';
|
import { PosLandingStore } from '../store/main.store';
|
||||||
|
import { FavoriteCTAComponent } from './favorite-CTA.component';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'pos-goods-list-view',
|
selector: 'pos-goods-list-view',
|
||||||
templateUrl: './list-view.component.html',
|
templateUrl: './list-view.component.html',
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
imports: [ButtonDirective, Skeleton],
|
imports: [ButtonDirective, Skeleton, FavoriteCTAComponent],
|
||||||
})
|
})
|
||||||
export class PosGoodsListViewComponent {
|
export class PosGoodsListViewComponent {
|
||||||
private readonly store = inject(PosLandingStore);
|
private readonly store = inject(PosLandingStore);
|
||||||
|
|
||||||
@Output() onAdd = new EventEmitter<IGoodResponse>();
|
@Output() onAdd = new EventEmitter<IGoodResponse>();
|
||||||
|
|
||||||
goods = computed(() => this.store.filteredGoods());
|
goodPlaceholder = images.placeholders.default;
|
||||||
|
|
||||||
private readonly pageSize = 120;
|
private readonly pageSize = 120;
|
||||||
readonly renderLimit = signal(this.pageSize);
|
readonly renderLimit = signal(this.pageSize);
|
||||||
|
|
||||||
|
goods = computed(() => this.store.filteredGoods());
|
||||||
visibleGoods = computed(() => this.goods()?.slice(0, this.renderLimit()));
|
visibleGoods = computed(() => this.goods()?.slice(0, this.renderLimit()));
|
||||||
hasMore = computed(() => (this.goods()?.length || 0) > (this.visibleGoods()?.length || 0));
|
hasMore = computed(() => (this.goods()?.length || 0) > (this.visibleGoods()?.length || 0));
|
||||||
loading = computed(() => this.store.getGoodsLoading());
|
loading = computed(() => this.store.getGoodsLoading());
|
||||||
|
|
||||||
goodPlaceholder = images.placeholders.default;
|
|
||||||
|
|
||||||
addGood(good: IGoodResponse) {
|
addGood(good: IGoodResponse) {
|
||||||
this.onAdd.emit(good);
|
this.onAdd.emit(good);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<form [formGroup]="form">
|
<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-input [control]="form.controls.quantity" name="quantity" label="مقدار" type="number" suffix="گرم" />
|
||||||
<app-enum-select [control]="form.controls.payload.controls.karat" type="goldKarat" name="karat" />
|
<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>,
|
||||||
IPosOrderItem<IGoldPayload>
|
IPosOrderItem<IGoldPayload>
|
||||||
> {
|
> {
|
||||||
// override defaultValues: Partial<IPosOrderItem<IGoldPayload>> = {
|
override defaultValues: Partial<IPosOrderItem<IGoldPayload>> = {
|
||||||
// unit_price: 200_000_000,
|
// unit_price: 200_000_000,
|
||||||
// quantity: 2,
|
// quantity: 2,
|
||||||
// payload: {
|
payload: {
|
||||||
// // commission: 10_000,
|
// commission: 10_000,
|
||||||
// // wages: 10_000,
|
// wages: 10_000,
|
||||||
// // profit: 10_000,
|
// profit: 10_000,
|
||||||
// karat: 'KARAT_18' as TGoldKarat,
|
karat: 'KARAT_18' as TGoldKarat,
|
||||||
// } as any,
|
} as any,
|
||||||
// };
|
};
|
||||||
|
|
||||||
@Input({ required: true }) vatPercentage!: number;
|
@Input({ required: true }) vatPercentage!: number;
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
</form>
|
</form>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<uikit-field label="تعداد مراحل پرداخت با پایانه" name="pay_by_terminal_steps">
|
<uikit-field label="تعداد مراحل پرداخت با پوز" name="pay_by_terminal_steps">
|
||||||
<p-select
|
<p-select
|
||||||
[options]="payByTerminalSteps()"
|
[options]="payByTerminalSteps()"
|
||||||
[ngModel]="selectedPayByTerminalStep()"
|
[ngModel]="selectedPayByTerminalStep()"
|
||||||
@@ -30,7 +30,7 @@
|
|||||||
<app-input
|
<app-input
|
||||||
[control]="terminalControl"
|
[control]="terminalControl"
|
||||||
[name]="'terminal_' + $index"
|
[name]="'terminal_' + $index"
|
||||||
[label]="$index === 0 ? 'پرداخت با پایانه' : 'پرداخت با پایانه - مرحله ' + ($index + 1)"
|
[label]="$index === 0 ? 'پرداخت با پوز' : 'پرداخت با پوز - مرحله ' + ($index + 1)"
|
||||||
type="price"
|
type="price"
|
||||||
[max]="terminalsMax()[$index]"
|
[max]="terminalsMax()[$index]"
|
||||||
[disabled]="payByTerminalSteps()[$index].payed"
|
[disabled]="payByTerminalSteps()[$index].payed"
|
||||||
@@ -43,24 +43,30 @@
|
|||||||
[disabled]="!remainedAmount() || payByTerminalSteps()[$index].payed"
|
[disabled]="!remainedAmount() || payByTerminalSteps()[$index].payed"
|
||||||
(click)="fillTerminalRemained($index)"
|
(click)="fillTerminalRemained($index)"
|
||||||
>
|
>
|
||||||
تمامی بدهی باقیمانده
|
باقیمانده مبلغ
|
||||||
</button>
|
</button>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
</app-input>
|
</app-input>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
<form [formGroup]="form" (submit)="submit()">
|
<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>
|
<ng-template #suffixTemp>
|
||||||
<button pButton size="small" type="button" [disabled]="!remainedAmount()" (click)="fillRemained('CASH')">
|
<button pButton size="small" type="button" [disabled]="!remainedAmount()" (click)="fillRemained('CASH')">
|
||||||
تمامی بدهی باقیمانده
|
باقیمانده مبلغ
|
||||||
</button>
|
</button>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
</app-input>
|
</app-input>
|
||||||
<app-input [control]="form.controls.set_off" name="setOff" label="تهاتر" type="price" [max]="setOffMax()">
|
<app-input [control]="form.controls.set_off" name="setOff" label="تهاتر" type="price" [max]="setOffMax()">
|
||||||
<ng-template #suffixTemp>
|
<ng-template #suffixTemp>
|
||||||
<button pButton size="small" type="button" [disabled]="!remainedAmount()" (click)="fillRemained('SET_OFF')">
|
<button pButton size="small" type="button" [disabled]="!remainedAmount()" (click)="fillRemained('SET_OFF')">
|
||||||
تمامی بدهی باقیمانده
|
باقیمانده مبلغ
|
||||||
</button>
|
</button>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
</app-input>
|
</app-input>
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
const baseUrl = (good_id: string) => `/api/v1/pos/goods/${good_id}/favorite`;
|
||||||
|
|
||||||
|
export const POS_GOOD_FAVORITE_API_ROUTES = {
|
||||||
|
favorite: (good_id: string) => `${baseUrl(good_id)}`,
|
||||||
|
};
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
export * from './favorite';
|
||||||
const baseUrl = '/api/v1/pos';
|
const baseUrl = '/api/v1/pos';
|
||||||
|
|
||||||
export const POS_API_ROUTES = {
|
export const POS_API_ROUTES = {
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
import { IPosInfoRawResponse, IPosInfoResponse } from '@/domains/pos/models/pos.io';
|
||||||
|
import { IPosProfileRawResponse, IPosProfileResponse } from '@/domains/pos/models/profile.io';
|
||||||
|
import { HttpClient } from '@angular/common/http';
|
||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { Observable } from 'rxjs';
|
||||||
|
import { POS_GOOD_FAVORITE_API_ROUTES } from '../constants';
|
||||||
|
|
||||||
|
@Injectable({ providedIn: 'root' })
|
||||||
|
export class PosGoodFavoriteService {
|
||||||
|
constructor(private http: HttpClient) {}
|
||||||
|
|
||||||
|
private apiRoutes = POS_GOOD_FAVORITE_API_ROUTES;
|
||||||
|
|
||||||
|
attach(good_id: string): Observable<IPosProfileResponse> {
|
||||||
|
return this.http.post<IPosProfileRawResponse>(this.apiRoutes.favorite(good_id), {});
|
||||||
|
}
|
||||||
|
|
||||||
|
detach(good_id: string): Observable<IPosInfoResponse> {
|
||||||
|
return this.http.delete<IPosInfoRawResponse>(this.apiRoutes.favorite(good_id));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,12 +5,13 @@ import { IGoodCategoryResponse, IGoodResponse } from '@/domains/pos/models/good.
|
|||||||
import { CustomerType } from '@/shared/localEnum/constants/customerTypes';
|
import { CustomerType } from '@/shared/localEnum/constants/customerTypes';
|
||||||
import { createUUID, JALALI_DATE_FORMATS, nowJalali } from '@/utils';
|
import { createUUID, JALALI_DATE_FORMATS, nowJalali } from '@/utils';
|
||||||
import { computed, inject, Injectable, signal } from '@angular/core';
|
import { computed, inject, Injectable, signal } from '@angular/core';
|
||||||
import { catchError, finalize, map, throwError } from 'rxjs';
|
import { catchError, finalize, firstValueFrom, map, throwError } from 'rxjs';
|
||||||
import { ICustomer, IPayment, IPosOrderRequest, IPosOrderResponse } from '../models';
|
import { ICustomer, IPayment, IPosOrderRequest, IPosOrderResponse } from '../models';
|
||||||
import { IPosInOrderGood, IPosOrderItem } from '../models/types';
|
import { IPosInOrderGood, IPosOrderItem } from '../models/types';
|
||||||
import { PosService } from '../services/main.service';
|
import { PosService } from '../services/main.service';
|
||||||
|
|
||||||
export type TViewType = 'list' | 'grid';
|
export type TViewType = 'list' | 'grid';
|
||||||
|
const FAVORITE_CATEGORY_ID = 'favorite';
|
||||||
|
|
||||||
interface IPosLandingState {
|
interface IPosLandingState {
|
||||||
getGoodsLoading: boolean;
|
getGoodsLoading: boolean;
|
||||||
@@ -74,6 +75,9 @@ export class PosLandingStore {
|
|||||||
if (!this.activeGoodCategory()) {
|
if (!this.activeGoodCategory()) {
|
||||||
return this.state$().goods;
|
return this.state$().goods;
|
||||||
}
|
}
|
||||||
|
if (this.activeGoodCategory() === FAVORITE_CATEGORY_ID) {
|
||||||
|
return this.state$().goods?.filter((good) => good.is_favorite);
|
||||||
|
}
|
||||||
return this.state$().goods?.filter((s) => s.category.id === this.state$().activeGoodCategory);
|
return this.state$().goods?.filter((s) => s.category.id === this.state$().activeGoodCategory);
|
||||||
});
|
});
|
||||||
readonly filteredGoods = computed(() =>
|
readonly filteredGoods = computed(() =>
|
||||||
@@ -115,55 +119,38 @@ export class PosLandingStore {
|
|||||||
this.state$.set({ ...INITIAL_POS_STATE });
|
this.state$.set({ ...INITIAL_POS_STATE });
|
||||||
}
|
}
|
||||||
|
|
||||||
initial() {
|
async initial() {
|
||||||
this.getGoods();
|
await Promise.all([this.getGoods(), this.getGoodCategories()]);
|
||||||
this.getGoodCategories();
|
|
||||||
|
this.prepareGoodCategories();
|
||||||
}
|
}
|
||||||
|
|
||||||
fillInitial(data: Partial<IPosLandingState>) {
|
fillInitial(data: Partial<IPosLandingState>) {
|
||||||
this.state$.set({ ...INITIAL_POS_STATE, ...data });
|
this.state$.set({ ...INITIAL_POS_STATE, ...data });
|
||||||
}
|
}
|
||||||
|
|
||||||
getGoods() {
|
private async getGoods() {
|
||||||
this.setState({ getGoodsLoading: true });
|
this.setState({ getGoodsLoading: true });
|
||||||
this.service.getGoods(this.state$().searchQuery).subscribe({
|
try {
|
||||||
next: (res) => {
|
const res = await firstValueFrom(this.service.getGoods(this.state$().searchQuery));
|
||||||
this.setState({ goods: res.data, getGoodsLoading: false });
|
this.setState({ goods: res.data, getGoodsLoading: false });
|
||||||
},
|
} catch {
|
||||||
error: () => {
|
this.setState({ getGoodsLoading: false });
|
||||||
this.setState({ getGoodsLoading: false });
|
}
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getGoodCategories() {
|
private async getGoodCategories() {
|
||||||
this.setState({ getGoodCategoriesLoading: true });
|
this.setState({ getGoodCategoriesLoading: true });
|
||||||
this.service
|
try {
|
||||||
.getGoodCategories()
|
const res = await firstValueFrom(this.service.getGoodCategories());
|
||||||
.pipe(
|
|
||||||
map((res) => {
|
this.setState({
|
||||||
return [
|
goodCategories: res.data,
|
||||||
{
|
getGoodCategoriesLoading: false,
|
||||||
id: '',
|
|
||||||
name: 'همه',
|
|
||||||
goods_count: res.data.reduce((acc, curr) => acc + curr.goods_count, 0),
|
|
||||||
},
|
|
||||||
...res.data,
|
|
||||||
];
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
.subscribe({
|
|
||||||
next: (res) => {
|
|
||||||
this.setState({
|
|
||||||
goodCategories: res,
|
|
||||||
getGoodCategoriesLoading: false,
|
|
||||||
activeGoodCategory: res[0]?.id,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
error: () => {
|
|
||||||
this.setState({ getGoodCategoriesLoading: false });
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
} catch {
|
||||||
|
this.setState({ getGoodCategoriesLoading: false });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
changeActiveCategory(categoryId: string) {
|
changeActiveCategory(categoryId: string) {
|
||||||
@@ -210,6 +197,46 @@ export class PosLandingStore {
|
|||||||
this.setState({ searchQuery });
|
this.setState({ searchQuery });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
toggleGoodFavorite(goodId: string, newState: boolean) {
|
||||||
|
const updatedGoods = this.state$().goods?.map((good) => {
|
||||||
|
if (good.id === goodId) {
|
||||||
|
return { ...good, is_favorite: newState };
|
||||||
|
}
|
||||||
|
return good;
|
||||||
|
});
|
||||||
|
this.setState({ goods: updatedGoods || this.state$().goods });
|
||||||
|
this.prepareGoodCategories();
|
||||||
|
}
|
||||||
|
|
||||||
|
private prepareGoodCategories() {
|
||||||
|
const rawCategories =
|
||||||
|
this.goodCategories()?.filter(({ id }) => id !== '' && id !== FAVORITE_CATEGORY_ID) || [];
|
||||||
|
const favoriteGoodsCount = this.goods()?.filter((good) => good.is_favorite).length || 0;
|
||||||
|
const goodsCount = this.goods()?.length || 0;
|
||||||
|
const preparedGoodCategories = rawCategories;
|
||||||
|
|
||||||
|
if (favoriteGoodsCount > 0) {
|
||||||
|
preparedGoodCategories.unshift({
|
||||||
|
id: FAVORITE_CATEGORY_ID,
|
||||||
|
name: 'علاقهمندیها',
|
||||||
|
goods_count: favoriteGoodsCount,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
preparedGoodCategories.unshift({
|
||||||
|
id: '',
|
||||||
|
name: 'همه',
|
||||||
|
goods_count: goodsCount,
|
||||||
|
});
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
goodCategories: preparedGoodCategories,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!this.activeGoodCategory()) {
|
||||||
|
this.setState({ activeGoodCategory: '' });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
filterGoods() {}
|
filterGoods() {}
|
||||||
|
|
||||||
resetInOrderGoods() {
|
resetInOrderGoods() {
|
||||||
|
|||||||
@@ -13,5 +13,6 @@ export class LayoutComponent {
|
|||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
this.layoutService.setMenuItems(SUPER_ADMIN_MENU_ITEMS);
|
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">
|
<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-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-partner-token [control]="form.controls.partner_token" />
|
||||||
<field-guild-id [control]="form.controls.guild_id" />
|
<field-guild-id [control]="form.controls.guild_id" />
|
||||||
<app-form-footer-actions [submitLabel]="'ذخیره'" [loading]="submitLoading()" (onCancel)="close()" />
|
<app-form-footer-actions [submitLabel]="'ذخیره'" [loading]="submitLoading()" (onCancel)="close()" />
|
||||||
|
|||||||
@@ -6,8 +6,8 @@
|
|||||||
</ng-template>
|
</ng-template>
|
||||||
<div class="flex flex-col gap-4">
|
<div class="flex flex-col gap-4">
|
||||||
<div class="listKeyValue">
|
<div class="listKeyValue">
|
||||||
<app-key-value label="عنوان شریک تجاری" [value]="partner()?.name" />
|
<app-key-value label="عنوان" [value]="partner()?.name" />
|
||||||
<app-key-value label="کد شریک تجاری" [value]="partner()?.code" />
|
<app-key-value label="کد" [value]="partner()?.code" />
|
||||||
|
|
||||||
<div class="col-span-3 grid grid-cols-3 gap-4 mt-6">
|
<div class="col-span-3 grid grid-cols-3 gap-4 mt-6">
|
||||||
<partner-license-info-template
|
<partner-license-info-template
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
<p-select
|
<p-select
|
||||||
[loading]="loading()"
|
[loading]="loading()"
|
||||||
[options]="items()"
|
[options]="items()"
|
||||||
optionLabel="name"
|
optionLabel="fullname"
|
||||||
[optionValue]="selectOptionValue"
|
[optionValue]="selectOptionValue"
|
||||||
placeholder="انتخاب شناسه کالا"
|
placeholder="انتخاب شناسه کالا"
|
||||||
[formControl]="control"
|
[formControl]="control"
|
||||||
|
|||||||
@@ -3,11 +3,12 @@ import { FormControl, ReactiveFormsModule } from '@angular/forms';
|
|||||||
import { InputComponent } from '../input/input.component';
|
import { InputComponent } from '../input/input.component';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'field-fiscal-code',
|
selector: 'field-fiscal-id',
|
||||||
template: `<app-input
|
template: `<app-input
|
||||||
label="کد مالیاتی"
|
label="شناسه یکتا"
|
||||||
[control]="control"
|
[control]="control"
|
||||||
[name]="name"
|
[name]="name"
|
||||||
|
[length]="6"
|
||||||
[isLtrInput]="true"
|
[isLtrInput]="true"
|
||||||
/>`,
|
/>`,
|
||||||
imports: [ReactiveFormsModule, InputComponent],
|
imports: [ReactiveFormsModule, InputComponent],
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import { InputComponent } from '../input/input.component';
|
|||||||
[name]="name"
|
[name]="name"
|
||||||
type="number"
|
type="number"
|
||||||
[min]="min"
|
[min]="min"
|
||||||
|
[maxLength]="9"
|
||||||
/>`,
|
/>`,
|
||||||
imports: [ReactiveFormsModule, InputComponent],
|
imports: [ReactiveFormsModule, InputComponent],
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -4,12 +4,7 @@ import { InputComponent } from '../input/input.component';
|
|||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'field-partner-token',
|
selector: 'field-partner-token',
|
||||||
template: `<app-input
|
template: `<app-input label="توکن" [control]="control" [name]="name" [isLtrInput]="true" />`,
|
||||||
label="توکن پارتنر"
|
|
||||||
[control]="control"
|
|
||||||
[name]="name"
|
|
||||||
[isLtrInput]="true"
|
|
||||||
/>`,
|
|
||||||
imports: [ReactiveFormsModule, InputComponent],
|
imports: [ReactiveFormsModule, InputComponent],
|
||||||
})
|
})
|
||||||
export class PartnerTokenComponent {
|
export class PartnerTokenComponent {
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { InputComponent } from '../input/input.component';
|
|||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'field-terminal',
|
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],
|
imports: [ReactiveFormsModule, InputComponent],
|
||||||
})
|
})
|
||||||
export class TerminalComponent {
|
export class TerminalComponent {
|
||||||
|
|||||||
@@ -66,6 +66,7 @@ export class InputComponent {
|
|||||||
@Input() isLtrInput = false;
|
@Input() isLtrInput = false;
|
||||||
@Input() suffix: string = '';
|
@Input() suffix: string = '';
|
||||||
@Input() maxLength?: number;
|
@Input() maxLength?: number;
|
||||||
|
@Input() length?: number;
|
||||||
@Input() min?: number;
|
@Input() min?: number;
|
||||||
@Input() max?: number;
|
@Input() max?: number;
|
||||||
@Input() fixed?: number;
|
@Input() fixed?: number;
|
||||||
@@ -132,6 +133,8 @@ export class InputComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get preparedMaxLength(): number | null {
|
get preparedMaxLength(): number | null {
|
||||||
|
const length = this.length || this.maxLength;
|
||||||
|
if (length) return length;
|
||||||
switch (this.type) {
|
switch (this.type) {
|
||||||
case 'mobile':
|
case 'mobile':
|
||||||
return 11;
|
return 11;
|
||||||
|
|||||||
@@ -33,7 +33,7 @@
|
|||||||
<app-key-value label="مجموع قابل پرداخت" [value]="invoice.total_amount" type="price" />
|
<app-key-value label="مجموع قابل پرداخت" [value]="invoice.total_amount" type="price" />
|
||||||
@for (payment of invoice.payments; track $index) {
|
@for (payment of invoice.payments; track $index) {
|
||||||
<app-key-value
|
<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"
|
[value]="payment.amount"
|
||||||
type="price"
|
type="price"
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -11,8 +11,7 @@ import { Password } from 'primeng/password';
|
|||||||
export class SharedPasswordInputComponent {
|
export class SharedPasswordInputComponent {
|
||||||
@Input({ required: true }) passwordControl = new FormControl<string>('');
|
@Input({ required: true }) passwordControl = new FormControl<string>('');
|
||||||
@Input({ required: true }) confirmPasswordControl = new FormControl<string>('');
|
@Input({ required: true }) confirmPasswordControl = new FormControl<string>('');
|
||||||
@Input() hint: string =
|
@Input() hint: string = 'رمز باید حداقل ۶ کاراکتر باشد.';
|
||||||
'رمز باید حداقل ۸ کاراکتر بوده و شامل حداقل یک حرف بزرگ، یک حرف کوچک، یک عدد و یک کاراکتر ویژه باشد.';
|
|
||||||
@Input() fluid: boolean = true;
|
@Input() fluid: boolean = true;
|
||||||
@Input() size: 'small' | 'large' = 'large';
|
@Input() size: 'small' | 'large' = 'large';
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import {
|
import {
|
||||||
fiscalCodeValidator,
|
fiscalIdValidator,
|
||||||
mobileValidator,
|
mobileValidator,
|
||||||
password,
|
password,
|
||||||
postalCodeValidator,
|
postalCodeValidator,
|
||||||
@@ -30,15 +30,10 @@ export const fieldControl = {
|
|||||||
value,
|
value,
|
||||||
isRequired ? required() : [],
|
isRequired ? required() : [],
|
||||||
],
|
],
|
||||||
firstName: (value = '', isRequired = true): ControlConfig => [
|
|
||||||
value,
|
|
||||||
isRequired ? required() : [],
|
|
||||||
],
|
|
||||||
last_name: (value = '', isRequired = true): ControlConfig => [
|
last_name: (value = '', isRequired = true): ControlConfig => [
|
||||||
value,
|
value,
|
||||||
isRequired ? required() : [],
|
isRequired ? required() : [],
|
||||||
],
|
],
|
||||||
lastName: (value = '', isRequired = true): ControlConfig => [value, isRequired ? required() : []],
|
|
||||||
legal_name: (value = '', isRequired = true): ControlConfig => [
|
legal_name: (value = '', isRequired = true): ControlConfig => [
|
||||||
value,
|
value,
|
||||||
isRequired ? required() : [],
|
isRequired ? required() : [],
|
||||||
@@ -55,10 +50,6 @@ export const fieldControl = {
|
|||||||
value,
|
value,
|
||||||
isRequired ? [Validators.required, mobileValidator()] : [mobileValidator()],
|
isRequired ? [Validators.required, mobileValidator()] : [mobileValidator()],
|
||||||
],
|
],
|
||||||
mobile: (value = '', isRequired = true): ControlConfig => [
|
|
||||||
value,
|
|
||||||
isRequired ? [Validators.required, mobileValidator()] : [mobileValidator()],
|
|
||||||
],
|
|
||||||
email: (value = '', isRequired = false): ControlConfig => [
|
email: (value = '', isRequired = false): ControlConfig => [
|
||||||
value,
|
value,
|
||||||
isRequired ? [Validators.required, Validators.email] : [Validators.email],
|
isRequired ? [Validators.required, Validators.email] : [Validators.email],
|
||||||
@@ -85,7 +76,7 @@ export const fieldControl = {
|
|||||||
],
|
],
|
||||||
fiscal_id: (value = '', isRequired = true): ControlConfig => [
|
fiscal_id: (value = '', isRequired = true): ControlConfig => [
|
||||||
value,
|
value,
|
||||||
isRequired ? [Validators.required, fiscalCodeValidator()] : [fiscalCodeValidator()],
|
isRequired ? [Validators.required, fiscalIdValidator()] : [fiscalIdValidator()],
|
||||||
],
|
],
|
||||||
partner_token: (value = '', isRequired = true): ControlConfig => [
|
partner_token: (value = '', isRequired = true): ControlConfig => [
|
||||||
value,
|
value,
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
// TIS tenant environment configuration
|
// TIS tenant environment configuration
|
||||||
export const environment = {
|
export const environment = {
|
||||||
production: true,
|
production: true,
|
||||||
apiBaseUrl: 'https://psp-api.shift-am.ir',
|
// apiBaseUrl: 'https://psp-api.shift-am.ir',
|
||||||
// apiBaseUrl: 'http://192.168.128.73:5002',
|
apiBaseUrl: 'http://192.168.128.73:5002',
|
||||||
host: 'localhost',
|
host: 'localhost',
|
||||||
port: 5000,
|
port: 5000,
|
||||||
enableLogging: false,
|
enableLogging: false,
|
||||||
|
|||||||
Reference in New Issue
Block a user