Compare commits

..

1 Commits

Author SHA1 Message Date
ahasani 93ebc80da3 feat: enhance sale invoice component with dynamic invoice ID handling and navigation improvements
Production CI / validate-and-build (push) Failing after 1m6s
2026-06-17 14:57:44 +03:30
5 changed files with 50 additions and 57 deletions
@@ -1,10 +1,10 @@
import { ToastService } from '@/core/services/toast.service'; import { ToastService } from '@/core/services/toast.service';
import taxProviderStatusTranslatorUtil from '@/shared/catalog/taxProviderStatus/tax-provider-status-translator.util'; import taxProviderStatusTranslatorUtil from '@/shared/catalog/taxProviderStatus/tax-provider-status-translator.util';
import { SharedSaleInvoiceSingleViewComponent } from '@/shared/components/invoices/sale-invoice-single-view.component'; import { SharedSaleInvoiceSingleViewComponent } from '@/shared/components/invoices/sale-invoice-single-view.component';
import pageParamsUtils from '@/utils/page-params.utils';
import { Component, computed, inject, signal } from '@angular/core'; import { Component, computed, inject, signal } from '@angular/core';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { ActivatedRoute } from '@angular/router'; import { ActivatedRoute } from '@angular/router';
import { finalize } from 'rxjs'; import { distinctUntilChanged, finalize, map } from 'rxjs';
import { posSaleInvoicesNamedRoutes } from '../constants'; import { posSaleInvoicesNamedRoutes } from '../constants';
import { IPosCorrectionRequest } from '../models/correction'; import { IPosCorrectionRequest } from '../models/correction';
import { IPosReturnFromSaleRequest } from '../models/returnFromSale'; import { IPosReturnFromSaleRequest } from '../models/returnFromSale';
@@ -20,8 +20,20 @@ export class PosSaleInvoiceComponent {
private readonly store = inject(PosSaleInvoiceStore); private readonly store = inject(PosSaleInvoiceStore);
private readonly toastService = inject(ToastService); private readonly toastService = inject(ToastService);
pageParams = computed(() => pageParamsUtils(this.route)); invoiceId = signal<string>('');
invoiceId = signal<string>(this.pageParams()['invoiceId']);
constructor() {
this.route.paramMap
.pipe(
map((params) => params.get('invoiceId') ?? ''),
distinctUntilChanged(),
takeUntilDestroyed()
)
.subscribe((invoiceId) => {
this.invoiceId.set(invoiceId);
this.store.getData(invoiceId);
});
}
inquiryLoading = signal(false); inquiryLoading = signal(false);
sendToTspLoading = signal(false); sendToTspLoading = signal(false);
@@ -133,8 +145,4 @@ export class PosSaleInvoiceComponent {
this.store.updateStatus(res.status); this.store.updateStatus(res.status);
}); });
} }
ngOnInit() {
this.store.getData(this.invoiceId());
}
} }
@@ -4,18 +4,18 @@
@if (invoice.referenced_by) { @if (invoice.referenced_by) {
<p-message severity="warn" [closable]="false"> <p-message severity="warn" [closable]="false">
این صورت‌حساب، مرجع صورت‌حساب شماره‌ی این صورت‌حساب، مرجع صورت‌حساب شماره‌ی
<a [href]="referenceByInvoiceRoute()" class="text-lg font-bold"> <span (click)="navigateToRelatedInvoice(invoice.referenced_by.id)" class="text-lg font-bold">
{{ invoice.referenced_by.invoice_number }} {{ invoice.referenced_by.invoice_number }}
</a> </span>
می‌باشد. می‌باشد.
</p-message> </p-message>
} }
@if (invoice.reference_invoice) { @if (invoice.reference_invoice) {
<p-message severity="info" [closable]="false"> <p-message severity="info" [closable]="false">
صورت‌حساب مرجع به شماره‌ی صورت‌حساب مرجع به شماره‌ی
<a [href]="referenceInvoiceRoute()" class="text-lg font-bold"> <span (click)="navigateToRelatedInvoice(invoice.reference_invoice.id)" class="text-lg font-bold">
{{ invoice.reference_invoice.invoice_number }} {{ invoice.reference_invoice.invoice_number }}
</a> </span>
می‌باشد. می‌باشد.
</p-message> </p-message>
} }
@@ -5,8 +5,8 @@ import {
} from '@/shared/catalog'; } from '@/shared/catalog';
import { CatalogInvoiceSettlementTypeTagComponent } from '@/shared/catalog/invoiceSettlementType'; import { CatalogInvoiceSettlementTypeTagComponent } from '@/shared/catalog/invoiceSettlementType';
import { PriceMaskDirective } from '@/shared/directives'; import { PriceMaskDirective } from '@/shared/directives';
import { Component, computed, inject, Input } from '@angular/core'; import { Component, inject, Input } from '@angular/core';
import { ActivatedRoute } from '@angular/router'; import { ActivatedRoute, Router } from '@angular/router';
import { Card } from 'primeng/card'; import { Card } from 'primeng/card';
import { Divider } from 'primeng/divider'; import { Divider } from 'primeng/divider';
import { Message } from 'primeng/message'; import { Message } from 'primeng/message';
@@ -37,6 +37,7 @@ export class SaleInvoiceSingleInfoCardComponent {
@Input() variant: TSaleInvoiceSingleViewVariant = 'full'; @Input() variant: TSaleInvoiceSingleViewVariant = 'full';
private readonly route = inject(ActivatedRoute); private readonly route = inject(ActivatedRoute);
private readonly router = inject(Router);
columns: IColumn[] = [ columns: IColumn[] = [
{ {
@@ -107,34 +108,18 @@ export class SaleInvoiceSingleInfoCardComponent {
}, },
]; ];
referenceInvoiceRoute = computed(() => { navigateToRelatedInvoice(invoiceId: string) {
const referenceInvoiceId = this.invoice?.reference_invoice?.id;
const pagePath = this.route.pathFromRoot const pagePath = this.route.pathFromRoot
.flatMap((route) => route.snapshot.url.map((segment) => segment.path)) .flatMap((route) => route.snapshot.url.map((segment) => segment.path))
.join('/'); .join('/');
if (!pagePath || !referenceInvoiceId) { if (pagePath && invoiceId) {
return pagePath;
}
const pathParts = pagePath.split('/').filter(Boolean); const pathParts = pagePath.split('/').filter(Boolean);
pathParts[pathParts.length - 1] = referenceInvoiceId; pathParts[pathParts.length - 1] = invoiceId;
return `/${pathParts.join('/')}`; this.router.navigateByUrl(`/${pathParts.join('/')}`, {
skipLocationChange: false,
}); });
referenceByInvoiceRoute = computed(() => {
const referenceInvoiceId = this.invoice?.referenced_by?.id;
const pagePath = this.route.pathFromRoot
.flatMap((route) => route.snapshot.url.map((segment) => segment.path))
.join('/');
if (!pagePath || !referenceInvoiceId) {
return pagePath;
} }
}
const pathParts = pagePath.split('/').filter(Boolean);
pathParts[pathParts.length - 1] = referenceInvoiceId;
return `/${pathParts.join('/')}`;
});
} }
@@ -7,7 +7,7 @@
<ng-template #actions> <ng-template #actions>
<p-button (click)="showQrCode()" icon="pi pi-qrcode" outlined size="small" /> <p-button (click)="showQrCode()" icon="pi pi-qrcode" outlined size="small" />
<p-button (click)="printInvoice()" icon="pi pi-print" outlined size="small" label="چاپ" /> <p-button (click)="printInvoice()" icon="pi pi-print" outlined size="small" label="چاپ" />
@if (!isApplication && canDoActionOnInvoice()) { @if (!isApplication && canDoActionOnInvoice) {
<p-button (click)="menu.toggle($event)" icon="pi pi-ellipsis-v" label="عملیات بیشتر" outlined size="small"> <p-button (click)="menu.toggle($event)" icon="pi pi-ellipsis-v" label="عملیات بیشتر" outlined size="small">
</p-button> </p-button>
<p-menu #menu [model]="moreActionMenuItems()" [popup]="true"></p-menu> <p-menu #menu [model]="moreActionMenuItems()" [popup]="true"></p-menu>
@@ -18,7 +18,7 @@
<shared-sale-invoice-single-info-card [loading]="loading" [invoice]="invoice" [variant]="variant" /> <shared-sale-invoice-single-info-card [loading]="loading" [invoice]="invoice" [variant]="variant" />
</div> </div>
@if (isApplication && moreActionMenuItems().length && canDoActionOnInvoice()) { @if (isApplication && moreActionMenuItems().length && canDoActionOnInvoice) {
<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="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"> <div class="flex flex-wrap justify-center gap-2">
@for (action of moreActionMenuItems(); track action.label || $index) { @for (action of moreActionMenuItems(); track action.label || $index) {
@@ -35,7 +35,7 @@
</div> </div>
} }
@if (canDoActionOnInvoice()) { @if (canDoActionOnInvoice) {
<shared-light-bottomsheet [(visible)]="showReturnFromSaleForm" header=" برگشت از فروش"> <shared-light-bottomsheet [(visible)]="showReturnFromSaleForm" header=" برگشت از فروش">
<shared-return-form <shared-return-form
[initialValues]="invoice.items" [initialValues]="invoice.items"
@@ -65,15 +65,15 @@
(onClose)="cancelCorrectionPayment()" /> (onClose)="cancelCorrectionPayment()" />
} }
} }
}
<shared-light-bottomsheet [(visible)]="showQrCodeDialog" header="کد QR"> <shared-light-bottomsheet [(visible)]="showQrCodeDialog" header="کد QR">
<div class="flex flex-col gap-4"> <div class="flex flex-col gap-4">
<span class="text-center text-lg font-bold"> <span class="text-center text-lg font-bold">
برای امکان دسترسی به صورت‌حساب به صورت آنلاین، کد QR را اسکن کنید. برای امکان دسترسی به صورت‌حساب به صورت آنلاین، کد QR را اسکن کنید.
</span> </span>
<p-card class="mx-auto"> <p-card class="mx-auto">
<qrcode [qrdata]="publicInvoiceRoute()" [width]="220" [errorCorrectionLevel]="'M'"></qrcode> <qrcode [qrdata]="publicInvoiceRoute" [width]="220" [errorCorrectionLevel]="'M'"></qrcode>
</p-card> </p-card>
</div> </div>
</shared-light-bottomsheet> </shared-light-bottomsheet>
}
@@ -115,16 +115,16 @@ export class SharedSaleInvoiceSingleViewComponent {
private pendingCorrectionSubmitResolver: Maybe<(allowed: boolean, payment?: IPayment) => void> = private pendingCorrectionSubmitResolver: Maybe<(allowed: boolean, payment?: IPayment) => void> =
null; null;
publicInvoiceRoute = computed(() => { get publicInvoiceRoute() {
if (this.invoice) { if (this.invoice) {
return `${window.location.origin}/sale-invoices/${this.invoice.id}`; return `${window.location.origin}/sale-invoices/${this.invoice.id}`;
} }
return ''; return '';
}); }
canDoActionOnInvoice = computed(() => { get canDoActionOnInvoice() {
return !this.invoice?.referenced_by; return !this.invoice?.referenced_by;
}); }
showErrors = () => {}; showErrors = () => {};
inquiry = () => { inquiry = () => {