diff --git a/src/app/domains/pos/modules/shop/components/order/order-section.component.ts b/src/app/domains/pos/modules/shop/components/order/order-section.component.ts
index ca1066f..a2a8812 100644
--- a/src/app/domains/pos/modules/shop/components/order/order-section.component.ts
+++ b/src/app/domains/pos/modules/shop/components/order/order-section.component.ts
@@ -1,8 +1,7 @@
// import { CustomersSelectComponent } from '@/modules/customers/components/select/select.component';
// import { ICustomerResponse } from '@/modules/customers/models';
import { KeyValueComponent } from '@/shared/components';
-import { MiniMonthlyCalendarComponent } from '@/uikit';
-import { formatGregorian, gregorianAddUnit } from '@/utils';
+import { FieldInvoiceDateComponent } from '@/shared/components/fields/invoice_date.component';
import {
ChangeDetectionStrategy,
Component,
@@ -35,7 +34,8 @@ import { POSOrderPriceInfoCardComponent } from './price-info-card.component';
PosOrderCustomerDialogComponent,
KeyValueComponent,
Card,
- MiniMonthlyCalendarComponent,
+
+ FieldInvoiceDateComponent,
],
})
export class PosOrderSectionComponent {
@@ -46,8 +46,6 @@ export class PosOrderSectionComponent {
placeholderImage = images.placeholders.default;
isVisibleCustomerForm = signal(false);
- invoiceMinDate = formatGregorian(gregorianAddUnit(new Date(), -7, 'days'));
- invoiceMaxDate = formatGregorian(new Date());
invoiceDate = new FormControl(this.store.invoiceDate());
diff --git a/src/app/shared/abstractClasses/abstract-form.ts b/src/app/shared/abstractClasses/abstract-form.ts
index e4a23a3..dc5ab7e 100644
--- a/src/app/shared/abstractClasses/abstract-form.ts
+++ b/src/app/shared/abstractClasses/abstract-form.ts
@@ -1,5 +1,13 @@
import { ToastService } from '@/core/services/toast.service';
-import { Component, EventEmitter, inject, Input, Output, signal } from '@angular/core';
+import {
+ Component,
+ EventEmitter,
+ inject,
+ Input,
+ Output,
+ signal,
+ SimpleChanges,
+} from '@angular/core';
import { FormBuilder, ReactiveFormsModule } from '@angular/forms';
import { finalize, Observable } from 'rxjs';
@@ -43,7 +51,7 @@ export abstract class AbstractForm<
// });
}
- ngOnChanges() {
+ ngOnChanges(changes: SimpleChanges) {
this.form.patchValue(this.initialValues ?? this.defaultValues);
}
diff --git a/src/app/shared/components/confirmationDialog/confirmation-dialog.component.html b/src/app/shared/components/confirmationDialog/confirmation-dialog.component.html
index b209939..badad39 100644
--- a/src/app/shared/components/confirmationDialog/confirmation-dialog.component.html
+++ b/src/app/shared/components/confirmationDialog/confirmation-dialog.component.html
@@ -1,7 +1,7 @@
@if (message) {
-
+
@@ -9,11 +9,19 @@
{{ message.header }}
{{ message.message }}
diff --git a/src/app/shared/components/confirmationDialog/confirmation-dialog.service.ts b/src/app/shared/components/confirmationDialog/confirmation-dialog.service.ts
index 1b21d74..4f65b5d 100644
--- a/src/app/shared/components/confirmationDialog/confirmation-dialog.service.ts
+++ b/src/app/shared/components/confirmationDialog/confirmation-dialog.service.ts
@@ -1,13 +1,9 @@
import { Injectable, inject } from '@angular/core';
import { Confirmation, ConfirmationService } from 'primeng/api';
-export interface AppConfirmOptions {
- header: string;
- message: string;
- acceptLabel?: string;
- rejectLabel?: string;
- accept?: () => void;
- reject?: () => void;
+export interface AppConfirmOptions extends Confirmation {
+ variant?: 'warn' | 'danger' | 'default';
+ acceptLoading?: boolean;
}
@Injectable({
@@ -16,16 +12,38 @@ export interface AppConfirmOptions {
export class AppConfirmationService {
private confirmationService = inject(ConfirmationService);
- ask(options: Confirmation): Promise
{
+ ask(options: AppConfirmOptions): Promise {
+ const { variant = 'default', ...confirmationOptions } = options;
+
+ switch (variant) {
+ case 'danger':
+ options.acceptButtonProps.severity = 'danger';
+ break;
+ case 'warn':
+ options.acceptButtonProps.severity = 'warn';
+ break;
+ }
+
return new Promise((resolve) => {
this.confirmationService.confirm({
position: 'bottom',
acceptLabel: 'تایید',
rejectLabel: 'لغو',
- ...options,
closeOnEscape: true,
- accept: () => options.accept?.() || resolve(true),
- reject: () => resolve(false),
+ ...confirmationOptions,
+ accept: async () => {
+ try {
+ options.acceptLoading = true;
+ await confirmationOptions.accept?.();
+ resolve(true);
+ } finally {
+ options.acceptLoading = false;
+ }
+ },
+ reject: () => {
+ if (options.acceptLoading) return;
+ resolve(false);
+ },
});
});
}
diff --git a/src/app/shared/components/fields/invoice_date.component.ts b/src/app/shared/components/fields/invoice_date.component.ts
new file mode 100644
index 0000000..a003002
--- /dev/null
+++ b/src/app/shared/components/fields/invoice_date.component.ts
@@ -0,0 +1,31 @@
+import { DatepickerComponent } from '@/uikit';
+import { formatGregorian, gregorianAddUnit } from '@/utils';
+import { Component, EventEmitter, Input, Output } from '@angular/core';
+import { FormControl } from '@angular/forms';
+
+@Component({
+ selector: 'field-invoice-date',
+ template: `
+
+ `,
+ imports: [DatepickerComponent],
+})
+export class FieldInvoiceDateComponent {
+ @Input() alignment: 'horizontal' | 'vertical' = 'vertical';
+ @Input({ required: true }) control!: FormControl;
+ @Output() onChangeDate = new EventEmitter();
+
+ invoiceMinDate = formatGregorian(gregorianAddUnit(new Date(), -7, 'days'));
+ invoiceMaxDate = formatGregorian(new Date());
+
+ changeDate(date: string) {
+ this.onChangeDate.emit(date);
+ }
+}
diff --git a/src/app/shared/components/input/input.component.html b/src/app/shared/components/input/input.component.html
index d98eb66..574651e 100644
--- a/src/app/shared/components/input/input.component.html
+++ b/src/app/shared/components/input/input.component.html
@@ -62,7 +62,9 @@
@if (suffixTemp) {
} @else if (preparedSuffix()) {
- {{ preparedSuffix() }}
+ {{
+ preparedSuffix()
+ }}
}
}
diff --git a/src/app/shared/components/invoices/returnForm/form.component.html b/src/app/shared/components/invoices/returnForm/form.component.html
new file mode 100644
index 0000000..d0b10ea
--- /dev/null
+++ b/src/app/shared/components/invoices/returnForm/form.component.html
@@ -0,0 +1,18 @@
+
diff --git a/src/app/shared/components/invoices/returnForm/form.component.ts b/src/app/shared/components/invoices/returnForm/form.component.ts
new file mode 100644
index 0000000..42acb51
--- /dev/null
+++ b/src/app/shared/components/invoices/returnForm/form.component.ts
@@ -0,0 +1,108 @@
+import { Maybe } from '@/core';
+import { AbstractForm } from '@/shared/abstractClasses';
+import { formatDate, GREGORIAN_DATE_FORMATS } from '@/utils';
+import { Component, computed, Input, signal, SimpleChanges } from '@angular/core';
+import { FormControl, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms';
+import { Message } from 'primeng/message';
+import { FieldInvoiceDateComponent } from '../../fields/invoice_date.component';
+import { FormFooterActionsComponent } from '../../formFooterActions/form-footer-actions.component';
+import { IInvoiceItem } from '../sale-invoice-full-response.model';
+import { SharedReturnFormItemComponent } from './item-form.component';
+
+type ItemForm = FormGroup<{
+ quantity: FormControl;
+}>;
+
+type BackFromSaleFormValue = {
+ items: {
+ id: string;
+ quantity: number;
+ maxQuantity: number;
+ }[];
+};
+
+@Component({
+ selector: 'shared-return-form',
+ templateUrl: 'form.component.html',
+ imports: [
+ Message,
+ FormFooterActionsComponent,
+ ReactiveFormsModule,
+ FieldInvoiceDateComponent,
+ SharedReturnFormItemComponent,
+ ],
+})
+export class SharedReturnFormComponent extends AbstractForm<
+ BackFromSaleFormValue,
+ any,
+ IInvoiceItem[]
+> {
+ @Input({ required: true }) invoiceDate!: string;
+
+ form = this.fb.group({
+ invoice_date: [this.invoiceDate],
+ items: this.fb.array([], {
+ // validators: [atLeastOneQuantityValidator],
+ }),
+ });
+
+ goodItems = computed(() => this.initialValues!);
+
+ get items() {
+ return this.form.controls.items;
+ }
+
+ preparedCalculation = signal>(null);
+
+ init() {
+ this.initForm();
+ this.prepareCalculation();
+ }
+
+ prepareCalculation() {
+ const calculated = [];
+ this.initialValues?.forEach((item) => {});
+ }
+
+ initForm() {
+ this.items.clear();
+ this.form.controls.invoice_date.setValue(
+ formatDate(this.invoiceDate, 'gregory', 'en', GREGORIAN_DATE_FORMATS.FULL)
+ );
+
+ this.initialValues?.forEach((item: any) => {
+ this.items.push(
+ this.fb.group({
+ quantity: [
+ Number(item.quantity),
+ [Validators.required, Validators.min(0), Validators.max(Number(item.quantity))],
+ ],
+ })
+ );
+ });
+ }
+
+ override ngOnInit(): void {
+ this.init();
+ }
+
+ override ngOnChanges(changes: SimpleChanges) {
+ if (changes['initialValues']) {
+ this.init();
+ }
+ }
+
+ override submitForm() {
+ const payload = this.items.controls.map((control) => ({
+ id: control.get('id')?.value,
+ quantity: control.get('quantity')?.value,
+ }));
+
+ if (!payload.some((item) => item.quantity)) {
+ this.toastService.warn({
+ text: 'حداقل مقدار یکی از کالاها/خدمات باید بیشتر از ۰ باشد.',
+ });
+ return;
+ }
+ }
+}
diff --git a/src/app/shared/components/invoices/returnForm/item-form.component.html b/src/app/shared/components/invoices/returnForm/item-form.component.html
new file mode 100644
index 0000000..add84dd
--- /dev/null
+++ b/src/app/shared/components/invoices/returnForm/item-form.component.html
@@ -0,0 +1,13 @@
+
+
+ {{ index + 1 }}- {{ good.name }}
+
+
+
diff --git a/src/app/shared/components/invoices/returnForm/item-form.component.ts b/src/app/shared/components/invoices/returnForm/item-form.component.ts
new file mode 100644
index 0000000..d834079
--- /dev/null
+++ b/src/app/shared/components/invoices/returnForm/item-form.component.ts
@@ -0,0 +1,45 @@
+import { Maybe } from '@/core';
+import { Component, Input, signal, SimpleChanges } from '@angular/core';
+import { FormControl, FormGroup, ReactiveFormsModule } from '@angular/forms';
+import { Divider } from 'primeng/divider';
+import { InputComponent } from '../../input/input.component';
+import { Goodsnapshot } from '../sale-invoice-full-response.model';
+
+type ItemForm = FormGroup<{
+ quantity: FormControl;
+}>;
+
+type BackFromSaleFormValue = {
+ items: {
+ id: string;
+ quantity: number;
+ maxQuantity: number;
+ }[];
+};
+
+@Component({
+ selector: 'shared-return-form-item',
+ templateUrl: './item-form.component.html',
+ imports: [InputComponent, ReactiveFormsModule, Divider],
+})
+export class SharedReturnFormItemComponent {
+ @Input({ required: true }) control!: FormControl;
+ @Input({ required: true }) good!: Goodsnapshot;
+ @Input({ required: true }) index!: number;
+
+ preparedCalculation = signal>(null);
+
+ prepareCalculation() {
+ const calculated = [];
+ }
+
+ ngOnInit(): void {
+ this.prepareCalculation();
+ }
+
+ ngOnChanges(changes: SimpleChanges) {
+ if (changes['initialValues']) {
+ this.prepareCalculation();
+ }
+ }
+}
diff --git a/src/app/shared/components/invoices/sale-invoice-full-response.model.ts b/src/app/shared/components/invoices/sale-invoice-full-response.model.ts
index 35b04fb..e0272f4 100644
--- a/src/app/shared/components/invoices/sale-invoice-full-response.model.ts
+++ b/src/app/shared/components/invoices/sale-invoice-full-response.model.ts
@@ -14,7 +14,7 @@ export interface ISaleInvoiceFullRawResponse {
consumer_account: ConsumerAccount;
pos: Pos;
payments: Payment[];
- items: Item[];
+ items: IInvoiceItem[];
invoice_number: number;
type: IEnumTranslate;
created_at: string;
@@ -30,7 +30,7 @@ export interface ISaleInvoiceFullRawResponse {
export interface ISaleInvoiceFullResponse extends ISaleInvoiceFullRawResponse {}
-interface Item {
+export interface IInvoiceItem {
id: string;
good_id: string;
service_id: null;
@@ -64,9 +64,7 @@ interface Category {
updated_at: string;
}
-interface Goodsnapshot {
- good: Good;
-}
+export interface Goodsnapshot extends Good {}
interface Good {
id: string;
diff --git a/src/app/shared/components/invoices/sale-invoice-single-view.component.html b/src/app/shared/components/invoices/sale-invoice-single-view.component.html
index 9114718..3612071 100644
--- a/src/app/shared/components/invoices/sale-invoice-single-view.component.html
+++ b/src/app/shared/components/invoices/sale-invoice-single-view.component.html
@@ -20,6 +20,7 @@
+
+
+
+
}
diff --git a/src/app/shared/components/invoices/sale-invoice-single-view.component.ts b/src/app/shared/components/invoices/sale-invoice-single-view.component.ts
index cea83cb..1bf7103 100644
--- a/src/app/shared/components/invoices/sale-invoice-single-view.component.ts
+++ b/src/app/shared/components/invoices/sale-invoice-single-view.component.ts
@@ -43,6 +43,7 @@ import { ProgressSpinner } from 'primeng/progressspinner';
import { TableModule } from 'primeng/table';
import { Observable } from 'rxjs';
import { AppConfirmationService } from '../confirmationDialog/confirmation-dialog.service';
+import { SharedReturnFormComponent } from './returnForm/form.component';
export type TSaleInvoiceSingleViewVariant = 'full' | 'customer' | 'pos';
@@ -65,6 +66,7 @@ export type TSaleInvoiceSingleViewVariant = 'full' | 'customer' | 'pos';
CatalogInvoiceSettlementTypeTagComponent,
SharedLightBottomsheetComponent,
ProgressSpinner,
+ SharedReturnFormComponent,
],
})
export class SharedSaleInvoiceSingleViewComponent {
@@ -90,6 +92,8 @@ export class SharedSaleInvoiceSingleViewComponent {
@ViewChild('totalAmount', { static: false }) totalAmount!: TemplateRef
;
moreActionMenuItems = signal