add salesInvoice in pos of consumer domain

This commit is contained in:
2026-03-30 13:17:34 +03:30
parent c10623bc3f
commit 44097fe1ac
35 changed files with 855 additions and 122 deletions
@@ -24,23 +24,23 @@ export class CustomerIndividualFormComponent extends AbstractForm<
override showSuccessMessage = false;
form = this.fb.group({
first_name: [
this.store.customer().info?.customerIndividuals?.first_name || '',
this.store.customer().info?.customer_individual?.first_name || '',
[Validators.required],
],
last_name: [
this.store.customer().info?.customerIndividuals?.last_name || '',
this.store.customer().info?.customer_individual?.last_name || '',
[Validators.required],
],
national_id: [
this.store.customer().info?.customerIndividuals?.national_id || '',
this.store.customer().info?.customer_individual?.national_id || '',
[Validators.required, nationalIdValidator()],
],
economic_code: [
this.store.customer().info?.customerIndividuals?.economic_code || '',
this.store.customer().info?.customer_individual?.economic_code || '',
[Validators.required],
],
postal_code: [
this.store.customer().info?.customerIndividuals?.postal_code || '',
this.store.customer().info?.customer_individual?.postal_code || '',
[postalCodeValidator()],
],
});
@@ -52,7 +52,7 @@ export class CustomerIndividualFormComponent extends AbstractForm<
customer_id: '',
type: CustomerType.INDIVIDUAL,
info: {
customerIndividuals: info,
customer_individual: info,
},
});
return observer.next(info);
@@ -21,19 +21,19 @@ export class CustomerLegalFormComponent extends AbstractForm<ILegalCustomer, ILe
form = this.fb.group({
company_name: [
this.store.customer().info?.customerLegals?.company_name || '',
this.store.customer().info?.customer_legal?.company_name || '',
[Validators.required],
],
registration_number: [
this.store.customer().info?.customerLegals?.registration_number || '',
this.store.customer().info?.customer_legal?.registration_number || '',
[Validators.required],
],
economic_code: [
this.store.customer().info?.customerLegals?.economic_code || '',
this.store.customer().info?.customer_legal?.economic_code || '',
[Validators.required],
],
postal_code: [
this.store.customer().info?.customerLegals?.postal_code || '',
this.store.customer().info?.customer_legal?.postal_code || '',
[postalCodeValidator()],
],
});
@@ -45,7 +45,7 @@ export class CustomerLegalFormComponent extends AbstractForm<ILegalCustomer, ILe
customer_id: '',
type: CustomerType.LEGAL,
info: {
customerLegals: info,
customer_legal: info,
},
});
return observer.next(info);
@@ -20,10 +20,10 @@ export class CustomerUnknownFormComponent extends AbstractForm<IUnknownCustomer,
override showSuccessMessage = false;
form = this.fb.group({
national_id: [
this.store?.customer().info?.customerUnknown?.national_id || '',
this.store?.customer().info?.customer_unknown?.national_id || '',
[nationalIdValidator()],
],
name: [this.store?.customer().info?.customerUnknown?.name || ''],
name: [this.store?.customer().info?.customer_unknown?.name || ''],
});
override submitForm() {
@@ -33,7 +33,7 @@ export class CustomerUnknownFormComponent extends AbstractForm<IUnknownCustomer,
customer_id: '',
type: CustomerType.UNKNOWN,
info: {
customerUnknown: info,
customer_unknown: info,
},
});
return observer.next(info);
@@ -12,11 +12,15 @@
<div class="mt-2">
<span class="text-lg font-bold">
{{ good.name }}
<!-- <small class="text-xs text-muted-color">({{ good.quantity }})</small> -->
@if (!good.is_default_guild_good) {
<small class="text-xs text-muted-color">*</small>
}
</span>
<div class="flex items-center justify-between mt-1">
<div class="flex items-center justify-between mt-1 w-full px-2 my-2">
<!-- <span [appPriceMask]="good.salePrice" class="text-base font-bold"></span> -->
<button pButton type="button" icon="pi pi-plus" size="small" (click)="addProduct(good)"></button>
<button pButton type="button" icon="pi pi-plus" size="small" class="w-full!" (click)="addProduct(good)">
انتخاب
</button>
</div>
</div>
</div>
@@ -13,6 +13,9 @@
<div class="flex flex-col gap-2">
<span class="text-lg font-bold">
{{ good.name }}
@if (!good.is_default_guild_good) {
<small class="text-xs text-muted-color">*</small>
}
<!-- <small class="text-xs text-muted-color">({{ good.quantity }})</small> -->
</span>
<span class="text-sm text-muted-color">
@@ -22,7 +25,7 @@
</div>
<div class="shrink-0 flex items-center justify-between gap-3">
<!-- <span [appPriceMask]="good.salePrice" class="text-base font-bold"></span> -->
<button pButton type="button" icon="pi pi-plus" size="small" (click)="addGood(good)"></button>
<button pButton type="button" icon="pi pi-plus" size="small" (click)="addGood(good)">انتخاب</button>
</div>
</div>
}
@@ -1,10 +1,5 @@
<div class="bg-surface-card w-md shrink-0 h-full overflow-hidden rounded-xl p-4 pb-0 flex flex-col">
<div class="grow overflow-auto flex flex-col">
<div class="flex gap-2 items-center justify-between shrink-0">
<app-key-value label="مشتری" [value]="customerNameToShow()" />
<button pButton outlined icon="pi pi-pencil" size="small" (click)="openCustomerDialog()"></button>
</div>
<hr />
<div class="grow flex flex-col overflow-hidden">
<div class="flex items-center justify-between shrink-0">
<div class="flex items-center gap-2 text-muted-color">
<i class="pi pi-shopping-cart"></i>
@@ -20,7 +15,7 @@
<span class="text-lg text-muted-color">هیچ سفارشی ثبت نشده است.</span>
</div>
} @else {
<div class="flex flex-col gap-2 mt-3">
<div class="flex flex-col gap-2 mt-3 overflow-auto">
@for (inOrderGood of inOrderGoods(); track inOrderGood.id) {
@if (inOrderGood.good.pricing_model === "GOLD") {
<gold-payload-order-card [orderItem]="inOrderGood" />
@@ -31,20 +26,25 @@
</div>
}
</div>
<div class="flex flex-col sticky bottom-0 py-2">
<hr />
<div class="flex flex-col gap-4 sticky bottom-0 py-2">
<p-card class="border border-surface-border">
<div class="flex gap-2 items-center justify-between shrink-0">
<app-key-value label="مشتری" [value]="customerNameToShow()" />
<button pButton outlined icon="pi pi-pencil" size="small" (click)="openCustomerDialog()"></button>
</div>
</p-card>
<pos-order-price-info-card />
<div class="sticky bottom-0 pt-4">
<button
pButton
type="button"
label="ثبت و پرداخت"
severity="primary"
icon="pi pi-check"
class="w-full"
[attr.disabled]="inOrderGoods().length === 0 ? true : null"
(click)="submitAndPay()"
></button>
</div>
<button
pButton
type="button"
label="ثبت و پرداخت"
severity="primary"
icon="pi pi-check"
class="w-full"
[attr.disabled]="inOrderGoods().length === 0 ? true : null"
(click)="submitAndPay()"
></button>
</div>
</div>
@@ -4,6 +4,7 @@ import { KeyValueComponent } from '@/shared/components';
import { Component, computed, inject, signal } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { ButtonDirective } from 'primeng/button';
import { Card } from 'primeng/card';
import images from 'src/assets/images';
import { PosLandingStore } from '../../store/main.store';
import { PosOrderCustomerDialogComponent } from '../customers/customer-dialog.component';
@@ -24,6 +25,7 @@ import { POSOrderPriceInfoCardComponent } from './price-info-card.component';
PosOrderCustomerDialogComponent,
KeyValueComponent,
PosPaymentFormDialogComponent,
Card,
],
})
export class PosOrderSectionComponent {
@@ -44,13 +46,13 @@ export class PosOrderSectionComponent {
switch (type) {
case 'UNKNOWN':
customerNameToShow = `${info?.customerUnknown?.name || 'نامشخص'} (نوع دوم)`;
customerNameToShow = `${info?.customer_unknown?.name || 'نامشخص'} (نوع دوم)`;
break;
case 'INDIVIDUAL':
customerNameToShow = `${info?.customerIndividuals ? info?.customerIndividuals.first_name + ' ' + info?.customerIndividuals.last_name : 'نامشخص'} (نوع اول)`;
customerNameToShow = `${info?.customer_individual ? info?.customer_individual.first_name + ' ' + info?.customer_individual.last_name : 'نامشخص'} (نوع اول)`;
break;
case 'LEGAL':
customerNameToShow = `${info?.customerLegals?.company_name || 'نامشخص'} (نوع اول)`;
customerNameToShow = `${info?.customer_legal?.company_name || 'نامشخص'} (نوع اول)`;
break;
}
@@ -43,7 +43,6 @@
submitLabel="ثبت اطلاعات پرداخت و ادامه"
[loading]="submitOrderLoading()"
(onCancel)="close()"
(onSubmit)="submit()"
/>
</form>
</p-dialog>
@@ -6,7 +6,7 @@ import { Component, computed, inject, signal } from '@angular/core';
import { ReactiveFormsModule, Validators } from '@angular/forms';
import { ButtonDirective } from 'primeng/button';
import { Dialog } from 'primeng/dialog';
import { Observable } from 'rxjs';
import { catchError, throwError } from 'rxjs';
import { IPayment, TOrderPaymentTypes } from '../../models';
import { PosLandingStore } from '../../store/main.store';
@@ -90,6 +90,8 @@ export class PosPaymentFormDialogComponent extends AbstractFormDialog<IPayment,
}
}
override showSuccessMessage = false;
override submitForm() {
if (this.remainedAmount() > 0) {
return this.toastServices.warn({
@@ -100,16 +102,19 @@ export class PosPaymentFormDialogComponent extends AbstractFormDialog<IPayment,
const payment = this.form.value as IPayment;
this.store.setPayment(payment);
this.store.submitOrder().then((res) => {
if (res) {
this.store
.submitOrder()
.pipe(
catchError((err) => {
return throwError(() => err);
}),
)
.subscribe(() => {
this.close();
this.toastServices.success({
text: 'فاکتور شما با موفقیت ایجاد شد',
});
}
});
return new Observable<IPayment>((observer) => observer.next(payment));
});
}
override onSuccess(response: IPayment): void {}
@@ -20,9 +20,9 @@ export interface IUnknownCustomer {
}
export interface ICustomer {
customerIndividuals?: IIndividualCustomer;
customerLegals?: ILegalCustomer;
customerUnknown?: IUnknownCustomer;
customer_individual?: IIndividualCustomer;
customer_legal?: ILegalCustomer;
customer_unknown?: IUnknownCustomer;
}
export type TCustomerInfo = IIndividualCustomer | ILegalCustomer | IUnknownCustomer;
+1 -1
View File
@@ -10,7 +10,7 @@ export interface IPosOrderRequest {
items: IPosOrderItem[];
notes?: string;
customer_type?: CustomerType;
customerId?: string;
customer_id?: string;
customer?: ICustomer;
}
@@ -4,7 +4,7 @@ import { IGoodCategoryResponse, IGoodResponse } from '@/domains/pos/models/good.
import { CustomerType } from '@/shared/localEnum/constants/customerTypes';
import { createUUID, JALALI_DATE_FORMATS, nowJalali, parseJalali } from '@/utils';
import { computed, inject, Injectable, signal } from '@angular/core';
import { catchError, finalize, map, Observable, throwError } from 'rxjs';
import { catchError, finalize, map, Observable } from 'rxjs';
import { ICustomer, IPayment, IPosOrderRequest, IPosOrderResponse } from '../models';
import { IPosInOrderGood, IPosOrderItem } from '../models/types';
import { PosService } from '../services/main.service';
@@ -208,10 +208,10 @@ export class PosLandingStore {
this.setState({ payments });
}
async submitOrder() {
submitOrder() {
this.setState({ submitOrderLoading: true });
const orderPayload: IPosOrderRequest = {
customerId: this.customer().customer_id,
customer_id: this.customer().customer_id,
customer_type: this.customer().type,
customer: this.customer().info,
items: this.inOrderGoods().map(({ good, ...rest }) => ({
@@ -226,24 +226,30 @@ export class PosLandingStore {
let res: Maybe<IPosOrderResponse> = null;
await this.service
.submitOrder(orderPayload)
.pipe(
finalize(() => {
this.setState({ submitOrderLoading: false });
}),
catchError(() => {
return throwError('');
}),
)
.subscribe((_res) => {
this.reset();
res = _res;
});
return this.service.submitOrder(orderPayload).pipe(
finalize(() => {
this.setState({ submitOrderLoading: false });
}),
catchError(() => {
return new Observable((observer) => observer.error());
}),
if (res) {
return new Observable<IPosOrderResponse>((observer) => observer.next(res!));
}
return new Observable((observer) => observer.error());
map((_res) => {
// this.setState({
// inOrderGoods: [],
// customerDetails: {
// type: CustomerType.UNKNOWN,
// },
// invoiceDate: nowJalali(JALALI_DATE_FORMATS.NUMERIC),
// payments: {
// cash: 0,
// set_off: 0,
// terminal: 0,
// },
// orderNote: '',
// });
return _res;
}),
);
}
}