invoice payments
This commit is contained in:
@@ -11,6 +11,8 @@ import { NumberFormat } from '@/config/func'
|
|||||||
import moment from 'moment-jalaali'
|
import moment from 'moment-jalaali'
|
||||||
import type { InvoiceItem as ApiInvoiceItem } from './types/InvoiceTypes'
|
import type { InvoiceItem as ApiInvoiceItem } from './types/InvoiceTypes'
|
||||||
import { Paths } from '@/config/Paths'
|
import { Paths } from '@/config/Paths'
|
||||||
|
import { useGetPaymentInvoice } from '../payment/hooks/usePaymentData'
|
||||||
|
import InvoicePaymentList from './InvoicePaymentList'
|
||||||
|
|
||||||
interface TableInvoiceItem extends RowDataType {
|
interface TableInvoiceItem extends RowDataType {
|
||||||
id: string
|
id: string
|
||||||
@@ -26,9 +28,11 @@ interface TableInvoiceItem extends RowDataType {
|
|||||||
const formatPrice = (num: number) => `${NumberFormat(num)} تومان`
|
const formatPrice = (num: number) => `${NumberFormat(num)} تومان`
|
||||||
|
|
||||||
const InvoiceDetail: FC = () => {
|
const InvoiceDetail: FC = () => {
|
||||||
|
|
||||||
const { id } = useParams()
|
const { id } = useParams()
|
||||||
const queryClient = useQueryClient()
|
const queryClient = useQueryClient()
|
||||||
const { data, isPending } = useGetInvoiceDetail(id!)
|
const { data, isPending } = useGetInvoiceDetail(id!)
|
||||||
|
useGetPaymentInvoice(id)
|
||||||
const { mutate: confirmInvoiceItem, isPending: isConfirming } = useConfirmInvoiceItem()
|
const { mutate: confirmInvoiceItem, isPending: isConfirming } = useConfirmInvoiceItem()
|
||||||
const [confirmingItemId, setConfirmingItemId] = useState<string | null>(null)
|
const [confirmingItemId, setConfirmingItemId] = useState<string | null>(null)
|
||||||
const invoice = data?.data
|
const invoice = data?.data
|
||||||
@@ -241,6 +245,8 @@ const InvoiceDetail: FC = () => {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
{/* Notes Section */}
|
{/* Notes Section */}
|
||||||
<div className='mt-8 pt-6 border-t border-[#EBEDF5]'>
|
<div className='mt-8 pt-6 border-t border-[#EBEDF5]'>
|
||||||
<div className='text-xs text-[#8C90A3] leading-6 space-y-2'>
|
<div className='text-xs text-[#8C90A3] leading-6 space-y-2'>
|
||||||
@@ -250,6 +256,11 @@ const InvoiceDetail: FC = () => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* لیست پرداختها */}
|
||||||
|
<div className="mt-8">
|
||||||
|
<InvoicePaymentList invoiceId={id!} />
|
||||||
|
</div>
|
||||||
|
|
||||||
<ModalConfirm
|
<ModalConfirm
|
||||||
open={!!confirmingItemId}
|
open={!!confirmingItemId}
|
||||||
onClose={() => setConfirmingItemId(null)}
|
onClose={() => setConfirmingItemId(null)}
|
||||||
|
|||||||
@@ -0,0 +1,98 @@
|
|||||||
|
import { type FC, useState } from 'react'
|
||||||
|
import Table from '@/components/Table'
|
||||||
|
import type { RowDataType } from '@/components/types/TableTypes'
|
||||||
|
import { NumberFormat } from '@/config/func'
|
||||||
|
import moment from 'moment-jalaali'
|
||||||
|
import { useGetPaymentInvoice } from '../payment/hooks/usePaymentData'
|
||||||
|
import type { PaymentItemType } from '../payment/types/Types'
|
||||||
|
import {
|
||||||
|
PaymentMethodEnum,
|
||||||
|
PaymentStatusEnum,
|
||||||
|
PaymentGatewayEnum,
|
||||||
|
} from '../payment/enum/Enum'
|
||||||
|
|
||||||
|
interface PaymentRowType extends RowDataType {
|
||||||
|
id: string
|
||||||
|
amount: string
|
||||||
|
method: string
|
||||||
|
gateway: string
|
||||||
|
status: string
|
||||||
|
date: string
|
||||||
|
transactionId: string
|
||||||
|
description: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const formatPrice = (num: number) => `${NumberFormat(num)} تومان`
|
||||||
|
|
||||||
|
const statusLabels: Record<string, string> = {
|
||||||
|
[PaymentStatusEnum.Pending]: 'در انتظار',
|
||||||
|
[PaymentStatusEnum.Paid]: 'پرداخت شده',
|
||||||
|
[PaymentStatusEnum.Failed]: 'ناموفق',
|
||||||
|
[PaymentStatusEnum.Refunded]: 'مسترد شده',
|
||||||
|
}
|
||||||
|
|
||||||
|
const methodLabels: Record<string, string> = {
|
||||||
|
[PaymentMethodEnum.Online]: 'آنلاین',
|
||||||
|
[PaymentMethodEnum.Cash]: 'نقد',
|
||||||
|
[PaymentMethodEnum.Credit]: 'اعتباری',
|
||||||
|
}
|
||||||
|
|
||||||
|
const gatewayLabels: Record<string, string> = {
|
||||||
|
[PaymentGatewayEnum.ZarinPal]: 'زرینپال',
|
||||||
|
}
|
||||||
|
|
||||||
|
const mapPaymentToRow = (p: PaymentItemType): PaymentRowType => {
|
||||||
|
const date = p.paidAt || p.failedAt || p.createdAt
|
||||||
|
return {
|
||||||
|
id: p.id,
|
||||||
|
amount: formatPrice(p.amount),
|
||||||
|
method: methodLabels[p.method as keyof typeof methodLabels] ?? p.method,
|
||||||
|
gateway: p.gateway ? (gatewayLabels[p.gateway as keyof typeof gatewayLabels] ?? p.gateway) : '-',
|
||||||
|
status: statusLabels[p.status as keyof typeof statusLabels] ?? p.status,
|
||||||
|
date: date ? moment(date).format('jYYYY/jMM/jDD - HH:mm') : '-',
|
||||||
|
transactionId: p.transactionId ?? '-',
|
||||||
|
description: p.description ?? '-',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface InvoicePaymentListProps {
|
||||||
|
invoiceId: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const InvoicePaymentList: FC<InvoicePaymentListProps> = ({ invoiceId }) => {
|
||||||
|
const [page, setPage] = useState(1)
|
||||||
|
const { data, isPending } = useGetPaymentInvoice(invoiceId, page)
|
||||||
|
const payments = data?.data ?? []
|
||||||
|
const rows: PaymentRowType[] = payments.map(mapPaymentToRow)
|
||||||
|
const totalPages = data?.meta?.totalPages ?? 1
|
||||||
|
|
||||||
|
const columns = [
|
||||||
|
{ key: 'amount', title: 'مبلغ' },
|
||||||
|
{ key: 'method', title: 'روش پرداخت' },
|
||||||
|
{ key: 'gateway', title: 'درگاه' },
|
||||||
|
{ key: 'status', title: 'وضعیت' },
|
||||||
|
{ key: 'date', title: 'تاریخ' },
|
||||||
|
{ key: 'transactionId', title: 'شناسه تراکنش' },
|
||||||
|
{ key: 'description', title: 'توضیحات' },
|
||||||
|
]
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="bg-white rounded-2xl p-8">
|
||||||
|
<div className="text-sm mb-6">لیست پرداختها</div>
|
||||||
|
<Table<PaymentRowType>
|
||||||
|
columns={columns}
|
||||||
|
data={rows}
|
||||||
|
showHeader={true}
|
||||||
|
isLoading={isPending}
|
||||||
|
noDataMessage="پرداختی برای این فاکتور ثبت نشده است."
|
||||||
|
pagination={{
|
||||||
|
currentPage: page,
|
||||||
|
totalPages,
|
||||||
|
onPageChange: setPage,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default InvoicePaymentList
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import { useMutation } from "@tanstack/react-query";
|
import { useMutation, useQuery } from "@tanstack/react-query";
|
||||||
import * as api from "../service/PaymentService";
|
import * as api from "../service/PaymentService";
|
||||||
import type { PayInvoiceParamsType } from "../types/Types";
|
import type { PayInvoiceParamsType } from "../types/Types";
|
||||||
|
|
||||||
@@ -13,3 +13,14 @@ export const usePayInvoice = () => {
|
|||||||
}) => api.payInvoice(invoiceId, params),
|
}) => api.payInvoice(invoiceId, params),
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const useGetPaymentInvoice = (
|
||||||
|
invoiceId?: string,
|
||||||
|
page: number = 1,
|
||||||
|
) => {
|
||||||
|
return useQuery({
|
||||||
|
queryKey: ["payment", invoiceId, page],
|
||||||
|
queryFn: () => api.getPaymentInvoice(invoiceId, page),
|
||||||
|
enabled: !!invoiceId,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
import axios from "@/config/axios";
|
import axios from "@/config/axios";
|
||||||
import type { PayInvoiceParamsType } from "../types/Types";
|
import type {
|
||||||
|
GetPaymentInvoiceResponseType,
|
||||||
|
PayInvoiceParamsType,
|
||||||
|
} from "../types/Types";
|
||||||
|
|
||||||
export const payInvoice = async (
|
export const payInvoice = async (
|
||||||
invoiceId: string,
|
invoiceId: string,
|
||||||
@@ -11,3 +14,23 @@ export const payInvoice = async (
|
|||||||
);
|
);
|
||||||
return data;
|
return data;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const DEFAULT_PAGE_SIZE = 10;
|
||||||
|
|
||||||
|
export const getPaymentInvoice = async (
|
||||||
|
invoiceId?: string,
|
||||||
|
page: number = 1,
|
||||||
|
limit: number = DEFAULT_PAGE_SIZE,
|
||||||
|
): Promise<GetPaymentInvoiceResponseType> => {
|
||||||
|
const { data } = await axios.get<GetPaymentInvoiceResponseType>(
|
||||||
|
`/public/payments`,
|
||||||
|
{
|
||||||
|
params: {
|
||||||
|
invoiceId,
|
||||||
|
page,
|
||||||
|
limit,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
return data;
|
||||||
|
};
|
||||||
|
|||||||
@@ -1,4 +1,9 @@
|
|||||||
import type { PaymentGatewayEnum, PaymentMethodEnum } from "../enum/Enum";
|
import type { BaseResponse } from "@/shared/types/Types";
|
||||||
|
import type {
|
||||||
|
PaymentGatewayEnum,
|
||||||
|
PaymentMethodEnum,
|
||||||
|
PaymentStatusEnum,
|
||||||
|
} from "../enum/Enum";
|
||||||
|
|
||||||
export type PayInvoiceParamsType = {
|
export type PayInvoiceParamsType = {
|
||||||
amount: number;
|
amount: number;
|
||||||
@@ -7,3 +12,24 @@ export type PayInvoiceParamsType = {
|
|||||||
description: string;
|
description: string;
|
||||||
attachments: string[];
|
attachments: string[];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type PaymentItemType = {
|
||||||
|
id: string;
|
||||||
|
createdAt: string;
|
||||||
|
deletedAt: string | null;
|
||||||
|
invoice: string;
|
||||||
|
creator: string | null;
|
||||||
|
amount: number;
|
||||||
|
token: string | null;
|
||||||
|
method: PaymentMethodEnum | string;
|
||||||
|
gateway: PaymentGatewayEnum | string | null;
|
||||||
|
transactionId: string | null;
|
||||||
|
status: PaymentStatusEnum | string;
|
||||||
|
verifyResponse: unknown;
|
||||||
|
paidAt: string | null;
|
||||||
|
failedAt: string | null;
|
||||||
|
attachments: string[] | null;
|
||||||
|
description: string | null;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type GetPaymentInvoiceResponseType = BaseResponse<PaymentItemType[]>;
|
||||||
|
|||||||
Reference in New Issue
Block a user