feat: implement payment result handling; refactor payment components and enhance terminal payment logic
This commit is contained in:
@@ -1,40 +1,41 @@
|
||||
<div class="w-full min-h-[60svh] p-4 md:p-6">
|
||||
<section class="mx-auto max-w-3xl overflow-hidden rounded-2xl border border-surface-200 bg-white">
|
||||
<div class="bg-gradient-to-l from-emerald-50 via-teal-50 to-cyan-50 p-5 md:p-7">
|
||||
<p class="mb-2 inline-flex items-center rounded-full bg-white px-3 py-1 text-xs font-semibold text-emerald-700">
|
||||
{{ brandingInfo.title }}
|
||||
</p>
|
||||
<span class="block mb-4 text-xl font-bold text-surface-900 md:text-3xl">{{ brandingInfo.fullTitle }}</span>
|
||||
<p class="mt-3 max-w-2xl text-sm leading-7 text-surface-700 md:text-base">
|
||||
این نسخه برای استفاده روزانه روی دستگاه های PSP بهینه شده است تا عملیات ثبت و صدور صورتحسابهای مالیاتی با سرعت
|
||||
بالاتر، پایداری بیشتر و تجربه کاربری ساده تر انجام شود.
|
||||
</p>
|
||||
</div>
|
||||
@defer {
|
||||
<div class="w-full min-h-[60svh] p-4 md:p-6">
|
||||
<section class="mx-auto max-w-3xl overflow-hidden rounded-2xl border border-surface-200 bg-white">
|
||||
<div class="bg-gradient-to-l from-emerald-50 via-teal-50 to-cyan-50 p-5 md:p-7">
|
||||
<p class="mb-2 inline-flex items-center rounded-full bg-white px-3 py-1 text-xs font-semibold text-emerald-700">
|
||||
{{ brandingInfo.title }}
|
||||
</p>
|
||||
<span class="block mb-4 text-xl font-bold text-surface-900 md:text-3xl">{{ brandingInfo.fullTitle }}</span>
|
||||
<p class="mt-3 max-w-2xl text-sm leading-7 text-surface-700 md:text-base">
|
||||
این نسخه برای استفاده روزانه روی دستگاه های PSP بهینه شده است تا عملیات ثبت و صدور صورتحسابهای مالیاتی با
|
||||
سرعت بالاتر، پایداری بیشتر و تجربه کاربری ساده تر انجام شود.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="grid gap-3 p-4 md:grid-cols-3 md:gap-4 md:p-6">
|
||||
<!-- <article class="rounded-xl border border-surface-200 bg-surface-50 p-4">
|
||||
<div class="grid gap-3 p-4 md:grid-cols-3 md:gap-4 md:p-6">
|
||||
<!-- <article class="rounded-xl border border-surface-200 bg-surface-50 p-4">
|
||||
<h2 class="mb-2 text-sm font-bold text-surface-900">پایداری در وب ویو</h2>
|
||||
<p class="text-sm leading-6 text-surface-700">
|
||||
رفتار رابط کاربری برای محیط WebView تنظیم شده تا روی سخت افزارهای ضعیف تر هم اجرای روان تری داشته باشد.
|
||||
</p>
|
||||
</article> -->
|
||||
|
||||
<article class="rounded-xl border border-surface-200 bg-surface-50 p-4">
|
||||
<span class="block mb-4 text-xl font-bold text-surface-900">تمرکز بر سرعت عملیات</span>
|
||||
<p class="text-sm leading-6 text-surface-700">
|
||||
مسیرهای کلیدی مانند انتخاب کالا، ورود مقادیر و ثبت نهایی برای کاهش تاخیر و افزایش سرعت کاربر بهینه شده اند.
|
||||
</p>
|
||||
</article>
|
||||
<article class="rounded-xl border border-surface-200 bg-surface-50 p-4">
|
||||
<span class="block mb-4 text-xl font-bold text-surface-900">تمرکز بر سرعت عملیات</span>
|
||||
<p class="text-sm leading-6 text-surface-700">
|
||||
مسیرهای کلیدی مانند انتخاب کالا، ورود مقادیر و ثبت نهایی برای کاهش تاخیر و افزایش سرعت کاربر بهینه شده اند.
|
||||
</p>
|
||||
</article>
|
||||
|
||||
<article class="rounded-xl border border-surface-200 bg-surface-50 p-4">
|
||||
<span class="block mb-4 text-xl font-bold text-surface-900">سازگار با سناریوهای فروش</span>
|
||||
<p class="text-sm leading-6 text-surface-700">
|
||||
طراحی این نسخه بر مبنای سناریوهای واقعی فروشگاه انجام شده تا اپراتور با کمترین کلیک فرآیند را تکمیل کند.
|
||||
</p>
|
||||
</article>
|
||||
</div>
|
||||
<article class="rounded-xl border border-surface-200 bg-surface-50 p-4">
|
||||
<span class="block mb-4 text-xl font-bold text-surface-900">سازگار با سناریوهای فروش</span>
|
||||
<p class="text-sm leading-6 text-surface-700">
|
||||
طراحی این نسخه بر مبنای سناریوهای واقعی فروشگاه انجام شده تا اپراتور با کمترین کلیک فرآیند را تکمیل کند.
|
||||
</p>
|
||||
</article>
|
||||
</div>
|
||||
|
||||
<!-- <div class="mx-4 mb-4 rounded-xl border border-surface-200 bg-surface-50 p-4 md:mx-6">
|
||||
<!-- <div class="mx-4 mb-4 rounded-xl border border-surface-200 bg-surface-50 p-4 md:mx-6">
|
||||
<div class="flex flex-wrap items-center justify-between gap-3">
|
||||
<div class="text-sm text-surface-700">
|
||||
نسخه برنامه:
|
||||
@@ -52,10 +53,11 @@
|
||||
</div>
|
||||
</div> -->
|
||||
|
||||
<div class="border-t border-surface-200 bg-surface-50 px-4 py-4 md:px-6">
|
||||
<p class="text-xs leading-6 text-surface-600 md:text-sm">
|
||||
در صورت نیاز به راهنمایی بیشتر، به صفحهی راهنمای استفاده مراجعه کنید یا با پشتیبانی تماس بگیرید.
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
<div class="border-t border-surface-200 bg-surface-50 px-4 py-4 md:px-6">
|
||||
<p class="text-xs leading-6 text-surface-600 md:text-sm">
|
||||
در صورت نیاز به راهنمایی بیشتر، به صفحهی راهنمای استفاده مراجعه کنید یا با پشتیبانی تماس بگیرید.
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
}
|
||||
|
||||
@@ -45,7 +45,6 @@ export class PosConfigPrintFormComponent extends AbstractForm<
|
||||
override async ngOnInit() {
|
||||
this.loading.set(true);
|
||||
const initialValues = await this.service.get();
|
||||
console.log('initialValues', initialValues);
|
||||
|
||||
this.form.patchValue(initialValues);
|
||||
this.loading.set(false);
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
<div class="form-group">
|
||||
<uikit-field label="تعداد مراحل پرداخت با پایانه" name="pay_by_terminal_steps">
|
||||
<p-select
|
||||
[options]="payByTerminalSteps"
|
||||
[options]="payByTerminalSteps()"
|
||||
[ngModel]="selectedPayByTerminalStep()"
|
||||
[ngModelOptions]="{ standalone: true }"
|
||||
optionLabel="label"
|
||||
@@ -33,13 +33,14 @@
|
||||
[label]="$index === 0 ? 'پرداخت با پایانه' : 'پرداخت با پایانه - مرحله ' + ($index + 1)"
|
||||
type="price"
|
||||
[max]="terminalsMax()[$index]"
|
||||
[disabled]="payByTerminalSteps()[$index].payed"
|
||||
>
|
||||
<ng-template #suffixTemp>
|
||||
<button
|
||||
pButton
|
||||
size="small"
|
||||
type="button"
|
||||
[disabled]="!remainedAmount()"
|
||||
[disabled]="!remainedAmount() || payByTerminalSteps()[$index].payed"
|
||||
(click)="fillTerminalRemained($index)"
|
||||
>
|
||||
تمامی بدهی باقیمانده
|
||||
|
||||
@@ -12,7 +12,13 @@ import { FormControl, FormsModule, ReactiveFormsModule, Validators } from '@angu
|
||||
import { ButtonDirective } from 'primeng/button';
|
||||
import { Select } from 'primeng/select';
|
||||
import { catchError, throwError } from 'rxjs';
|
||||
import { IPayment, IPosOrderResponse, TOrderPaymentTypes } from '../../models';
|
||||
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';
|
||||
@@ -48,25 +54,57 @@ export class PosPaymentFormDialogComponent extends AbstractFormDialog<IPayment,
|
||||
|
||||
submitOrderLoading = computed(() => this.store.submitOrderLoading());
|
||||
|
||||
payByTerminalSteps = Array.from({ length: 10 }, (_, i) => ({
|
||||
value: i + 1,
|
||||
label: `${i + 1} مرحلهای`,
|
||||
}));
|
||||
payByTerminalSteps = signal(
|
||||
Array.from({ length: 10 }, (_, i) => ({
|
||||
value: i + 1,
|
||||
payed: false,
|
||||
info: {} as TerminalSuccessPaymentPayload,
|
||||
label: `${i + 1} مرحلهای`,
|
||||
amount: 0,
|
||||
})),
|
||||
);
|
||||
|
||||
selectedPayByTerminalStep = signal(1);
|
||||
payedInTerminal = signal<Array<number>>([]);
|
||||
|
||||
private restorePaymentCallback: Maybe<() => void> = null;
|
||||
|
||||
private readonly injectedPaymentResultHandler = (payload: unknown) => {
|
||||
alert('پرداخت با موفقیت انجام شد');
|
||||
this.toastServices.success({ text: 'شد شد شد شد شد' });
|
||||
// this.handlePaymentResult(payload);
|
||||
private readonly injectedPaymentResultHandler = (payload: PosPaymentResult) => {
|
||||
if (payload.status === 'SUCCESS') {
|
||||
const paidStepId = Number(payload.id || 0);
|
||||
this.payByTerminalSteps.update((prev) =>
|
||||
prev.map((step, index) => {
|
||||
if (step.value === paidStepId) {
|
||||
return {
|
||||
...step,
|
||||
payed: true,
|
||||
amount: this.terminalControls[step.value - 1]?.value || 0,
|
||||
info: {
|
||||
customer_card_no: payload.data?.customerCardNO || '',
|
||||
description: payload.message || '',
|
||||
rrn: payload.data?.rrn || '',
|
||||
stan: payload.data?.stan || '',
|
||||
terminal_id: payload.data?.terminalId || '',
|
||||
transaction_date_time: new Date(payload.data?.transactionDateTime || ''),
|
||||
},
|
||||
};
|
||||
}
|
||||
return step;
|
||||
}),
|
||||
);
|
||||
const terminalControl = this.terminalControls[paidStepId - 1];
|
||||
if (terminalControl) {
|
||||
terminalControl.disable({ onlySelf: true, emitEvent: false });
|
||||
}
|
||||
const amount = Number(
|
||||
terminalControl?.value || this.extractAmountFromPaymentResult(payload) || 0,
|
||||
);
|
||||
this.toastServices.success({ text: `پرداخت با موفقیت انجام شد. مبلغ: ${amount}` });
|
||||
} else {
|
||||
this.toastServices.error({ text: payload.message || 'پرداخت با دستگاه انجام نشد.' });
|
||||
}
|
||||
};
|
||||
|
||||
ngAfterViewInit() {
|
||||
console.log('ngOnViewInit');
|
||||
|
||||
this.restorePaymentCallback = this.paymentBridge.registerPaymentResultListener(
|
||||
this.injectedPaymentResultHandler,
|
||||
);
|
||||
@@ -118,7 +156,11 @@ export class PosPaymentFormDialogComponent extends AbstractFormDialog<IPayment,
|
||||
this.terminalsMax.update((max) => {
|
||||
const terminalMax = remainedAmount + (terminal.value || 0);
|
||||
terminal.addValidators([Validators.max(terminalMax)]);
|
||||
if (terminalMax) {
|
||||
if (this.isTerminalStepPaid(i + 1)) {
|
||||
terminal.disable({
|
||||
onlySelf: true,
|
||||
});
|
||||
} else if (terminalMax) {
|
||||
terminal.enable({
|
||||
onlySelf: true,
|
||||
});
|
||||
@@ -145,7 +187,13 @@ export class PosPaymentFormDialogComponent extends AbstractFormDialog<IPayment,
|
||||
}
|
||||
|
||||
changePayByTerminalSteps(steps: number) {
|
||||
const normalizedSteps = Math.max(1, Number(steps) || 1);
|
||||
const minSteps = this.getMinAllowedSteps();
|
||||
const normalizedSteps = Math.max(minSteps, Number(steps) || 1);
|
||||
if ((Number(steps) || 1) < minSteps) {
|
||||
this.toastServices.warn({
|
||||
text: `حداقل تعداد مراحل به دلیل پرداخت های انجام شده ${minSteps} است.`,
|
||||
});
|
||||
}
|
||||
this.selectedPayByTerminalStep.set(normalizedSteps);
|
||||
|
||||
const terminals = this.form.controls.terminals;
|
||||
@@ -219,10 +267,24 @@ export class PosPaymentFormDialogComponent extends AbstractFormDialog<IPayment,
|
||||
}
|
||||
|
||||
const rawPayment = this.form.getRawValue();
|
||||
|
||||
const terminalPayments = this.payByTerminalSteps()
|
||||
.slice(0, this.selectedPayByTerminalStep())
|
||||
.filter((step) => step.payed)
|
||||
.map((step) => ({
|
||||
amount: Number(this.terminalControls[step.value - 1]?.value || step.amount || 0),
|
||||
terminal_id: step.info.terminal_id || '',
|
||||
stan: step.info.stan || '',
|
||||
rrn: step.info.rrn || '',
|
||||
response_code: '00',
|
||||
customer_card_no: step.info.customer_card_no || '',
|
||||
transaction_date_time: step.info.transaction_date_time || new Date(),
|
||||
description: step.info.description || '',
|
||||
}));
|
||||
const payment: IPayment = {
|
||||
cash: Number(rawPayment.cash || 0),
|
||||
set_off: Number(rawPayment.set_off || 0),
|
||||
terminals: (rawPayment.terminals || []).map((value) => Number(value || 0)),
|
||||
terminals: terminalPayments,
|
||||
};
|
||||
|
||||
this.store.setPayment(payment);
|
||||
@@ -230,6 +292,9 @@ export class PosPaymentFormDialogComponent extends AbstractFormDialog<IPayment,
|
||||
const terminalPaymentIsDone = this.checkTerminalPayments();
|
||||
|
||||
if (terminalPaymentIsDone) {
|
||||
this.toastService.info({
|
||||
text: `terminalPayments[0].customer_card_no: ${payment.terminals[0]?.customer_card_no || ''}`,
|
||||
});
|
||||
this.store
|
||||
.submitOrder()
|
||||
.pipe(
|
||||
@@ -245,23 +310,15 @@ export class PosPaymentFormDialogComponent extends AbstractFormDialog<IPayment,
|
||||
}
|
||||
|
||||
private checkTerminalPayments(): boolean {
|
||||
this.form.controls.terminals.controls = this.form.controls.terminals.controls.filter(
|
||||
(control) => control.value,
|
||||
);
|
||||
const terminalPayments = this.form.controls.terminals.controls.map(
|
||||
(terminal) => terminal.value!,
|
||||
);
|
||||
|
||||
this.selectedPayByTerminalStep.set(terminalPayments.length || 1);
|
||||
|
||||
if (terminalPayments.length) {
|
||||
const pendingTerminalPayments = this.getPendingTerminalPayments();
|
||||
if (pendingTerminalPayments.length) {
|
||||
if (this.nativeBridgeService.isEnabled()) {
|
||||
for (let i in terminalPayments) {
|
||||
const terminal = terminalPayments[i];
|
||||
this.toastServices.info({ text: 'در حال پردازش پرداخت با دستگاه...', life: 3000 });
|
||||
this.payByTerminal(parseInt(i), terminal);
|
||||
break;
|
||||
}
|
||||
const nextPending = pendingTerminalPayments[0];
|
||||
this.payByTerminal(nextPending.stepId, nextPending.amount);
|
||||
this.toastServices.info({
|
||||
text: 'پس از تایید پرداخت هر مرحله، برای مرحله بعد دوباره روی ثبت پرداخت بزنید.',
|
||||
life: 3500,
|
||||
});
|
||||
} else {
|
||||
this.toastServices.error({
|
||||
text: 'امکان ارتباط با دستگاه پرداخت میسر نیست.',
|
||||
@@ -278,7 +335,7 @@ export class PosPaymentFormDialogComponent extends AbstractFormDialog<IPayment,
|
||||
private payByTerminal(id: number, amount: number) {
|
||||
const payResult = this.nativeBridgeService.pay({
|
||||
amount,
|
||||
id: id.toString(),
|
||||
id: String(id),
|
||||
});
|
||||
|
||||
// if (!payResult.success) {
|
||||
@@ -288,6 +345,27 @@ export class PosPaymentFormDialogComponent extends AbstractFormDialog<IPayment,
|
||||
// }
|
||||
}
|
||||
|
||||
private isTerminalStepPaid(stepId: number): boolean {
|
||||
return this.payByTerminalSteps().some((step) => step.value === stepId && step.payed);
|
||||
}
|
||||
|
||||
private getMinAllowedSteps() {
|
||||
const paidSteps = this.payByTerminalSteps()
|
||||
.slice(0, this.selectedPayByTerminalStep())
|
||||
.filter((step) => step.payed)
|
||||
.map((step) => step.value);
|
||||
return paidSteps.length ? Math.max(...paidSteps) : 1;
|
||||
}
|
||||
|
||||
private getPendingTerminalPayments() {
|
||||
return this.terminalControls
|
||||
.map((control, index) => ({
|
||||
stepId: index + 1,
|
||||
amount: Number(control.value || 0),
|
||||
}))
|
||||
.filter(({ stepId, amount }) => amount > 0 && !this.isTerminalStepPaid(stepId));
|
||||
}
|
||||
|
||||
override onSuccess(response: IPosOrderResponse): void {}
|
||||
|
||||
private extractAmountFromPaymentResult(payload: unknown): number | null {
|
||||
|
||||
@@ -2,4 +2,5 @@ export * from './customer';
|
||||
export * from './io';
|
||||
export * from './payload';
|
||||
export * from './payment';
|
||||
export * from './posPaymentResult';
|
||||
export * from './types';
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
export interface IPayment {
|
||||
terminals: number[];
|
||||
terminals: IPaymentTerminal[];
|
||||
cash: number;
|
||||
set_off: number;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
export interface PosPaymentResult {
|
||||
id?: string;
|
||||
status: 'SUCCESS' | 'FAILURE';
|
||||
message?: string;
|
||||
data?: {
|
||||
terminalId: string;
|
||||
stan: string;
|
||||
rrn: string;
|
||||
responseCode: string;
|
||||
customerCardNO: string;
|
||||
transactionDateTime: string;
|
||||
};
|
||||
}
|
||||
|
||||
export interface TerminalSuccessPaymentPayload {
|
||||
terminal_id: string;
|
||||
stan: string;
|
||||
rrn: string;
|
||||
transaction_date_time: Date;
|
||||
customer_card_no: string;
|
||||
description: string;
|
||||
}
|
||||
@@ -1,4 +1,6 @@
|
||||
import { PosPaymentResult } from '../models';
|
||||
|
||||
export abstract class PosPaymentBridgeAbstract {
|
||||
abstract registerPaymentResultListener(handler: (payload: unknown) => void): () => void;
|
||||
abstract registerPaymentResultListener(handler: (payload: PosPaymentResult) => void): () => void;
|
||||
abstract emitPaymentResultForTest(payload: unknown): void;
|
||||
}
|
||||
|
||||
@@ -1,33 +1,51 @@
|
||||
import { ToastService } from '@/core/services/toast.service';
|
||||
import { inject, Injectable } from '@angular/core';
|
||||
import { PosPaymentResult } from '../models';
|
||||
import { PosPaymentBridgeAbstract } from './payment-bridge.abstract';
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class PosPaymentBridgeService extends PosPaymentBridgeAbstract {
|
||||
private readonly toastServices = inject(ToastService);
|
||||
registerPaymentResultListener(handler: (payload: unknown) => void): () => void {
|
||||
// const w = window as unknown as {
|
||||
// WebV?: {
|
||||
// onPaymentResult?: (payload: unknown) => void;
|
||||
// AndroidBridge?: {
|
||||
// onPaymentResult?: (payload: unknown) => void;
|
||||
// };
|
||||
// };
|
||||
// };
|
||||
this.toastServices.error({ text: '@@@@@@@@@@@@@' });
|
||||
|
||||
registerPaymentResultListener(handler: (payload: PosPaymentResult) => void): () => void {
|
||||
// @ts-ignore
|
||||
window.WebV = {
|
||||
onPaymentResult: () => {
|
||||
this.toastServices.error({ text: 'asdasdsadassadasdas' });
|
||||
onPaymentResult: (payload: PosPaymentResult) => {
|
||||
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);
|
||||
} catch (e) {
|
||||
this.toastServices.error({ text: 'Failed to parse payment result', life: 10000 });
|
||||
this.toastServices.error({
|
||||
text: e instanceof Error ? e.message : String(e),
|
||||
life: 10000,
|
||||
});
|
||||
this.toastServices.error({ text: typeof payload, life: 10000 });
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
return () => {};
|
||||
}
|
||||
|
||||
emitPaymentResultForTest(payload: unknown): void {
|
||||
const w = window as unknown as { WebV?: { onPaymentResult?: (payload: unknown) => void } };
|
||||
emitPaymentResultForTest(payload: PosPaymentResult): void {
|
||||
const w = window as unknown as {
|
||||
WebV?: { onPaymentResult?: (payload: PosPaymentResult) => void };
|
||||
};
|
||||
w.WebV?.onPaymentResult?.(payload);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { Maybe } from '@/core';
|
||||
// import { ICustomerResponse } from '@/modules/customers/models';
|
||||
import { ToastService } from '@/core/services/toast.service';
|
||||
import { IGoodCategoryResponse, IGoodResponse } from '@/domains/pos/models/good.io';
|
||||
import { CustomerType } from '@/shared/localEnum/constants/customerTypes';
|
||||
import { createUUID, JALALI_DATE_FORMATS, nowJalali } from '@/utils';
|
||||
@@ -55,6 +56,7 @@ export const INITIAL_POS_STATE: IPosLandingState = {
|
||||
@Injectable({ providedIn: 'any' })
|
||||
export class PosLandingStore {
|
||||
private readonly service = inject(PosService);
|
||||
private readonly toastServices = inject(ToastService);
|
||||
|
||||
private state$ = signal<IPosLandingState>({ ...INITIAL_POS_STATE });
|
||||
|
||||
@@ -238,6 +240,10 @@ export class PosLandingStore {
|
||||
total_amount: this.orderPricingInfo().totalAmount,
|
||||
};
|
||||
|
||||
this.toastServices.info({
|
||||
text: `payment submitted: ${this.payments().terminals[0]?.customer_card_no || ''}`,
|
||||
});
|
||||
|
||||
let res: Maybe<IPosOrderResponse> = null;
|
||||
|
||||
return this.service.submitOrder(orderPayload).pipe(
|
||||
|
||||
@@ -1,49 +1,51 @@
|
||||
<div class="w-full min-h-[60svh] p-4 md:p-6">
|
||||
<section class="mx-auto max-w-3xl overflow-hidden rounded-2xl border border-surface-200 bg-white">
|
||||
<div class="bg-gradient-to-l from-sky-50 via-cyan-50 to-teal-50 p-5 md:p-7">
|
||||
<span class="block mb-4 text-xl font-bold text-surface-900 md:text-3xl">پشتیبانی {{ brandingInfo.title }}</span>
|
||||
<p class="mt-3 max-w-2xl text-sm leading-7 text-surface-700 md:text-base">
|
||||
در صورت بروز مشکل در ثبت سفارش، صدور صورتحساب یا تنظیمات دستگاه، تیم پشتیبانی آماده پاسخگویی است. لطفا هنگام
|
||||
تماس، نام پایانه و نام کاربری را همراه داشته باشید.
|
||||
</p>
|
||||
</div>
|
||||
@defer {
|
||||
<div class="w-full min-h-[60svh] p-4 md:p-6">
|
||||
<section class="mx-auto max-w-3xl overflow-hidden rounded-2xl border border-surface-200 bg-white">
|
||||
<div class="bg-gradient-to-l from-sky-50 via-cyan-50 to-teal-50 p-5 md:p-7">
|
||||
<span class="block mb-4 text-xl font-bold text-surface-900 md:text-3xl">پشتیبانی {{ brandingInfo.title }}</span>
|
||||
<p class="mt-3 max-w-2xl text-sm leading-7 text-surface-700 md:text-base">
|
||||
در صورت بروز مشکل در ثبت سفارش، صدور صورتحساب یا تنظیمات دستگاه، تیم پشتیبانی آماده پاسخگویی است. لطفا هنگام
|
||||
تماس، نام پایانه و نام کاربری را همراه داشته باشید.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="grid gap-3 p-4 md:grid-cols-2 md:gap-4 md:p-6">
|
||||
<article class="rounded-xl border border-surface-200 bg-surface-50 p-4">
|
||||
<span class="block mb-4 text-xl font-bold text-surface-900">شماره های تماس مستقیم</span>
|
||||
@if (brandingInfo!.support.phones?.length) {
|
||||
<ul class="space-y-2 text-sm text-surface-700">
|
||||
@for (phone of brandingInfo.support.phones!; track $index) {
|
||||
<li class="flex items-start justify-between gap-2">
|
||||
<span>{{ phone.title }}</span>
|
||||
<div class="flex flex-col gap-2">
|
||||
@for (item of phone.phones; track $index) {
|
||||
<a class="font-semibold text-primary" href="tel:{{ item }}" dir="ltr">{{ item }}</a>
|
||||
}
|
||||
</div>
|
||||
</li>
|
||||
}
|
||||
<div class="grid gap-3 p-4 md:grid-cols-2 md:gap-4 md:p-6">
|
||||
<article class="rounded-xl border border-surface-200 bg-surface-50 p-4">
|
||||
<span class="block mb-4 text-xl font-bold text-surface-900">شماره های تماس مستقیم</span>
|
||||
@if (brandingInfo!.support.phones?.length) {
|
||||
<ul class="space-y-2 text-sm text-surface-700">
|
||||
@for (phone of brandingInfo.support.phones!; track $index) {
|
||||
<li class="flex items-start justify-between gap-2">
|
||||
<span>{{ phone.title }}</span>
|
||||
<div class="flex flex-col gap-2">
|
||||
@for (item of phone.phones; track $index) {
|
||||
<a class="font-semibold text-primary" href="tel:{{ item }}" dir="ltr">{{ item }}</a>
|
||||
}
|
||||
</div>
|
||||
</li>
|
||||
}
|
||||
</ul>
|
||||
} @else {
|
||||
<p class="text-sm text-surface-700">در حال حاضر شماره تماس مستقیمی برای پشتیبانی اعلام نشده است.</p>
|
||||
}
|
||||
</article>
|
||||
|
||||
<article class="rounded-xl border border-surface-200 bg-surface-50 p-4">
|
||||
<span class="block mb-4 text-xl font-bold text-surface-900">راهنمای سریع قبل از تماس</span>
|
||||
<ul class="space-y-2 text-sm leading-6 text-surface-700">
|
||||
<li>اتصال اینترنت دستگاه را بررسی کنید.</li>
|
||||
<li>یک بار برنامه را ببندید و دوباره اجرا کنید.</li>
|
||||
<li>در صفحه درباره نرم افزار، نسخه برنامه را اعلام کنید.</li>
|
||||
<li>در صورت خطا، متن خطا یا تصویر آن را برای پشتیبانی ارسال کنید.</li>
|
||||
</ul>
|
||||
} @else {
|
||||
<p class="text-sm text-surface-700">در حال حاضر شماره تماس مستقیمی برای پشتیبانی اعلام نشده است.</p>
|
||||
}
|
||||
</article>
|
||||
</article>
|
||||
</div>
|
||||
|
||||
<article class="rounded-xl border border-surface-200 bg-surface-50 p-4">
|
||||
<span class="block mb-4 text-xl font-bold text-surface-900">راهنمای سریع قبل از تماس</span>
|
||||
<ul class="space-y-2 text-sm leading-6 text-surface-700">
|
||||
<li>اتصال اینترنت دستگاه را بررسی کنید.</li>
|
||||
<li>یک بار برنامه را ببندید و دوباره اجرا کنید.</li>
|
||||
<li>در صفحه درباره نرم افزار، نسخه برنامه را اعلام کنید.</li>
|
||||
<li>در صورت خطا، متن خطا یا تصویر آن را برای پشتیبانی ارسال کنید.</li>
|
||||
</ul>
|
||||
</article>
|
||||
</div>
|
||||
|
||||
<div class="border-t border-surface-200 bg-surface-50 px-4 py-4 md:px-6">
|
||||
<p class="text-xs leading-6 text-surface-600 md:text-sm">
|
||||
ساعات پاسخگویی: شنبه تا چهارشنبه 8:30 تا 17:30 و پنجشنبه 8:30 تا 13:00
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
<div class="border-t border-surface-200 bg-surface-50 px-4 py-4 md:px-6">
|
||||
<p class="text-xs leading-6 text-surface-600 md:text-sm">
|
||||
ساعات پاسخگویی: شنبه تا چهارشنبه 8:30 تا 17:30 و پنجشنبه 8:30 تا 13:00
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
// TIS tenant environment configuration
|
||||
export const environment = {
|
||||
production: true,
|
||||
// apiBaseUrl: 'https://psp-api.shift-am.ir',
|
||||
apiBaseUrl: 'http://192.168.128.73:5002',
|
||||
apiBaseUrl: 'https://psp-api.shift-am.ir',
|
||||
// apiBaseUrl: 'http://192.168.128.73:5002',
|
||||
host: 'localhost',
|
||||
port: 5000,
|
||||
enableLogging: false,
|
||||
|
||||
Reference in New Issue
Block a user