feat: enhance invoice correction form with item editing and price info display
This commit is contained in:
@@ -1,5 +1,4 @@
|
||||
<div class="p-4">
|
||||
<shared-sale-invoice-single-view
|
||||
<shared-sale-invoice-single-view
|
||||
[loading]="loading()"
|
||||
[invoice]="invoice()"
|
||||
[backRoute]="backRoute()"
|
||||
@@ -10,4 +9,3 @@
|
||||
(onCorrection)="correction($event)"
|
||||
(onReturnFromSale)="returnFromSale($event)"
|
||||
variant="pos" />
|
||||
</div>
|
||||
|
||||
@@ -1,8 +1,15 @@
|
||||
import { PriceMaskDirective } from '@/shared/directives';
|
||||
import { Component, computed, inject } from '@angular/core';
|
||||
import { Component, Input, inject } from '@angular/core';
|
||||
import { Card } from 'primeng/card';
|
||||
import { PosLandingStore } from '../../store/main.store';
|
||||
|
||||
type TOrderPriceInfo = {
|
||||
totalBaseAmount: number;
|
||||
discountAmount: number;
|
||||
taxAmount: number;
|
||||
totalAmount: number;
|
||||
};
|
||||
|
||||
@Component({
|
||||
selector: 'pos-order-price-info-card',
|
||||
templateUrl: './price-info-card.component.html',
|
||||
@@ -10,6 +17,12 @@ import { PosLandingStore } from '../../store/main.store';
|
||||
})
|
||||
export class POSOrderPriceInfoCardComponent {
|
||||
private readonly store = inject(PosLandingStore);
|
||||
@Input() info?: TOrderPriceInfo;
|
||||
|
||||
priceInfo = computed(() => this.store.orderPricingInfo());
|
||||
priceInfo(): TOrderPriceInfo {
|
||||
if (this.info) {
|
||||
return this.info;
|
||||
}
|
||||
return this.store.orderPricingInfo();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<div class="light-bottomsheet-mask" (click)="onMaskClick()"></div>
|
||||
@if (rendered) {
|
||||
<div class="light-bottomsheet-mask" (click)="onMaskClick()"></div>
|
||||
|
||||
<section class="light-bottomsheet-panel" [ngStyle]="style || { 'max-height': mobileDrawerHeight, height: 'auto' }">
|
||||
<section class="light-bottomsheet-panel" [ngStyle]="style || { 'max-height': mobileDrawerHeight, height: 'auto' }">
|
||||
<div class="light-bottomsheet-content">
|
||||
@if (header || closable) {
|
||||
<header class="light-bottomsheet-header border-surface-border border-b">
|
||||
@@ -22,4 +23,5 @@
|
||||
<ng-content></ng-content>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</section>
|
||||
}
|
||||
|
||||
@@ -90,8 +90,8 @@ import { Button } from 'primeng/button';
|
||||
'[attr.pfocustrap]': 'true',
|
||||
'[attr.data-pc-name]': "'drawer'",
|
||||
'[attr.data-pc-section]': "'root'",
|
||||
'[attr.aria-hidden]': '!visible',
|
||||
'[style.display]': 'visible ? "block" : "none"',
|
||||
'[attr.aria-hidden]': '!rendered',
|
||||
'[style.display]': 'rendered ? "block" : "none"',
|
||||
'[style.--sheet-transition]': 'transitionOptions',
|
||||
},
|
||||
})
|
||||
@@ -105,8 +105,11 @@ export class SharedLightBottomsheetComponent implements OnInit, OnDestroy, OnCha
|
||||
@Input() style: Record<string, string> | undefined;
|
||||
@Input() mobileDrawerHeight = '90vh';
|
||||
@Input() transitionOptions = '130ms cubic-bezier(0.2, 0, 0, 1)';
|
||||
@Input() closeUnmountDelay = 150;
|
||||
|
||||
@Output() onHide = new EventEmitter<any>();
|
||||
rendered = false;
|
||||
private closeTimer: ReturnType<typeof setTimeout> | null = null;
|
||||
|
||||
private originalParent: Node | null = null;
|
||||
private readonly defaultDrawerZIndex = 1102;
|
||||
@@ -123,21 +126,27 @@ export class SharedLightBottomsheetComponent implements OnInit, OnDestroy, OnCha
|
||||
const host = this.elementRef.nativeElement;
|
||||
this.originalParent = host.parentNode;
|
||||
this.renderer.appendChild(this.document.body, host);
|
||||
this.rendered = this.visible;
|
||||
this.syncZIndex();
|
||||
this.toggleBodyScrollLock(this.visible);
|
||||
}
|
||||
|
||||
ngOnChanges(changes: SimpleChanges) {
|
||||
if (changes['visible'] && this.visible) {
|
||||
this.clearCloseTimer();
|
||||
this.rendered = true;
|
||||
this.syncZIndex();
|
||||
}
|
||||
if (changes['visible']) {
|
||||
|
||||
this.toggleBodyScrollLock(this.visible);
|
||||
if (!this.visible) {
|
||||
this.scheduleUnmount();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this.clearCloseTimer();
|
||||
this.toggleBodyScrollLock(false);
|
||||
this.removeDrawer();
|
||||
}
|
||||
@@ -154,9 +163,12 @@ export class SharedLightBottomsheetComponent implements OnInit, OnDestroy, OnCha
|
||||
this.visibleChange.emit(nextValue);
|
||||
this.toggleBodyScrollLock(nextValue);
|
||||
if (nextValue) {
|
||||
this.clearCloseTimer();
|
||||
this.rendered = true;
|
||||
this.syncZIndex();
|
||||
}
|
||||
if (!nextValue) {
|
||||
this.scheduleUnmount();
|
||||
this.onHide.emit();
|
||||
}
|
||||
}
|
||||
@@ -203,4 +215,19 @@ export class SharedLightBottomsheetComponent implements OnInit, OnDestroy, OnCha
|
||||
: this.defaultDrawerZIndex;
|
||||
this.renderer.setStyle(host, 'z-index', `${maxDrawerZIndex + 1}`);
|
||||
}
|
||||
|
||||
private scheduleUnmount() {
|
||||
this.clearCloseTimer();
|
||||
this.closeTimer = setTimeout(() => {
|
||||
if (!this.visible) {
|
||||
this.rendered = false;
|
||||
}
|
||||
}, this.closeUnmountDelay);
|
||||
}
|
||||
|
||||
private clearCloseTimer() {
|
||||
if (!this.closeTimer) return;
|
||||
clearTimeout(this.closeTimer);
|
||||
this.closeTimer = null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<button
|
||||
pButton
|
||||
[label]="submitLabel"
|
||||
type="submit"
|
||||
[type]="submitBtnType"
|
||||
class="min-w-28"
|
||||
[disabled]="disabled"
|
||||
[loading]="loading"
|
||||
|
||||
@@ -11,6 +11,7 @@ export class FormFooterActionsComponent {
|
||||
@Input() cancelLabel = 'انصراف';
|
||||
@Input() loading = false;
|
||||
@Input() disabled = false;
|
||||
@Input() submitBtnType: 'button' | 'submit' = 'submit';
|
||||
@Output() onSubmit = new EventEmitter<void>();
|
||||
@Output() onCancel = new EventEmitter<void>();
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<div class="flex items-center p-4">
|
||||
<div class="bg-surface-card flex items-center p-4">
|
||||
<div class="grow">
|
||||
<div class="flex items-center gap-2">
|
||||
@if (backRoute) {
|
||||
|
||||
@@ -1,28 +1,78 @@
|
||||
<form [formGroup]="form" (ngSubmit)="submit()">
|
||||
<field-invoice-date [control]="form.controls.invoice_date" />
|
||||
|
||||
<div class="mt-4 flex flex-col gap-3">
|
||||
@for (item of initialValues || []; track item.id) {
|
||||
<div class="py-3">
|
||||
@if ($index) {
|
||||
<hr class="h-1 w-full" />
|
||||
}
|
||||
<p-divider align="right">
|
||||
{{ $index + 1 }}- <b>{{ item.good_snapshot.name }}</b>
|
||||
</p-divider>
|
||||
@if (isGold(item)) {
|
||||
<shared-gold-payload-form
|
||||
[initialValues]="$any(mappedInitialItems[$index])"
|
||||
[vatPercentage]="vatFromItem(item)"
|
||||
[isCorrection]="true" />
|
||||
<div class="border-surface-border rounded-xl border p-2 shadow-sm">
|
||||
<div class="flex items-center gap-3">
|
||||
<img
|
||||
[src]="item.good_snapshot.image_url || ''"
|
||||
loading="lazy"
|
||||
decoding="async"
|
||||
class="bg-surface-100 border-surface-border h-20 w-20 shrink-0 rounded-md border object-cover" />
|
||||
<div class="flex grow flex-col gap-2 overflow-hidden py-1">
|
||||
<div class="flex items-center justify-between gap-2">
|
||||
<span class="overflow-hidden text-base font-bold text-nowrap text-ellipsis">
|
||||
{{ item.good_snapshot.name }}
|
||||
</span>
|
||||
<button
|
||||
pButton
|
||||
type="button"
|
||||
icon="pi pi-pencil"
|
||||
size="small"
|
||||
outlined
|
||||
(click)="openItemEditor($index)"></button>
|
||||
</div>
|
||||
@let preview = getItemPreview($index);
|
||||
<span class="text-sm">{{ item.quantity }} {{ item.measure_unit_text }}</span>
|
||||
<div class="flex flex-col">
|
||||
@if (!preview.discount_amount) {
|
||||
<span [appPriceMask]="preview.base_total_amount"></span>
|
||||
} @else {
|
||||
<shared-standard-payload-form
|
||||
[initialValues]="$any(mappedInitialItems[$index])"
|
||||
[measureUnit]="item.good_snapshot.measure_unit"
|
||||
[vatPercentage]="vatFromItem(item)"
|
||||
[isCorrection]="true" />
|
||||
}
|
||||
<div class="flex flex-col items-end">
|
||||
<span class="text-muted-color text-xs line-through" [appPriceMask]="preview.base_total_amount"></span>
|
||||
<span [appPriceMask]="preview.base_total_amount - preview.discount_amount"></span>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
<pos-order-price-info-card [info]="getTotalPriceInfo()" />
|
||||
|
||||
<app-form-footer-actions />
|
||||
</form>
|
||||
|
||||
<shared-light-bottomsheet
|
||||
[(visible)]="showItemEditSheet"
|
||||
[header]="`ویرایش ${activeItem()?.good_snapshot?.name || ''}`">
|
||||
@if (activeItem()) {
|
||||
<div class="pt-4">
|
||||
@if (isGold(activeItem()!)) {
|
||||
<shared-gold-payload-form
|
||||
#goldEditor
|
||||
[initialValues]="$any(activeInitialValue())"
|
||||
[vatPercentage]="vatFromItem(activeItem()!)"
|
||||
[isCorrection]="true" />
|
||||
<div class="mt-6 flex justify-end gap-2">
|
||||
<button pButton type="button" severity="secondary" (click)="backToList()">انصراف</button>
|
||||
<button pButton type="button" (click)="saveActiveItemDraft(goldEditor)">ذخیره تغییرات</button>
|
||||
</div>
|
||||
} @else {
|
||||
<shared-standard-payload-form
|
||||
#standardEditor
|
||||
[initialValues]="$any(activeInitialValue())"
|
||||
[measureUnit]="activeItem()!.good_snapshot.measure_unit"
|
||||
[vatPercentage]="vatFromItem(activeItem()!)"
|
||||
[isCorrection]="true" />
|
||||
<div class="mt-6 flex justify-end gap-2">
|
||||
<button pButton type="button" severity="secondary" (click)="backToList()">انصراف</button>
|
||||
<button pButton type="button" (click)="saveActiveItemDraft(undefined, standardEditor)">ذخیره تغییرات</button>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
</shared-light-bottomsheet>
|
||||
|
||||
@@ -1,16 +1,19 @@
|
||||
import ISummary from '@/core/models/summary';
|
||||
import { POSOrderPriceInfoCardComponent } from '@/domains/pos/modules/shop/components/order/price-info-card.component';
|
||||
import { IPosOrderItem } from '@/domains/pos/modules/shop/models';
|
||||
import { AbstractForm } from '@/shared/abstractClasses';
|
||||
import {
|
||||
SharedGoldPayloadFormComponent,
|
||||
SharedStandardPayloadFormComponent,
|
||||
} from '@/shared/components';
|
||||
import { PriceMaskDirective } from '@/shared/directives';
|
||||
import { goldDiscountType } from '@/shared/localEnum/constants/goldDiscountType';
|
||||
import { IGoldPayload, IStandardPayload } from '@/shared/models';
|
||||
import { formatDate, GREGORIAN_DATE_FORMATS } from '@/utils';
|
||||
import { Component, Input, QueryList, SimpleChanges, ViewChildren } from '@angular/core';
|
||||
import { Component, Input, signal, SimpleChanges } from '@angular/core';
|
||||
import { ReactiveFormsModule } from '@angular/forms';
|
||||
import { Divider } from 'primeng/divider';
|
||||
import { ButtonDirective } from 'primeng/button';
|
||||
import { SharedLightBottomsheetComponent } from '../../dialog';
|
||||
import { FieldInvoiceDateComponent } from '../../fields/invoice_date.component';
|
||||
import { FormFooterActionsComponent } from '../../formFooterActions/form-footer-actions.component';
|
||||
import { CorrectionInvoiceFormValue, TCorrectionItemPayload } from '../models';
|
||||
@@ -25,7 +28,10 @@ import { IInvoiceItem } from '../sale-invoice-full-response.model';
|
||||
FormFooterActionsComponent,
|
||||
SharedGoldPayloadFormComponent,
|
||||
SharedStandardPayloadFormComponent,
|
||||
Divider,
|
||||
ButtonDirective,
|
||||
SharedLightBottomsheetComponent,
|
||||
POSOrderPriceInfoCardComponent,
|
||||
PriceMaskDirective,
|
||||
],
|
||||
})
|
||||
export class SharedCorrectionFormComponent extends AbstractForm<
|
||||
@@ -34,11 +40,7 @@ export class SharedCorrectionFormComponent extends AbstractForm<
|
||||
IInvoiceItem[]
|
||||
> {
|
||||
@Input({ required: true }) invoiceDate!: string;
|
||||
|
||||
@ViewChildren(SharedGoldPayloadFormComponent)
|
||||
goldForms!: QueryList<SharedGoldPayloadFormComponent>;
|
||||
@ViewChildren(SharedStandardPayloadFormComponent)
|
||||
standardForms!: QueryList<SharedStandardPayloadFormComponent>;
|
||||
@Input() visible = false;
|
||||
|
||||
form = this.fb.group({
|
||||
invoice_date: [this.invoiceDate],
|
||||
@@ -46,6 +48,8 @@ export class SharedCorrectionFormComponent extends AbstractForm<
|
||||
|
||||
itemDrafts: Record<string, TCorrectionItemPayload> = {};
|
||||
mappedInitialItems: TCorrectionItemPayload[] = [];
|
||||
showItemEditSheet = signal(false);
|
||||
activeItemIndex = signal<number | null>(null);
|
||||
|
||||
initForm() {
|
||||
this.form.controls.invoice_date.setValue(
|
||||
@@ -53,6 +57,8 @@ export class SharedCorrectionFormComponent extends AbstractForm<
|
||||
);
|
||||
this.itemDrafts = {};
|
||||
this.mappedInitialItems = (this.initialValues || []).map((item) => this.mapToInitialItem(item));
|
||||
this.showItemEditSheet.set(false);
|
||||
this.activeItemIndex.set(null);
|
||||
}
|
||||
|
||||
override ngOnInit(): void {
|
||||
@@ -60,6 +66,11 @@ export class SharedCorrectionFormComponent extends AbstractForm<
|
||||
}
|
||||
|
||||
override ngOnChanges(changes: SimpleChanges) {
|
||||
if (changes['visible'] && !this.visible) {
|
||||
this.initForm();
|
||||
return;
|
||||
}
|
||||
|
||||
if (changes['initialValues'] || changes['invoiceDate']) {
|
||||
this.initForm();
|
||||
}
|
||||
@@ -115,23 +126,88 @@ export class SharedCorrectionFormComponent extends AbstractForm<
|
||||
return this.mapToStandardInitialItem(item) as TCorrectionItemPayload;
|
||||
}
|
||||
|
||||
override submitForm() {
|
||||
const goldQueue = [...(this.goldForms?.toArray() || [])];
|
||||
const standardQueue = [...(this.standardForms?.toArray() || [])];
|
||||
const items =
|
||||
this.initialValues?.map((item, index) => {
|
||||
openItemEditor(index: number) {
|
||||
this.activeItemIndex.set(index);
|
||||
this.showItemEditSheet.set(true);
|
||||
}
|
||||
|
||||
backToList() {
|
||||
this.showItemEditSheet.set(false);
|
||||
this.activeItemIndex.set(null);
|
||||
}
|
||||
|
||||
activeItem() {
|
||||
const index = this.activeItemIndex();
|
||||
if (index === null) return null;
|
||||
return this.initialValues?.[index] || null;
|
||||
}
|
||||
|
||||
activeInitialValue() {
|
||||
const item = this.activeItem();
|
||||
const index = this.activeItemIndex();
|
||||
if (!item || index === null) return null;
|
||||
return this.itemDrafts[item.id] || this.mappedInitialItems[index] || null;
|
||||
}
|
||||
|
||||
getItemPreview(index: number) {
|
||||
const item = this.initialValues?.[index];
|
||||
return this.itemDrafts[item!.id] || this.mappedInitialItems[index] || null;
|
||||
}
|
||||
|
||||
getTotalPriceInfo() {
|
||||
const previews = (this.initialValues || [])
|
||||
.map((_, index) => this.getItemPreview(index))
|
||||
.filter(Boolean);
|
||||
|
||||
console.log('previews', previews);
|
||||
|
||||
return previews.reduce(
|
||||
(acc, item: any) => {
|
||||
acc.totalBaseAmount += Number(item.base_total_amount || 0);
|
||||
acc.discountAmount += Number(item.discount_amount || 0);
|
||||
acc.taxAmount += Number(item.tax_amount || 0);
|
||||
acc.totalAmount += Number(item.total_amount || 0);
|
||||
return acc;
|
||||
},
|
||||
{
|
||||
totalBaseAmount: 0,
|
||||
discountAmount: 0,
|
||||
taxAmount: 0,
|
||||
totalAmount: 0,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
saveActiveItemDraft(
|
||||
goldForm?: SharedGoldPayloadFormComponent,
|
||||
standardForm?: SharedStandardPayloadFormComponent
|
||||
) {
|
||||
const item = this.activeItem();
|
||||
const index = this.activeItemIndex();
|
||||
if (!item || index === null) return;
|
||||
|
||||
const current = this.isGold(item)
|
||||
? goldQueue.shift()?.getCorrectionValue()
|
||||
: standardQueue.shift()?.getCorrectionValue();
|
||||
const merged = (current || this.mappedInitialItems[index]) as TCorrectionItemPayload;
|
||||
return {
|
||||
...merged,
|
||||
? goldForm?.getCorrectionValue()
|
||||
: standardForm?.getCorrectionValue();
|
||||
|
||||
if (!current) {
|
||||
this.toastService.warn({ text: 'اطلاعات آیتم برای ذخیره در دسترس نیست.' });
|
||||
return;
|
||||
}
|
||||
|
||||
this.itemDrafts[item.id] = {
|
||||
...(current as TCorrectionItemPayload),
|
||||
id: item.id,
|
||||
pricing_model: item.good_snapshot?.pricing_model || '',
|
||||
} as TCorrectionItemPayload;
|
||||
}) || [];
|
||||
};
|
||||
this.backToList();
|
||||
}
|
||||
|
||||
console.log('items', items);
|
||||
override submitForm() {
|
||||
const items =
|
||||
this.initialValues?.map(
|
||||
(item, index) => this.itemDrafts[item.id] || this.mappedInitialItems[index]
|
||||
) || [];
|
||||
|
||||
const hasChanges = (this.initialValues || []).some((item, index) => {
|
||||
const source = this.mappedInitialItems[index];
|
||||
|
||||
@@ -3,20 +3,19 @@
|
||||
} @else if (!invoice) {
|
||||
<uikit-empty-state> </uikit-empty-state>
|
||||
} @else {
|
||||
<shared-light-bottomsheet [visible]="actionLoading" [closable]="false" header="لطفا صبر کنید">
|
||||
<p-dialog [visible]="actionLoading" [closable]="false" header="لطفا صبر کنید">
|
||||
<div class="flex h-[30svh] flex-col items-center justify-center gap-6">
|
||||
<p-progressSpinner />
|
||||
<span class="text-lg font-bold">در حال ارسال درخواست شما...</span>
|
||||
</div>
|
||||
</shared-light-bottomsheet>
|
||||
<div class="flex flex-col gap-6">
|
||||
<app-card-data cardTitle="اطلاعات صورتحساب" [editable]="false" [backRoute]="backRoute">
|
||||
<ng-template #moreActions>
|
||||
<div class="">
|
||||
<p-menu #menu [model]="moreActionMenuItems()" [popup]="true" appendTo="body" />
|
||||
<p-button (click)="menu.toggle($event)" icon="pi pi-ellipsis-v" outlined size="small" />
|
||||
</div>
|
||||
</p-dialog>
|
||||
<app-inner-pages-header pageTitle="اطلاعات صورتحساب" [backRoute]="backRoute">
|
||||
<ng-template #actions>
|
||||
<p-button (click)="printInvoice()" icon="pi pi-print" outlined size="small" label="چاپ" />
|
||||
</ng-template>
|
||||
</app-inner-pages-header>
|
||||
<div class="p-4">
|
||||
<p-card>
|
||||
<div class="flex flex-col gap-4">
|
||||
<div class="listKeyValue">
|
||||
<app-key-value label="شماره صورتحساب" [value]="invoice.invoice_number" />
|
||||
@@ -93,9 +92,7 @@
|
||||
<p class="text-text-color pt-3 pb-5 text-center">اطلاعات مشتری ثبت نشده است.</p>
|
||||
}
|
||||
}
|
||||
</div>
|
||||
</app-card-data>
|
||||
<app-card-data cardTitle="کالاهای خریداری شده" [editable]="false">
|
||||
<p-divider align="center"> کالاهای خریداری شده </p-divider>
|
||||
<app-page-data-list
|
||||
[columns]="columns"
|
||||
[items]="invoice.items"
|
||||
@@ -108,8 +105,26 @@
|
||||
}
|
||||
</ng-template>
|
||||
</app-page-data-list>
|
||||
</app-card-data>
|
||||
</div>
|
||||
</p-card>
|
||||
</div>
|
||||
|
||||
@if (moreActionMenuItems().length) {
|
||||
<div class="border-surface-border bg-surface-card sticky bottom-0 z-10 mt-4 w-full rounded-t-2xl border-t p-3">
|
||||
<div class="flex flex-wrap justify-center gap-2">
|
||||
@for (action of moreActionMenuItems(); track action.label || $index) {
|
||||
<button
|
||||
pButton
|
||||
[label]="action.label || ''"
|
||||
[icon]="action.icon || ''"
|
||||
size="small"
|
||||
[severity]="$any(action).severity || 'secondary'"
|
||||
[outlined]="$any(action).severity === 'secondary'"
|
||||
(click)="action.command()"></button>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
<shared-light-bottomsheet [(visible)]="showReturnFromSaleForm" header=" برگشت از فروش">
|
||||
<shared-return-form
|
||||
@@ -119,6 +134,7 @@
|
||||
</shared-light-bottomsheet>
|
||||
<shared-light-bottomsheet [(visible)]="showCorrectionForm" header="اصلاحی">
|
||||
<shared-correction-form
|
||||
[visible]="showCorrectionForm()"
|
||||
[initialValues]="invoice.items"
|
||||
[invoiceDate]="invoice.invoice_date"
|
||||
(onSubmit)="correctionSubmit($event)" />
|
||||
|
||||
@@ -11,11 +11,7 @@ import {
|
||||
TspProviderResponseStatus,
|
||||
} from '@/shared/catalog';
|
||||
import { CatalogInvoiceSettlementTypeTagComponent } from '@/shared/catalog/invoiceSettlementType';
|
||||
import {
|
||||
AppCardComponent,
|
||||
KeyValueComponent,
|
||||
SharedLightBottomsheetComponent,
|
||||
} from '@/shared/components';
|
||||
import { KeyValueComponent, SharedLightBottomsheetComponent } from '@/shared/components';
|
||||
import { ISaleInvoiceFullResponse } from '@/shared/components/invoices/sale-invoice-full-response.model';
|
||||
import { PageLoadingComponent } from '@/shared/components/page-loading.component';
|
||||
import {
|
||||
@@ -38,25 +34,32 @@ import {
|
||||
ViewChild,
|
||||
} from '@angular/core';
|
||||
import { UrlTree } from '@angular/router';
|
||||
import { MenuItem } from 'primeng/api';
|
||||
import { Button } from 'primeng/button';
|
||||
import { Button, ButtonDirective } from 'primeng/button';
|
||||
import { Card } from 'primeng/card';
|
||||
import { Dialog } from 'primeng/dialog';
|
||||
import { Divider } from 'primeng/divider';
|
||||
import { Menu } from 'primeng/menu';
|
||||
import { ProgressSpinner } from 'primeng/progressspinner';
|
||||
import { TableModule } from 'primeng/table';
|
||||
import { Observable } from 'rxjs';
|
||||
import { AppConfirmationService } from '../confirmationDialog/confirmation-dialog.service';
|
||||
import { InnerPagesHeaderComponent } from '../innerPagesHeader/inner-pages-header.component';
|
||||
import { SharedCorrectionFormComponent } from './correctionForm';
|
||||
import { CorrectionInvoiceFormValue, ICorrectionRequest, ReturnFromSaleFormValue } from './models';
|
||||
import { SharedReturnFormComponent } from './returnForm/form.component';
|
||||
|
||||
export type TSaleInvoiceSingleViewVariant = 'full' | 'customer' | 'pos';
|
||||
type TActionMenuItem = {
|
||||
label: string;
|
||||
icon: string;
|
||||
command: () => void;
|
||||
neededStatus?: TspProviderResponseStatus;
|
||||
severity?: 'secondary' | 'info' | 'warn' | 'danger' | 'contrast';
|
||||
};
|
||||
|
||||
@Component({
|
||||
selector: 'shared-sale-invoice-single-view',
|
||||
templateUrl: './sale-invoice-single-view.component.html',
|
||||
imports: [
|
||||
AppCardComponent,
|
||||
KeyValueComponent,
|
||||
Divider,
|
||||
PageDataListComponent,
|
||||
@@ -66,13 +69,16 @@ export type TSaleInvoiceSingleViewVariant = 'full' | 'customer' | 'pos';
|
||||
PriceMaskDirective,
|
||||
CatalogTaxProviderStatusTagComponent,
|
||||
CatalogInvoiceTypeTagComponent,
|
||||
Menu,
|
||||
Button,
|
||||
CatalogInvoiceSettlementTypeTagComponent,
|
||||
SharedLightBottomsheetComponent,
|
||||
ProgressSpinner,
|
||||
SharedReturnFormComponent,
|
||||
SharedCorrectionFormComponent,
|
||||
Dialog,
|
||||
ButtonDirective,
|
||||
InnerPagesHeaderComponent,
|
||||
Card,
|
||||
],
|
||||
})
|
||||
export class SharedSaleInvoiceSingleViewComponent {
|
||||
@@ -100,7 +106,7 @@ export class SharedSaleInvoiceSingleViewComponent {
|
||||
|
||||
@ViewChild('totalAmount', { static: false }) totalAmount!: TemplateRef<any>;
|
||||
|
||||
moreActionMenuItems = signal<MenuItem[]>([]);
|
||||
moreActionMenuItems = signal<TActionMenuItem[]>([]);
|
||||
showCorrectionForm = signal(false);
|
||||
showReturnFromSaleForm = signal(false);
|
||||
|
||||
@@ -115,26 +121,85 @@ export class SharedSaleInvoiceSingleViewComponent {
|
||||
this.inquiry = this.inquiry.bind(this);
|
||||
}
|
||||
|
||||
showErrors = () => {};
|
||||
inquiry = () => {
|
||||
this.onInquiry.emit();
|
||||
};
|
||||
send = () => {
|
||||
this.onSendToTsp.emit();
|
||||
};
|
||||
sendCorrection = (event: ICorrectionRequest) => {
|
||||
this.onCorrection.emit(event);
|
||||
};
|
||||
resend = () => {
|
||||
this.onResendToTsp.emit();
|
||||
};
|
||||
sendReturnFromSale = (event: IPosReturnFromSaleRequest) => {
|
||||
this.onReturnFromSale.emit(event);
|
||||
};
|
||||
showCorrection = () => {
|
||||
this.showCorrectionForm.set(true);
|
||||
};
|
||||
showReturnFromSale = () => {
|
||||
this.showReturnFromSaleForm.set(true);
|
||||
};
|
||||
|
||||
private readonly actions: TActionMenuItem[] = [
|
||||
{
|
||||
label: 'ارسال مجدد صورتحساب',
|
||||
icon: 'pi pi-send',
|
||||
neededStatus: TspProviderResponseStatus.SEND_FAILURE,
|
||||
severity: 'info',
|
||||
command: this.resend,
|
||||
},
|
||||
{
|
||||
label: 'ارسال صورتحساب',
|
||||
icon: 'pi pi-send',
|
||||
neededStatus: TspProviderResponseStatus.NOT_SEND,
|
||||
severity: 'info',
|
||||
command: this.send,
|
||||
},
|
||||
{
|
||||
label: 'استعلام وضعیت',
|
||||
icon: 'pi pi-info',
|
||||
neededStatus: TspProviderResponseStatus.FISCAL_QUEUED,
|
||||
severity: 'info',
|
||||
command: this.inquiry,
|
||||
},
|
||||
{
|
||||
label: 'دلایل خطا',
|
||||
icon: 'pi pi-question',
|
||||
neededStatus: TspProviderResponseStatus.FAILURE,
|
||||
severity: 'danger',
|
||||
command: this.showErrors,
|
||||
},
|
||||
{
|
||||
label: 'ابطال',
|
||||
icon: 'pi pi-eraser',
|
||||
neededStatus: TspProviderResponseStatus.SUCCESS,
|
||||
severity: 'danger',
|
||||
command: this.showRevokeConfirmation,
|
||||
},
|
||||
{
|
||||
label: 'اصلاح',
|
||||
icon: 'pi pi-file-edit',
|
||||
neededStatus: TspProviderResponseStatus.SUCCESS,
|
||||
severity: 'warn',
|
||||
command: this.showCorrection,
|
||||
},
|
||||
{
|
||||
label: 'برگشت از فروش',
|
||||
icon: 'pi pi-cart-minus',
|
||||
neededStatus: TspProviderResponseStatus.SUCCESS,
|
||||
severity: 'warn',
|
||||
command: this.showReturnFromSale,
|
||||
},
|
||||
];
|
||||
|
||||
get actionLoading() {
|
||||
return this.sendToTspLoading || this.inquiryLoading || this.resendToTspLoading;
|
||||
}
|
||||
|
||||
showErrors() {}
|
||||
inquiry() {
|
||||
this.onInquiry.emit();
|
||||
}
|
||||
send() {
|
||||
this.onSendToTsp.emit();
|
||||
}
|
||||
sendCorrection(event: ICorrectionRequest) {
|
||||
this.onCorrection.emit(event);
|
||||
}
|
||||
resend() {
|
||||
this.onResendToTsp.emit();
|
||||
}
|
||||
sendReturnFromSale(event: IPosReturnFromSaleRequest) {
|
||||
this.onReturnFromSale.emit(event);
|
||||
}
|
||||
async showRevokeConfirmation() {
|
||||
await this.confirmationService.ask({
|
||||
header: `ابطال صورتحساب شماره ${this.invoice!.invoice_number}`,
|
||||
@@ -156,72 +221,13 @@ export class SharedSaleInvoiceSingleViewComponent {
|
||||
},
|
||||
});
|
||||
}
|
||||
showCorrection() {
|
||||
this.showCorrectionForm.set(true);
|
||||
}
|
||||
showReturnFromSale() {
|
||||
this.showReturnFromSaleForm.set(true);
|
||||
}
|
||||
|
||||
ngOnChanges(changes: SimpleChanges) {
|
||||
if (changes['invoice'] && this.invoice) {
|
||||
const invoiceStatus = this.invoice.status.value;
|
||||
const actions: MenuItem[] = [
|
||||
{
|
||||
label: 'ارسال مجدد صورتحساب',
|
||||
icon: 'pi pi-send',
|
||||
neededStatus: TspProviderResponseStatus.SEND_FAILURE,
|
||||
command: this.resend,
|
||||
},
|
||||
{
|
||||
label: 'ارسال صورتحساب',
|
||||
icon: 'pi pi-send',
|
||||
neededStatus: TspProviderResponseStatus.NOT_SEND,
|
||||
command: this.send,
|
||||
},
|
||||
{
|
||||
label: 'استعلام وضعیت',
|
||||
icon: 'pi pi-info',
|
||||
neededStatus: TspProviderResponseStatus.FISCAL_QUEUED,
|
||||
command: this.inquiry,
|
||||
},
|
||||
{
|
||||
label: 'دلایل خطا',
|
||||
icon: 'pi pi-question',
|
||||
neededStatus: TspProviderResponseStatus.FAILURE,
|
||||
command: this.showErrors,
|
||||
},
|
||||
{
|
||||
label: 'ابطال',
|
||||
icon: 'pi pi-eraser',
|
||||
neededStatus: TspProviderResponseStatus.SUCCESS,
|
||||
command: this.showRevokeConfirmation,
|
||||
},
|
||||
{
|
||||
label: 'اصلاح',
|
||||
icon: 'pi pi-file-edit',
|
||||
neededStatus: TspProviderResponseStatus.SUCCESS,
|
||||
command: this.showCorrection,
|
||||
},
|
||||
{
|
||||
label: 'برگشت از فروش',
|
||||
icon: 'pi pi-cart-minus',
|
||||
neededStatus: TspProviderResponseStatus.SUCCESS,
|
||||
command: this.showReturnFromSale,
|
||||
},
|
||||
{
|
||||
label: 'چاپ',
|
||||
icon: 'pi pi-print',
|
||||
neededStatus: '',
|
||||
command: this.printInvoice,
|
||||
},
|
||||
];
|
||||
|
||||
this.moreActionMenuItems.set(
|
||||
actions.filter(
|
||||
// @ts-ignore
|
||||
(action) => !action.neededStatus || action.neededStatus === invoiceStatus
|
||||
)
|
||||
this.actions.filter((action) => action.neededStatus === invoiceStatus)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,9 @@
|
||||
<form [formGroup]="form">
|
||||
<app-price-input [control]="form.controls.unit_price" name="unit_price" label="مبلغ واحد" />
|
||||
<app-input [control]="form.controls.quantity" name="quantity" label="مقدار" type="number" suffix="گرم" />
|
||||
@if (!isCorrection) {
|
||||
<app-enum-select [control]="form.controls.payload.controls.karat" type="goldKarat" name="karat" />
|
||||
}
|
||||
|
||||
<app-amount-percentage-input
|
||||
[percentageControl]="form.controls.payload.controls.wages_percentage"
|
||||
|
||||
Reference in New Issue
Block a user