Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(billing): 다중 실시간 결제 및 청구서 발송 구현 #174

Merged
merged 12 commits into from
Jul 25, 2024
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions client/src/apis/billing.js
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ export const cancelSendInvoice = async billingId => {
};

// 청구 실시간 결제
export const payBilling = async billingId => {
export const payRealTimeBilling = async billingId => {
try {
const res = await privateAxios.get(`/v1/vendor/billing/payment/${billingId}`);
return res;
Expand All @@ -103,7 +103,7 @@ export const payBilling = async billingId => {
}
};

// 청구 실시간 결제
// 청구 실시간 결제취소
export const cancelPayBilling = async billingId => {
try {
const res = await privateAxios.get(`/v1/vendor/billing/payment/${billingId}/cancel`);
Expand Down
4 changes: 4 additions & 0 deletions client/src/components/common/tables/Table.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ const Table = ({
handleChangeSearch,
onRowClick,
handleClickSearch,
handleChangeSelection = newSelection => {},
}) => {
const [selection, setSelection] = useState([]);
const itemKey = cols[0].key;
Expand All @@ -26,15 +27,18 @@ const Table = ({
newSelection.push(value);
}
setSelection(newSelection);
handleChangeSelection(newSelection);
};

// 모든 체크박스 선택
const handleClickCheckBoxAll = e => {
if (e.target.checked) {
const allCheckedSelection = rows.map(item => item);
setSelection(allCheckedSelection);
handleChangeSelection(allCheckedSelection);
} else {
setSelection([]);
handleChangeSelection([]);
}
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ const BillingDetailProduct = ({ billingProducts, products, editable, onChange, b
<div className='flex space-x-4'>
<div className='w-full'>
<div className='flex justify-between'>
<div className='w-2/6 flex-row mb-3'>
<div className={`w-2/6 flex-row mb-3 ${editable ? 'visible' : 'invisible'}`}>
<label className={`block text-text_black text-15 font-700 mb-2 ml-2`}>
상품 추가
</label>
Expand All @@ -107,6 +107,7 @@ const BillingDetailProduct = ({ billingProducts, products, editable, onChange, b
disabled={!editable}
/>
</div>

<div className='flex items-end'>
<p className='font-bold text-lg mr-2'>합계:</p>
<p className='text-right font-bold text-lg border-none'>{`${calcBillingPrice(localBillingProducts).toLocaleString()}원`}</p>
Expand Down
17 changes: 0 additions & 17 deletions client/src/components/vendor/modal/BillingRegisterModal.jsx

This file was deleted.

2 changes: 2 additions & 0 deletions client/src/components/vendor/modal/MemberChooseModal.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ const MemberChooseModal = ({ icon, isShowModal, setIsShowModal, modalTitle }) =>
});
setMemberList(res.data.content);
setTotalPages(res.data.totalPage || 1);
setCurrentPage(1);
setPageGroup(0);
} catch (err) {
console.error('계약생성 - 회원 기본정보 목록 조회 실패', err);
}
Expand Down
75 changes: 75 additions & 0 deletions client/src/components/vendor/modal/PayRealtimeErrorModal.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import React from 'react';
import { formatId } from '@/utils/format/formatId';
import { formatPhone } from '@/utils/format/formatPhone';
import { Tooltip } from 'react-tooltip';
import 'react-tooltip/dist/react-tooltip.css';
import BaseModal from '@/components/common/BaseModal';
import { FaExclamationCircle } from 'react-icons/fa';

const PayRealtimeErrorModal = ({ errors, isShowModal, icon, setIsShowModal, modalTitle }) => {
return (
<BaseModal
isShowModal={isShowModal}
setIsShowModal={setIsShowModal}
modalTitle={modalTitle}
icon={icon}
height={'h-5/6'}
width={'w-5/6'}>
<div className='flex flex-col h-full'>
<div className='mb-4 text-lg font-semibold text-red-500'>
{errors && errors[0].total}건 중 {errors.length}건의 실시간 결제가 실패했습니다.
</div>
<div className='flex-grow overflow-auto'>
<table className='w-full mb-3'>
<thead>
<tr className='bg-table_col'>
<th className='p-2 text-left text-text_black w-8'></th>
<th className='p-2 text-left text-text_black'>청구번호</th>
<th className='p-2 text-left text-text_black'>회원이름</th>
<th className='p-2 text-left text-text_black'>휴대전화</th>
<th className='p-2 text-left text-text_black'>결제일</th>
</tr>
</thead>
<tbody>
{errors.map(({ from, res }, idx) => (
<tr key={idx} className='hover:bg-gray-100'>
<td className='border-b border-ipt_border p-2 text-text_black'>
<div className='mr-2'>
<FaExclamationCircle
className='text-red-500 cursor-help'
data-tooltip-id={`error-${idx}`}
data-tooltip-html={res.message}
/>
<Tooltip id={`error-${idx}`} place='top' type='error' effect='solid' />
</div>
</td>
<td className='border-b border-ipt_border p-2 text-text_black'>
{formatId(from.billingId)}
</td>
<td className='border-b border-ipt_border p-2 text-text_black'>
{from.memberName}
</td>
<td className='border-b border-ipt_border p-2 text-text_black'>
{formatPhone(from.memberPhone)}
</td>
<td className='border-b border-ipt_border p-2 text-text_black'>
{from.billingDate}
</td>
</tr>
))}
</tbody>
</table>
</div>
<div className='mt-4 flex justify-center'>
<button
onClick={() => setIsShowModal(false)}
className='px-4 py-2 bg-mint text-white rounded hover:bg-mint_hover transition-colors'>
확인
</button>
</div>
</div>
</BaseModal>
);
};

export default PayRealtimeErrorModal;
79 changes: 79 additions & 0 deletions client/src/components/vendor/modal/SendInvoiceErrorModal.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import React from 'react';
import { formatId } from '@/utils/format/formatId';
import { formatPhone } from '@/utils/format/formatPhone';
import { Tooltip } from 'react-tooltip';
import 'react-tooltip/dist/react-tooltip.css';
import BaseModal from '@/components/common/BaseModal';
import { FaExclamationCircle } from 'react-icons/fa';

const SendInvoiceErrorModal = ({ errors, isShowModal, icon, setIsShowModal, modalTitle }) => {
return (
<BaseModal
isShowModal={isShowModal}
setIsShowModal={setIsShowModal}
modalTitle={modalTitle}
icon={icon}
height={'h-5/6'}
width={'w-5/6'}>
<div className='flex flex-col h-full'>
<div className='mb-4 text-lg font-semibold text-red-500'>
{errors && errors[0].total}건 중 {errors.length}건의 청구서 발송이 실패했습니다.
</div>
<div className='flex-grow overflow-auto'>
<table className='w-full mb-3'>
<thead>
<tr className='bg-table_col'>
<th className='p-2 text-left text-text_black w-8'></th>
<th className='p-2 text-left text-text_black'>청구번호</th>
<th className='p-2 text-left text-text_black'>회원이름</th>
<th className='p-2 text-left text-text_black'>휴대전화</th>
<th className='p-2 text-left text-text_black'>청구상태</th>
<th className='p-2 text-left text-text_black'>결제일</th>
</tr>
</thead>
<tbody>
{errors.map(({ from, res }, idx) => (
<tr key={idx} className='hover:bg-gray-100'>
<td className='border-b border-ipt_border p-2 text-text_black'>
<div className='mr-2'>
<FaExclamationCircle
className='text-red-500 cursor-help'
data-tooltip-id={`error-${idx}`}
data-tooltip-html={res.message}
/>
<Tooltip id={`error-${idx}`} place='top' type='error' effect='solid' />
</div>
</td>
<td className='border-b border-ipt_border p-2 text-text_black'>
{formatId(from.billingId)}
</td>
<td className='border-b border-ipt_border p-2 text-text_black'>
{from.memberName}
</td>
<td className='border-b border-ipt_border p-2 text-text_black'>
{formatPhone(from.memberPhone)}
</td>
<td className='border-b border-ipt_border p-2 text-text_black'>
{from.billingStatus}
</td>
<td className='border-b border-ipt_border p-2 text-text_black'>
{from.billingDate}
</td>
</tr>
))}
</tbody>
</table>
</div>
<div className='mt-4 flex justify-center'>
<button
onClick={() => setIsShowModal(false)}
className='px-4 py-2 bg-mint text-white rounded hover:bg-mint_hover transition-colors'>
확인
</button>
</div>
</div>
</BaseModal>
);
};

export default SendInvoiceErrorModal;
4 changes: 2 additions & 2 deletions client/src/pages/vendor/billing/BillingDetailPage.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {
sendInvoice,
updateBilling,
cancelSendInvoice,
payBilling,
payRealTimeBilling,
cancelPayBilling,
getBillingProducts,
} from '@/apis/billing';
Expand Down Expand Up @@ -159,7 +159,7 @@ const BillingDetailPage = () => {

const handlePay = async () => {
try {
await payBilling(billingId);
await payRealTimeBilling(billingId);
alert('청구가 결제되었습니다.');
fetchBillingDetail();
} catch (err) {
Expand Down
Loading
Loading