update sale invoices models and some ui fix

This commit is contained in:
2026-05-26 12:06:43 +03:30
parent eb671d5949
commit 9fdd5e451c
20 changed files with 147 additions and 77 deletions
@@ -1,5 +1,5 @@
<p-card class="border border-surface-border p-0! relative overflow-visible"> <p-card class="border-surface-border relative overflow-visible border p-0!">
<div class="flex items-center gap-4 justify-between w-full"> <div class="flex w-full items-center justify-between gap-4">
<span> {{ saleInvoice.invoice_number }} - {{ saleInvoice.code }} </span> <span> {{ saleInvoice.invoice_number }} - {{ saleInvoice.code }} </span>
<catalog-tax-provider-status-tag [status]="saleInvoice.status.value" [translate]="saleInvoice.status.translate" /> <catalog-tax-provider-status-tag [status]="saleInvoice.status.value" [translate]="saleInvoice.status.translate" />
</div> </div>
@@ -9,48 +9,46 @@
{{ preparedInvoiceType() }} {{ preparedInvoiceType() }}
</app-key-value> </app-key-value>
<app-key-value alignment="end" label="مبلغ کل" [value]="saleInvoice.total_amount" type="price" /> <app-key-value alignment="end" label="مبلغ کل" [value]="saleInvoice.total_amount" type="price" />
<app-key-value alignment="end" label="نوع فاکتور">
<catalog-invoice-type-tag [status]="saleInvoice.type.value" />
</app-key-value>
<app-key-value alignment="end" label="تاریخ فاکتور" [value]="saleInvoice.created_at" type="dateTime" /> <app-key-value alignment="end" label="تاریخ فاکتور" [value]="saleInvoice.created_at" type="dateTime" />
<app-key-value alignment="end" label="تاریخ ایجاد" [value]="saleInvoice.created_at" type="dateTime" />
</div> </div>
<hr /> <hr />
<div class="flex items-center justify-center gap-4"> <div class="flex items-center justify-center gap-4">
@if (["success", "failure"].includes(saleInvoice.status.value.toLowerCase())) { @if (['success', 'failure'].includes(saleInvoice.status.value.toLowerCase())) {
<p-button <p-button
label="ابطال" label="ابطال"
type="button" type="button"
severity="danger" severity="danger"
(click)="revokeInvoice()" (click)="revokeInvoice()"
[loading]="revokingInvoiceLoading()" [loading]="revokingInvoiceLoading()"
[disabled]="onAction()" [disabled]="onAction()"></p-button>
></p-button>
} }
@if (saleInvoice.status.value.toLowerCase() === "not_send") { @if (saleInvoice.status.value.toLowerCase() === 'not_send') {
<p-button <p-button
label="ارسال فاکتور" label="ارسال فاکتور"
type="button" type="button"
(click)="sendInvoice()" (click)="sendInvoice()"
[loading]="sendingLoading()" [loading]="sendingLoading()"
[disabled]="onAction()" [disabled]="onAction()"></p-button>
></p-button>
} }
@if (saleInvoice.status.value.toLowerCase() === "queued") { @if (saleInvoice.status.value.toLowerCase() === 'queued') {
<p-button <p-button
label="استعلام وضعیت ارسال" label="استعلام وضعیت ارسال"
type="button" type="button"
severity="info" severity="info"
(click)="getStatus()" (click)="getStatus()"
[loading]="gettingStatusLoading()" [loading]="gettingStatusLoading()"
[disabled]="onAction()" [disabled]="onAction()"></p-button>
></p-button>
} }
@if (saleInvoice.status.value.toLowerCase() === "failure") { @if (saleInvoice.status.value.toLowerCase() === 'failure') {
<p-button <p-button
label="ارسال مجدد" label="ارسال مجدد"
type="button" type="button"
(click)="resendInvoice()" (click)="resendInvoice()"
[loading]="resendingLoading()" [loading]="resendingLoading()"
[disabled]="onAction()" [disabled]="onAction()"></p-button>
></p-button>
} }
<a [routerLink]="singlePageRoute()" pButton type="button" (click)="viewDetails()" outlined>مشاهده جزییات</a> <a [routerLink]="singlePageRoute()" pButton type="button" (click)="viewDetails()" outlined>مشاهده جزییات</a>
</div> </div>
@@ -1,4 +1,5 @@
import { ToastService } from '@/core/services/toast.service'; import { ToastService } from '@/core/services/toast.service';
import { CatalogInvoiceTypeTagComponent } from '@/shared/catalog';
import { CatalogTaxProviderStatusTagComponent } from '@/shared/catalog/taxProviderStatus'; import { CatalogTaxProviderStatusTagComponent } from '@/shared/catalog/taxProviderStatus';
import { KeyValueComponent } from '@/shared/components'; import { KeyValueComponent } from '@/shared/components';
import { Component, computed, EventEmitter, inject, Input, Output, signal } from '@angular/core'; import { Component, computed, EventEmitter, inject, Input, Output, signal } from '@angular/core';
@@ -20,6 +21,7 @@ import { PosSaleInvoicesService } from '../services/main.service';
RouterLink, RouterLink,
ButtonDirective, ButtonDirective,
CatalogTaxProviderStatusTagComponent, CatalogTaxProviderStatusTagComponent,
CatalogInvoiceTypeTagComponent,
], ],
}) })
export class SaleInvoiceCardComponent { export class SaleInvoiceCardComponent {
@@ -39,11 +41,11 @@ export class SaleInvoiceCardComponent {
this.sendingLoading() || this.sendingLoading() ||
this.gettingStatusLoading() || this.gettingStatusLoading() ||
this.resendingLoading() || this.resendingLoading() ||
this.revokingInvoiceLoading(), this.revokingInvoiceLoading()
); );
singlePageRoute = computed(() => singlePageRoute = computed(() =>
posSaleInvoicesNamedRoutes.saleInvoice.meta.pagePath!(this.saleInvoice?.id), posSaleInvoicesNamedRoutes.saleInvoice.meta.pagePath!(this.saleInvoice?.id)
); );
preparedInvoiceType = computed(() => { preparedInvoiceType = computed(() => {
if (!this.saleInvoice) return ''; if (!this.saleInvoice) return '';
@@ -76,7 +78,7 @@ export class SaleInvoiceCardComponent {
} else { } else {
this.toastService.success({ text: 'ارسال فاکتور با موفقیت انجام شد.' }); this.toastService.success({ text: 'ارسال فاکتور با موفقیت انجام شد.' });
} }
this.toastService.success({ text: 'ارسال فاکتور با موفقیت انجام شد.' }); // this.toastService.success({ text: 'ارسال فاکتور با موفقیت انجام شد.' });
this.refreshRequested.emit(); this.refreshRequested.emit();
}); });
} }
+18 -18
View File
@@ -1,22 +1,8 @@
import { Maybe } from '@/core';
import ISummary from '@/core/models/summary'; import ISummary from '@/core/models/summary';
import { TspProviderResponseStatus } from '@/shared/catalog'; import { InvoiceTypes, TspProviderResponseStatus } from '@/shared/catalog';
import { IEnumTranslate } from '@/shared/models/enum_translate.type'; import { IEnumTranslate } from '@/shared/models/enum_translate.type';
export interface IPosSaleInvoicesRawResponse {
id: string;
code: string;
invoice_date: string;
notes?: string;
total_amount: string;
pos: Pos;
consumer_account: ConsumerAccount;
created_at: string;
items_count: number;
payments: Payments[];
}
export interface IPosSaleInvoicesResponse extends IPosSaleInvoicesRawResponse {}
export interface IPosSaleInvoicesSummaryRawResponse { export interface IPosSaleInvoicesSummaryRawResponse {
id: string; id: string;
code: string; code: string;
@@ -24,12 +10,26 @@ export interface IPosSaleInvoicesSummaryRawResponse {
invoice_date: string; invoice_date: string;
created_at: string; created_at: string;
total_amount: string; total_amount: string;
customer: Customer; customer: Maybe<Customer>;
fiscal: null;
status: IEnumTranslate<TspProviderResponseStatus>; status: IEnumTranslate<TspProviderResponseStatus>;
type: IEnumTranslate<InvoiceTypes>;
main_id: Maybe<string>;
tax_id: Maybe<string>;
reference_invoice: Maybe<string>;
notes: Maybe<string>;
} }
export interface IPosSaleInvoicesSummaryResponse extends IPosSaleInvoicesSummaryRawResponse {} export interface IPosSaleInvoicesSummaryResponse extends IPosSaleInvoicesSummaryRawResponse {}
export interface IPosSaleInvoicesRawResponse extends IPosSaleInvoicesSummaryRawResponse {
pos: Pos;
consumer_account: ConsumerAccount;
items_count: number;
payments: Payments[];
}
export interface IPosSaleInvoicesResponse extends IPosSaleInvoicesRawResponse {}
interface ConsumerAccount { interface ConsumerAccount {
role: string; role: string;
account: Account; account: Account;
@@ -49,17 +49,17 @@ export class PosOrderSectionComponent {
customerNameToShow = computed(() => { customerNameToShow = computed(() => {
const type = this.store.customer().type; const type = this.store.customer().type;
const info = this.store.customer().info; const info = this.store.customer().info;
let customerNameToShow = 'نامشخص'; let customerNameToShow = 'رهگذر';
switch (type) { switch (type) {
case 'UNKNOWN': case 'UNKNOWN':
customerNameToShow = `${info?.customer_unknown?.name || 'نامشخص'} (نوع دوم)`; customerNameToShow = `${info?.customer_unknown?.name || 'رهگذر'} (نوع دوم)`;
break; break;
case 'INDIVIDUAL': case 'INDIVIDUAL':
customerNameToShow = `${info?.customer_individual ? info?.customer_individual.first_name + ' ' + info?.customer_individual.last_name : 'نامشخص'} (نوع اول)`; customerNameToShow = `${info?.customer_individual ? info?.customer_individual.first_name + ' ' + info?.customer_individual.last_name : '-----'} (نوع اول)`;
break; break;
case 'LEGAL': case 'LEGAL':
customerNameToShow = `${info?.customer_legal?.company_name || 'نامشخص'} (نوع اول)`; customerNameToShow = `${info?.customer_legal?.company_name || '-----'} (نوع اول)`;
break; break;
} }
@@ -13,15 +13,18 @@
</form> </form>
<div class="form-group"> <div class="form-group">
<div class="flex items-center justify-between gap-3"> @if (!isUnknownCustomer()) {
<uikit-label> نوع تسویه‌حساب </uikit-label> <div class="flex items-center justify-between gap-3">
<p-selectButton <uikit-label> نوع تسویه‌حساب </uikit-label>
[options]="settlementTypeItems" <p-selectButton
[(ngModel)]="selectedSettlementType" [options]="settlementTypeItems"
[allowEmpty]="false" [(ngModel)]="selectedSettlementType"
optionLabel="label" [allowEmpty]="false"
optionValue="value" /> optionLabel="label"
</div> optionValue="value" />
</div>
}
v
<uikit-field label="تعداد مراحل پرداخت با پوز" name="pay_by_terminal_steps"> <uikit-field label="تعداد مراحل پرداخت با پوز" name="pay_by_terminal_steps">
<p-select <p-select
[options]="payByTerminalSteps()" [options]="payByTerminalSteps()"
@@ -76,6 +76,7 @@ export class PosPaymentFormDialogComponent extends AbstractFormDialog<IPayment,
readonly submitOrderLoading = computed(() => this.store.submitOrderLoading()); readonly submitOrderLoading = computed(() => this.store.submitOrderLoading());
readonly isGoldGuild = computed(() => this.posInfo.entity()?.guild.code.toLowerCase() === 'gold'); readonly isGoldGuild = computed(() => this.posInfo.entity()?.guild.code.toLowerCase() === 'gold');
readonly isUnknownCustomer = computed(() => this.store.customer().type === 'UNKNOWN');
payByTerminalSteps = signal( payByTerminalSteps = signal(
Array.from({ length: 10 }, (_, i) => ({ Array.from({ length: 10 }, (_, i) => ({
@@ -8,7 +8,7 @@ import { Component, computed, EventEmitter, Input, Output } from '@angular/core'
imports: [CommonModule], imports: [CommonModule],
}) })
export class PosStatisticsInvoiceTypeCardComponent { export class PosStatisticsInvoiceTypeCardComponent {
@Input() type: 'all' | 'success' | 'failure' | 'notSended' | 'pending' | 'credit' = 'all'; @Input() type: 'all' | 'success' | 'failure' | 'not_sended' | 'pending' | 'not_original' = 'all';
@Input() count?: number = 0; @Input() count?: number = 0;
@Input() totalAmount?: number = 0; @Input() totalAmount?: number = 0;
@Input() totalTaxAmount?: number = 0; @Input() totalTaxAmount?: number = 0;
@@ -36,18 +36,18 @@ export class PosStatisticsInvoiceTypeCardComponent {
case 'success': case 'success':
return { return {
title: 'تایید شده', title: 'تایید شده',
icon: 'check', icon: 'check-circle',
bgColor: '#22C55E1A', bgColor: '#22C55E1A',
borderColor: '#22C55E', borderColor: '#22C55E',
}; };
case 'failure': case 'failure':
return { return {
title: 'خطادار', title: 'خطادار',
icon: 'times', icon: 'ban',
bgColor: '#EF44441A', bgColor: '#EF44441A',
borderColor: '#EF4444', borderColor: '#EF4444',
}; };
case 'notSended': case 'not_sended':
return { return {
title: 'ارسال نشده', title: 'ارسال نشده',
icon: 'clock', icon: 'clock',
@@ -61,9 +61,9 @@ export class PosStatisticsInvoiceTypeCardComponent {
bgColor: '#3B82F61A', bgColor: '#3B82F61A',
borderColor: '#3B82F6', borderColor: '#3B82F6',
}; };
case 'credit': case 'not_original':
return { return {
title: 'اعتبار', title: 'ارجاعی',
icon: 'credit-card', icon: 'credit-card',
bgColor: '#8B5CF61A', bgColor: '#8B5CF61A',
borderColor: '#8B5CF6', borderColor: '#8B5CF6',
+2 -2
View File
@@ -3,8 +3,8 @@ export interface IPosStatisticsRawResponse {
success: IPosStatisticsItem; success: IPosStatisticsItem;
failure: IPosStatisticsItem; failure: IPosStatisticsItem;
pending: IPosStatisticsItem; pending: IPosStatisticsItem;
notSended: IPosStatisticsItem; not_sended: IPosStatisticsItem;
credit: IPosStatisticsItem; not_original: IPosStatisticsItem;
} }
export interface IPosStatisticsResponse extends IPosStatisticsRawResponse {} export interface IPosStatisticsResponse extends IPosStatisticsRawResponse {}
@@ -30,12 +30,12 @@
[count]="item()?.failure?.count" [count]="item()?.failure?.count"
(onClick)="toInvoicesPage('failure')" /> (onClick)="toInvoicesPage('failure')" />
<pos-statistics-invoice-type-card <pos-statistics-invoice-type-card
type="notSended" type="not_sended"
[loading]="loading()" [loading]="loading()"
[totalAmount]="item()?.notSended?.total_amount" [totalAmount]="item()?.not_sended?.total_amount"
[totalTaxAmount]="item()?.notSended?.total_tax" [totalTaxAmount]="item()?.not_sended?.total_tax"
[count]="item()?.notSended?.count" [count]="item()?.not_sended?.count"
(onClick)="toInvoicesPage('notSended')" /> (onClick)="toInvoicesPage('not_sended')" />
<pos-statistics-invoice-type-card <pos-statistics-invoice-type-card
type="pending" type="pending"
[loading]="loading()" [loading]="loading()"
@@ -44,11 +44,11 @@
[count]="item()?.pending?.count" [count]="item()?.pending?.count"
(onClick)="toInvoicesPage('pending')" /> (onClick)="toInvoicesPage('pending')" />
<pos-statistics-invoice-type-card <pos-statistics-invoice-type-card
type="credit" type="not_original"
[loading]="loading()" [loading]="loading()"
[totalAmount]="item()?.credit?.total_amount" [totalAmount]="item()?.not_original?.total_amount"
[totalTaxAmount]="item()?.credit?.total_tax" [totalTaxAmount]="item()?.not_original?.total_tax"
[count]="item()?.credit?.count" /> [count]="item()?.not_original?.count" />
} }
</div> </div>
<div class="fixed inset-x-0 bottom-0 p-4"> <div class="fixed inset-x-0 bottom-0 p-4">
+1
View File
@@ -1,4 +1,5 @@
export * from './devices'; export * from './devices';
export * from './guild'; export * from './guild';
export * from './invoiceTypes';
export * from './providers'; export * from './providers';
export * from './taxProviderStatus'; export * from './taxProviderStatus';
@@ -0,0 +1,2 @@
export * from './invoice-type-tag.component';
export * from './type';
@@ -0,0 +1 @@
<p-tag [severity]="severity" size="large" [value]="translate || label"></p-tag>
@@ -0,0 +1,35 @@
import { Component, Input } from '@angular/core';
import { Tag } from 'primeng/tag';
import { InvoiceTypes } from './type';
@Component({
selector: 'catalog-invoice-type-tag',
templateUrl: './invoice-type-tag.component.html',
imports: [Tag],
})
export class CatalogInvoiceTypeTagComponent {
@Input({ required: true }) status!: InvoiceTypes;
@Input() translate!: string;
get severity() {
switch (this.status) {
case 'ORIGINAL':
return 'success';
case 'CORRECTION':
return 'warn';
case 'REVOKE':
return 'danger';
}
}
get label() {
switch (this.status) {
case 'ORIGINAL':
return 'اصلی';
case 'CORRECTION':
return 'اصلاحی';
case 'REVOKE':
return 'ابطالی';
}
}
}
@@ -0,0 +1,7 @@
export const InvoiceTypes = {
ORIGINAL: 'ORIGINAL',
CORRECTION: 'CORRECTION',
REVOKE: 'REVOKE',
} as const;
export type InvoiceTypes = (typeof InvoiceTypes)[keyof typeof InvoiceTypes];
@@ -1,4 +1,11 @@
<div class="flex justify-end gap-2"> <div class="flex justify-end gap-2">
<p-button [label]="cancelLabel" severity="secondary" (click)="cancel()" /> <button pButton [label]="cancelLabel" severity="secondary" (click)="cancel()"></button>
<p-button [label]="submitLabel" type="submit" [disabled]="disabled" [loading]="loading" (onClick)="submit()" /> <button
pButton
[label]="submitLabel"
type="submit"
class="min-w-28"
[disabled]="disabled"
[loading]="loading"
(onClick)="submit()"></button>
</div> </div>
@@ -1,10 +1,10 @@
import { Component, EventEmitter, Input, Output } from '@angular/core'; import { Component, EventEmitter, Input, Output } from '@angular/core';
import { Button } from 'primeng/button'; import { ButtonDirective } from 'primeng/button';
@Component({ @Component({
selector: 'app-form-footer-actions', selector: 'app-form-footer-actions',
templateUrl: './form-footer-actions.component.html', templateUrl: './form-footer-actions.component.html',
imports: [Button], imports: [ButtonDirective],
}) })
export class FormFooterActionsComponent { export class FormFooterActionsComponent {
@Input() submitLabel = 'تایید'; @Input() submitLabel = 'تایید';
@@ -1,5 +1,6 @@
import { Maybe } from '@/core';
import ISummary from '@/core/models/summary'; import ISummary from '@/core/models/summary';
import { TspProviderResponseStatus } from '@/shared/catalog'; import { InvoiceTypes, TspProviderResponseStatus } from '@/shared/catalog';
import { IEnumTranslate } from '@/shared/models/enum_translate.type'; import { IEnumTranslate } from '@/shared/models/enum_translate.type';
export interface ISaleInvoiceFullRawResponse { export interface ISaleInvoiceFullRawResponse {
@@ -11,11 +12,16 @@ export interface ISaleInvoiceFullRawResponse {
pos: Pos; pos: Pos;
payments: Payment[]; payments: Payment[];
items: Item[]; items: Item[];
invoice_number: number;
type: IEnumTranslate<InvoiceTypes>;
created_at: string; created_at: string;
notes?: string;
customer?: Customer; customer?: Customer;
unknown_customer?: UnknownCustomer; unknown_customer?: UnknownCustomer;
status: IEnumTranslate<TspProviderResponseStatus>; status: IEnumTranslate<TspProviderResponseStatus>;
main_id: Maybe<string>;
tax_id: Maybe<string>;
reference_invoice: Maybe<string>;
notes: Maybe<string>;
} }
export interface ISaleInvoiceFullResponse extends ISaleInvoiceFullRawResponse {} export interface ISaleInvoiceFullResponse extends ISaleInvoiceFullRawResponse {}
@@ -17,14 +17,17 @@
</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]="invoice.code" /> <app-key-value label="شماره صورت‌حساب" [value]="invoice.invoice_number" />
<app-key-value label="تاریخ فاکتور" [value]="invoice.invoice_date" type="dateTime" /> <app-key-value label="تاریخ فاکتور" [value]="invoice.invoice_date" type="dateTime" />
<app-key-value label="تاریخ ایجاد فاکتور" [value]="invoice.created_at" type="dateTime" /> <app-key-value label="تاریخ ایجاد فاکتور" [value]="invoice.created_at" type="dateTime" />
<app-key-value label="نوع صورت‌حساب">
<catalog-invoice-type-tag [status]="invoice.type.value" />
</app-key-value>
<app-key-value label="وضعیت صدور به سامانه‌ی مودیان"> <app-key-value label="وضعیت صدور به سامانه‌ی مودیان">
<catalog-tax-provider-status-tag [status]="invoice.status.value" [translate]="invoice.status.translate" /> <catalog-tax-provider-status-tag [status]="invoice.status.value" [translate]="invoice.status.translate" />
</app-key-value> </app-key-value>
<div class="col-span-full"> <div class="col-span-full">
<app-key-value label="توضیحات" [value]="invoice.notes" /> <app-key-value label="توضیحات" [value]="invoice.notes || '----'" />
</div> </div>
</div> </div>
<p-divider align="center"> اطلاعات پرداخت </p-divider> <p-divider align="center"> اطلاعات پرداخت </p-divider>
@@ -3,7 +3,10 @@ import { NativeBridgeService } from '@/core/services';
import { ToastService } from '@/core/services/toast.service'; import { ToastService } from '@/core/services/toast.service';
import { PosConfigPrintService } from '@/domains/pos/modules/configs/components/print/services/main.service'; import { PosConfigPrintService } from '@/domains/pos/modules/configs/components/print/services/main.service';
import { PosInfoStore } from '@/domains/pos/store'; import { PosInfoStore } from '@/domains/pos/store';
import { CatalogTaxProviderStatusTagComponent } from '@/shared/catalog'; import {
CatalogInvoiceTypeTagComponent,
CatalogTaxProviderStatusTagComponent,
} from '@/shared/catalog';
import { AppCardComponent, KeyValueComponent } from '@/shared/components'; import { AppCardComponent, KeyValueComponent } from '@/shared/components';
import { ISaleInvoiceFullResponse } from '@/shared/components/invoices/sale-invoice-full-response.model'; import { ISaleInvoiceFullResponse } from '@/shared/components/invoices/sale-invoice-full-response.model';
import { PageLoadingComponent } from '@/shared/components/page-loading.component'; import { PageLoadingComponent } from '@/shared/components/page-loading.component';
@@ -45,6 +48,7 @@ export type TSaleInvoiceSingleViewVariant = 'full' | 'customer' | 'pos';
UikitEmptyStateComponent, UikitEmptyStateComponent,
PriceMaskDirective, PriceMaskDirective,
CatalogTaxProviderStatusTagComponent, CatalogTaxProviderStatusTagComponent,
CatalogInvoiceTypeTagComponent,
], ],
}) })
export class SharedSaleInvoiceSingleViewComponent { export class SharedSaleInvoiceSingleViewComponent {
@@ -88,12 +88,12 @@ export class KeyValueComponent {
} }
switch (this.type) { switch (this.type) {
case 'simple': case 'simple':
return this.value || '-'; return this.value || '-----';
} }
return value; return value;
} }
valueType = computed(() => valueType = computed(() =>
this.variant || ['boolean', 'has', 'active'].includes(this.type) ? 'tag' : 'text', this.variant || ['boolean', 'has', 'active'].includes(this.type) ? 'tag' : 'text'
); );
} }