feat(payment): update payment bridge and form components
- Refactor PosPaymentBridgeAbstract to enforce PosPaymentResult type for emitPaymentResultForTest method. - Simplify PosPaymentBridgeService by removing commented-out code and improving error handling. - Replace PosPaymentFormDialogComponent with SharedInvoicePaymentFormDialog in root.component.html for better payment handling. - Enhance root.component.ts to manage payment form visibility and submission logic. - Update light-bottomsheet.component.html for improved styling. - Add new return form features including item removal and total price calculation in returnForm components. - Introduce new payment form dialog for handling invoice payments with detailed structure and validation. - Implement payment handling logic in sale-invoice-single-view component to support correction payments. - Ensure proper integration of payment components in the shared components index.
This commit is contained in:
@@ -73,14 +73,14 @@ export class NativeBridgeService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pay(request: INativePayRequest): any {
|
pay(request: INativePayRequest): any {
|
||||||
if (request.amount <= 10_000) {
|
// if (request.amount <= 1_000) {
|
||||||
const errorMessage = 'برای مقادیر زیر ۱۰۰ هزار ریال، پرداخت ممکن نیست.';
|
// const errorMessage = 'برای مقادیر زیر ۱۰ هزار ریال، پرداخت ممکن نیست.';
|
||||||
this.toastService.warn({ text: errorMessage, life: 3000 });
|
// this.toastService.warn({ text: errorMessage, life: 3000 });
|
||||||
return {
|
// return {
|
||||||
success: false,
|
// success: false,
|
||||||
error: errorMessage,
|
// error: errorMessage,
|
||||||
};
|
// };
|
||||||
}
|
// }
|
||||||
this.toastService.info({ text: 'در حال پردازش پرداخت...' });
|
this.toastService.info({ text: 'در حال پردازش پرداخت...' });
|
||||||
try {
|
try {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
|
|||||||
@@ -28,8 +28,6 @@ export class ConsumerCustomerSaleInvoiceComponent {
|
|||||||
constructor() {
|
constructor() {
|
||||||
effect(() => {
|
effect(() => {
|
||||||
if (this.invoice()?.id) {
|
if (this.invoice()?.id) {
|
||||||
console.log('inja');
|
|
||||||
|
|
||||||
this.setBreadcrumb();
|
this.setBreadcrumb();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -85,8 +85,6 @@ export class PosOrderSectionComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
submitAndPay() {
|
submitAndPay() {
|
||||||
console.log('submitAndPay');
|
|
||||||
|
|
||||||
this.onSubmit.emit();
|
this.onSubmit.emit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,5 +2,5 @@ import { PosPaymentResult } from '../models';
|
|||||||
|
|
||||||
export abstract class PosPaymentBridgeAbstract {
|
export abstract class PosPaymentBridgeAbstract {
|
||||||
abstract registerPaymentResultListener(handler: (payload: PosPaymentResult) => void): () => void;
|
abstract registerPaymentResultListener(handler: (payload: PosPaymentResult) => void): () => void;
|
||||||
abstract emitPaymentResultForTest(payload: unknown): void;
|
abstract emitPaymentResultForTest(payload: PosPaymentResult): void;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,25 +11,9 @@ export class PosPaymentBridgeService extends PosPaymentBridgeAbstract {
|
|||||||
window.WebV = {
|
window.WebV = {
|
||||||
onPaymentResult: (payload: PosPaymentResult) => {
|
onPaymentResult: (payload: PosPaymentResult) => {
|
||||||
try {
|
try {
|
||||||
// const parsedPayload: PosPaymentResult = JSON.parse(payload);
|
|
||||||
// this.toastServices.info({ text: payload.data?.customerCardNO || '', life: 10000 });
|
|
||||||
// this.toastServices.info({
|
|
||||||
// text: payload.data?.responseCode || 'No response code',
|
|
||||||
// life: 10000,
|
|
||||||
// });
|
|
||||||
// this.toastServices.info({ text: payload.data?.rrn || 'No response code', life: 10000 });
|
|
||||||
// this.toastServices.info({ text: payload.data?.stan || 'No response code', life: 10000 });
|
|
||||||
// this.toastServices.info({
|
|
||||||
// text: payload.data?.terminalId || 'No response code',
|
|
||||||
// life: 10000,
|
|
||||||
// });
|
|
||||||
// this.toastServices.info({
|
|
||||||
// text: payload.data?.transactionDateTime || 'No response code',
|
|
||||||
// life: 10000,
|
|
||||||
// });
|
|
||||||
handler(payload);
|
handler(payload);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.toastServices.error({ text: 'Failed to parse payment result', life: 10000 });
|
// this.toastServices.error({ text: 'Failed to parse payment result', life: 10000 });
|
||||||
this.toastServices.error({
|
this.toastServices.error({
|
||||||
text: e instanceof Error ? e.message : String(e),
|
text: e instanceof Error ? e.message : String(e),
|
||||||
life: 10000,
|
life: 10000,
|
||||||
|
|||||||
@@ -40,7 +40,13 @@
|
|||||||
(onSubmit)="submitOrder()" />
|
(onSubmit)="submitOrder()" />
|
||||||
}
|
}
|
||||||
@if (isVisiblePaymentForm()) {
|
@if (isVisiblePaymentForm()) {
|
||||||
<pos-payment-form-dialog [(visible)]="isVisiblePaymentForm" (onSubmit)="submitPayment($event)" />
|
<shared-invoice-payment-form-dialog
|
||||||
|
[(visible)]="isVisiblePaymentForm"
|
||||||
|
[totalAmount]="priceInfo().totalAmount"
|
||||||
|
[submitOrderLoading]="submitOrderLoading()"
|
||||||
|
[isGoldGuild]="isGoldGuild()"
|
||||||
|
[isUnknownCustomer]="isUnknownCustomer()"
|
||||||
|
(onSubmitPayment)="submitPayment($event)" />
|
||||||
}
|
}
|
||||||
|
|
||||||
@if (responseInvoice()) {
|
@if (responseInvoice()) {
|
||||||
|
|||||||
@@ -2,16 +2,17 @@
|
|||||||
import { Maybe } from '@/core';
|
import { Maybe } from '@/core';
|
||||||
import { PosInfoStore } from '@/domains/pos/store/pos.store';
|
import { PosInfoStore } from '@/domains/pos/store/pos.store';
|
||||||
import { AbstractIsMobileComponent } from '@/shared/abstractClasses/abstract-is-mobile';
|
import { AbstractIsMobileComponent } from '@/shared/abstractClasses/abstract-is-mobile';
|
||||||
|
import { PosPaymentFormDialogComponent } from '@/shared/components/invoices/payment';
|
||||||
import { PageLoadingComponent } from '@/shared/components/page-loading.component';
|
import { PageLoadingComponent } from '@/shared/components/page-loading.component';
|
||||||
import { PriceMaskDirective } from '@/shared/directives';
|
import { PriceMaskDirective } from '@/shared/directives';
|
||||||
import { ChangeDetectionStrategy, Component, computed, inject, signal } from '@angular/core';
|
import { ChangeDetectionStrategy, Component, computed, inject, signal } from '@angular/core';
|
||||||
import { ButtonDirective } from 'primeng/button';
|
import { ButtonDirective } from 'primeng/button';
|
||||||
|
import { finalize } from 'rxjs';
|
||||||
import { PosOrderSectionDialogComponent } from '../components/order/order-section-dialog.component';
|
import { PosOrderSectionDialogComponent } from '../components/order/order-section-dialog.component';
|
||||||
import { PosOrderSectionComponent } from '../components/order/order-section.component';
|
import { PosOrderSectionComponent } from '../components/order/order-section.component';
|
||||||
import { PosOrderSubmittedDialogComponent } from '../components/order/order-submitted-dialog.component';
|
import { PosOrderSubmittedDialogComponent } from '../components/order/order-submitted-dialog.component';
|
||||||
import { PosPaymentFormDialogComponent } from '../components/payment/form-dialog.component';
|
|
||||||
import { PosGoodsComponent } from '../components/views/goods.component';
|
import { PosGoodsComponent } from '../components/views/goods.component';
|
||||||
import { IPosOrderResponse } from '../models';
|
import { IPayment, IPosOrderResponse } from '../models';
|
||||||
import { PosLandingStore } from '../store/main.store';
|
import { PosLandingStore } from '../store/main.store';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
@@ -45,6 +46,11 @@ export class PosShopComponent extends AbstractIsMobileComponent {
|
|||||||
readonly error = computed(() => this.infoStore.error());
|
readonly error = computed(() => this.infoStore.error());
|
||||||
readonly inOrderGoods = computed(() => this.landingStore.inOrderGoods());
|
readonly inOrderGoods = computed(() => this.landingStore.inOrderGoods());
|
||||||
readonly priceInfo = computed(() => this.landingStore.orderPricingInfo());
|
readonly priceInfo = computed(() => this.landingStore.orderPricingInfo());
|
||||||
|
readonly submitOrderLoading = computed(() => this.landingStore.submitOrderLoading());
|
||||||
|
readonly isGoldGuild = computed(
|
||||||
|
() => this.infoStore.entity()?.guild.code.toLowerCase() === 'gold'
|
||||||
|
);
|
||||||
|
readonly isUnknownCustomer = computed(() => this.landingStore.customer().type === 'UNKNOWN');
|
||||||
|
|
||||||
// getData() {
|
// getData() {
|
||||||
// this.infoStore.getData().subscribe();
|
// this.infoStore.getData().subscribe();
|
||||||
@@ -66,10 +72,25 @@ export class PosShopComponent extends AbstractIsMobileComponent {
|
|||||||
this.isVisiblePaymentForm.set(true);
|
this.isVisiblePaymentForm.set(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
submitPayment(invoice: IPosOrderResponse) {
|
submitPayment(event: {
|
||||||
this.closeInvoiceBottomSheet();
|
payment: IPayment;
|
||||||
this.responseInvoice.set(invoice);
|
settlementType: 'CASH' | 'CREDIT' | 'MIXED';
|
||||||
this.isVisibleSuccessDialog.set(true);
|
}) {
|
||||||
|
this.landingStore.setSettlementType(event.settlementType);
|
||||||
|
this.landingStore.setPayment(event.payment);
|
||||||
|
|
||||||
|
this.landingStore
|
||||||
|
.submitOrder()
|
||||||
|
.pipe(
|
||||||
|
finalize(() => {
|
||||||
|
this.isVisiblePaymentForm.set(false);
|
||||||
|
})
|
||||||
|
)
|
||||||
|
.subscribe((invoice) => {
|
||||||
|
this.closeInvoiceBottomSheet();
|
||||||
|
this.responseInvoice.set(invoice as IPosOrderResponse);
|
||||||
|
this.isVisibleSuccessDialog.set(true);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
closeSuccessDialog() {
|
closeSuccessDialog() {
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
}
|
}
|
||||||
</header>
|
</header>
|
||||||
}
|
}
|
||||||
<div class="light-bottomsheet-body p-4">
|
<div class="light-bottomsheet-body mx-auto w-full max-w-xl p-4">
|
||||||
@if (contentRendered) {
|
@if (contentRendered) {
|
||||||
<ng-content></ng-content>
|
<ng-content></ng-content>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ export * from './fields';
|
|||||||
export * from './inlineConfirmation/inline-confirmation.component';
|
export * from './inlineConfirmation/inline-confirmation.component';
|
||||||
export * from './inlineEdit/inline-edit.component';
|
export * from './inlineEdit/inline-edit.component';
|
||||||
export * from './input/input.component';
|
export * from './input/input.component';
|
||||||
|
export * from './invoices/payment';
|
||||||
export * from './key-value.component/key-value.component';
|
export * from './key-value.component/key-value.component';
|
||||||
export * from './passwordInput/password-input.component';
|
export * from './passwordInput/password-input.component';
|
||||||
export * from './posPayment';
|
export * from './posPayment';
|
||||||
|
|||||||
+7
-4
@@ -7,13 +7,13 @@
|
|||||||
(onHide)="close()">
|
(onHide)="close()">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<form [formGroup]="form" (submit)="submit()">
|
<form [formGroup]="form" (submit)="submit()">
|
||||||
<app-key-value label="مبلغ قابل پرداخت" [value]="totalAmount().toString()" type="price" />
|
<app-key-value label="مبلغ قابل پرداخت" [value]="totalAmount.toString()" type="price" />
|
||||||
<app-key-value label="مبلغ باقیمانده" [value]="remainedAmount().toString()" type="price" />
|
<app-key-value label="مبلغ باقیمانده" [value]="remainedAmount().toString()" type="price" />
|
||||||
<hr />
|
<hr />
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
@if (!isUnknownCustomer()) {
|
@if (!isUnknownCustomer) {
|
||||||
<div class="flex items-center justify-between gap-3">
|
<div class="flex items-center justify-between gap-3">
|
||||||
<uikit-label> نوع تسویهحساب </uikit-label>
|
<uikit-label> نوع تسویهحساب </uikit-label>
|
||||||
<p-selectButton
|
<p-selectButton
|
||||||
@@ -24,6 +24,9 @@
|
|||||||
optionValue="value" />
|
optionValue="value" />
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
amount:{{ payByTerminalSteps()[0]?.amount || '' }} - terminalId:
|
||||||
|
{{ payByTerminalSteps()[0]?.info?.terminal_id || '' }}- payed:{{ payByTerminalSteps()[0]?.payed || '' }}
|
||||||
<uikit-field label="تعداد مراحل پرداخت با پوز" name="pay_by_terminal_steps">
|
<uikit-field label="تعداد مراحل پرداخت با پوز" name="pay_by_terminal_steps">
|
||||||
<p-select
|
<p-select
|
||||||
[options]="payByTerminalSteps()"
|
[options]="payByTerminalSteps()"
|
||||||
@@ -68,7 +71,7 @@
|
|||||||
</button>
|
</button>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
</app-price-input>
|
</app-price-input>
|
||||||
@if (isGoldGuild()) {
|
@if (isGoldGuild) {
|
||||||
<app-price-input [control]="form.controls.set_off" name="setOff" label="تهاتر" [max]="setOffMax()">
|
<app-price-input [control]="form.controls.set_off" name="setOff" label="تهاتر" [max]="setOffMax()">
|
||||||
<ng-template #suffixTemp>
|
<ng-template #suffixTemp>
|
||||||
<button pButton size="small" type="button" [disabled]="!remainedAmount()" (click)="fillRemained('SET_OFF')">
|
<button pButton size="small" type="button" [disabled]="!remainedAmount()" (click)="fillRemained('SET_OFF')">
|
||||||
@@ -80,7 +83,7 @@
|
|||||||
|
|
||||||
<app-form-footer-actions
|
<app-form-footer-actions
|
||||||
submitLabel="ثبت اطلاعات پرداخت و ادامه"
|
submitLabel="ثبت اطلاعات پرداخت و ادامه"
|
||||||
[loading]="submitOrderLoading()"
|
[loading]="submitOrderLoading"
|
||||||
(onCancel)="close()" />
|
(onCancel)="close()" />
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
+130
-88
@@ -1,33 +1,38 @@
|
|||||||
import { Maybe } from '@/core';
|
import { Maybe } from '@/core';
|
||||||
import { NativeBridgeService } from '@/core/services';
|
import { NativeBridgeService } from '@/core/services';
|
||||||
import { ToastService } from '@/core/services/toast.service';
|
import { ToastService } from '@/core/services/toast.service';
|
||||||
import { PosInfoStore } from '@/domains/pos/store';
|
import {
|
||||||
|
IPayment,
|
||||||
|
PosPaymentResult,
|
||||||
|
TerminalSuccessPaymentPayload,
|
||||||
|
TOrderPaymentTypes,
|
||||||
|
} from '@/domains/pos/modules/shop/models';
|
||||||
|
import { PosPaymentBridgeAbstract } from '@/domains/pos/modules/shop/services/payment-bridge.abstract';
|
||||||
|
import { PosPaymentBridgeService } from '@/domains/pos/modules/shop/services/payment-bridge.service';
|
||||||
import { AbstractFormDialog } from '@/shared/abstractClasses';
|
import { AbstractFormDialog } from '@/shared/abstractClasses';
|
||||||
import { KeyValueComponent } from '@/shared/components';
|
import { KeyValueComponent } from '@/shared/components';
|
||||||
import { SharedLightBottomsheetComponent } from '@/shared/components/dialog/light-bottomsheet.component';
|
import { SharedLightBottomsheetComponent } from '@/shared/components/dialog/light-bottomsheet.component';
|
||||||
import { FormFooterActionsComponent } from '@/shared/components/formFooterActions/form-footer-actions.component';
|
import { FormFooterActionsComponent } from '@/shared/components/formFooterActions/form-footer-actions.component';
|
||||||
import { PriceInputComponent } from '@/shared/components/input/price-input.component';
|
import { PriceInputComponent } from '@/shared/components/input/price-input.component';
|
||||||
import { UikitFieldComponent, UikitLabelComponent } from '@/uikit';
|
import { UikitFieldComponent, UikitLabelComponent } from '@/uikit';
|
||||||
import { Component, computed, inject, signal } from '@angular/core';
|
import { formatWithCurrency } from '@/utils';
|
||||||
|
import {
|
||||||
|
Component,
|
||||||
|
EventEmitter,
|
||||||
|
inject,
|
||||||
|
Input,
|
||||||
|
Output,
|
||||||
|
signal,
|
||||||
|
SimpleChanges,
|
||||||
|
} from '@angular/core';
|
||||||
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
||||||
import { FormControl, FormsModule, ReactiveFormsModule, Validators } from '@angular/forms';
|
import { FormControl, FormsModule, ReactiveFormsModule, Validators } from '@angular/forms';
|
||||||
import { ButtonDirective } from 'primeng/button';
|
import { ButtonDirective } from 'primeng/button';
|
||||||
import { Select } from 'primeng/select';
|
import { Select } from 'primeng/select';
|
||||||
import { SelectButton } from 'primeng/selectbutton';
|
import { SelectButton } from 'primeng/selectbutton';
|
||||||
import { catchError, throwError } from 'rxjs';
|
|
||||||
import {
|
|
||||||
IPayment,
|
|
||||||
IPosOrderResponse,
|
|
||||||
PosPaymentResult,
|
|
||||||
TerminalSuccessPaymentPayload,
|
|
||||||
TOrderPaymentTypes,
|
|
||||||
} from '../../models';
|
|
||||||
import { PosPaymentBridgeAbstract } from '../../services/payment-bridge.abstract';
|
|
||||||
import { PosPaymentBridgeService } from '../../services/payment-bridge.service';
|
|
||||||
import { PosLandingStore } from '../../store/main.store';
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'pos-payment-form-dialog',
|
selector: 'shared-invoice-payment-form-dialog',
|
||||||
templateUrl: './form-dialog.component.html',
|
templateUrl: './form-dialog.component.html',
|
||||||
imports: [
|
imports: [
|
||||||
FormsModule,
|
FormsModule,
|
||||||
@@ -44,9 +49,16 @@ import { PosLandingStore } from '../../store/main.store';
|
|||||||
],
|
],
|
||||||
providers: [{ provide: PosPaymentBridgeAbstract, useExisting: PosPaymentBridgeService }],
|
providers: [{ provide: PosPaymentBridgeAbstract, useExisting: PosPaymentBridgeService }],
|
||||||
})
|
})
|
||||||
export class PosPaymentFormDialogComponent extends AbstractFormDialog<IPayment, IPosOrderResponse> {
|
export class PosPaymentFormDialogComponent extends AbstractFormDialog<IPayment, any> {
|
||||||
private readonly store = inject(PosLandingStore);
|
@Input() totalAmount = 0;
|
||||||
private readonly posInfo = inject(PosInfoStore);
|
@Input() submitOrderLoading = false;
|
||||||
|
@Input() isGoldGuild = false;
|
||||||
|
@Input() isUnknownCustomer = false;
|
||||||
|
@Output() onSubmitPayment = new EventEmitter<{
|
||||||
|
payment: IPayment;
|
||||||
|
settlementType: 'CASH' | 'CREDIT' | 'MIXED';
|
||||||
|
}>();
|
||||||
|
|
||||||
private readonly paymentBridge = inject(PosPaymentBridgeAbstract);
|
private readonly paymentBridge = inject(PosPaymentBridgeAbstract);
|
||||||
private readonly nativeBridgeService = inject(NativeBridgeService);
|
private readonly nativeBridgeService = inject(NativeBridgeService);
|
||||||
private readonly toastServices = inject(ToastService);
|
private readonly toastServices = inject(ToastService);
|
||||||
@@ -67,18 +79,11 @@ export class PosPaymentFormDialogComponent extends AbstractFormDialog<IPayment,
|
|||||||
];
|
];
|
||||||
|
|
||||||
readonly selectedSettlementType = signal<'CASH' | 'CREDIT' | 'MIXED'>('CASH');
|
readonly selectedSettlementType = signal<'CASH' | 'CREDIT' | 'MIXED'>('CASH');
|
||||||
|
readonly remainedAmount = signal(0);
|
||||||
readonly totalAmount = computed(() => this.store.orderPricingInfo().totalAmount);
|
|
||||||
|
|
||||||
readonly remainedAmount = signal(this.totalAmount());
|
|
||||||
setOffMax = signal(this.remainedAmount());
|
setOffMax = signal(this.remainedAmount());
|
||||||
cashMax = signal(this.remainedAmount());
|
cashMax = signal(this.remainedAmount());
|
||||||
terminalsMax = signal([this.remainedAmount()]);
|
terminalsMax = signal([this.remainedAmount()]);
|
||||||
|
|
||||||
readonly submitOrderLoading = computed(() => this.store.submitOrderLoading());
|
|
||||||
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) => ({
|
||||||
value: i + 1,
|
value: i + 1,
|
||||||
@@ -95,35 +100,51 @@ export class PosPaymentFormDialogComponent extends AbstractFormDialog<IPayment,
|
|||||||
|
|
||||||
private readonly injectedPaymentResultHandler = (payload: PosPaymentResult) => {
|
private readonly injectedPaymentResultHandler = (payload: PosPaymentResult) => {
|
||||||
if (payload.status === 'SUCCESS') {
|
if (payload.status === 'SUCCESS') {
|
||||||
const paidStepId = Number(payload.id || 0);
|
const paidStepId = this.resolvePaidStepId(payload);
|
||||||
this.payByTerminalSteps.update((prev) =>
|
const terminalId = payload.data?.terminalId || '';
|
||||||
prev.map((step, index) => {
|
const stan = payload.data?.stan || '';
|
||||||
if (step.value === paidStepId) {
|
const rrn = payload.data?.rrn || '';
|
||||||
return {
|
const customerCardNo = payload.data?.customerCardNO || '';
|
||||||
...step,
|
const transactionDateTime = payload.data?.transactionDateTime;
|
||||||
payed: true,
|
|
||||||
amount: this.terminalControls[step.value - 1]?.value || 0,
|
const targetIndex = Math.max(0, paidStepId - 1);
|
||||||
info: {
|
const prevSteps = this.payByTerminalSteps();
|
||||||
customer_card_no: payload.data?.customerCardNO || '',
|
const steps = [...prevSteps];
|
||||||
description: payload.message || '',
|
const currentStep = steps[targetIndex];
|
||||||
rrn: payload.data?.rrn || '',
|
if (!currentStep) return;
|
||||||
stan: payload.data?.stan || '',
|
|
||||||
terminal_id: payload.data?.terminalId || '',
|
steps[targetIndex] = {
|
||||||
transaction_date_time: new Date(payload.data?.transactionDateTime || ''),
|
...currentStep,
|
||||||
},
|
payed: true,
|
||||||
};
|
amount: Number(this.terminalControls[targetIndex]?.value || currentStep.amount || 0),
|
||||||
}
|
info: {
|
||||||
return step;
|
customer_card_no: customerCardNo,
|
||||||
})
|
description: payload.message || '',
|
||||||
);
|
rrn,
|
||||||
const terminalControl = this.terminalControls[paidStepId - 1];
|
stan,
|
||||||
|
terminal_id: terminalId,
|
||||||
|
transaction_date_time: transactionDateTime ? new Date(transactionDateTime) : new Date(),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
this.payByTerminalSteps.set(steps);
|
||||||
|
|
||||||
|
const terminalControl = this.terminalControls[targetIndex];
|
||||||
if (terminalControl) {
|
if (terminalControl) {
|
||||||
terminalControl.disable({ onlySelf: true, emitEvent: false });
|
terminalControl.disable({ onlySelf: true, emitEvent: false });
|
||||||
}
|
}
|
||||||
const amount = Number(
|
const amount = Number(
|
||||||
terminalControl?.value || this.extractAmountFromPaymentResult(payload) || 0
|
terminalControl?.value || this.extractAmountFromPaymentResult(payload) || 0
|
||||||
);
|
);
|
||||||
this.toastServices.success({ text: `پرداخت با موفقیت انجام شد. مبلغ: ${amount}` });
|
let successMessage = '';
|
||||||
|
if (this.payByTerminalSteps()?.length > 1) {
|
||||||
|
successMessage = `پرداخت مرحله ${paidStepId} به مبلغ ${formatWithCurrency(amount)} با موفقیت انجام شد.`;
|
||||||
|
} else {
|
||||||
|
successMessage = `پرداخت به مبلغ ${formatWithCurrency(amount)} با موفقیت انجام شد.`;
|
||||||
|
}
|
||||||
|
this.toastServices.success({
|
||||||
|
text: successMessage,
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
this.toastServices.error({ text: payload.message || 'پرداخت با دستگاه انجام نشد.' });
|
this.toastServices.error({ text: payload.message || 'پرداخت با دستگاه انجام نشد.' });
|
||||||
}
|
}
|
||||||
@@ -147,7 +168,7 @@ export class PosPaymentFormDialogComponent extends AbstractFormDialog<IPayment,
|
|||||||
(value.terminals || []).reduce((acc, curr) => (acc || 0) + (curr || 0), 0) || 0;
|
(value.terminals || []).reduce((acc, curr) => (acc || 0) + (curr || 0), 0) || 0;
|
||||||
|
|
||||||
const totalValue = (value.cash || 0) + (value.set_off || 0) + terminalTotal;
|
const totalValue = (value.cash || 0) + (value.set_off || 0) + terminalTotal;
|
||||||
const remainedAmount = this.totalAmount() - totalValue;
|
const remainedAmount = this.totalAmount - totalValue;
|
||||||
|
|
||||||
const setOffMax = remainedAmount + (value.set_off || 0);
|
const setOffMax = remainedAmount + (value.set_off || 0);
|
||||||
const cashMax = remainedAmount + (value.cash || 0);
|
const cashMax = remainedAmount + (value.cash || 0);
|
||||||
@@ -280,6 +301,26 @@ export class PosPaymentFormDialogComponent extends AbstractFormDialog<IPayment,
|
|||||||
|
|
||||||
override showSuccessMessage = false;
|
override showSuccessMessage = false;
|
||||||
|
|
||||||
|
private resetLimits() {
|
||||||
|
const amount = Number(this.totalAmount || 0);
|
||||||
|
this.remainedAmount.set(amount);
|
||||||
|
this.cashMax.set(amount);
|
||||||
|
this.setOffMax.set(amount);
|
||||||
|
this.terminalsMax.set(this.terminalControls.map(() => amount));
|
||||||
|
}
|
||||||
|
|
||||||
|
override ngOnInit(): void {
|
||||||
|
super.ngOnInit();
|
||||||
|
this.resetLimits();
|
||||||
|
}
|
||||||
|
|
||||||
|
override ngOnChanges(changes: SimpleChanges): void {
|
||||||
|
super.ngOnChanges(changes);
|
||||||
|
if (changes['totalAmount']) {
|
||||||
|
this.resetLimits();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ngOnDestroy() {
|
ngOnDestroy() {
|
||||||
this.restorePaymentCallback?.();
|
this.restorePaymentCallback?.();
|
||||||
}
|
}
|
||||||
@@ -293,8 +334,6 @@ export class PosPaymentFormDialogComponent extends AbstractFormDialog<IPayment,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.store.setSettlementType(this.selectedSettlementType());
|
|
||||||
|
|
||||||
const rawPayment = this.form.getRawValue();
|
const rawPayment = this.form.getRawValue();
|
||||||
|
|
||||||
const terminalPayments = this.payByTerminalSteps()
|
const terminalPayments = this.payByTerminalSteps()
|
||||||
@@ -310,50 +349,39 @@ export class PosPaymentFormDialogComponent extends AbstractFormDialog<IPayment,
|
|||||||
transaction_date_time: step.info.transaction_date_time || new Date(),
|
transaction_date_time: step.info.transaction_date_time || new Date(),
|
||||||
description: step.info.description || '',
|
description: step.info.description || '',
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const payment: IPayment = {
|
const payment: IPayment = {
|
||||||
cash: Number(rawPayment.cash || 0),
|
cash: Number(rawPayment.cash || 0),
|
||||||
set_off: Number(rawPayment.set_off || 0),
|
set_off: Number(rawPayment.set_off || 0),
|
||||||
terminals: terminalPayments,
|
terminals: terminalPayments,
|
||||||
};
|
};
|
||||||
|
|
||||||
this.store.setPayment(payment);
|
|
||||||
|
|
||||||
const terminalPaymentIsDone = this.checkTerminalPayments();
|
const terminalPaymentIsDone = this.checkTerminalPayments();
|
||||||
|
|
||||||
if (terminalPaymentIsDone) {
|
if (terminalPaymentIsDone) {
|
||||||
// this.toastService.info({
|
this.onSubmitPayment.emit({
|
||||||
// text: `terminalPayments[0].customer_card_no: ${payment.terminals[0]?.customer_card_no || ''}`,
|
payment,
|
||||||
// });
|
settlementType: this.selectedSettlementType(),
|
||||||
this.store
|
});
|
||||||
.submitOrder()
|
|
||||||
.pipe(
|
|
||||||
catchError((err) => {
|
|
||||||
return throwError(() => err);
|
|
||||||
})
|
|
||||||
)
|
|
||||||
.subscribe((res) => {
|
|
||||||
this.close();
|
|
||||||
this.onSubmit.emit(res);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private checkTerminalPayments(): boolean {
|
private checkTerminalPayments(): boolean {
|
||||||
const pendingTerminalPayments = this.getPendingTerminalPayments();
|
const pendingTerminalPayments = this.getPendingTerminalPayments();
|
||||||
if (pendingTerminalPayments.length) {
|
if (pendingTerminalPayments.length) {
|
||||||
if (this.nativeBridgeService.isEnabled()) {
|
// if (this.nativeBridgeService.isEnabled()) {
|
||||||
const nextPending = pendingTerminalPayments[0];
|
const nextPending = pendingTerminalPayments[0];
|
||||||
this.payByTerminal(nextPending.stepId, nextPending.amount);
|
this.payByTerminal(nextPending.stepId, nextPending.amount);
|
||||||
this.toastServices.info({
|
this.toastServices.info({
|
||||||
text: 'پس از تایید پرداخت هر مرحله، برای مرحله بعد دوباره روی ثبت پرداخت بزنید.',
|
text: 'پس از تایید پرداخت هر مرحله، برای مرحله بعد دوباره روی ثبت پرداخت بزنید.',
|
||||||
life: 3500,
|
life: 3500,
|
||||||
});
|
});
|
||||||
} else {
|
// } else {
|
||||||
this.toastServices.error({
|
// this.toastServices.error({
|
||||||
text: 'امکان ارتباط با دستگاه پرداخت میسر نیست.',
|
// text: 'امکان ارتباط با دستگاه پرداخت میسر نیست.',
|
||||||
});
|
// });
|
||||||
return false;
|
// return false;
|
||||||
}
|
// }
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -362,16 +390,22 @@ export class PosPaymentFormDialogComponent extends AbstractFormDialog<IPayment,
|
|||||||
}
|
}
|
||||||
|
|
||||||
private payByTerminal(id: number, amount: number) {
|
private payByTerminal(id: number, amount: number) {
|
||||||
|
// this.paymentBridge.emitPaymentResultForTest({
|
||||||
|
// status: 'SUCCESS',
|
||||||
|
// id: id.toString(),
|
||||||
|
// data: {
|
||||||
|
// customerCardNO: '123',
|
||||||
|
// responseCode: '2',
|
||||||
|
// rrn: 'rrn',
|
||||||
|
// stan: 'stan',
|
||||||
|
// terminalId: 'terminalId',
|
||||||
|
// transactionDateTime: new Date().toISOString(),
|
||||||
|
// },
|
||||||
|
// });
|
||||||
const payResult = this.nativeBridgeService.pay({
|
const payResult = this.nativeBridgeService.pay({
|
||||||
amount,
|
amount,
|
||||||
id: String(id),
|
id: String(id),
|
||||||
});
|
});
|
||||||
|
|
||||||
// if (!payResult.success) {
|
|
||||||
// return this.toastServices.warn({
|
|
||||||
// text: payResult.error || 'پرداخت با دستگاه انجام نشد. لطفا دوباره تلاش کنید.',
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private isTerminalStepPaid(stepId: number): boolean {
|
private isTerminalStepPaid(stepId: number): boolean {
|
||||||
@@ -395,8 +429,6 @@ export class PosPaymentFormDialogComponent extends AbstractFormDialog<IPayment,
|
|||||||
.filter(({ stepId, amount }) => amount > 0 && !this.isTerminalStepPaid(stepId));
|
.filter(({ stepId, amount }) => amount > 0 && !this.isTerminalStepPaid(stepId));
|
||||||
}
|
}
|
||||||
|
|
||||||
override onSuccess(response: IPosOrderResponse): void {}
|
|
||||||
|
|
||||||
private extractAmountFromPaymentResult(payload: unknown): number | null {
|
private extractAmountFromPaymentResult(payload: unknown): number | null {
|
||||||
if (!payload || typeof payload !== 'object') return null;
|
if (!payload || typeof payload !== 'object') return null;
|
||||||
const amount = (payload as { amount?: unknown }).amount;
|
const amount = (payload as { amount?: unknown }).amount;
|
||||||
@@ -405,4 +437,14 @@ export class PosPaymentFormDialogComponent extends AbstractFormDialog<IPayment,
|
|||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private resolvePaidStepId(payload: PosPaymentResult): number {
|
||||||
|
const rawId = Number(payload.id || 0);
|
||||||
|
if (rawId > 0 && this.payByTerminalSteps().some((step) => step.value === rawId)) return rawId;
|
||||||
|
|
||||||
|
const pendingStep = this.terminalControls.findIndex(
|
||||||
|
(control, index) => Number(control.value || 0) > 0 && !this.isTerminalStepPaid(index + 1)
|
||||||
|
);
|
||||||
|
return pendingStep >= 0 ? pendingStep + 1 : 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export * from './form-dialog.component';
|
||||||
@@ -13,9 +13,14 @@
|
|||||||
<shared-return-form-item
|
<shared-return-form-item
|
||||||
[control]="item.controls.quantity"
|
[control]="item.controls.quantity"
|
||||||
[good]="goodItems()[$index]!.good_snapshot"
|
[good]="goodItems()[$index]!.good_snapshot"
|
||||||
[index]="$index" />
|
[index]="$index"
|
||||||
|
[maxQuantity]="item.controls.maxQuantity.value"
|
||||||
|
[canRemove]="canRemoveItem()"
|
||||||
|
(onRemove)="removeItem($index)" />
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
<pos-order-price-info-card [info]="totalPriceInfo" />
|
||||||
|
|
||||||
<app-form-footer-actions />
|
<app-form-footer-actions />
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { Maybe } from '@/core';
|
import { Maybe } from '@/core';
|
||||||
|
import { POSOrderPriceInfoCardComponent } from '@/domains/pos/modules/shop/components/order/price-info-card.component';
|
||||||
import { AbstractForm } from '@/shared/abstractClasses';
|
import { AbstractForm } from '@/shared/abstractClasses';
|
||||||
import { formatDate, GREGORIAN_DATE_FORMATS } from '@/utils';
|
import { formatDate, GREGORIAN_DATE_FORMATS } from '@/utils';
|
||||||
import { Component, computed, Input, signal, SimpleChanges } from '@angular/core';
|
import { Component, computed, Input, signal, SimpleChanges } from '@angular/core';
|
||||||
@@ -32,6 +33,7 @@ type BackFromSaleFormValue = {
|
|||||||
ReactiveFormsModule,
|
ReactiveFormsModule,
|
||||||
FieldInvoiceDateComponent,
|
FieldInvoiceDateComponent,
|
||||||
SharedReturnFormItemComponent,
|
SharedReturnFormItemComponent,
|
||||||
|
POSOrderPriceInfoCardComponent,
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
export class SharedReturnFormComponent extends AbstractForm<
|
export class SharedReturnFormComponent extends AbstractForm<
|
||||||
@@ -54,11 +56,33 @@ export class SharedReturnFormComponent extends AbstractForm<
|
|||||||
return this.form.controls.items;
|
return this.form.controls.items;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
canRemoveItem() {
|
||||||
|
return this.items.length > 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
removeItem(index: number) {
|
||||||
|
if (!this.canRemoveItem()) {
|
||||||
|
this.toastService.warn({ text: 'حداقل یک آیتم باید باقی بماند.' });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.items.removeAt(index);
|
||||||
|
this.form.controls.items.updateValueAndValidity();
|
||||||
|
this.recalculateTotalPriceInfo();
|
||||||
|
}
|
||||||
|
|
||||||
preparedCalculation = signal<Maybe<any>>(null);
|
preparedCalculation = signal<Maybe<any>>(null);
|
||||||
|
totalPriceInfo = {
|
||||||
|
totalBaseAmount: 0,
|
||||||
|
discountAmount: 0,
|
||||||
|
taxAmount: 0,
|
||||||
|
totalAmount: 0,
|
||||||
|
};
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
this.initForm();
|
this.initForm();
|
||||||
this.prepareCalculation();
|
this.prepareCalculation();
|
||||||
|
this.recalculateTotalPriceInfo();
|
||||||
}
|
}
|
||||||
|
|
||||||
prepareCalculation() {
|
prepareCalculation() {
|
||||||
@@ -85,6 +109,38 @@ export class SharedReturnFormComponent extends AbstractForm<
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.items.valueChanges.subscribe(() => {
|
||||||
|
this.recalculateTotalPriceInfo();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private recalculateTotalPriceInfo() {
|
||||||
|
const initialMap = new Map((this.initialValues || []).map((item) => [String(item.id), item]));
|
||||||
|
const currentItems = this.items.getRawValue();
|
||||||
|
|
||||||
|
this.totalPriceInfo = currentItems.reduce(
|
||||||
|
(acc, current) => {
|
||||||
|
const source = initialMap.get(String(current.id || ''));
|
||||||
|
if (!source) return acc;
|
||||||
|
|
||||||
|
const maxQuantity = Number(current.maxQuantity || 0);
|
||||||
|
const quantity = Number(current.quantity || 0);
|
||||||
|
const ratio = maxQuantity > 0 ? quantity / maxQuantity : 0;
|
||||||
|
|
||||||
|
acc.totalBaseAmount += Number(source.unit_price || 0) * quantity;
|
||||||
|
acc.discountAmount += Number(source.discount_amount || 0) * ratio;
|
||||||
|
acc.taxAmount += Number(source.tax_amount || 0) * ratio;
|
||||||
|
acc.totalAmount += Number(source.total_amount || 0) * ratio;
|
||||||
|
return acc;
|
||||||
|
},
|
||||||
|
{
|
||||||
|
totalBaseAmount: 0,
|
||||||
|
discountAmount: 0,
|
||||||
|
taxAmount: 0,
|
||||||
|
totalAmount: 0,
|
||||||
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
override ngOnInit(): void {
|
override ngOnInit(): void {
|
||||||
@@ -98,15 +154,17 @@ export class SharedReturnFormComponent extends AbstractForm<
|
|||||||
}
|
}
|
||||||
|
|
||||||
override submitForm() {
|
override submitForm() {
|
||||||
const payload = this.items.getRawValue().map((item) => ({
|
const rawItems = this.items.getRawValue();
|
||||||
|
const payload = rawItems.map((item) => ({
|
||||||
id: item.id || '',
|
id: item.id || '',
|
||||||
quantity: Number(item.quantity || 0),
|
quantity: Number(item.quantity || 0),
|
||||||
maxQuantity: Number(item.maxQuantity || 0),
|
maxQuantity: Number(item.maxQuantity || 0),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const hasChanges = payload.some(
|
const initialMap = new Map(
|
||||||
(item, index) => item.quantity !== Number(this.initialValues?.[index]?.quantity || 0)
|
(this.initialValues || []).map((item) => [String(item.id), Number(item.quantity || 0)])
|
||||||
);
|
);
|
||||||
|
const hasChanges = payload.some((item) => item.quantity !== Number(initialMap.get(item.id) || 0));
|
||||||
|
|
||||||
if (!hasChanges) {
|
if (!hasChanges) {
|
||||||
this.toastService.warn({
|
this.toastService.warn({
|
||||||
|
|||||||
@@ -1,13 +1,34 @@
|
|||||||
<div>
|
<div class="border-surface-border rounded-xl border p-2">
|
||||||
<p-divider align="right">
|
<div class="flex items-center gap-3">
|
||||||
{{ index + 1 }}- <b>{{ good.name }}</b>
|
<img
|
||||||
</p-divider>
|
[src]="good.image_url || ''"
|
||||||
<app-input
|
loading="lazy"
|
||||||
[control]="control"
|
decoding="async"
|
||||||
name="quantity"
|
class="bg-surface-100 border-surface-border h-20 w-20 shrink-0 rounded-md border object-cover" />
|
||||||
[label]="good.measure_unit.name"
|
<div class="flex grow flex-col gap-2 overflow-hidden py-1">
|
||||||
type="number"
|
<div class="flex items-center justify-between gap-2">
|
||||||
[suffix]="good.measure_unit.name"
|
<span class="overflow-hidden text-base font-bold text-nowrap text-ellipsis">
|
||||||
[max]="control.defaultValue || undefined"
|
{{ good.name }}
|
||||||
[min]="0" />
|
</span>
|
||||||
|
<button
|
||||||
|
pButton
|
||||||
|
type="button"
|
||||||
|
icon="pi pi-trash"
|
||||||
|
size="small"
|
||||||
|
severity="danger"
|
||||||
|
outlined
|
||||||
|
[disabled]="!canRemove"
|
||||||
|
(click)="remove()"></button>
|
||||||
|
</div>
|
||||||
|
<span class="text-muted-color text-sm">مقدار: {{ maxQuantity || 0 }} {{ good.measure_unit.name }}</span>
|
||||||
|
<app-input
|
||||||
|
[control]="control"
|
||||||
|
name="quantity"
|
||||||
|
label="مقدار"
|
||||||
|
type="number"
|
||||||
|
[suffix]="good.measure_unit.name"
|
||||||
|
[max]="maxQuantity || undefined"
|
||||||
|
[min]="0" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { Maybe } from '@/core';
|
import { Maybe } from '@/core';
|
||||||
import { Component, Input, signal, SimpleChanges } from '@angular/core';
|
import { Component, EventEmitter, Input, Output, signal, SimpleChanges } from '@angular/core';
|
||||||
import { FormControl, FormGroup, ReactiveFormsModule } from '@angular/forms';
|
import { FormControl, FormGroup, ReactiveFormsModule } from '@angular/forms';
|
||||||
import { Divider } from 'primeng/divider';
|
import { ButtonDirective } from 'primeng/button';
|
||||||
import { InputComponent } from '../../input/input.component';
|
import { InputComponent } from '../../input/input.component';
|
||||||
import { Goodsnapshot } from '../sale-invoice-full-response.model';
|
import { Goodsnapshot } from '../sale-invoice-full-response.model';
|
||||||
|
|
||||||
@@ -20,12 +20,15 @@ type BackFromSaleFormValue = {
|
|||||||
@Component({
|
@Component({
|
||||||
selector: 'shared-return-form-item',
|
selector: 'shared-return-form-item',
|
||||||
templateUrl: './item-form.component.html',
|
templateUrl: './item-form.component.html',
|
||||||
imports: [InputComponent, ReactiveFormsModule, Divider],
|
imports: [InputComponent, ReactiveFormsModule, ButtonDirective],
|
||||||
})
|
})
|
||||||
export class SharedReturnFormItemComponent {
|
export class SharedReturnFormItemComponent {
|
||||||
@Input({ required: true }) control!: FormControl<number | null>;
|
@Input({ required: true }) control!: FormControl<number | null>;
|
||||||
@Input({ required: true }) good!: Goodsnapshot;
|
@Input({ required: true }) good!: Goodsnapshot;
|
||||||
@Input({ required: true }) index!: number;
|
@Input({ required: true }) index!: number;
|
||||||
|
@Input() canRemove = true;
|
||||||
|
@Input() maxQuantity: number | null = null;
|
||||||
|
@Output() onRemove = new EventEmitter<void>();
|
||||||
|
|
||||||
preparedCalculation = signal<Maybe<any>>(null);
|
preparedCalculation = signal<Maybe<any>>(null);
|
||||||
|
|
||||||
@@ -42,4 +45,8 @@ export class SharedReturnFormItemComponent {
|
|||||||
this.prepareCalculation();
|
this.prepareCalculation();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
remove() {
|
||||||
|
this.onRemove.emit();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -60,26 +60,12 @@
|
|||||||
</div>
|
</div>
|
||||||
</shared-light-bottomsheet>
|
</shared-light-bottomsheet>
|
||||||
|
|
||||||
<shared-light-bottomsheet [(visible)]="showCorrectionPaymentDialog" header="پرداخت اصلاحی">
|
<shared-invoice-payment-form-dialog
|
||||||
<div class="flex flex-col gap-4">
|
[(visible)]="showCorrectionPaymentDialog"
|
||||||
<div class="text-sm">مبلغ اصلاحی افزایش یافته است. لطفاً مبلغ پرداختی را ثبت کنید.</div>
|
[totalAmount]="correctionRequiredPayment()"
|
||||||
<div class="border-surface-border rounded-lg border p-3">
|
[submitOrderLoading]="false"
|
||||||
<div class="text-muted-color text-xs">حداقل مبلغ موردنیاز</div>
|
[isGoldGuild]="false"
|
||||||
<div class="text-lg font-bold" [appPriceMask]="correctionRequiredPayment()"></div>
|
[isUnknownCustomer]="true"
|
||||||
</div>
|
(onSubmitPayment)="confirmCorrectionPayment($event)"
|
||||||
<div class="flex flex-col gap-2">
|
(onClose)="cancelCorrectionPayment()" />
|
||||||
<label class="text-sm font-medium">مبلغ پرداختی</label>
|
|
||||||
<input
|
|
||||||
type="number"
|
|
||||||
min="0"
|
|
||||||
class="border-surface-border w-full rounded-md border px-3 py-2"
|
|
||||||
[value]="correctionPaymentAmount()"
|
|
||||||
(input)="onCorrectionPaymentAmountChange($event)" />
|
|
||||||
</div>
|
|
||||||
<div class="flex justify-end gap-2">
|
|
||||||
<button pButton type="button" severity="secondary" label="انصراف" (click)="cancelCorrectionPayment()"></button>
|
|
||||||
<button pButton type="button" label="تایید پرداخت" (click)="confirmCorrectionPayment()"></button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</shared-light-bottomsheet>
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,13 +4,13 @@ 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 { IPayment, IPosOrderItem } from '@/domains/pos/modules/shop/models';
|
||||||
import { PosInfoStore } from '@/domains/pos/store';
|
import { PosInfoStore } from '@/domains/pos/store';
|
||||||
import { TspProviderResponseStatus } from '@/shared/catalog';
|
import { TspProviderResponseStatus } from '@/shared/catalog';
|
||||||
import { SharedLightBottomsheetComponent } from '@/shared/components';
|
import { SharedLightBottomsheetComponent } from '@/shared/components';
|
||||||
|
import { PosPaymentFormDialogComponent } from '@/shared/components/invoices/payment';
|
||||||
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 { PriceMaskDirective } from '@/shared/directives';
|
|
||||||
import { UikitEmptyStateComponent } from '@/uikit';
|
import { UikitEmptyStateComponent } from '@/uikit';
|
||||||
import {
|
import {
|
||||||
Component,
|
Component,
|
||||||
@@ -67,8 +67,8 @@ type TActionMenuItem = {
|
|||||||
SaleInvoiceSingleInfoCardComponent,
|
SaleInvoiceSingleInfoCardComponent,
|
||||||
QRCodeComponent,
|
QRCodeComponent,
|
||||||
Card,
|
Card,
|
||||||
PriceMaskDirective,
|
|
||||||
Menu,
|
Menu,
|
||||||
|
PosPaymentFormDialogComponent,
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
export class SharedSaleInvoiceSingleViewComponent {
|
export class SharedSaleInvoiceSingleViewComponent {
|
||||||
@@ -105,7 +105,6 @@ export class SharedSaleInvoiceSingleViewComponent {
|
|||||||
showQrCodeDialog = signal(false);
|
showQrCodeDialog = signal(false);
|
||||||
showCorrectionPaymentDialog = signal(false);
|
showCorrectionPaymentDialog = signal(false);
|
||||||
correctionRequiredPayment = signal(0);
|
correctionRequiredPayment = signal(0);
|
||||||
correctionPaymentAmount = signal(0);
|
|
||||||
readonly isApplication = config.isPosApplication;
|
readonly isApplication = config.isPosApplication;
|
||||||
private pendingCorrectionSubmitResolver: Maybe<(allowed: boolean) => void> = null;
|
private pendingCorrectionSubmitResolver: Maybe<(allowed: boolean) => void> = null;
|
||||||
|
|
||||||
@@ -292,7 +291,6 @@ export class SharedSaleInvoiceSingleViewComponent {
|
|||||||
|
|
||||||
const requiredAmount = newTotalAmount - oldTotalAmount;
|
const requiredAmount = newTotalAmount - oldTotalAmount;
|
||||||
this.correctionRequiredPayment.set(requiredAmount);
|
this.correctionRequiredPayment.set(requiredAmount);
|
||||||
this.correctionPaymentAmount.set(requiredAmount);
|
|
||||||
this.showCorrectionPaymentDialog.set(true);
|
this.showCorrectionPaymentDialog.set(true);
|
||||||
|
|
||||||
const allowByDialog = await new Promise<boolean>((resolve) => {
|
const allowByDialog = await new Promise<boolean>((resolve) => {
|
||||||
@@ -306,17 +304,10 @@ export class SharedSaleInvoiceSingleViewComponent {
|
|||||||
return (await this.beforeCorrectionSubmit?.(mapped)) ?? true;
|
return (await this.beforeCorrectionSubmit?.(mapped)) ?? true;
|
||||||
}
|
}
|
||||||
|
|
||||||
onCorrectionPaymentAmountChange(event: Event) {
|
confirmCorrectionPayment(_event: {
|
||||||
const value = Number((event.target as HTMLInputElement)?.value || 0);
|
payment: IPayment;
|
||||||
this.correctionPaymentAmount.set(Number.isFinite(value) ? value : 0);
|
settlementType: 'CASH' | 'CREDIT' | 'MIXED';
|
||||||
}
|
}) {
|
||||||
|
|
||||||
confirmCorrectionPayment() {
|
|
||||||
if (this.correctionPaymentAmount() < this.correctionRequiredPayment()) {
|
|
||||||
this.toastService.warn({ text: 'مبلغ پرداختی باید حداقل برابر با افزایش مبلغ باشد.' });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.showCorrectionPaymentDialog.set(false);
|
this.showCorrectionPaymentDialog.set(false);
|
||||||
this.pendingCorrectionSubmitResolver?.(true);
|
this.pendingCorrectionSubmitResolver?.(true);
|
||||||
this.pendingCorrectionSubmitResolver = null;
|
this.pendingCorrectionSubmitResolver = null;
|
||||||
|
|||||||
Reference in New Issue
Block a user