+ );
+ }
+
+ const customerName =
+ invoice.user?.firstName && invoice.user?.lastName
+ ? `${invoice.user.firstName} ${invoice.user.lastName}`
+ : invoice.user?.phone ?? '-';
+
+ return (
+
+
+
+ پیش فاکتور #{invoice.invoiceNumber}
+
+
+
+
+
+
+
+
اطلاعات پیش فاکتور
+
+
+
+
مشتری
+
{customerName}
+
+
+
تاریخ صدور
+
+ {invoice.createdAt
+ ? moment(invoice.createdAt).format('jYYYY/jMM/jDD')
+ : '-'}
-
-
-
اطلاعات پیش فاکتور
-
-
-
-
-
-
مالیات بر ارزش افزوده
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
مهلت تایید
+
+ {invoice.approvalDeadline
+ ? moment(invoice.approvalDeadline).format('jYYYY/jMM/jDD')
+ : '-'}
+
- )
-}
-export default DetailPerfomaInvoice
\ No newline at end of file
+
+
+
+
+ | محصول |
+ توضیحات |
+ تعداد |
+ مبلغ واحد |
+ تخفیف |
+ مبلغ کل |
+
+
+
+ {invoice.items?.map((item) => {
+ const lineTotal =
+ (item.quantity || 0) * (item.unitPrice || 0) -
+ (item.discount || 0);
+ return (
+
+ | {item.product?.title ?? '-'} |
+ {item.description || '-'} |
+ {item.quantity ?? '-'} |
+
+ {item.unitPrice != null
+ ? formatAmount(item.unitPrice)
+ : '-'}
+ |
+
+ {item.discount != null
+ ? formatAmount(item.discount)
+ : '-'}
+ |
+ {formatAmount(lineTotal)} |
+
+ );
+ })}
+
+
+
+
+ {invoice.enableTax && (
+
+ مالیات بر ارزش افزوده (۱۰ درصد) فعال است
+
+ )}
+
+ {invoice.paymentMethod && (
+
+
روش پرداخت
+
+ {invoice.paymentMethod}
+
+
+ )}
+
+ {invoice.description && (
+
+
توضیحات
+
+ {invoice.description}
+
+
+ )}
+
+
+
+
مبلغ کل:
+
+ {formatAmount(invoice.total ?? 0)}
+
+
+
+
پرداخت شده:
+
+ {formatAmount(invoice.paidAmount ?? 0)}
+
+
+
+
مانده:
+
+ {formatAmount(invoice.balance ?? 0)}
+
+
+
+
+
+ {id &&
}
+
+ );
+};
+
+export default DetailPerfomaInvoice;
diff --git a/src/pages/invoice/ProformaInvoice.tsx b/src/pages/invoice/ProformaInvoice.tsx
index a00f61f..9f5f562 100644
--- a/src/pages/invoice/ProformaInvoice.tsx
+++ b/src/pages/invoice/ProformaInvoice.tsx
@@ -3,7 +3,7 @@ import { useMemo, useState, type FC } from 'react'
import { ProformaInvoiceStatusEnum } from './enum/InvoiceEnum'
import Filters from '@/components/Filters'
import Table from '@/components/Table'
-import { Eye, Add } from 'iconsax-react'
+import { Eye, Add, Edit2 } from 'iconsax-react'
import { Link } from 'react-router-dom'
import { Paths } from '@/config/Paths'
import moment from 'moment-jalaali'
@@ -180,9 +180,12 @@ const ProformaInvoice: FC = () => {
title: '',
render: (item) => (
-
+
+
+
+
),
},
diff --git a/src/pages/invoice/components/InvoicePaymentsSection.tsx b/src/pages/invoice/components/InvoicePaymentsSection.tsx
new file mode 100644
index 0000000..c0b873a
--- /dev/null
+++ b/src/pages/invoice/components/InvoicePaymentsSection.tsx
@@ -0,0 +1,223 @@
+import { type FC, useState } from 'react';
+import moment from 'moment-jalaali';
+import Table from '@/components/Table';
+import ModalConfrim from '@/components/ModalConfrim';
+import type { ColumnType } from '@/components/types/TableTypes';
+import { toast } from '@/shared/toast';
+import { extractErrorMessage } from '@/config/func';
+import {
+ useConfirmCashPayment,
+ useDenyCashPayment,
+ useGetPayments,
+ useVerifyOnlinePayment,
+} from '@/pages/payment/hooks/usePaymentData';
+import {
+ PaymentMethodEnum,
+ PaymentStatusEnum,
+} from '@/pages/payment/enum/PaymentEnum';
+import type { PaymentType } from '@/pages/payment/types/Types';
+
+type Props = {
+ invoiceId: string;
+};
+
+const methodLabels: Record
= {
+ [PaymentMethodEnum.Online]: 'آنلاین',
+ [PaymentMethodEnum.Cash]: 'نقدی',
+ [PaymentMethodEnum.Credit]: 'اعتبار',
+};
+
+const statusLabels: Record = {
+ [PaymentStatusEnum.Pending]: 'در انتظار',
+ [PaymentStatusEnum.Paid]: 'پرداخت شده',
+ [PaymentStatusEnum.Failed]: 'ناموفق',
+ [PaymentStatusEnum.Refunded]: 'بازگشت داده شده',
+};
+
+const statusBadgeClass: Record = {
+ [PaymentStatusEnum.Pending]: 'bg-amber-100 text-amber-800',
+ [PaymentStatusEnum.Paid]: 'bg-green-100 text-green-800',
+ [PaymentStatusEnum.Failed]: 'bg-red-100 text-red-800',
+ [PaymentStatusEnum.Refunded]: 'bg-gray-100 text-gray-800',
+};
+
+const formatAmount = (amount: number) =>
+ Number(amount).toLocaleString('fa-IR') + ' تومان';
+
+const InvoicePaymentsSection: FC = ({ invoiceId }) => {
+ const [page, setPage] = useState(1);
+ const [confirmAction, setConfirmAction] = useState<{
+ type: 'cash-confirm' | 'cash-deny' | 'online-verify';
+ payment: PaymentType;
+ } | null>(null);
+
+ const { data, isLoading } = useGetPayments({
+ page,
+ limit: 10,
+ invoiceId,
+ });
+
+ const confirmCash = useConfirmCashPayment();
+ const denyCash = useDenyCashPayment();
+ const verifyOnline = useVerifyOnlinePayment();
+
+ const payments = data?.data ?? [];
+ const meta = data?.meta;
+
+ const isActionLoading =
+ confirmCash.isPending || denyCash.isPending || verifyOnline.isPending;
+
+ const handleConfirmAction = () => {
+ if (!confirmAction) return;
+
+ const { type, payment } = confirmAction;
+ const onSuccess = () => {
+ toast('عملیات با موفقیت انجام شد', 'success');
+ setConfirmAction(null);
+ };
+ const onError = (error: unknown) => {
+ toast(extractErrorMessage(error), 'error');
+ };
+
+ if (type === 'cash-confirm') {
+ confirmCash.mutate(payment.id, { onSuccess, onError });
+ return;
+ }
+ if (type === 'cash-deny') {
+ denyCash.mutate(payment.id, { onSuccess, onError });
+ return;
+ }
+ if (type === 'online-verify' && payment.token) {
+ verifyOnline.mutate(payment.token, { onSuccess, onError });
+ }
+ };
+
+ const getConfirmLabel = () => {
+ if (!confirmAction) return '';
+ switch (confirmAction.type) {
+ case 'cash-confirm':
+ return 'آیا از تایید این پرداخت نقدی اطمینان دارید؟';
+ case 'cash-deny':
+ return 'آیا از رد این پرداخت نقدی اطمینان دارید؟';
+ case 'online-verify':
+ return 'آیا از تایید این پرداخت آنلاین اطمینان دارید؟';
+ default:
+ return '';
+ }
+ };
+
+ const columns: ColumnType[] = [
+ {
+ title: 'مبلغ',
+ key: 'amount',
+ render: (item) => formatAmount(item.amount),
+ },
+ {
+ title: 'روش پرداخت',
+ key: 'method',
+ render: (item) => methodLabels[item.method] ?? item.method,
+ },
+ {
+ title: 'وضعیت',
+ key: 'status',
+ render: (item) => (
+
+ {statusLabels[item.status] ?? item.status}
+
+ ),
+ },
+ {
+ title: 'تاریخ',
+ key: 'createdAt',
+ render: (item) =>
+ item.createdAt
+ ? moment(item.createdAt).format('jYYYY/jMM/jDD HH:mm')
+ : '-',
+ },
+ {
+ title: 'عملیات',
+ key: 'actions',
+ render: (item) => {
+ const isPending = item.status === PaymentStatusEnum.Pending;
+ const isCash = item.method === PaymentMethodEnum.Cash;
+ const isOnline = item.method === PaymentMethodEnum.Online;
+
+ return (
+
+ {isPending && isCash && (
+ <>
+
+
+ >
+ )}
+ {isPending && isOnline && item.token && (
+
+ )}
+
+ );
+ },
+ },
+ ];
+
+ return (
+ <>
+
+
پرداختها
+
+
+
1
+ ? {
+ currentPage: meta.page,
+ totalPages: meta.totalPages,
+ onPageChange: setPage,
+ }
+ : undefined
+ }
+ />
+
+
+
+ setConfirmAction(null)}
+ onConfrim={handleConfirmAction}
+ isloading={isActionLoading}
+ label={getConfirmLabel()}
+ />
+ >
+ );
+};
+
+export default InvoicePaymentsSection;
diff --git a/src/pages/payment/hooks/usePaymentData.ts b/src/pages/payment/hooks/usePaymentData.ts
index 092252b..45ceb07 100644
--- a/src/pages/payment/hooks/usePaymentData.ts
+++ b/src/pages/payment/hooks/usePaymentData.ts
@@ -15,6 +15,7 @@ export const useConfirmCashPayment = () => {
mutationFn: (paymentId: string) => api.confirmCashPayment(paymentId),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['payments'] });
+ queryClient.invalidateQueries({ queryKey: ['invoice'] });
},
});
};
@@ -25,6 +26,7 @@ export const useDenyCashPayment = () => {
mutationFn: (paymentId: string) => api.denyCashPayment(paymentId),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['payments'] });
+ queryClient.invalidateQueries({ queryKey: ['invoice'] });
},
});
};
@@ -35,6 +37,7 @@ export const useVerifyOnlinePayment = () => {
mutationFn: (token: string) => api.verifyOnlinePayment(token),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['payments'] });
+ queryClient.invalidateQueries({ queryKey: ['invoice'] });
},
});
};