feat: add public sale invoices module with routing and service integration

- Introduced PUBLIC_SALE_INVOICES_ROUTES for handling sale invoice routes.
- Created PublicSaleInvoicesService to fetch single invoice data from the API.
- Implemented PublicSaleInvoiceStore for managing invoice state.
- Added PublicSaleInvoiceComponent to display invoice details.
- Updated app routes to include public sale invoices.
- Removed pre-invoice dialog component and its associated files.
- Enhanced order submitted dialog with QR code for invoice sharing.
- Updated sale invoice single view to reflect new data structure.
- Refactored page data list component for improved layout and functionality.
This commit is contained in:
2026-05-21 21:35:34 +03:30
parent 1b4ac0789c
commit 8c07dc7c3f
25 changed files with 1036 additions and 349 deletions
@@ -30,8 +30,6 @@ import { Button } from 'primeng/button';
position: absolute;
inset: 0;
background: rgba(15, 23, 42, 0.22);
will-change: opacity;
animation: sheetFadeIn var(--sheet-transition, 130ms cubic-bezier(0.2, 0, 0, 1));
}
.light-bottomsheet-panel {
@@ -45,10 +43,7 @@ import { Button } from 'primeng/button';
background: var(--surface-card, #fff);
border-radius: 12px 12px 0 0;
box-shadow: none;
transform: translate3d(0, 0, 0);
will-change: transform;
backface-visibility: hidden;
animation: sheetSlideUp var(--sheet-transition, 130ms cubic-bezier(0.2, 0, 0, 1));
overflow: hidden;
}
@@ -87,24 +82,6 @@ import { Button } from 'primeng/button';
line-height: 1;
font-size: 1.25rem;
}
@keyframes sheetSlideUp {
from {
transform: translate3d(0, 100%, 0);
}
to {
transform: translate3d(0, 0, 0);
}
}
@keyframes sheetFadeIn {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
`,
],
host: {
@@ -139,7 +116,7 @@ export class SharedLightBottomsheetComponent implements OnInit, OnDestroy, OnCha
constructor(
private readonly elementRef: ElementRef<HTMLElement>,
private readonly renderer: Renderer2,
@Inject(DOCUMENT) private readonly document: Document,
@Inject(DOCUMENT) private readonly document: Document
) {}
ngOnInit() {
@@ -214,7 +191,7 @@ export class SharedLightBottomsheetComponent implements OnInit, OnDestroy, OnCha
private syncZIndex() {
const host = this.elementRef.nativeElement;
const siblingDrawers = Array.from(
this.document.body.querySelectorAll('.p-drawer'),
this.document.body.querySelectorAll('.p-drawer')
) as HTMLElement[];
const drawerZIndexes = siblingDrawers
.map((drawer) => Number.parseInt(drawer.style.zIndex || '0', 10))
@@ -13,8 +13,7 @@
icon="pi pi-print"
outlined
size="small"
(click)="printInvoice()"
></button>
(click)="printInvoice()"></button>
</ng-template>
<div class="flex flex-col gap-4">
<div class="listKeyValue">
@@ -29,14 +28,13 @@
</div>
</div>
<p-divider align="center"> اطلاعات پرداخت </p-divider>
<div class="grid md:grid-cols-3 sm:grid-cols-2 sm:gap-4 gap-3 items-center">
<div class="grid items-center gap-3 sm:grid-cols-2 sm:gap-4 md:grid-cols-3">
<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' ? 'پوز' : 'نقدی/ کارت‌خوان دیگر/ کارت به کارت'}`"
[value]="payment.amount"
type="price"
/>
type="price" />
}
</div>
@@ -45,14 +43,16 @@
<app-key-value label="کسب و کار" [value]="invoice.pos!.complex.business_activity.name" />
<app-key-value label="مجموعه" [value]="invoice.pos!.complex.name" />
<app-key-value label="پایانه فروش" [value]="invoice.pos!.name" />
<app-key-value label="ایجاد کننده" [value]="invoice.consumer_account.account.username" />
@if (invoice.consumer_account?.account?.username) {
<app-key-value label="ایجاد کننده" [value]="invoice.consumer_account.account.username" />
}
</div>
@if (variant !== "customer") {
@if (variant !== 'customer') {
<p-divider align="center"> اطلاعات مشتری </p-divider>
@if (invoice.customer) {
<div class="listKeyValue">
@if (invoice.customer.type === "INDIVIDUAL") {
@if (invoice.customer.type === 'INDIVIDUAL') {
<app-key-value label="نوع مشتری" value="حقیقی" />
<app-key-value label="نام" [value]="invoice.customer.individual?.first_name" />
<app-key-value label="نام خانوادگی" [value]="invoice.customer.individual?.last_name" />
@@ -74,7 +74,7 @@
<app-key-value label="کد ثبتی" [value]="invoice.unknown_customer.national_code" />
</div>
} @else {
<p class="text-center text-text-color pt-3 pb-5">اطلاعات مشتری ثبت نشده است.</p>
<p class="text-text-color pt-3 pb-5 text-center">اطلاعات مشتری ثبت نشده است.</p>
}
}
</div>
@@ -88,16 +88,10 @@
[items]="invoice.items"
[loading]="loading"
pageTitle=""
[showRefresh]="false"
>
[showRefresh]="false">
<ng-template #totalAmount let-item>
@if (!item.discount_amount) {
<span [appPriceMask]="item.total_amount"></span>
} @else {
<!-- <div class="flex flex-col items-end">
<span class="line-through text-muted-color text-xs" [appPriceMask]="item.total_amount"></span>
<span [appPriceMask]="item.total_amount - item.discount_amount"></span>
</div> -->
}
</ng-template>
</app-page-data-list>
@@ -250,7 +250,7 @@ export class SharedSaleInvoiceSingleViewComponent {
header: 'عنوان',
type: 'nested',
nestedOption: {
path: 'good.name',
path: 'good_snapshot.name',
},
},
{
@@ -1,8 +1,12 @@
<div class="h-full bg-surface-overlay rounded-lg shadow border border-surface-border p-0! overflow-hidden">
<div
[ngClass]="{
'bg-surface-overlay h-full overflow-hidden rounded-lg p-0!': true,
'border-surface-border border shadow': hasCaption(),
}">
<ng-template #captionTemplate let-isMobileView="isMobileView">
@if (pageTitle || showAdd || filter || showRefresh || moreActions) {
<ng-container [ngTemplateOutlet]="caption">
<div class="flex justify-between items-center gap-4">
<div class="flex items-center justify-between gap-4">
<h5 class="font-bold">{{ pageTitle }}</h5>
@if (showAdd || filter || showRefresh || moreActions) {
<div class="flex items-center gap-2">
@@ -15,8 +19,7 @@
badgeSeverity="info"
size="small"
[badge]="isFiltered ? '1' : undefined"
(click)="openFilter()"
></p-button>
(click)="openFilter()"></p-button>
}
@if (showRefresh) {
<p-button icon="pi pi-refresh" outlined size="small" (click)="refresh()"></p-button>
@@ -36,8 +39,7 @@
label="{{ addNewCtaLabel }}"
icon="pi pi-plus"
size="small"
(click)="openAddForm()"
></p-button>
(click)="openAddForm()"></p-button>
}
}
</div>
@@ -52,8 +54,7 @@
[description]="emptyPlaceholderDescription"
[ctaLabel]="emptyPlaceholderCtaLabel || addNewCtaLabel"
[showCTA]="showAdd"
(ctaClick)="openAddForm()"
></uikit-empty-state>
(ctaClick)="openAddForm()"></uikit-empty-state>
</ng-template>
<ng-template #paginatorTemplate>
<app-paginator
@@ -61,8 +62,7 @@
[totalRecords]="totalRecords"
[perPage]="perPage || 10"
[loading]="loading"
(onChange)="onPage($event)"
/>
(onChange)="onPage($event)" />
</ng-template>
@if (!isMobile) {
@@ -81,7 +81,7 @@
[showDelete]="showDelete"
[showDetails]="showDetails"
[showIndex]="showIndex"
[hasCaption]="!!(pageTitle || showAdd || filter || showRefresh || moreActions)"
[hasCaption]="hasCaption()"
[expandable]="expandable"
[expandableItemKey]="expandableItemKey"
[expandColumns]="expandColumns"
@@ -89,8 +89,7 @@
(onEdit)="edit($event)"
(onDelete)="remove($event)"
(onDetails)="details($event)"
(onChangePage)="onPage($event)"
>
(onChangePage)="onPage($event)">
@if (pageTitle || showAdd || filter || showRefresh || moreActions) {
<ng-template #captionBox>
<ng-container [ngTemplateOutlet]="captionTemplate" [ngTemplateOutletContext]="{ isMobileView: false }" />
@@ -117,14 +116,13 @@
[showEdit]="showEdit"
[showDelete]="showDelete"
[showDetails]="showDetails"
[hasCaption]="!!(pageTitle || showAdd || filter || showRefresh || moreActions)"
[hasCaption]="hasCaption()"
[expandable]="expandable"
[expandableItemKey]="expandableItemKey"
[expandColumns]="expandColumns"
(onEdit)="edit($event)"
(onDelete)="remove($event)"
(onDetails)="details($event)"
>
(onDetails)="details($event)">
<ng-template #captionBox>
@if (pageTitle || showAdd || filter || showRefresh || moreActions) {
<ng-container [ngTemplateOutlet]="captionTemplate" [ngTemplateOutletContext]="{ isMobileView: true }" />
@@ -145,8 +143,7 @@
(onHide)="closeFilter()"
header="فیلتر"
class="contnet"
styleClass="!w-[80vw] md:!w-96 md:!max-w-96 !max-w-sm"
>
styleClass="!w-[80vw] md:!w-96 md:!max-w-96 !max-w-sm">
<hr class="mt-0!" />
<div class="pt-2">
<ng-container [ngTemplateOutlet]="filter" [ngTemplateOutletContext]="{ $implicit: columns }"> </ng-container>
@@ -4,6 +4,7 @@ import { jalaliDiff } from '@/utils';
import { CommonModule } from '@angular/common';
import {
Component,
computed,
ContentChild,
ElementRef,
EventEmitter,
@@ -92,7 +93,7 @@ export interface IColumn<T = any> {
export class PageDataListComponent<I = any> {
constructor(
private host: ElementRef,
private renderer: Renderer2,
private renderer: Renderer2
) {}
@Input({ required: true }) pageTitle!: string;
@@ -138,12 +139,16 @@ export class PageDataListComponent<I = any> {
filterDrawerVisible = signal<boolean>(false);
expandedRows: { [key: string]: boolean } = {};
hasCaption = computed(
() => !!(this.pageTitle || this.showAdd || this.filter || this.showRefresh || this.moreActions)
);
ngOnInit() {
if (this.height)
this.renderer.setStyle(
this.host.nativeElement,
'height',
this.fullHeight ? '100cqmin' : this.height,
this.fullHeight ? '100cqmin' : this.height
);
// if (this.fullHeight) {
// }