feat: enhance inner pages header with back button functionality and emit event on back click
fix: update total price info handling in correction form and ensure accurate calculations refactor: streamline sale invoice single view component by extracting info card into a separate component style: adjust button sizes in payment forms for better UI consistency feat: implement season picker navigation controls to prevent out-of-bounds selection fix: improve price formatting utility to handle undefined values gracefully chore: update environment configuration for API base URL feat: add navigation service to manage routing history and back navigation
This commit is contained in:
@@ -1,3 +1,4 @@
|
|||||||
|
import { NavigationService } from '@/core/services/navigation.service';
|
||||||
import { PwaInstallService } from '@/core/services/pwa-install.service';
|
import { PwaInstallService } from '@/core/services/pwa-install.service';
|
||||||
import { ConfirmationDialogComponent } from '@/shared/components/confirmationDialog/confirmation-dialog.component';
|
import { ConfirmationDialogComponent } from '@/shared/components/confirmationDialog/confirmation-dialog.component';
|
||||||
import { Component, HostListener } from '@angular/core';
|
import { Component, HostListener } from '@angular/core';
|
||||||
@@ -18,7 +19,10 @@ import { brandingConfig } from './app/branding/branding.config';
|
|||||||
export class AppComponent {
|
export class AppComponent {
|
||||||
toastPosition: 'top-center' | 'bottom-right' = 'bottom-right';
|
toastPosition: 'top-center' | 'bottom-right' = 'bottom-right';
|
||||||
|
|
||||||
constructor(private readonly pwaInstallService: PwaInstallService) {
|
constructor(
|
||||||
|
private readonly pwaInstallService: PwaInstallService,
|
||||||
|
private readonly navigationService: NavigationService
|
||||||
|
) {
|
||||||
this.updateToastPosition();
|
this.updateToastPosition();
|
||||||
if (brandingConfig.enableInstallPrompt) {
|
if (brandingConfig.enableInstallPrompt) {
|
||||||
this.pwaInstallService.init();
|
this.pwaInstallService.init();
|
||||||
|
|||||||
@@ -135,6 +135,8 @@ export class NativeBridgeService {
|
|||||||
this.toastService.info({ text: 'در حال چاپ ...' });
|
this.toastService.info({ text: 'در حال چاپ ...' });
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
window.NativeBridge.print(JSON.stringify(payload));
|
window.NativeBridge.print(JSON.stringify(payload));
|
||||||
|
console.log('payload', payload);
|
||||||
|
|
||||||
return { success: true };
|
return { success: true };
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.toastService.warn({ text: 'متاسفانه ارتباط با چاپگر برقرار نیست.' });
|
this.toastService.warn({ text: 'متاسفانه ارتباط با چاپگر برقرار نیست.' });
|
||||||
|
|||||||
@@ -0,0 +1,51 @@
|
|||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { NavigationEnd, Router } from '@angular/router';
|
||||||
|
import { filter } from 'rxjs/operators';
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root',
|
||||||
|
})
|
||||||
|
export class NavigationService {
|
||||||
|
private readonly CURRENT_URL_KEY = 'currentUrl';
|
||||||
|
private readonly PREVIOUS_URL_KEY = 'previousUrl';
|
||||||
|
|
||||||
|
currentUrl: string | null;
|
||||||
|
previousUrl: string | null;
|
||||||
|
|
||||||
|
constructor(private router: Router) {
|
||||||
|
this.currentUrl = sessionStorage.getItem(this.CURRENT_URL_KEY);
|
||||||
|
this.previousUrl = sessionStorage.getItem(this.PREVIOUS_URL_KEY);
|
||||||
|
|
||||||
|
this.router.events
|
||||||
|
.pipe(filter((event): event is NavigationEnd => event instanceof NavigationEnd))
|
||||||
|
.subscribe((event) => {
|
||||||
|
const newUrl = event.urlAfterRedirects;
|
||||||
|
|
||||||
|
// Ignore duplicate events
|
||||||
|
if (newUrl === this.currentUrl) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.previousUrl = this.currentUrl;
|
||||||
|
this.currentUrl = newUrl;
|
||||||
|
|
||||||
|
if (this.previousUrl) {
|
||||||
|
sessionStorage.setItem(this.PREVIOUS_URL_KEY, this.previousUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
sessionStorage.setItem(this.CURRENT_URL_KEY, this.currentUrl);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
canGoBack(): boolean {
|
||||||
|
return !!this.previousUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
back(fallbackUrl = '/'): void {
|
||||||
|
if (this.canGoBack()) {
|
||||||
|
window.history.back();
|
||||||
|
} else {
|
||||||
|
this.router.navigateByUrl(fallbackUrl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -11,22 +11,17 @@
|
|||||||
<app-checkbox
|
<app-checkbox
|
||||||
[control]="form.controls.customer_national_id"
|
[control]="form.controls.customer_national_id"
|
||||||
name="customer_national_id"
|
name="customer_national_id"
|
||||||
label="نمایش کد ملی خریدار"
|
label="نمایش کد ملی خریدار" />
|
||||||
/>
|
|
||||||
<app-checkbox
|
<app-checkbox
|
||||||
[control]="form.controls.customer_postal_code"
|
[control]="form.controls.customer_postal_code"
|
||||||
name="customer_postal_code"
|
name="customer_postal_code"
|
||||||
label="نمایش کد پستی خریدار"
|
label="نمایش کد پستی خریدار" />
|
||||||
/>
|
|
||||||
<app-checkbox
|
<app-checkbox
|
||||||
[control]="form.controls.customer_economic_code"
|
[control]="form.controls.customer_economic_code"
|
||||||
name="customer_economic_code"
|
name="customer_economic_code"
|
||||||
label="نمایش شناسهملی / اقتصادی خریدار"
|
label="نمایش شناسهملی / اقتصادی خریدار" />
|
||||||
/>
|
|
||||||
<app-checkbox [control]="form.controls.payment_type" name="payment_type" label="نمایش نوع پرداخت" />
|
<app-checkbox [control]="form.controls.payment_type" name="payment_type" label="نمایش نوع پرداخت" />
|
||||||
<app-checkbox [control]="form.controls.show_payment_info" name="show_payment_info" label="نمایش جزییات پرداخت" />
|
<app-checkbox [control]="form.controls.show_payment_info" name="show_payment_info" label="نمایش جزییات پرداخت" />
|
||||||
<app-checkbox [control]="form.controls.show_items" name="show_items" label="نمایش کالاهای خریداری شده" />
|
<app-checkbox [control]="form.controls.show_items" name="show_items" label="نمایش کالاهای خریداری شده" />
|
||||||
|
|
||||||
<app-form-footer-actions (onCancel)="close()" />
|
|
||||||
</form>
|
</form>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import { AbstractForm } from '@/shared/abstractClasses';
|
import { AbstractForm } from '@/shared/abstractClasses';
|
||||||
import { AppCheckboxComponent } from '@/shared/components/checkbox/checkbox.component';
|
import { AppCheckboxComponent } from '@/shared/components/checkbox/checkbox.component';
|
||||||
import { FormFooterActionsComponent } from '@/shared/components/formFooterActions/form-footer-actions.component';
|
|
||||||
import { Component, inject, signal } from '@angular/core';
|
import { Component, inject, signal } from '@angular/core';
|
||||||
import { ReactiveFormsModule } from '@angular/forms';
|
import { ReactiveFormsModule } from '@angular/forms';
|
||||||
import { IPosConfigPrintRequestPayload, IPosConfigPrintResponse } from './models';
|
import { IPosConfigPrintRequestPayload, IPosConfigPrintResponse } from './models';
|
||||||
@@ -9,7 +8,7 @@ import { PosConfigPrintService } from './services/main.service';
|
|||||||
@Component({
|
@Component({
|
||||||
selector: 'pos-config-print-form',
|
selector: 'pos-config-print-form',
|
||||||
templateUrl: 'form.component.html',
|
templateUrl: 'form.component.html',
|
||||||
imports: [ReactiveFormsModule, AppCheckboxComponent, FormFooterActionsComponent],
|
imports: [ReactiveFormsModule, AppCheckboxComponent],
|
||||||
})
|
})
|
||||||
export class PosConfigPrintFormComponent extends AbstractForm<
|
export class PosConfigPrintFormComponent extends AbstractForm<
|
||||||
IPosConfigPrintRequestPayload,
|
IPosConfigPrintRequestPayload,
|
||||||
@@ -37,6 +36,10 @@ export class PosConfigPrintFormComponent extends AbstractForm<
|
|||||||
show_items: [false],
|
show_items: [false],
|
||||||
});
|
});
|
||||||
|
|
||||||
|
form.valueChanges.subscribe(() => {
|
||||||
|
this.submitForm();
|
||||||
|
});
|
||||||
|
|
||||||
return form;
|
return form;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -52,7 +55,7 @@ export class PosConfigPrintFormComponent extends AbstractForm<
|
|||||||
|
|
||||||
override submitForm() {
|
override submitForm() {
|
||||||
const formValue = this.form.value as IPosConfigPrintRequestPayload;
|
const formValue = this.form.value as IPosConfigPrintRequestPayload;
|
||||||
this.toastService.success({ text: 'تغییرات با موفقیت اعمال شد.' });
|
// this.toastService.success({ text: 'تغییرات با موفقیت اعمال شد.' });
|
||||||
return this.service.submit(formValue);
|
return this.service.submit(formValue);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,17 +15,15 @@ export interface IPosConfigPrintRequestPayload {
|
|||||||
payment_type: boolean;
|
payment_type: boolean;
|
||||||
show_payment_info: boolean;
|
show_payment_info: boolean;
|
||||||
show_items: boolean;
|
show_items: boolean;
|
||||||
items: {
|
item_name: boolean;
|
||||||
name: boolean;
|
item_sku: boolean;
|
||||||
sku: boolean;
|
item_quantity: boolean;
|
||||||
quantity: boolean;
|
item_base_amount: boolean;
|
||||||
base_amount: boolean;
|
item_tax: boolean;
|
||||||
tax: boolean;
|
item_discount: boolean;
|
||||||
discount: boolean;
|
item_karat: boolean;
|
||||||
karat: boolean;
|
item_profit: boolean;
|
||||||
profit: boolean;
|
item_commission: boolean;
|
||||||
commission: boolean;
|
item_wage: boolean;
|
||||||
wage: boolean;
|
item_total_amount: boolean;
|
||||||
total_amount: boolean;
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,12 @@ export class PosConfigPrintService {
|
|||||||
get(): IPosConfigPrintResponse {
|
get(): IPosConfigPrintResponse {
|
||||||
const data = window.localStorage.getItem(LOCAL_STORAGE_KEYS.POS_CONFIG_PRINT);
|
const data = window.localStorage.getItem(LOCAL_STORAGE_KEYS.POS_CONFIG_PRINT);
|
||||||
if (data) {
|
if (data) {
|
||||||
return JSON.parse(data) as IPosConfigPrintResponse;
|
const dataObject = JSON.parse(data) as Record<keyof IPosConfigPrintResponse, string>;
|
||||||
|
return Object.keys(dataObject).reduce((prev, curr) => {
|
||||||
|
const key = curr as keyof IPosConfigPrintResponse;
|
||||||
|
prev[key] = dataObject[key] === 'true';
|
||||||
|
return prev;
|
||||||
|
}, {} as IPosConfigPrintResponse);
|
||||||
}
|
}
|
||||||
const defaultData = {
|
const defaultData = {
|
||||||
business_name: true,
|
business_name: true,
|
||||||
@@ -23,25 +28,37 @@ export class PosConfigPrintService {
|
|||||||
customer_economic_code: true,
|
customer_economic_code: true,
|
||||||
payment_type: true,
|
payment_type: true,
|
||||||
show_payment_info: true,
|
show_payment_info: true,
|
||||||
show_items: false,
|
show_items: true,
|
||||||
items: {
|
|
||||||
name: false,
|
item_name: false,
|
||||||
sku: false,
|
item_sku: false,
|
||||||
quantity: false,
|
item_quantity: false,
|
||||||
base_amount: false,
|
item_base_amount: false,
|
||||||
tax: false,
|
item_tax: false,
|
||||||
discount: false,
|
item_discount: false,
|
||||||
karat: false,
|
item_karat: false,
|
||||||
profit: false,
|
item_profit: false,
|
||||||
commission: false,
|
item_commission: false,
|
||||||
wage: false,
|
item_wage: false,
|
||||||
total_amount: false,
|
item_total_amount: false,
|
||||||
},
|
|
||||||
};
|
};
|
||||||
this.submit(defaultData);
|
this.submit(defaultData);
|
||||||
return defaultData;
|
return defaultData;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getVisibleItems() {
|
||||||
|
const items = this.get();
|
||||||
|
return Object.keys(items).reduce((result, key) => {
|
||||||
|
const itemKey = key as keyof IPosConfigPrintRequestPayload;
|
||||||
|
|
||||||
|
if (items[itemKey]) {
|
||||||
|
result[itemKey] = items[itemKey];
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}, {} as Partial<IPosConfigPrintRequestPayload>);
|
||||||
|
}
|
||||||
|
|
||||||
submit(data: IPosConfigPrintRequestPayload) {
|
submit(data: IPosConfigPrintRequestPayload) {
|
||||||
window.localStorage.setItem(LOCAL_STORAGE_KEYS.POS_CONFIG_PRINT, JSON.stringify(data));
|
window.localStorage.setItem(LOCAL_STORAGE_KEYS.POS_CONFIG_PRINT, JSON.stringify(data));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,9 @@
|
|||||||
<div class="min-h-dvh p-4">
|
<div class="min-h-dvh p-4">
|
||||||
<shared-sale-invoice-single-view [loading]="loading()" [invoice]="invoice()" />
|
<p-card class="mb-4">
|
||||||
|
<div class="flex items-center justify-between">
|
||||||
|
<span class="text-lg font-bold">صورتحساب شماره {{ invoice()?.invoice_number }}</span>
|
||||||
|
<button pButton type="button" icon="pi pi-share-alt" outlined (onClick)="share()"></button>
|
||||||
|
</div>
|
||||||
|
</p-card>
|
||||||
|
<shared-sale-invoice-single-info-card [loading]="loading()" [invoice]="invoice()" />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,13 +1,15 @@
|
|||||||
import { SharedSaleInvoiceSingleViewComponent } from '@/shared/components/invoices/sale-invoice-single-view.component';
|
import { SaleInvoiceSingleInfoCardComponent } from '@/shared/components/invoices/sale-invoice-single-info-card.component';
|
||||||
import pageParamsUtils from '@/utils/page-params.utils';
|
import pageParamsUtils from '@/utils/page-params.utils';
|
||||||
import { Component, computed, inject, signal } from '@angular/core';
|
import { Component, computed, inject, signal } from '@angular/core';
|
||||||
import { ActivatedRoute } from '@angular/router';
|
import { ActivatedRoute } from '@angular/router';
|
||||||
|
import { ButtonDirective } from 'primeng/button';
|
||||||
|
import { Card } from 'primeng/card';
|
||||||
import { PublicSaleInvoiceStore } from '../store/main.store';
|
import { PublicSaleInvoiceStore } from '../store/main.store';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'public-saleInvoice',
|
selector: 'public-saleInvoice',
|
||||||
templateUrl: './single.component.html',
|
templateUrl: './single.component.html',
|
||||||
imports: [SharedSaleInvoiceSingleViewComponent],
|
imports: [SaleInvoiceSingleInfoCardComponent, Card, ButtonDirective],
|
||||||
})
|
})
|
||||||
export class PublicSaleInvoiceComponent {
|
export class PublicSaleInvoiceComponent {
|
||||||
private readonly route = inject(ActivatedRoute);
|
private readonly route = inject(ActivatedRoute);
|
||||||
@@ -22,4 +24,14 @@ export class PublicSaleInvoiceComponent {
|
|||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
this.store.getData(this.invoiceId());
|
this.store.getData(this.invoiceId());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async share() {
|
||||||
|
if (navigator.share) {
|
||||||
|
await navigator.share({
|
||||||
|
title: window.document.title,
|
||||||
|
text: 'برای مشاهده صورتحساب اینجا کلیک کنید.',
|
||||||
|
url: window.location.href,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,7 +20,9 @@
|
|||||||
</header>
|
</header>
|
||||||
}
|
}
|
||||||
<div class="light-bottomsheet-body p-4">
|
<div class="light-bottomsheet-body p-4">
|
||||||
|
@if (contentRendered) {
|
||||||
<ng-content></ng-content>
|
<ng-content></ng-content>
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|||||||
@@ -105,10 +105,12 @@ export class SharedLightBottomsheetComponent implements OnInit, OnDestroy, OnCha
|
|||||||
@Input() style: Record<string, string> | undefined;
|
@Input() style: Record<string, string> | undefined;
|
||||||
@Input() mobileDrawerHeight = '90vh';
|
@Input() mobileDrawerHeight = '90vh';
|
||||||
@Input() transitionOptions = '130ms cubic-bezier(0.2, 0, 0, 1)';
|
@Input() transitionOptions = '130ms cubic-bezier(0.2, 0, 0, 1)';
|
||||||
@Input() closeUnmountDelay = 150;
|
@Input() closeUnmountDelay = 20;
|
||||||
|
|
||||||
@Output() onHide = new EventEmitter<any>();
|
@Output() onHide = new EventEmitter<any>();
|
||||||
rendered = false;
|
rendered = false;
|
||||||
|
contentRendered = false;
|
||||||
|
private isClosing = false;
|
||||||
private closeTimer: ReturnType<typeof setTimeout> | null = null;
|
private closeTimer: ReturnType<typeof setTimeout> | null = null;
|
||||||
|
|
||||||
private originalParent: Node | null = null;
|
private originalParent: Node | null = null;
|
||||||
@@ -127,14 +129,16 @@ export class SharedLightBottomsheetComponent implements OnInit, OnDestroy, OnCha
|
|||||||
this.originalParent = host.parentNode;
|
this.originalParent = host.parentNode;
|
||||||
this.renderer.appendChild(this.document.body, host);
|
this.renderer.appendChild(this.document.body, host);
|
||||||
this.rendered = this.visible;
|
this.rendered = this.visible;
|
||||||
|
this.contentRendered = this.visible;
|
||||||
this.syncZIndex();
|
this.syncZIndex();
|
||||||
this.toggleBodyScrollLock(this.visible);
|
this.toggleBodyScrollLock(this.visible);
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnChanges(changes: SimpleChanges) {
|
ngOnChanges(changes: SimpleChanges) {
|
||||||
if (changes['visible'] && this.visible) {
|
if (changes['visible'] && this.visible && !this.isClosing) {
|
||||||
this.clearCloseTimer();
|
this.clearCloseTimer();
|
||||||
this.rendered = true;
|
this.rendered = true;
|
||||||
|
this.contentRendered = true;
|
||||||
this.syncZIndex();
|
this.syncZIndex();
|
||||||
}
|
}
|
||||||
if (changes['visible']) {
|
if (changes['visible']) {
|
||||||
@@ -159,18 +163,20 @@ export class SharedLightBottomsheetComponent implements OnInit, OnDestroy, OnCha
|
|||||||
}
|
}
|
||||||
|
|
||||||
onVisibilityChange(nextValue: boolean) {
|
onVisibilityChange(nextValue: boolean) {
|
||||||
this.visible = nextValue;
|
|
||||||
this.visibleChange.emit(nextValue);
|
|
||||||
this.toggleBodyScrollLock(nextValue);
|
|
||||||
if (nextValue) {
|
if (nextValue) {
|
||||||
|
this.visible = nextValue;
|
||||||
|
this.isClosing = false;
|
||||||
this.clearCloseTimer();
|
this.clearCloseTimer();
|
||||||
this.rendered = true;
|
this.rendered = true;
|
||||||
|
this.contentRendered = true;
|
||||||
this.syncZIndex();
|
this.syncZIndex();
|
||||||
}
|
}
|
||||||
if (!nextValue) {
|
if (!nextValue) {
|
||||||
|
this.isClosing = true;
|
||||||
this.scheduleUnmount();
|
this.scheduleUnmount();
|
||||||
this.onHide.emit();
|
|
||||||
}
|
}
|
||||||
|
this.visibleChange.emit(nextValue);
|
||||||
|
this.toggleBodyScrollLock(nextValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
onMaskClick() {
|
onMaskClick() {
|
||||||
@@ -217,12 +223,14 @@ export class SharedLightBottomsheetComponent implements OnInit, OnDestroy, OnCha
|
|||||||
}
|
}
|
||||||
|
|
||||||
private scheduleUnmount() {
|
private scheduleUnmount() {
|
||||||
this.clearCloseTimer();
|
|
||||||
this.closeTimer = setTimeout(() => {
|
|
||||||
if (!this.visible) {
|
if (!this.visible) {
|
||||||
|
this.isClosing = false;
|
||||||
|
this.contentRendered = false;
|
||||||
this.rendered = false;
|
this.rendered = false;
|
||||||
|
this.onHide.emit();
|
||||||
}
|
}
|
||||||
}, this.closeUnmountDelay);
|
this.clearCloseTimer();
|
||||||
|
this.closeTimer = setTimeout(() => {}, this.closeUnmountDelay);
|
||||||
}
|
}
|
||||||
|
|
||||||
private clearCloseTimer() {
|
private clearCloseTimer() {
|
||||||
|
|||||||
@@ -1,10 +1,16 @@
|
|||||||
<div class="bg-surface-card flex items-center p-4">
|
<div class="bg-surface-card flex items-center px-4 py-3">
|
||||||
<div class="grow">
|
<div class="grow">
|
||||||
<div class="flex items-center gap-2">
|
<div class="flex items-center gap-2">
|
||||||
@if (backRoute) {
|
@if (backRoute || onBackClick) {
|
||||||
<p-button icon="pi pi-arrow-right" aria-label="بازگشت" outlined size="small" [routerLink]="backRoute" />
|
<p-button
|
||||||
|
icon="pi pi-arrow-right"
|
||||||
|
aria-label="بازگشت"
|
||||||
|
outlined
|
||||||
|
size="small"
|
||||||
|
[routerLink]="backRoute"
|
||||||
|
(click)="onBackClick.emit()" />
|
||||||
}
|
}
|
||||||
<h5 class="py-1.5 font-bold">{{ pageTitle }}</h5>
|
<h5 class="py-1.25 text-lg! font-bold">{{ pageTitle }}</h5>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@if (actions) {
|
@if (actions) {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Maybe } from '@/core';
|
import { Maybe } from '@/core';
|
||||||
import { NgTemplateOutlet } from '@angular/common';
|
import { NgTemplateOutlet } from '@angular/common';
|
||||||
import { Component, ContentChild, Input, TemplateRef } from '@angular/core';
|
import { Component, ContentChild, EventEmitter, Input, Output, TemplateRef } from '@angular/core';
|
||||||
import { RouterLink, UrlTree } from '@angular/router';
|
import { RouterLink, UrlTree } from '@angular/router';
|
||||||
import { Button } from 'primeng/button';
|
import { Button } from 'primeng/button';
|
||||||
|
|
||||||
@@ -12,6 +12,7 @@ import { Button } from 'primeng/button';
|
|||||||
export class InnerPagesHeaderComponent {
|
export class InnerPagesHeaderComponent {
|
||||||
@Input() pageTitle!: string;
|
@Input() pageTitle!: string;
|
||||||
@Input() backRoute?: UrlTree | string | any[];
|
@Input() backRoute?: UrlTree | string | any[];
|
||||||
|
@Output() onBackClick = new EventEmitter<void>();
|
||||||
@ContentChild('actions', { static: true }) actions?: Maybe<TemplateRef<any>>;
|
@ContentChild('actions', { static: true }) actions?: Maybe<TemplateRef<any>>;
|
||||||
constructor() {}
|
constructor() {}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,7 +41,7 @@
|
|||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<pos-order-price-info-card [info]="getTotalPriceInfo()" />
|
<pos-order-price-info-card [info]="totalPriceInfo" />
|
||||||
|
|
||||||
<app-form-footer-actions />
|
<app-form-footer-actions />
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
@@ -48,6 +48,12 @@ export class SharedCorrectionFormComponent extends AbstractForm<
|
|||||||
|
|
||||||
itemDrafts: Record<string, TCorrectionItemPayload> = {};
|
itemDrafts: Record<string, TCorrectionItemPayload> = {};
|
||||||
mappedInitialItems: TCorrectionItemPayload[] = [];
|
mappedInitialItems: TCorrectionItemPayload[] = [];
|
||||||
|
totalPriceInfo = {
|
||||||
|
totalBaseAmount: 0,
|
||||||
|
discountAmount: 0,
|
||||||
|
taxAmount: 0,
|
||||||
|
totalAmount: 0,
|
||||||
|
};
|
||||||
showItemEditSheet = signal(false);
|
showItemEditSheet = signal(false);
|
||||||
activeItemIndex = signal<number | null>(null);
|
activeItemIndex = signal<number | null>(null);
|
||||||
|
|
||||||
@@ -57,6 +63,7 @@ export class SharedCorrectionFormComponent extends AbstractForm<
|
|||||||
);
|
);
|
||||||
this.itemDrafts = {};
|
this.itemDrafts = {};
|
||||||
this.mappedInitialItems = (this.initialValues || []).map((item) => this.mapToInitialItem(item));
|
this.mappedInitialItems = (this.initialValues || []).map((item) => this.mapToInitialItem(item));
|
||||||
|
this.recalculateTotalPriceInfo();
|
||||||
this.showItemEditSheet.set(false);
|
this.showItemEditSheet.set(false);
|
||||||
this.activeItemIndex.set(null);
|
this.activeItemIndex.set(null);
|
||||||
}
|
}
|
||||||
@@ -91,9 +98,9 @@ export class SharedCorrectionFormComponent extends AbstractForm<
|
|||||||
good_id: item.good_id,
|
good_id: item.good_id,
|
||||||
unit_price: Number(item.unit_price || 0),
|
unit_price: Number(item.unit_price || 0),
|
||||||
quantity: Number(item.quantity || 0),
|
quantity: Number(item.quantity || 0),
|
||||||
discount_amount: Number(item.discount || 0),
|
discount_amount: Number(item.discount_amount || 0),
|
||||||
total_amount: Number(item.total_amount || 0),
|
total_amount: Number(item.total_amount || 0),
|
||||||
tax_amount: 0,
|
tax_amount: Number(item.tax_amount || 0),
|
||||||
base_total_amount: Number(item.unit_price || 0) * Number(item.quantity || 0),
|
base_total_amount: Number(item.unit_price || 0) * Number(item.quantity || 0),
|
||||||
measure_unit: (item.good_snapshot?.measure_unit || {}) as ISummary,
|
measure_unit: (item.good_snapshot?.measure_unit || {}) as ISummary,
|
||||||
};
|
};
|
||||||
@@ -158,9 +165,6 @@ export class SharedCorrectionFormComponent extends AbstractForm<
|
|||||||
const previews = (this.initialValues || [])
|
const previews = (this.initialValues || [])
|
||||||
.map((_, index) => this.getItemPreview(index))
|
.map((_, index) => this.getItemPreview(index))
|
||||||
.filter(Boolean);
|
.filter(Boolean);
|
||||||
|
|
||||||
console.log('previews', previews);
|
|
||||||
|
|
||||||
return previews.reduce(
|
return previews.reduce(
|
||||||
(acc, item: any) => {
|
(acc, item: any) => {
|
||||||
acc.totalBaseAmount += Number(item.base_total_amount || 0);
|
acc.totalBaseAmount += Number(item.base_total_amount || 0);
|
||||||
@@ -178,6 +182,10 @@ export class SharedCorrectionFormComponent extends AbstractForm<
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private recalculateTotalPriceInfo() {
|
||||||
|
this.totalPriceInfo = this.getTotalPriceInfo();
|
||||||
|
}
|
||||||
|
|
||||||
saveActiveItemDraft(
|
saveActiveItemDraft(
|
||||||
goldForm?: SharedGoldPayloadFormComponent,
|
goldForm?: SharedGoldPayloadFormComponent,
|
||||||
standardForm?: SharedStandardPayloadFormComponent
|
standardForm?: SharedStandardPayloadFormComponent
|
||||||
@@ -200,6 +208,7 @@ export class SharedCorrectionFormComponent extends AbstractForm<
|
|||||||
id: item.id,
|
id: item.id,
|
||||||
pricing_model: item.good_snapshot?.pricing_model || '',
|
pricing_model: item.good_snapshot?.pricing_model || '',
|
||||||
};
|
};
|
||||||
|
this.recalculateTotalPriceInfo();
|
||||||
this.backToList();
|
this.backToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -40,12 +40,13 @@ export interface IInvoiceItem {
|
|||||||
measure_unit_text: string;
|
measure_unit_text: string;
|
||||||
sku_code: string;
|
sku_code: string;
|
||||||
unit_price: string;
|
unit_price: string;
|
||||||
discount: string;
|
discount_amount: string;
|
||||||
total_amount: string;
|
total_amount: string;
|
||||||
notes?: string;
|
notes?: string;
|
||||||
payload: Payload;
|
payload: Payload;
|
||||||
good_snapshot: Goodsnapshot;
|
good_snapshot: Goodsnapshot;
|
||||||
good: GoodSummary;
|
good: GoodSummary;
|
||||||
|
tax_amount: string;
|
||||||
}
|
}
|
||||||
interface GoodSummary {
|
interface GoodSummary {
|
||||||
name: string;
|
name: string;
|
||||||
@@ -132,7 +133,12 @@ interface Pos extends ISummary {
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface Complex extends ISummary {
|
interface Complex extends ISummary {
|
||||||
business_activity: ISummary;
|
business_activity: BusinessActivity;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface BusinessActivity extends ISummary {
|
||||||
|
economic_code: string;
|
||||||
|
guild: ISummary;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ConsumerAccount {
|
interface ConsumerAccount {
|
||||||
|
|||||||
@@ -0,0 +1,93 @@
|
|||||||
|
@if (invoice) {
|
||||||
|
<p-card>
|
||||||
|
<div class="flex flex-col gap-4">
|
||||||
|
<div class="listKeyValue">
|
||||||
|
<app-key-value label="شماره منحصر به فرد مالیاتی" [value]="invoice.tax_id" />
|
||||||
|
<app-key-value label="تاریخ صورتحساب" [value]="invoice.invoice_date" type="date" />
|
||||||
|
<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="وضعیت صدور به سامانهی مودیان">
|
||||||
|
<catalog-tax-provider-status-tag [status]="invoice.status.value" [translate]="invoice.status.translate" />
|
||||||
|
</app-key-value>
|
||||||
|
<div class="col-span-full">
|
||||||
|
<app-key-value label="توضیحات" [value]="invoice.notes || '----'" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<p-divider align="center"> اطلاعات مالی</p-divider>
|
||||||
|
<div class="grid items-center gap-3 sm:grid-cols-2 sm:gap-4 md:grid-cols-3">
|
||||||
|
<app-key-value label="مبلغ تخفیف" [value]="invoice.discount_amount" type="price" />
|
||||||
|
<app-key-value label="مبلغ مالیات" [value]="invoice.tax_amount" type="price" />
|
||||||
|
<app-key-value label="مجموع قابل پرداخت" [value]="invoice.total_amount" type="price" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p-divider align="center"> اطلاعات پرداخت</p-divider>
|
||||||
|
<div class="grid items-center gap-3 sm:grid-cols-2 sm:gap-4 md:grid-cols-3">
|
||||||
|
<app-key-value label="نحوهی تسویهحساب">
|
||||||
|
<catalog-invoice-settlement-type-tag [type]="invoice.settlement_type.value" />
|
||||||
|
</app-key-value>
|
||||||
|
|
||||||
|
@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" />
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p-divider align="center"> اطلاعات صادر کننده </p-divider>
|
||||||
|
<div class="listKeyValue">
|
||||||
|
<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" />
|
||||||
|
@if (invoice.consumer_account?.account?.username) {
|
||||||
|
<app-key-value label="ایجاد کننده" [value]="invoice.consumer_account.account.username" />
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@if (variant !== 'customer') {
|
||||||
|
<p-divider align="center"> اطلاعات مشتری </p-divider>
|
||||||
|
@if (invoice.customer) {
|
||||||
|
<div class="listKeyValue">
|
||||||
|
@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" />
|
||||||
|
<app-key-value label="کد اقتصادی" [value]="invoice.customer.individual?.economic_code" />
|
||||||
|
<app-key-value label="کد ملی" [value]="invoice.customer.individual?.national_id" />
|
||||||
|
<app-key-value label="کد پستی" [value]="invoice.customer.individual?.postal_code" />
|
||||||
|
} @else {
|
||||||
|
<app-key-value label="نوع مشتری" value="حقوقی" />
|
||||||
|
<app-key-value label="نام شرکت" [value]="invoice.customer.legal?.company_name" />
|
||||||
|
<app-key-value label="کد اقتصادی" [value]="invoice.customer.legal?.economic_code" />
|
||||||
|
<app-key-value label="کد ثبتی" [value]="invoice.customer.legal?.registration_number" />
|
||||||
|
<app-key-value label="کد پستی" [value]="invoice.customer.legal?.postal_code" />
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
} @else if (invoice.unknown_customer) {
|
||||||
|
<div class="listKeyValue">
|
||||||
|
<app-key-value label="نوع مشتری" value="نوع دوم" />
|
||||||
|
<app-key-value label="عنوان" [value]="invoice.unknown_customer.name" />
|
||||||
|
<app-key-value label="کد ثبتی" [value]="invoice.unknown_customer.national_code" />
|
||||||
|
</div>
|
||||||
|
} @else {
|
||||||
|
<p class="text-text-color pt-3 pb-5 text-center">اطلاعات مشتری ثبت نشده است.</p>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
<p-divider align="center"> کالاهای خریداری شده </p-divider>
|
||||||
|
<app-page-data-list
|
||||||
|
[columns]="columns"
|
||||||
|
[items]="invoice.items"
|
||||||
|
[loading]="loading"
|
||||||
|
pageTitle=""
|
||||||
|
[showRefresh]="false">
|
||||||
|
<ng-template #totalAmount let-item>
|
||||||
|
@if (!item.discount_amount) {
|
||||||
|
<span [appPriceMask]="item.total_amount"></span>
|
||||||
|
}
|
||||||
|
</ng-template>
|
||||||
|
</app-page-data-list>
|
||||||
|
</div>
|
||||||
|
</p-card>
|
||||||
|
}
|
||||||
@@ -0,0 +1,104 @@
|
|||||||
|
import { Maybe } from '@/core';
|
||||||
|
import {
|
||||||
|
CatalogInvoiceTypeTagComponent,
|
||||||
|
CatalogTaxProviderStatusTagComponent,
|
||||||
|
} from '@/shared/catalog';
|
||||||
|
import { CatalogInvoiceSettlementTypeTagComponent } from '@/shared/catalog/invoiceSettlementType';
|
||||||
|
import { PriceMaskDirective } from '@/shared/directives';
|
||||||
|
import { Component, Input } from '@angular/core';
|
||||||
|
import { Card } from 'primeng/card';
|
||||||
|
import { Divider } from 'primeng/divider';
|
||||||
|
import { KeyValueComponent } from '../key-value.component/key-value.component';
|
||||||
|
import { IColumn, PageDataListComponent } from '../pageDataList/page-data-list.component';
|
||||||
|
import { ISaleInvoiceFullResponse } from './sale-invoice-full-response.model';
|
||||||
|
|
||||||
|
export type TSaleInvoiceSingleViewVariant = 'full' | 'customer' | 'pos';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'shared-sale-invoice-single-info-card',
|
||||||
|
templateUrl: './sale-invoice-single-info-card.component.html',
|
||||||
|
imports: [
|
||||||
|
Card,
|
||||||
|
KeyValueComponent,
|
||||||
|
CatalogInvoiceTypeTagComponent,
|
||||||
|
CatalogTaxProviderStatusTagComponent,
|
||||||
|
Divider,
|
||||||
|
CatalogInvoiceSettlementTypeTagComponent,
|
||||||
|
PageDataListComponent,
|
||||||
|
PriceMaskDirective,
|
||||||
|
],
|
||||||
|
})
|
||||||
|
export class SaleInvoiceSingleInfoCardComponent {
|
||||||
|
@Input({ required: true }) loading!: boolean;
|
||||||
|
@Input() invoice!: Maybe<ISaleInvoiceFullResponse>;
|
||||||
|
@Input() variant: TSaleInvoiceSingleViewVariant = 'full';
|
||||||
|
|
||||||
|
columns: IColumn[] = [
|
||||||
|
{
|
||||||
|
field: 'name',
|
||||||
|
header: 'عنوان',
|
||||||
|
type: 'nested',
|
||||||
|
nestedOption: {
|
||||||
|
path: 'good_snapshot.name',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'sku_code',
|
||||||
|
header: 'شناسه کالا',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'unit_price',
|
||||||
|
header: 'قیمت واحد',
|
||||||
|
type: 'price',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'quantity',
|
||||||
|
header: 'مقدار',
|
||||||
|
customDataModel(item) {
|
||||||
|
return `${item.quantity} ${item.measure_unit_text}`;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'commission',
|
||||||
|
header: 'کارمزد',
|
||||||
|
type: 'nested',
|
||||||
|
nestedOption: {
|
||||||
|
path: 'payload.commission',
|
||||||
|
type: 'price',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'wages',
|
||||||
|
header: 'اجرت',
|
||||||
|
type: 'nested',
|
||||||
|
nestedOption: {
|
||||||
|
path: 'payload.wages',
|
||||||
|
type: 'price',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'profit',
|
||||||
|
header: 'سود',
|
||||||
|
type: 'nested',
|
||||||
|
nestedOption: {
|
||||||
|
path: 'payload.profit',
|
||||||
|
type: 'price',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'discount_amount',
|
||||||
|
header: 'مبلغ تخفیف',
|
||||||
|
type: 'price',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'tax_amount',
|
||||||
|
header: 'مبلغ مالیات',
|
||||||
|
type: 'price',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'total_amount',
|
||||||
|
header: 'مبلغ قابل پرداخت',
|
||||||
|
type: 'price',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
@@ -3,103 +3,14 @@
|
|||||||
} @else if (!invoice) {
|
} @else if (!invoice) {
|
||||||
<uikit-empty-state> </uikit-empty-state>
|
<uikit-empty-state> </uikit-empty-state>
|
||||||
} @else {
|
} @else {
|
||||||
<app-inner-pages-header [pageTitle]="`صورتحساب ${invoice.invoice_number}`" [backRoute]="backRoute">
|
<app-inner-pages-header [pageTitle]="`صورتحساب ${invoice.invoice_number}`" (onBackClick)="backToPrevPage()">
|
||||||
<ng-template #actions>
|
<ng-template #actions>
|
||||||
|
<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="چاپ" />
|
||||||
</ng-template>
|
</ng-template>
|
||||||
</app-inner-pages-header>
|
</app-inner-pages-header>
|
||||||
<div class="p-4">
|
<div class="p-4">
|
||||||
<p-card>
|
<shared-sale-invoice-single-info-card [loading]="loading" [invoice]="invoice" [variant]="variant" />
|
||||||
<div class="flex flex-col gap-4">
|
|
||||||
<div class="listKeyValue">
|
|
||||||
<app-key-value label="شماره منحصر به فرد مالیاتی" [value]="invoice.tax_id" />
|
|
||||||
<app-key-value label="تاریخ صورتحساب" [value]="invoice.invoice_date" type="date" />
|
|
||||||
<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="وضعیت صدور به سامانهی مودیان">
|
|
||||||
<catalog-tax-provider-status-tag [status]="invoice.status.value" [translate]="invoice.status.translate" />
|
|
||||||
</app-key-value>
|
|
||||||
<div class="col-span-full">
|
|
||||||
<app-key-value label="توضیحات" [value]="invoice.notes || '----'" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<p-divider align="center"> اطلاعات مالی</p-divider>
|
|
||||||
<div class="grid items-center gap-3 sm:grid-cols-2 sm:gap-4 md:grid-cols-3">
|
|
||||||
<app-key-value label="مبلغ تخفیف" [value]="invoice.discount_amount" type="price" />
|
|
||||||
<app-key-value label="مبلغ مالیات" [value]="invoice.tax_amount" type="price" />
|
|
||||||
<app-key-value label="مجموع قابل پرداخت" [value]="invoice.total_amount" type="price" />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<p-divider align="center"> اطلاعات پرداخت</p-divider>
|
|
||||||
<div class="grid items-center gap-3 sm:grid-cols-2 sm:gap-4 md:grid-cols-3">
|
|
||||||
<app-key-value label="نحوهی تسویهحساب">
|
|
||||||
<catalog-invoice-settlement-type-tag [type]="invoice.settlement_type.value" />
|
|
||||||
</app-key-value>
|
|
||||||
|
|
||||||
@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" />
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<p-divider align="center"> اطلاعات صادر کننده </p-divider>
|
|
||||||
<div class="listKeyValue">
|
|
||||||
<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" />
|
|
||||||
@if (invoice.consumer_account?.account?.username) {
|
|
||||||
<app-key-value label="ایجاد کننده" [value]="invoice.consumer_account.account.username" />
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
@if (variant !== 'customer') {
|
|
||||||
<p-divider align="center"> اطلاعات مشتری </p-divider>
|
|
||||||
@if (invoice.customer) {
|
|
||||||
<div class="listKeyValue">
|
|
||||||
@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" />
|
|
||||||
<app-key-value label="کد اقتصادی" [value]="invoice.customer.individual?.economic_code" />
|
|
||||||
<app-key-value label="کد ملی" [value]="invoice.customer.individual?.national_id" />
|
|
||||||
<app-key-value label="کد پستی" [value]="invoice.customer.individual?.postal_code" />
|
|
||||||
} @else {
|
|
||||||
<app-key-value label="نوع مشتری" value="حقوقی" />
|
|
||||||
<app-key-value label="نام شرکت" [value]="invoice.customer.legal?.company_name" />
|
|
||||||
<app-key-value label="کد اقتصادی" [value]="invoice.customer.legal?.economic_code" />
|
|
||||||
<app-key-value label="کد ثبتی" [value]="invoice.customer.legal?.registration_number" />
|
|
||||||
<app-key-value label="کد پستی" [value]="invoice.customer.legal?.postal_code" />
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
} @else if (invoice.unknown_customer) {
|
|
||||||
<div class="listKeyValue">
|
|
||||||
<app-key-value label="نوع مشتری" value="نوع دوم" />
|
|
||||||
<app-key-value label="عنوان" [value]="invoice.unknown_customer.name" />
|
|
||||||
<app-key-value label="کد ثبتی" [value]="invoice.unknown_customer.national_code" />
|
|
||||||
</div>
|
|
||||||
} @else {
|
|
||||||
<p class="text-text-color pt-3 pb-5 text-center">اطلاعات مشتری ثبت نشده است.</p>
|
|
||||||
}
|
|
||||||
}
|
|
||||||
<p-divider align="center"> کالاهای خریداری شده </p-divider>
|
|
||||||
<app-page-data-list
|
|
||||||
[columns]="columns"
|
|
||||||
[items]="invoice.items"
|
|
||||||
[loading]="loading"
|
|
||||||
pageTitle=""
|
|
||||||
[showRefresh]="false">
|
|
||||||
<ng-template #totalAmount let-item>
|
|
||||||
@if (!item.discount_amount) {
|
|
||||||
<span [appPriceMask]="item.total_amount"></span>
|
|
||||||
}
|
|
||||||
</ng-template>
|
|
||||||
</app-page-data-list>
|
|
||||||
</div>
|
|
||||||
</p-card>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@if (moreActionMenuItems().length) {
|
@if (moreActionMenuItems().length) {
|
||||||
@@ -132,4 +43,14 @@
|
|||||||
[invoiceDate]="invoice.invoice_date"
|
[invoiceDate]="invoice.invoice_date"
|
||||||
(onSubmit)="correctionSubmit($event)" />
|
(onSubmit)="correctionSubmit($event)" />
|
||||||
</shared-light-bottomsheet>
|
</shared-light-bottomsheet>
|
||||||
|
<shared-light-bottomsheet [(visible)]="showQrCodeDialog" header="کد QR">
|
||||||
|
<div class="flex flex-col gap-4">
|
||||||
|
<span class="text-center text-lg font-bold">
|
||||||
|
برای امکان دسترسی به صورتحساب به صورت آنلاین، کد QR را اسکن کنید.
|
||||||
|
</span>
|
||||||
|
<p-card class="mx-auto">
|
||||||
|
<qrcode [qrdata]="publicInvoiceRoute()" [width]="220" [errorCorrectionLevel]="'M'"></qrcode>
|
||||||
|
</p-card>
|
||||||
|
</div>
|
||||||
|
</shared-light-bottomsheet>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,26 +1,17 @@
|
|||||||
import { Maybe } from '@/core';
|
import { Maybe } from '@/core';
|
||||||
import { NativeBridgeService } from '@/core/services';
|
import { NativeBridgeService } from '@/core/services';
|
||||||
|
import { NavigationService } from '@/core/services/navigation.service';
|
||||||
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 { IPosReturnFromSaleRequest } from '@/domains/pos/modules/saleInvoices/models/returnFromSale';
|
import { IPosReturnFromSaleRequest } from '@/domains/pos/modules/saleInvoices/models/returnFromSale';
|
||||||
import { IPosOrderItem } from '@/domains/pos/modules/shop/models';
|
import { IPosOrderItem } from '@/domains/pos/modules/shop/models';
|
||||||
import { PosInfoStore } from '@/domains/pos/store';
|
import { PosInfoStore } from '@/domains/pos/store';
|
||||||
import {
|
import { TspProviderResponseStatus } from '@/shared/catalog';
|
||||||
CatalogInvoiceTypeTagComponent,
|
import { SharedLightBottomsheetComponent } from '@/shared/components';
|
||||||
CatalogTaxProviderStatusTagComponent,
|
|
||||||
TspProviderResponseStatus,
|
|
||||||
} from '@/shared/catalog';
|
|
||||||
import { CatalogInvoiceSettlementTypeTagComponent } from '@/shared/catalog/invoiceSettlementType';
|
|
||||||
import { KeyValueComponent, SharedLightBottomsheetComponent } 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';
|
||||||
import {
|
|
||||||
IColumn,
|
|
||||||
PageDataListComponent,
|
|
||||||
} from '@/shared/components/pageDataList/page-data-list.component';
|
|
||||||
import { PriceMaskDirective } from '@/shared/directives';
|
|
||||||
import { UikitEmptyStateComponent } from '@/uikit';
|
import { UikitEmptyStateComponent } from '@/uikit';
|
||||||
import { formatWithCurrency } from '@/utils';
|
import { formatJalali, formatWithCurrency } from '@/utils';
|
||||||
import {
|
import {
|
||||||
Component,
|
Component,
|
||||||
computed,
|
computed,
|
||||||
@@ -33,12 +24,10 @@ import {
|
|||||||
TemplateRef,
|
TemplateRef,
|
||||||
ViewChild,
|
ViewChild,
|
||||||
} from '@angular/core';
|
} from '@angular/core';
|
||||||
import { UrlTree } from '@angular/router';
|
import { Router } from '@angular/router';
|
||||||
|
import { QRCodeComponent } from 'angularx-qrcode';
|
||||||
import { Button, ButtonDirective } from 'primeng/button';
|
import { Button, ButtonDirective } from 'primeng/button';
|
||||||
import { Card } from 'primeng/card';
|
import { Card } from 'primeng/card';
|
||||||
import { Dialog } from 'primeng/dialog';
|
|
||||||
import { Divider } from 'primeng/divider';
|
|
||||||
import { ProgressSpinner } from 'primeng/progressspinner';
|
|
||||||
import { TableModule } from 'primeng/table';
|
import { TableModule } from 'primeng/table';
|
||||||
import { Observable } from 'rxjs';
|
import { Observable } from 'rxjs';
|
||||||
import { AppConfirmationService } from '../confirmationDialog/confirmation-dialog.service';
|
import { AppConfirmationService } from '../confirmationDialog/confirmation-dialog.service';
|
||||||
@@ -46,8 +35,11 @@ import { InnerPagesHeaderComponent } from '../innerPagesHeader/inner-pages-heade
|
|||||||
import { SharedCorrectionFormComponent } from './correctionForm';
|
import { SharedCorrectionFormComponent } from './correctionForm';
|
||||||
import { CorrectionInvoiceFormValue, ICorrectionRequest, ReturnFromSaleFormValue } from './models';
|
import { CorrectionInvoiceFormValue, ICorrectionRequest, ReturnFromSaleFormValue } from './models';
|
||||||
import { SharedReturnFormComponent } from './returnForm/form.component';
|
import { SharedReturnFormComponent } from './returnForm/form.component';
|
||||||
|
import {
|
||||||
|
SaleInvoiceSingleInfoCardComponent,
|
||||||
|
TSaleInvoiceSingleViewVariant,
|
||||||
|
} from './sale-invoice-single-info-card.component';
|
||||||
|
|
||||||
export type TSaleInvoiceSingleViewVariant = 'full' | 'customer' | 'pos';
|
|
||||||
type TActionMenuItem = {
|
type TActionMenuItem = {
|
||||||
label: string;
|
label: string;
|
||||||
icon: string;
|
icon: string;
|
||||||
@@ -60,24 +52,17 @@ type TActionMenuItem = {
|
|||||||
selector: 'shared-sale-invoice-single-view',
|
selector: 'shared-sale-invoice-single-view',
|
||||||
templateUrl: './sale-invoice-single-view.component.html',
|
templateUrl: './sale-invoice-single-view.component.html',
|
||||||
imports: [
|
imports: [
|
||||||
KeyValueComponent,
|
|
||||||
Divider,
|
|
||||||
PageDataListComponent,
|
|
||||||
TableModule,
|
TableModule,
|
||||||
PageLoadingComponent,
|
PageLoadingComponent,
|
||||||
UikitEmptyStateComponent,
|
UikitEmptyStateComponent,
|
||||||
PriceMaskDirective,
|
|
||||||
CatalogTaxProviderStatusTagComponent,
|
|
||||||
CatalogInvoiceTypeTagComponent,
|
|
||||||
Button,
|
Button,
|
||||||
CatalogInvoiceSettlementTypeTagComponent,
|
|
||||||
SharedLightBottomsheetComponent,
|
SharedLightBottomsheetComponent,
|
||||||
ProgressSpinner,
|
|
||||||
SharedReturnFormComponent,
|
SharedReturnFormComponent,
|
||||||
SharedCorrectionFormComponent,
|
SharedCorrectionFormComponent,
|
||||||
Dialog,
|
|
||||||
ButtonDirective,
|
ButtonDirective,
|
||||||
InnerPagesHeaderComponent,
|
InnerPagesHeaderComponent,
|
||||||
|
SaleInvoiceSingleInfoCardComponent,
|
||||||
|
QRCodeComponent,
|
||||||
Card,
|
Card,
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
@@ -88,11 +73,13 @@ export class SharedSaleInvoiceSingleViewComponent {
|
|||||||
//TODO: below service Must be transform from pos to shared.
|
//TODO: below service Must be transform from pos to shared.
|
||||||
private readonly service = inject(PosConfigPrintService);
|
private readonly service = inject(PosConfigPrintService);
|
||||||
private readonly confirmationService = inject(AppConfirmationService);
|
private readonly confirmationService = inject(AppConfirmationService);
|
||||||
|
private readonly router = inject(Router);
|
||||||
|
private readonly navigationService = inject(NavigationService);
|
||||||
|
|
||||||
@Input({ required: true }) loading!: boolean;
|
@Input({ required: true }) loading!: boolean;
|
||||||
@Input() invoice!: Maybe<ISaleInvoiceFullResponse>;
|
@Input() invoice!: Maybe<ISaleInvoiceFullResponse>;
|
||||||
@Input() variant: TSaleInvoiceSingleViewVariant = 'full';
|
@Input() variant: TSaleInvoiceSingleViewVariant = 'full';
|
||||||
@Input() backRoute?: UrlTree | string | any[];
|
@Input() backRoute?: string;
|
||||||
@Input() sendToTspLoading: boolean = false;
|
@Input() sendToTspLoading: boolean = false;
|
||||||
@Input() resendToTspLoading: boolean = false;
|
@Input() resendToTspLoading: boolean = false;
|
||||||
@Input() inquiryLoading: boolean = false;
|
@Input() inquiryLoading: boolean = false;
|
||||||
@@ -109,6 +96,14 @@ export class SharedSaleInvoiceSingleViewComponent {
|
|||||||
moreActionMenuItems = signal<TActionMenuItem[]>([]);
|
moreActionMenuItems = signal<TActionMenuItem[]>([]);
|
||||||
showCorrectionForm = signal(false);
|
showCorrectionForm = signal(false);
|
||||||
showReturnFromSaleForm = signal(false);
|
showReturnFromSaleForm = signal(false);
|
||||||
|
showQrCodeDialog = signal(false);
|
||||||
|
|
||||||
|
publicInvoiceRoute = computed(() => {
|
||||||
|
if (this.invoice) {
|
||||||
|
return `${window.location.origin}/sale-invoices/${this.invoice.id}`;
|
||||||
|
}
|
||||||
|
return '';
|
||||||
|
});
|
||||||
|
|
||||||
showErrors = () => {};
|
showErrors = () => {};
|
||||||
inquiry = () => {
|
inquiry = () => {
|
||||||
@@ -231,39 +226,39 @@ export class SharedSaleInvoiceSingleViewComponent {
|
|||||||
|
|
||||||
preparePrint = async () => {
|
preparePrint = async () => {
|
||||||
if (this.invoice) {
|
if (this.invoice) {
|
||||||
const mustPrintConfig = await this.service.get();
|
const mustPrintConfig = await this.service.getVisibleItems();
|
||||||
|
|
||||||
const defaultPrintItems = [
|
const defaultPrintItems = [
|
||||||
{
|
{
|
||||||
title: 'اطلاعات صورتحساب',
|
title: 'اطلاعات صورتحساب',
|
||||||
items: [
|
items: [
|
||||||
|
{
|
||||||
|
label: 'شماره',
|
||||||
|
value: this.invoice.invoice_number.toString(),
|
||||||
|
show: true,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
label: 'شماره منحصر به فرد مالیاتی',
|
label: 'شماره منحصر به فرد مالیاتی',
|
||||||
value: 'this.invoice',
|
value: this.invoice.tax_id || '-',
|
||||||
show: true,
|
show: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'شماره صورتحساب',
|
label: 'نوع',
|
||||||
value: this.invoice.code,
|
value: this.invoice.type.translate,
|
||||||
show: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'موضوع صورتحساب',
|
|
||||||
value: 'this.invoice',
|
|
||||||
show: mustPrintConfig.invoice_template ?? true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'نوع صورتحساب',
|
|
||||||
value: 'this.invoice',
|
|
||||||
show: mustPrintConfig.invoice_template,
|
show: mustPrintConfig.invoice_template,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'تاریخ و ساعت',
|
label: 'موضوع',
|
||||||
value: this.invoice.invoice_date,
|
value: this.invoice.pos.complex.business_activity.guild.name,
|
||||||
|
show: mustPrintConfig.invoice_template ?? true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'تاریخ',
|
||||||
|
value: formatJalali(this.invoice.invoice_date),
|
||||||
show: true,
|
show: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'وضعیت صورتحساب مالیاتی',
|
label: 'وضعیت صدور',
|
||||||
value: this.invoice.status.translate,
|
value: this.invoice.status.translate,
|
||||||
show: mustPrintConfig.show_payment_info ?? true,
|
show: mustPrintConfig.show_payment_info ?? true,
|
||||||
},
|
},
|
||||||
@@ -273,23 +268,23 @@ export class SharedSaleInvoiceSingleViewComponent {
|
|||||||
title: `اطلاعات فروشنده`,
|
title: `اطلاعات فروشنده`,
|
||||||
items: [
|
items: [
|
||||||
{
|
{
|
||||||
label: 'عنوان فروشگاه',
|
label: 'فروشگاه',
|
||||||
value: this.invoice.pos.complex.business_activity.name,
|
value: this.invoice.pos.complex.business_activity.name,
|
||||||
show: mustPrintConfig.business_name,
|
show: mustPrintConfig.business_name,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'عنوان شعبه',
|
label: 'شعبه',
|
||||||
value: this.invoice.pos.complex.name,
|
value: this.invoice.pos.complex.name,
|
||||||
show: mustPrintConfig.complex_name,
|
show: mustPrintConfig.complex_name,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'عنوان پایانه فروش',
|
label: 'پایانه فروش',
|
||||||
value: this.invoice.pos.name,
|
value: this.invoice.pos.name,
|
||||||
show: mustPrintConfig.pos_name,
|
show: mustPrintConfig.pos_name,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'شماره اقتصادی',
|
label: 'شماره اقتصادی',
|
||||||
value: 'this.invoice.pos.complex.business_activity.economic_code',
|
value: this.invoice.pos.complex.business_activity.economic_code,
|
||||||
show: mustPrintConfig.economic_code,
|
show: mustPrintConfig.economic_code,
|
||||||
},
|
},
|
||||||
].filter((item) => item.show),
|
].filter((item) => item.show),
|
||||||
@@ -328,59 +323,67 @@ export class SharedSaleInvoiceSingleViewComponent {
|
|||||||
items: [
|
items: [
|
||||||
{
|
{
|
||||||
label: 'شرح',
|
label: 'شرح',
|
||||||
value: item.good.name,
|
value: item.good_snapshot.name,
|
||||||
show: mustPrintConfig.items?.name,
|
show: mustPrintConfig.item_name ?? true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'شناسه کالا / خدمت',
|
label: 'شناسه کالا / خدمت',
|
||||||
value: item.sku_code,
|
value: item.sku_code,
|
||||||
show: mustPrintConfig.items?.sku,
|
show: mustPrintConfig.item_sku ?? true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'تعداد / مقدار',
|
label: 'تعداد / مقدار',
|
||||||
value: `${item.quantity} ${item.measure_unit_text}`,
|
value: `${item.quantity} ${item.measure_unit_text}`,
|
||||||
show: mustPrintConfig.items?.quantity,
|
show: mustPrintConfig.item_quantity ?? true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'قیمت واحد',
|
label: 'قیمت واحد',
|
||||||
value: formatWithCurrency(item.unit_price),
|
value: formatWithCurrency(item.unit_price),
|
||||||
show: mustPrintConfig.items?.base_amount,
|
show: mustPrintConfig.item_base_amount ?? true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'اجرت',
|
label: 'اجرت',
|
||||||
value: formatWithCurrency(item.payload.wages),
|
value: formatWithCurrency(item.payload.wages),
|
||||||
show: mustPrintConfig.items?.wage && item.good_snapshot.pricing_model === 'GOLD',
|
show:
|
||||||
|
(mustPrintConfig.item_wage ?? true) &&
|
||||||
|
item.good_snapshot.pricing_model === 'GOLD',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'سود',
|
label: 'سود',
|
||||||
value: formatWithCurrency(item.payload.wages),
|
value: formatWithCurrency(item.payload.profit),
|
||||||
show: mustPrintConfig.items?.profit && item.good_snapshot.pricing_model === 'GOLD',
|
show:
|
||||||
|
(mustPrintConfig.item_profit ?? true) &&
|
||||||
|
item.good_snapshot.pricing_model === 'GOLD',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'حقالعمل',
|
label: 'حقالعمل',
|
||||||
value: formatWithCurrency(item.payload.commission),
|
value: formatWithCurrency(item.payload.commission),
|
||||||
show:
|
show:
|
||||||
mustPrintConfig.items?.commission && item.good_snapshot.pricing_model === 'GOLD',
|
(mustPrintConfig.item_commission ?? true) &&
|
||||||
|
item.good_snapshot.pricing_model === 'GOLD',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'مالیات بر ارزش افزوده',
|
label: 'مالیات بر ارزش افزوده',
|
||||||
value: 'item.good.tax',
|
value: formatWithCurrency(item.tax_amount),
|
||||||
show: mustPrintConfig.items?.tax,
|
show: mustPrintConfig.item_tax ?? true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'تخفیف',
|
label: 'تخفیف',
|
||||||
value: formatWithCurrency(item.discount),
|
value: formatWithCurrency(item.discount_amount),
|
||||||
show: mustPrintConfig.items?.discount,
|
show: mustPrintConfig.item_discount ?? true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'مبلغ کل',
|
label: 'مبلغ کل',
|
||||||
value: formatWithCurrency(item.total_amount),
|
value: formatWithCurrency(item.total_amount),
|
||||||
show: mustPrintConfig.items?.total_amount,
|
show: mustPrintConfig.item_total_amount ?? true,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.log(defaultPrintItems);
|
||||||
|
|
||||||
return defaultPrintItems;
|
return defaultPrintItems;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
@@ -389,6 +392,7 @@ export class SharedSaleInvoiceSingleViewComponent {
|
|||||||
printInvoice = async () => {
|
printInvoice = async () => {
|
||||||
try {
|
try {
|
||||||
const printItems = await this.preparePrint();
|
const printItems = await this.preparePrint();
|
||||||
|
|
||||||
if (printItems) {
|
if (printItems) {
|
||||||
this.nativeBridge.print(printItems);
|
this.nativeBridge.print(printItems);
|
||||||
}
|
}
|
||||||
@@ -399,75 +403,6 @@ export class SharedSaleInvoiceSingleViewComponent {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
columns: IColumn[] = [
|
|
||||||
{
|
|
||||||
field: 'name',
|
|
||||||
header: 'عنوان',
|
|
||||||
type: 'nested',
|
|
||||||
nestedOption: {
|
|
||||||
path: 'good_snapshot.name',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
field: 'sku_code',
|
|
||||||
header: 'شناسه کالا',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
field: 'unit_price',
|
|
||||||
header: 'قیمت واحد',
|
|
||||||
type: 'price',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
field: 'quantity',
|
|
||||||
header: 'مقدار',
|
|
||||||
customDataModel(item) {
|
|
||||||
return `${item.quantity} ${item.measure_unit_text}`;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
field: 'commission',
|
|
||||||
header: 'کارمزد',
|
|
||||||
type: 'nested',
|
|
||||||
nestedOption: {
|
|
||||||
path: 'payload.commission',
|
|
||||||
type: 'price',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
field: 'wages',
|
|
||||||
header: 'اجرت',
|
|
||||||
type: 'nested',
|
|
||||||
nestedOption: {
|
|
||||||
path: 'payload.wages',
|
|
||||||
type: 'price',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
field: 'profit',
|
|
||||||
header: 'سود',
|
|
||||||
type: 'nested',
|
|
||||||
nestedOption: {
|
|
||||||
path: 'payload.profit',
|
|
||||||
type: 'price',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
field: 'discount_amount',
|
|
||||||
header: 'مبلغ تخفیف',
|
|
||||||
type: 'price',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
field: 'tax_amount',
|
|
||||||
header: 'مبلغ مالیات',
|
|
||||||
type: 'price',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
field: 'total_amount',
|
|
||||||
header: 'مبلغ قابل پرداخت',
|
|
||||||
type: 'price',
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
refresh() {
|
refresh() {
|
||||||
this.onRefresh.emit();
|
this.onRefresh.emit();
|
||||||
}
|
}
|
||||||
@@ -503,10 +438,10 @@ export class SharedSaleInvoiceSingleViewComponent {
|
|||||||
|
|
||||||
const quantity = Number(item.quantity || 0);
|
const quantity = Number(item.quantity || 0);
|
||||||
const unit_price = Number(source.unit_price || 0);
|
const unit_price = Number(source.unit_price || 0);
|
||||||
const discount_amount = Number(source.discount || 0);
|
const discount_amount = Number(source.discount_amount || 0);
|
||||||
const base_total_amount = unit_price * quantity;
|
const base_total_amount = unit_price * quantity;
|
||||||
const total_amount = Number(source.total_amount || 0);
|
const total_amount = Number(source.total_amount || 0);
|
||||||
const tax_amount = Number(source.payload?.wages || 0);
|
const tax_amount = Number(source.tax_amount || 0);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
good_id: source.good_id,
|
good_id: source.good_id,
|
||||||
@@ -537,4 +472,11 @@ export class SharedSaleInvoiceSingleViewComponent {
|
|||||||
returnSubmit(event: ReturnFromSaleFormValue) {
|
returnSubmit(event: ReturnFromSaleFormValue) {
|
||||||
this.sendReturnFromSale(this.mapReturnFromSaleRequest(event));
|
this.sendReturnFromSale(this.mapReturnFromSaleRequest(event));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
backToPrevPage() {
|
||||||
|
this.navigationService.back(this.backRoute);
|
||||||
|
}
|
||||||
|
showQrCode() {
|
||||||
|
this.showQrCodeDialog.set(true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -60,7 +60,7 @@
|
|||||||
[discountAmount]="form.controls.discount_amount.value || 0"
|
[discountAmount]="form.controls.discount_amount.value || 0"
|
||||||
[taxAmount]="taxAmount()" />
|
[taxAmount]="taxAmount()" />
|
||||||
@if (!isCorrection) {
|
@if (!isCorrection) {
|
||||||
<button pButton class="w-full sm:w-auto" (click)="submit()">{{ preparedCTAText() }}</button>
|
<button pButton class="w-full sm:w-auto" size="large" (click)="submit()">{{ preparedCTAText() }}</button>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -25,7 +25,7 @@
|
|||||||
[discountAmount]="discountAmount()"
|
[discountAmount]="discountAmount()"
|
||||||
[taxAmount]="taxAmount()" />
|
[taxAmount]="taxAmount()" />
|
||||||
@if (!isCorrection) {
|
@if (!isCorrection) {
|
||||||
<button pButton class="w-full sm:w-auto" (onClick)="submit()">{{ preparedCTAText() }}</button>
|
<button pButton class="w-full sm:w-auto" size="large" (onClick)="submit()">{{ preparedCTAText() }}</button>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
@@ -2,13 +2,14 @@
|
|||||||
<div class="mt-1">
|
<div class="mt-1">
|
||||||
<div class="flex flex-col gap-4">
|
<div class="flex flex-col gap-4">
|
||||||
<uikit-label name="rapidInvoice"> انتخاب سال</uikit-label>
|
<uikit-label name="rapidInvoice"> انتخاب سال</uikit-label>
|
||||||
<select
|
<p-select
|
||||||
[(ngModel)]="selectedYearDraft"
|
[(ngModel)]="selectedYearDraft"
|
||||||
class="w-full rounded-xl border border-slate-200 bg-white px-3 py-2 text-slate-700 outline-none">
|
appendTo="body"
|
||||||
@for (yearItem of years; track yearItem.value) {
|
class="w-full"
|
||||||
<option [value]="yearItem.value">{{ yearItem.year }}</option>
|
[options]="years"
|
||||||
}
|
optionLabel="year"
|
||||||
</select>
|
optionValue="value">
|
||||||
|
</p-select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { CommonModule } from '@angular/common';
|
|||||||
import { Component, computed, EventEmitter, Input, Output, signal } from '@angular/core';
|
import { Component, computed, EventEmitter, Input, Output, signal } from '@angular/core';
|
||||||
import { FormsModule } from '@angular/forms';
|
import { FormsModule } from '@angular/forms';
|
||||||
import { ButtonDirective, ButtonSeverity } from 'primeng/button';
|
import { ButtonDirective, ButtonSeverity } from 'primeng/button';
|
||||||
|
import { Select } from 'primeng/select';
|
||||||
import { SharedLightBottomsheetComponent } from '../dialog/light-bottomsheet.component';
|
import { SharedLightBottomsheetComponent } from '../dialog/light-bottomsheet.component';
|
||||||
|
|
||||||
type SeasonKey = 0 | 1 | 2 | 3;
|
type SeasonKey = 0 | 1 | 2 | 3;
|
||||||
@@ -23,6 +24,7 @@ interface SeasonItem {
|
|||||||
SharedLightBottomsheetComponent,
|
SharedLightBottomsheetComponent,
|
||||||
ButtonDirective,
|
ButtonDirective,
|
||||||
UikitLabelComponent,
|
UikitLabelComponent,
|
||||||
|
Select,
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
export class SeasonPickerDialogComponent extends AbstractDialog {
|
export class SeasonPickerDialogComponent extends AbstractDialog {
|
||||||
|
|||||||
@@ -1,9 +1,21 @@
|
|||||||
<div class="border-surface-border bg-surface-card flex items-center gap-2 overflow-hidden rounded-lg border p-2">
|
<div class="border-surface-border bg-surface-card flex items-center gap-2 overflow-hidden rounded-lg border p-2">
|
||||||
<p-button icon="pi pi-chevron-right" size="small" text class="shrink-0" (onClick)="prevSeason()" />
|
<p-button
|
||||||
|
icon="pi pi-chevron-right"
|
||||||
|
size="small"
|
||||||
|
text
|
||||||
|
class="shrink-0"
|
||||||
|
[disabled]="!canGoPrev()"
|
||||||
|
(onClick)="prevSeason()" />
|
||||||
<div class="flex flex-1 cursor-pointer justify-center" (click)="openPicker()">
|
<div class="flex flex-1 cursor-pointer justify-center" (click)="openPicker()">
|
||||||
<span class="text-base font-semibold">{{ currentSeason() }}</span>
|
<span class="text-base font-semibold">{{ currentSeason() }}</span>
|
||||||
</div>
|
</div>
|
||||||
<p-button icon="pi pi-chevron-left" size="small" text class="shrink-0" (onClick)="nextSeason()" />
|
<p-button
|
||||||
|
icon="pi pi-chevron-left"
|
||||||
|
size="small"
|
||||||
|
text
|
||||||
|
class="shrink-0"
|
||||||
|
[disabled]="!canGoNext()"
|
||||||
|
(onClick)="nextSeason()" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@if (isOpen()) {
|
@if (isOpen()) {
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ interface SeasonItem {
|
|||||||
imports: [CommonModule, Button, SeasonPickerDialogComponent],
|
imports: [CommonModule, Button, SeasonPickerDialogComponent],
|
||||||
})
|
})
|
||||||
export class SeasonPickerComponent implements OnInit {
|
export class SeasonPickerComponent implements OnInit {
|
||||||
@Input() minYear = 1390;
|
@Input() minYear = 1404;
|
||||||
@Input() maxYear = Number(nowJalali(JALALI_DATE_FORMATS.YEAR));
|
@Input() maxYear = Number(nowJalali(JALALI_DATE_FORMATS.YEAR));
|
||||||
@Input() value!: Date;
|
@Input() value!: Date;
|
||||||
|
|
||||||
@@ -66,13 +66,29 @@ export class SeasonPickerComponent implements OnInit {
|
|||||||
this.isOpen.set(false);
|
this.isOpen.set(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
canGoPrev(): boolean {
|
||||||
|
const index = this.selectedSeason();
|
||||||
|
const currentYear = this.selectedYear();
|
||||||
|
const nextSeason = index === 0 ? 3 : ((index - 1) as SeasonKey);
|
||||||
|
const nextYear = index === 0 ? currentYear - 1 : currentYear;
|
||||||
|
return this.isWithinBounds(nextYear, nextSeason);
|
||||||
|
}
|
||||||
|
|
||||||
|
canGoNext(): boolean {
|
||||||
|
const index = this.selectedSeason();
|
||||||
|
const currentYear = this.selectedYear();
|
||||||
|
const nextSeason = index === 3 ? 0 : ((index + 1) as SeasonKey);
|
||||||
|
const nextYear = index === 3 ? currentYear + 1 : currentYear;
|
||||||
|
return this.isWithinBounds(nextYear, nextSeason);
|
||||||
|
}
|
||||||
|
|
||||||
prevSeason(): void {
|
prevSeason(): void {
|
||||||
const index = this.selectedSeason();
|
const index = this.selectedSeason();
|
||||||
const currentYear = this.selectedYear();
|
const currentYear = this.selectedYear();
|
||||||
const nextSeason = index === 0 ? 3 : ((index - 1) as SeasonKey);
|
const nextSeason = index === 0 ? 3 : ((index - 1) as SeasonKey);
|
||||||
const nextYear = index === 0 ? currentYear - 1 : currentYear;
|
const nextYear = index === 0 ? currentYear - 1 : currentYear;
|
||||||
|
|
||||||
// if (!this.isWithinBounds(nextYear, nextSeason)) return;
|
if (!this.isWithinBounds(nextYear, nextSeason)) return;
|
||||||
|
|
||||||
this.selectedYear.set(nextYear);
|
this.selectedYear.set(nextYear);
|
||||||
this.selectedSeason.set(nextSeason);
|
this.selectedSeason.set(nextSeason);
|
||||||
@@ -85,14 +101,19 @@ export class SeasonPickerComponent implements OnInit {
|
|||||||
const nextSeason = index === 3 ? 0 : ((index + 1) as SeasonKey);
|
const nextSeason = index === 3 ? 0 : ((index + 1) as SeasonKey);
|
||||||
const nextYear = index === 3 ? currentYear + 1 : currentYear;
|
const nextYear = index === 3 ? currentYear + 1 : currentYear;
|
||||||
|
|
||||||
|
if (!this.isWithinBounds(nextYear, nextSeason)) return;
|
||||||
|
|
||||||
this.selectedYear.set(nextYear);
|
this.selectedYear.set(nextYear);
|
||||||
this.selectedSeason.set(nextSeason);
|
this.selectedSeason.set(nextSeason);
|
||||||
this.emitValue();
|
this.emitValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
submitPicker(value: { year: number; season: number }): void {
|
submitPicker(value: { year: number; season: number }): void {
|
||||||
|
const season = value.season as SeasonKey;
|
||||||
|
if (!this.isWithinBounds(value.year, season)) return;
|
||||||
|
|
||||||
this.selectedYear.set(value.year);
|
this.selectedYear.set(value.year);
|
||||||
this.selectedSeason.set(value.season as SeasonKey);
|
this.selectedSeason.set(season);
|
||||||
this.emitValue();
|
this.emitValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ export function formatWithCurrency(
|
|||||||
currency: string | null | undefined = 'ریال',
|
currency: string | null | undefined = 'ریال',
|
||||||
opts?: { locale?: string; useComma?: boolean; fraction?: number }
|
opts?: { locale?: string; useComma?: boolean; fraction?: number }
|
||||||
): string {
|
): string {
|
||||||
if (num === null) return '0 ریال';
|
if (num === null || num === undefined) return '0 ریال';
|
||||||
const formatted = formatNumber(parseFloat(num + ''), opts);
|
const formatted = formatNumber(parseFloat(num + ''), opts);
|
||||||
if (isInputHost) return formatted;
|
if (isInputHost) return formatted;
|
||||||
if (currency && currency.trim()) return `${formatted} ${currency.trim()}`;
|
if (currency && currency.trim()) return `${formatted} ${currency.trim()}`;
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
// TIS tenant environment configuration
|
// TIS tenant environment configuration
|
||||||
export const environment = {
|
export const environment = {
|
||||||
production: true,
|
production: true,
|
||||||
apiBaseUrl: 'https://psp-api.shift-am.ir',
|
// apiBaseUrl: 'https://psp-api.shift-am.ir',
|
||||||
// apiBaseUrl: 'http://192.168.128.73:5002',
|
// apiBaseUrl: 'http://192.168.128.73:5002',
|
||||||
// apiBaseUrl: 'http://192.168.0.162:5002',
|
apiBaseUrl: 'http://192.168.0.162:5002',
|
||||||
// apiBaseUrl: 'http://localhost:5002',
|
// apiBaseUrl: 'http://localhost:5002',
|
||||||
host: 'localhost',
|
host: 'localhost',
|
||||||
port: 5000,
|
port: 5000,
|
||||||
|
|||||||
Reference in New Issue
Block a user