This commit is contained in:
Jay Sheen
2025-11-11 09:49:56 +09:00
14 changed files with 666 additions and 171 deletions

View File

@@ -15,7 +15,7 @@ import { DetailMotionDuration, DetailMotionStyle, DetailMotionVariants } from '@
import { FullMenuClose } from '@/entities/common/ui/full-menu-close';
import { showAlert } from '@/widgets/show-alert';
import { snackBar } from '@/shared/lib';
import { TaxInvoiceSample } from '@/entities/common/ui/tax-invoice-sample';
import { DepositReceiptSample } from '@/entities/common/ui/deposit-receipt-sample';
export interface FundAccountResultDetailProps {
detailOn: boolean;
@@ -34,7 +34,7 @@ export const FundAccountResultDetail = ({
const [detail, setDetail] = useState<ExtensionFundAccountResultDetailResponse>();
const [downloadBottomSheetOn, setDownloadBottomSheetOn] = useState<boolean>(false);
const [taxInvoiceOn, setTaxInvoiceOn] = useState<boolean>(false);
const [depositReceiptSampleOn, setDepositReceiptSampleOn] = useState<boolean>(false);
const { mutateAsync: extensionFundAccountResultDetail } = useExtensionFundAccountResultDetailMutation();
const { mutateAsync: extensionFundAccountDownlaodReceipt } = useExtensionFundAccountDownloadReceiptMutation();
@@ -91,7 +91,7 @@ export const FundAccountResultDetail = ({
};
extensionFundAccountDownlaodReceipt(params).then((rs: ExtensionFundAccountDownloadReceiptResponse) => {
if (selectedMode === DownloadSelectedMode.IMAGE) {
setTaxInvoiceOn(true)
setDepositReceiptSampleOn(true)
} else if (selectedMode === DownloadSelectedMode.EMAIL) {
snackBar('입금확인서 다운 성공')
}
@@ -213,10 +213,10 @@ export const FundAccountResultDetail = ({
}
</motion.div>
{
<TaxInvoiceSample
taxInvoiceOn={taxInvoiceOn}
setTaxInvoiceOn={setTaxInvoiceOn}
></TaxInvoiceSample>
<DepositReceiptSample
depositReceiptSampleOn={depositReceiptSampleOn}
setDepositReceiptSampleOn={setDepositReceiptSampleOn}
></DepositReceiptSample>
}
</>
);

View File

@@ -1,5 +1,4 @@
import moment from 'moment';
import { NumericFormat } from 'react-number-format';
import { useTranslation } from 'react-i18next';
import { AdditionalServiceCategory, DetailInfoSectionKeys } from '../../model/types';
import { DetailInfoSectionProps } from '../../model/types';

View File

@@ -11,7 +11,7 @@ import { DownloadBottomSheet, DownloadSelectedMode } from '@/entities/common/ui/
import { showAlert } from '@/widgets/show-alert';
import { checkGrant } from '@/shared/lib/check-grant';
import { snackBar } from '@/shared/lib';
import { TaxInvoiceSample } from '@/entities/common/ui/tax-invoice-sample';
import { DepositReceiptSample } from '@/entities/common/ui/deposit-receipt-sample';
export interface PayoutDetailProps {
detailOn: boolean;
@@ -31,7 +31,7 @@ export const PayoutDetail = ({
const [detail, setDetail] = useState<ExtensionPayoutDetailResponse>();
const [downloadBottomSheetOn, setDownloadBottomSheetOn] = useState<boolean>(false);
const [taxInvoiceOn, setTaxInvoiceOn] = useState<boolean>(false);
const [depositReceiptSampleOn, setDepositReceiptSampleOn] = useState<boolean>(false);
const { mutateAsync: extensionPayoutDetail } = useExtensionPayoutDetailMutation();
const { mutateAsync: extensionPayoutDetailDownloadCertification } = useExtensionPayoutDetailDownloadCertificateMutation();
@@ -72,9 +72,9 @@ export const PayoutDetail = ({
};
extensionPayoutDetailDownloadCertification(params).then((rs: ExtensionPayoutDetailDownloadCertificateResponse) => {
console.log('Certificate Download Status:', rs);
if(selectedMode === DownloadSelectedMode.IMAGE) {
setTaxInvoiceOn(true)
} else if(selectedMode === DownloadSelectedMode.EMAIL) {
if (selectedMode === DownloadSelectedMode.IMAGE) {
setDepositReceiptSampleOn(true)
} else if (selectedMode === DownloadSelectedMode.EMAIL) {
snackBar('입금확인서 다운 성공')
}
}).catch((error) => {
@@ -205,10 +205,10 @@ export const PayoutDetail = ({
}
</motion.div>
{
<TaxInvoiceSample
taxInvoiceOn={taxInvoiceOn}
setTaxInvoiceOn={setTaxInvoiceOn}
></TaxInvoiceSample>
<DepositReceiptSample
depositReceiptSampleOn={depositReceiptSampleOn}
setDepositReceiptSampleOn={setDepositReceiptSampleOn}
></DepositReceiptSample>
}
</>
);

View File

@@ -0,0 +1,241 @@
import { IMAGE_ROOT } from '@/shared/constants/common';
import { motion } from 'framer-motion';
import { FilterMotionDuration, FilterMotionVariants } from '../model/constant';
import { snackBar } from '@/shared/lib';
import { toPng } from 'html-to-image';
import { useTranslation } from 'react-i18next';
import '@/shared/ui/assets/css/style-tax-invoice.css';
import { useEffect } from 'react';
import { NumericFormat } from 'react-number-format';
import { AmountInfo, CustomerInfo, IssueInfo, MerchantInfo, ProductInfo, TransactionInfo } from '@/entities/transaction/model/types';
import moment from 'moment';
export interface CashReceiptSampleProps {
cashReceiptSampleOn: boolean;
setCashReceiptSampleOn: (cashReceiptSampleOn: boolean) => void;
merchantInfo?: MerchantInfo;
issueInfo?: IssueInfo;
amountInfo?: AmountInfo;
transactionInfo?: TransactionInfo;
customerInfo?: CustomerInfo;
productInfo?: ProductInfo;
};
export const CashReceiptSample = ({
cashReceiptSampleOn,
setCashReceiptSampleOn,
merchantInfo,
issueInfo,
amountInfo,
transactionInfo,
customerInfo,
productInfo
}: CashReceiptSampleProps) => {
const { t } = useTranslation();
const downloadImage = () => {
const section = document.getElementById('image-section') as HTMLElement;
toPng(section).then((image) => {
const link = document.createElement('a');
link.download = 'downloadImage.png';
link.href = image;
link.click();
snackBar(t('common.imageRequested'), function(){
onClickToClose();
});
});
};
const onClickToClose = () => {
setCashReceiptSampleOn(false);
};
const getDateTime = () => {
let date = '';
let time = '';
if(!!issueInfo?.issueDate){
date = moment(issueInfo?.issueDate).format('YYYY.MM.DD');
}
if(!!issueInfo?.issueDate && !!issueInfo?.issueTime){
time = moment(issueInfo?.issueDate+' '+issueInfo?.issueTime).format('HH:mm:ss');
}
if(!!date && !!time){
return date + ' | ' + time;
}
else{
return '';
}
};
useEffect(() => {
if(!!cashReceiptSampleOn){
setTimeout(() => {
downloadImage();
}, 500);
}
}, [cashReceiptSampleOn]);
return (
<>
<motion.div
className="bottomsheet mail-page"
initial="hidden"
animate={ (cashReceiptSampleOn)? 'hidden': 'hidden' }
variants={ FilterMotionVariants }
transition={ FilterMotionDuration }
>
<div id="image-section">
<div className="wrap">
<div className="section">
<div className="head">
<div className="icon">
<svg
xmlns="http://www.w3.org/2000/svg"
width="20"
height="16"
viewBox="0 0 20 16"
fill="none"
>
<path d="M5.23633 0.378906C5.54719 0.379069 5.82854 0.555366 5.95898 0.825195L6.00488 0.946289L6.00586 0.947266L8.24023 8.89258H16.3174L17.4688 4.49805H8.92188C8.48682 4.49805 8.12402 4.15788 8.12402 3.72559C8.12427 3.2935 8.48697 2.9541 8.92188 2.9541H18.4941C18.7367 2.95411 18.9686 3.06058 19.1211 3.24805C19.2743 3.4358 19.3284 3.6835 19.2676 3.91602L17.7119 9.85449V9.85547C17.6207 10.2013 17.3001 10.4374 16.9385 10.4375H7.62891C7.27229 10.4375 6.95384 10.2072 6.8584 9.86719L4.62695 1.92383H1.64844C1.21341 1.92383 0.849609 1.58369 0.849609 1.15137C0.849699 0.719302 1.21267 0.37915 1.64746 0.378906H5.23633Z" fill="#2D3436" stroke="#2D3436" strokeWidth="0.3"/>
<path d="M8.92133 11.1777C10.187 11.1779 11.2279 12.1683 11.228 13.3984C11.228 14.6285 10.1872 15.6199 8.92133 15.6201C7.65619 15.6201 6.61469 14.6287 6.61469 13.3984C6.61473 12.1683 7.65601 11.1769 8.92133 11.1777ZM8.9223 12.7227C8.52551 12.7227 8.21141 13.0313 8.21136 13.3984C8.21136 13.7659 8.52494 14.0752 8.9223 14.0752C9.31966 14.075 9.63324 13.7656 9.63324 13.3984C9.6332 13.0312 9.31954 12.7221 8.9223 12.7227Z" fill="#2D3436" stroke="#2D3436" strokeWidth="0.3"/>
<path d="M15.6186 11.1777C16.8843 11.1779 17.9252 12.169 17.9252 13.3984C17.9252 14.6285 16.8844 15.6199 15.6186 15.6201C14.3535 15.6201 13.312 14.6287 13.312 13.3984C13.312 12.1691 14.3526 11.1777 15.6186 11.1777ZM15.6186 12.7236C15.2218 12.7236 14.9077 13.0322 14.9077 13.3994C14.9077 13.7669 15.2212 14.0762 15.6186 14.0762C16.016 14.076 16.3295 13.7666 16.3295 13.3994C16.3295 13.0331 16.0161 12.7238 15.6186 12.7236Z" fill="#2D3436" stroke="#2D3436" strokeWidth="0.3"/>
</svg>
</div>
<div className="h-title"></div>
</div>
<div className="row">
<div className="k"></div>
<div className="v">{ merchantInfo?.merchantName }</div>
</div>
<div className="row">
<div className="k"></div>
<div className="v">{ merchantInfo?.representativeName }</div>
</div>
<div className="row">
<div className="k"></div>
<div className="v">{ merchantInfo?.businessNumber }</div>
</div>
<div className="row">
<div className="k"></div>
<div className="v">{ merchantInfo?.phoneNumber }</div>
</div>
<div className="row">
<div className="k"></div>
<div className="v">{ merchantInfo?.address }</div>
</div>
<div className="row">
<div className="k">URL</div>
<div className="v"></div>
</div>
</div>
<div className="divider"></div>
<div className="section">
<div className="head">
<div className="icon">
<svg
xmlns="http://www.w3.org/2000/svg"
width="16"
height="20"
viewBox="0 0 16 20"
fill="none"
>
<path d="M13.7656 0.799805C14.839 0.800034 15.7001 1.70434 15.7002 2.7998V17.2002C15.7001 18.2957 14.839 19.2 13.7656 19.2002H2.23438C1.16097 19.2 0.299907 18.2957 0.299805 17.2002V2.7998C0.299907 1.70434 1.16097 0.800034 2.23438 0.799805H13.7656ZM2.23438 2.40039C2.03318 2.40062 1.85655 2.57273 1.85645 2.7998V17.2002C1.85655 17.4273 2.03318 17.5994 2.23438 17.5996H13.7656C13.9674 17.5994 14.1435 17.4274 14.1436 17.2002V2.7998C14.1435 2.57257 13.9674 2.40062 13.7656 2.40039H2.23438Z" fill="#2D3436" stroke="#2D3436" strokeWidth="0.4"/>
<path d="M11.9355 3.7998C12.3344 3.79989 12.7002 4.09119 12.7002 4.5C12.7002 4.90881 12.3344 5.20011 11.9355 5.2002H4.06445C3.66564 5.20011 3.2998 4.90881 3.2998 4.5C3.2998 4.09119 3.66564 3.79989 4.06445 3.7998H11.9355Z" fill="#2D3436" stroke="#2D3436" strokeWidth="0.4"/>
<path d="M11.9355 6.7998C12.3344 6.79989 12.7002 7.09119 12.7002 7.5C12.7002 7.90881 12.3344 8.20011 11.9355 8.2002H4.06445C3.66564 8.20011 3.2998 7.90881 3.2998 7.5C3.2998 7.09119 3.66564 6.79989 4.06445 6.7998H11.9355Z" fill="#2D3436" stroke="#2D3436" strokeWidth="0.4"/>
<path d="M6.91504 9.7998C7.31807 9.7998 7.7002 10.085 7.7002 10.5C7.7002 10.9157 7.31789 11.2002 6.91504 11.2002H4.08496C3.68193 11.2002 3.2998 10.915 3.2998 10.5C3.2998 10.085 3.68193 9.7998 4.08496 9.7998H6.91504Z" fill="#2D3436" stroke="#2D3436" strokeWidth="0.4"/>
<path d="M10.9414 14.7998C11.3391 14.7998 11.7002 15.093 11.7002 15.5C11.7002 15.907 11.3391 16.2002 10.9414 16.2002H6.05859C5.66094 16.2002 5.2998 15.907 5.2998 15.5C5.2998 15.093 5.66094 14.7998 6.05859 14.7998H10.9414Z" fill="#2D3436" stroke="#2D3436" strokeWidth="0.4"/>
</svg>
</div>
<div className="h-title"></div>
</div>
<div className="amount-row">
<div className="k"> </div>
<div className="v">
<NumericFormat
value={ amountInfo?.amount }
thousandSeparator
displayType="text"
></NumericFormat>
</div>
</div>
<div className="row">
<div className="k"></div>
<div className="v">
<NumericFormat
value={ amountInfo?.supplyAmount }
thousandSeparator
displayType="text"
></NumericFormat>
</div>
</div>
<div className="row">
<div className="k"></div>
<div className="v">
<NumericFormat
value={ amountInfo?.vatAmount }
thousandSeparator
displayType="text"
></NumericFormat>
</div>
</div>
<div className="row">
<div className="k"></div>
<div className="v">
<NumericFormat
value={ amountInfo?.taxFreeAmount }
thousandSeparator
displayType="text"
></NumericFormat>
</div>
</div>
<div className="row">
<div className="k"></div>
<div className="v">
<NumericFormat
value={ amountInfo?.serviceAmount }
thousandSeparator
displayType="text"
></NumericFormat>
</div>
</div>
<div className="row split"></div>
<div className="row">
<div className="k"></div>
<div className="v">{ transactionInfo?.paymentMethod }</div>
</div>
<div className="row">
<div className="k"></div>
<div className="v">{ issueInfo?.issueNumber }</div>
</div>
<div className="row">
<div className="k"></div>
<div className="v">{ customerInfo?.customerName }</div>
</div>
<div className="row">
<div className="k"></div>
<div className="v">{ productInfo?.productName }</div>
</div>
<div className="row">
<div className="k"></div>
<div className="v">{ issueInfo?.approvalNumber }</div>
</div>
<div className="row">
<div className="k"></div>
<div className="v">{ getDateTime() }</div>
</div>
<div className="row">
<div className="k"></div>
<div className="v">{ issueInfo?.purpose }</div>
</div>
</div>
<div className="notice">
* 126 3 121 3 , .<br />
* 2 .<br />
* 126-1-1
</div>
</div>
</div>
</motion.div>
</>
);
};

View File

@@ -0,0 +1,115 @@
import { IMAGE_ROOT } from '@/shared/constants/common';
import { motion } from 'framer-motion';
import { FilterMotionDuration, FilterMotionVariants } from '../model/constant';
import { snackBar } from '@/shared/lib';
import { toPng } from 'html-to-image';
import { useTranslation } from 'react-i18next';
import '@/shared/ui/assets/css/style-tax-invoice.css';
import { useEffect } from 'react';
import { NumericFormat } from 'react-number-format';
import { DepositInfo } from '@/entities/transaction/model/types';
export interface DepositReceiptSampleProps {
depositReceiptSampleOn: boolean;
setDepositReceiptSampleOn: (DepositReceiptSampleOn: boolean) => void;
depositInfo?: DepositInfo
};
export const DepositReceiptSample = ({
depositReceiptSampleOn,
setDepositReceiptSampleOn,
depositInfo
}: DepositReceiptSampleProps) => {
const { t } = useTranslation();
const downloadImage = () => {
const section = document.getElementById('image-section') as HTMLElement
toPng(section).then((image) => {
const link = document.createElement('a');
link.download = 'downloadImage.png';
link.href = image;
link.click();
snackBar(t('common.imageRequested'), function () {
onClickToClose();
});
});
}
const onClickToClose = () => {
setDepositReceiptSampleOn(false);
};
useEffect(() => {
if (!!depositReceiptSampleOn) {
downloadImage();
}
}, [depositReceiptSampleOn]);
return (
<>
<motion.div
className="bottomsheet mail-page"
initial="hidden"
animate={(depositReceiptSampleOn) ? 'hidden' : 'hidden'}
variants={FilterMotionVariants}
transition={FilterMotionDuration}
>
<div className="wrap">
<div id="image-section">
<div className="section">
<div className="head">
<div className="icon">
<svg
xmlns="http://www.w3.org/2000/svg"
width="16"
height="20"
viewBox="0 0 16 20"
fill="none"
>
<path d="M13.7656 0.799805C14.839 0.800034 15.7001 1.70434 15.7002 2.7998V17.2002C15.7001 18.2957 14.839 19.2 13.7656 19.2002H2.23438C1.16097 19.2 0.299907 18.2957 0.299805 17.2002V2.7998C0.299907 1.70434 1.16097 0.800034 2.23438 0.799805H13.7656ZM2.23438 2.40039C2.03318 2.40062 1.85655 2.57273 1.85645 2.7998V17.2002C1.85655 17.4273 2.03318 17.5994 2.23438 17.5996H13.7656C13.9674 17.5994 14.1435 17.4274 14.1436 17.2002V2.7998C14.1435 2.57257 13.9674 2.40062 13.7656 2.40039H2.23438Z" fill="#2D3436" stroke="#2D3436" strokeWidth="0.4" />
<path d="M11.9355 3.7998C12.3344 3.79989 12.7002 4.09119 12.7002 4.5C12.7002 4.90881 12.3344 5.20011 11.9355 5.2002H4.06445C3.66564 5.20011 3.2998 4.90881 3.2998 4.5C3.2998 4.09119 3.66564 3.79989 4.06445 3.7998H11.9355Z" fill="#2D3436" stroke="#2D3436" strokeWidth="0.4" />
<path d="M11.9355 6.7998C12.3344 6.79989 12.7002 7.09119 12.7002 7.5C12.7002 7.90881 12.3344 8.20011 11.9355 8.2002H4.06445C3.66564 8.20011 3.2998 7.90881 3.2998 7.5C3.2998 7.09119 3.66564 6.79989 4.06445 6.7998H11.9355Z" fill="#2D3436" stroke="#2D3436" strokeWidth="0.4" />
<path d="M6.91504 9.7998C7.31807 9.7998 7.7002 10.085 7.7002 10.5C7.7002 10.9157 7.31789 11.2002 6.91504 11.2002H4.08496C3.68193 11.2002 3.2998 10.915 3.2998 10.5C3.2998 10.085 3.68193 9.7998 4.08496 9.7998H6.91504Z" fill="#2D3436" stroke="#2D3436" strokeWidth="0.4" />
<path d="M10.9414 14.7998C11.3391 14.7998 11.7002 15.093 11.7002 15.5C11.7002 15.907 11.3391 16.2002 10.9414 16.2002H6.05859C5.66094 16.2002 5.2998 15.907 5.2998 15.5C5.2998 15.093 5.66094 14.7998 6.05859 14.7998H10.9414Z" fill="#2D3436" stroke="#2D3436" strokeWidth="0.4" />
</svg>
</div>
<div className="h-title"></div>
</div>
<div className="row">
<div className="k"></div>
<div className="v">{depositInfo?.depositDate}</div>
</div>
<div className="row">
<div className="k"></div>
<div className="v">{depositInfo?.depositBank}</div>
</div>
<div className="row">
<div className="k"></div>
<div className="v">{depositInfo?.depositAccount}</div>
</div>
<div className="row">
<div className="k"></div>
<div className="v">
<NumericFormat
value={depositInfo?.amount}
thousandSeparator
displayType='text'
>
</NumericFormat>
</div>
</div>
<div className="row">
<div className="k"></div>
<div className="v">{depositInfo?.depositReason}</div>
</div>
<div className="row">
<div className="k">ID</div>
<div className="v">{depositInfo?.depositId}</div>
</div>
</div>
</div>
</div>
</motion.div>
</>
)
}

View File

@@ -6,16 +6,31 @@ import { toPng } from 'html-to-image';
import { useTranslation } from 'react-i18next';
import '@/shared/ui/assets/css/style-tax-invoice.css';
import { useEffect } from 'react';
import { NumericFormat } from 'react-number-format';
import { AmountInfo, CustomerInfo, IssueInfo, MerchantInfo, ProductInfo, TransactionInfo } from '@/entities/transaction/model/types';
import moment from 'moment';
export interface TaxInoviceSampleProps {
taxInvoiceOn: boolean;
setTaxInvoiceOn: (taxInvoiceOn: boolean) => void;
export interface TaxInvoiceSampleProps {
taxInvoiceSampleOn: boolean;
setTaxInvoiceSampleOn: (taxInvoiceSampleOn: boolean) => void;
merchantInfo?: MerchantInfo;
issueInfo?: IssueInfo;
amountInfo?: AmountInfo;
transactionInfo?: TransactionInfo;
customerInfo?: CustomerInfo;
productInfo?: ProductInfo;
};
export const TaxInvoiceSample = ({
taxInvoiceOn,
setTaxInvoiceOn
}: TaxInoviceSampleProps) => {
taxInvoiceSampleOn,
setTaxInvoiceSampleOn,
merchantInfo,
issueInfo,
amountInfo,
transactionInfo,
customerInfo,
productInfo
}: TaxInvoiceSampleProps) => {
const { t } = useTranslation();
const downloadImage = () => {
@@ -31,14 +46,33 @@ export const TaxInvoiceSample = ({
});
};
const onClickToClose = () => {
setTaxInvoiceOn(false);
setTaxInvoiceSampleOn(false);
};
const getDateTime = () => {
let date = '';
let time = '';
if(!!issueInfo?.issueDate){
date = moment(issueInfo?.issueDate).format('YYYY.MM.DD');
}
if(!!issueInfo?.issueDate && !!issueInfo?.issueTime){
time = moment(issueInfo?.issueDate+' '+issueInfo?.issueTime).format('HH:mm:ss');
}
if(!!date && !!time){
return date + ' | ' + time;
}
else{
return '';
}
};
useEffect(() => {
if(!!taxInvoiceOn){
if(!!taxInvoiceSampleOn){
setTimeout
downloadImage();
}
}, [taxInvoiceOn]);
}, [taxInvoiceSampleOn]);
return (
@@ -46,52 +80,12 @@ export const TaxInvoiceSample = ({
<motion.div
className="bottomsheet mail-page"
initial="hidden"
animate={ (taxInvoiceOn)? 'hidden': 'hidden' }
animate={ (taxInvoiceSampleOn)? 'visible': 'hidden' }
variants={ FilterMotionVariants }
transition={ FilterMotionDuration }
>
<div className="wrap">
<div className="header">
<div className="header-top">
<img
className="logo"
src={ IMAGE_ROOT + '/mail_nicepay_logo.svg' }
alt="NICEPAY"
/>
<img
className="close-ic"
src={ IMAGE_ROOT + '/ico_close.svg' }
alt="닫기"
/>
</div>
<div className="email-row">
<div className="label"> </div>
<input
className="input40"
type="email"
placeholder="test@abc.com"
aria-label="수신 이메일 주소 입력"
/>
<a
className="btn btn-dark"
style={{ width: '46px' }}
></a>
<a className="btn btn-primary"></a>
</div>
<div className="divider"></div>
<div className="title-row">
<div className="title"> </div>
<select
className="select-outline"
aria-label="작업 선택"
>
<option value="cancel"></option>
<option value="reprint"></option>
</select>
</div>
</div>
<div id="image-section">
<div className="wrap">
<div className="section">
<div className="head">
<div className="icon">
@@ -111,27 +105,27 @@ export const TaxInvoiceSample = ({
</div>
<div className="row">
<div className="k"></div>
<div className="v"> </div>
<div className="v">{ merchantInfo?.merchantName }</div>
</div>
<div className="row">
<div className="k"></div>
<div className="v"></div>
<div className="v">{ merchantInfo?.representativeName }</div>
</div>
<div className="row">
<div className="k"></div>
<div className="v">815-81-00527</div>
<div className="v">{ merchantInfo?.businessNumber }</div>
</div>
<div className="row">
<div className="k"></div>
<div className="v">1661-7335</div>
<div className="v">{ merchantInfo?.phoneNumber }</div>
</div>
<div className="row">
<div className="k"></div>
<div className="v"> 217 11</div>
<div className="v">{ merchantInfo?.address }</div>
</div>
<div className="row">
<div className="k">URL</div>
<div className="v">home.nicepay.co.kr</div>
<div className="v"></div>
</div>
</div>
<div className="divider"></div>
@@ -157,103 +151,91 @@ export const TaxInvoiceSample = ({
</div>
<div className="amount-row">
<div className="k"> </div>
<div className="v">4,552,000</div>
<div className="v">
<NumericFormat
value={ amountInfo?.amount }
thousandSeparator
displayType="text"
></NumericFormat>
</div>
</div>
<div className="row">
<div className="k"></div>
<div className="v">343,940</div>
<div className="k"></div>
<div className="v">
<NumericFormat
value={ amountInfo?.supplyAmount }
thousandSeparator
displayType="text"
></NumericFormat>
</div>
</div>
<div className="row">
<div className="k"></div>
<div className="v">343,940</div>
<div className="v">
<NumericFormat
value={ amountInfo?.vatAmount }
thousandSeparator
displayType="text"
></NumericFormat>
</div>
</div>
<div className="row">
<div className="k"></div>
<div className="v">
<NumericFormat
value={ amountInfo?.taxFreeAmount }
thousandSeparator
displayType="text"
></NumericFormat>
</div>
</div>
<div className="row">
<div className="k"></div>
<div className="v">
<NumericFormat
value={ amountInfo?.serviceAmount }
thousandSeparator
displayType="text"
></NumericFormat>
</div>
</div>
<div className="row split"></div>
<div className="row">
<div className="k"></div>
<div className="v"></div>
<div className="k"></div>
<div className="v">{ transactionInfo?.paymentMethod }</div>
</div>
<div className="row">
<div className="k"></div>
<div className="v">489023******0070</div>
<div className="k"></div>
<div className="v">{ issueInfo?.issueNumber }</div>
</div>
<div className="row">
<div className="k"></div>
<div className="v">*</div>
<div className="k"></div>
<div className="v">{ customerInfo?.customerName }</div>
</div>
<div className="row">
<div className="k"></div>
<div className="v"></div>
</div>
<div className="row">
<div className="k"></div>
<div className="v"></div>
</div>
<div className="row">
<div className="k">/</div>
<div className="v"></div>
<div className="v">{ productInfo?.productName }</div>
</div>
<div className="row">
<div className="k"></div>
<div className="v">14219606</div>
<div className="v">{ issueInfo?.approvalNumber }</div>
</div>
<div className="row">
<div className="k"></div>
<div className="v">2025.09.22 | 11:32:29</div>
<div className="v">{ getDateTime() }</div>
</div>
<div className="row">
<div className="k"></div>
<div className="v">2025.09.22 | 23:10:16</div>
<div className="k"></div>
<div className="v">{ issueInfo?.purpose }</div>
</div>
</div>
<div className="divider"></div>
<div className="section">
<div className="head">
<div className="icon">
<svg
xmlns="http://www.w3.org/2000/svg"
width="20"
height="20"
viewBox="0 0 20 20"
fill="none"
>
<path d="M18.4004 0.849609C18.8129 0.849821 19.1502 1.18707 19.1504 1.59961V18.4004C19.1502 18.8129 18.8129 19.1502 18.4004 19.1504H1.59961C1.18707 19.1502 0.849821 18.8129 0.849609 18.4004V11.2002C0.849609 10.7875 1.18694 10.4504 1.59961 10.4502H8.0498V1.59961C8.05002 1.18701 8.38718 0.849716 8.7998 0.849609H18.4004ZM9.5498 17.6504H17.6504V2.34961H9.5498V17.6504ZM2.34961 17.6504H8.0498V11.9502H2.34961V17.6504Z" fill="#2D3436" stroke="#2D3436" strokeWidth="0.3"/>
<path d="M12.5496 4.44922V6.5498H11.0496V4.44922H12.5496Z" fill="#2D3436" stroke="#2D3436" strokeWidth="0.3"/>
<path d="M16.1496 4.44922V6.5498H14.6496V4.44922H16.1496Z" fill="#2D3436" stroke="#2D3436" strokeWidth="0.3"/>
<path d="M12.5496 7.44922V9.5498H11.0496V7.44922H12.5496Z" fill="#2D3436" stroke="#2D3436" strokeWidth="0.3"/>
<path d="M16.1496 7.44922V9.5498H14.6496V7.44922H16.1496Z" fill="#2D3436" stroke="#2D3436" strokeWidth="0.3"/>
<path d="M12.5496 10.4492V12.5498H11.0496V10.4492H12.5496Z" fill="#2D3436" stroke="#2D3436" strokeWidth="0.3"/>
<path d="M16.1496 10.4492V12.5498H14.6496V10.4492H16.1496Z" fill="#2D3436" stroke="#2D3436" strokeWidth="0.3"/>
<path d="M6.15039 13.8496V15.1504H3.84961V13.8496H6.15039Z" fill="#2D3436" stroke="#2D3436" strokeWidth="0.3"/>
</svg>
</div>
<div className="h-title"> </div>
</div>
<div className="row">
<div className="k"></div>
<div className="v"> </div>
</div>
<div className="row">
<div className="k"></div>
<div className="v"></div>
</div>
<div className="row">
<div className="k"></div>
<div className="v">815-81-00527</div>
</div>
<div className="row">
<div className="k"></div>
<div className="v">1661-0808</div>
</div>
<div className="row">
<div className="k"></div>
<div className="v"> 28 25 (3)</div>
<div className="notice">
* 126 3 121 3 , .<br />
* 2 .<br />
* 126-1-1
</div>
</div>
</div>
<div className="notice"> NICE로 .<br />* 46 .<br />( 33 2 .)</div>
</div>
</motion.div>
</>
);

View File

@@ -0,0 +1,39 @@
import axios from 'axios';
import { API_URL_TRANSACTION } from '@/shared/api/api-url-transaction';
import { resultify } from '@/shared/lib/resultify';
import { NiceAxiosError } from '@/shared/@types/error';
import {
CashReceiptExcelDownloadParams,
CashReceiptExcelDownloadResponse
} from '../model/types';
import {
useMutation,
UseMutationOptions
} from '@tanstack/react-query';
import { getHeaderUserAgent } from '@/shared/constants/url';
export const cashReceiptExcelDownload = (params: CashReceiptExcelDownloadParams) => {
let headerOptions = {
menuId: 32,
apiType: 'DOWNLOAD'
};
let options = {
headers: {
'X-User-Agent': getHeaderUserAgent(headerOptions)
}
};
return resultify(
axios.post<CashReceiptExcelDownloadResponse>(API_URL_TRANSACTION.cashReceiptReceiptDownload(), params, options),
);
};
export const useCashReceiptExcelDownloadMutation = (options?: UseMutationOptions<CashReceiptExcelDownloadResponse, NiceAxiosError, CashReceiptExcelDownloadParams>) => {
const mutation = useMutation<CashReceiptExcelDownloadResponse, NiceAxiosError, CashReceiptExcelDownloadParams>({
...options,
mutationFn: (params: CashReceiptExcelDownloadParams) => cashReceiptExcelDownload(params),
});
return {
...mutation,
};
};

View File

@@ -0,0 +1,39 @@
import axios from 'axios';
import { API_URL_TRANSACTION } from '@/shared/api/api-url-transaction';
import { resultify } from '@/shared/lib/resultify';
import { NiceAxiosError } from '@/shared/@types/error';
import {
EscrowDownloadExcelParams,
EscrowDownloadExcelResponse
} from '../model/types';
import {
useMutation,
UseMutationOptions
} from '@tanstack/react-query';
import { getHeaderUserAgent } from '@/shared/constants/url';
export const escrowDownloadExcelList = (params: EscrowDownloadExcelParams) => {
let headerOptions = {
menuId: 33,
apiType: 'DOWNLOAD'
};
let options = {
headers: {
'X-User-Agent': getHeaderUserAgent(headerOptions)
}
};
return resultify(
axios.post<EscrowDownloadExcelResponse>(API_URL_TRANSACTION.escrowList(), params, options),
);
};
export const useEscrowListMutation = (options?: UseMutationOptions<EscrowDownloadExcelResponse, NiceAxiosError, EscrowDownloadExcelParams>) => {
const mutation = useMutation<EscrowDownloadExcelResponse, NiceAxiosError, EscrowDownloadExcelParams>({
...options,
mutationFn: (params: EscrowDownloadExcelParams) => escrowDownloadExcelList(params),
});
return {
...mutation,
};
};

View File

@@ -292,6 +292,22 @@ export interface CashReceiptReceiptDownloadResponse {
customerInfo: CustomerInfo;
productInfo: ProductInfo;
};
export enum CashReceiptDateType {
ISSUE_DATE = 'ISSUE_DATE',
CANCEL_DATE = 'CANCEL_DATE'
};
export interface CashReceiptExcelDownloadParams {
email: string;
mid: string;
startDate: string;
endDate: string;
dateType: CashReceiptDateType;
purposeType: CashReceiptPurposeType;
transactionType: CashReceiptTransactionType;
processResult: CashReceiptProcessResult;
};
export interface CashReceiptExcelDownloadResponse {};
export interface EscrowListParams {
mid?: string;
searchType?: string;
@@ -305,6 +321,16 @@ export interface EscrowListParams {
page?: DefaultRequestPagination;
};
export interface EscrowDownloadExcelParams {
email: string;
mid: string;
startDate: string;
endDate: string;
deliveryStatus: EscrowDeliveryStatus;
settlementStatus: EscrowSettlementStatus;
};
export interface EscrowDownloadExcelResponse {};
export interface BillingListParams {
mid?: string;
searchType?: string;
@@ -375,6 +401,7 @@ export interface AmountInfo {
simplePaymentServiceCode?: string;
buyerName?: string;
vatAmount?: number;
};
export interface ImportantInfo {
moid?: string;
@@ -467,6 +494,9 @@ export interface TransactionInfo {
cashReceiptIssue?: string;
transactionDateTime?: string;
transactionAmount?: number;
tid?: string;
merchantTid?: string;
paymentMethod?: string;
};
export interface SettlementInfo {
approvalSettlementDate?: string;
@@ -548,6 +578,22 @@ export interface MerchantInfo {
companyName?: string;
gid?: string;
aid?: string;
merchantName?: string;
businessNumber?: string;
representativeName?: string;
address?: string;
phoneNumber?: string;
subMallName?: string;
subMallBusinessNumber?: string;
};
export interface DepositInfo {
depositDate?: string;
depositBank?: string;
depositAccount?: string;
amount?: number;
depositReason?: string;
depositId?: string;
}
export interface DetailResponse {

View File

@@ -1,14 +1,14 @@
import moment from 'moment';
import { useTranslation } from 'react-i18next';
import { SectionTitleArrow } from '@/entities/common/ui/section-title-arrow';
import { CashReceiptReceiptDownloadParams, CashReceiptReceiptDownloadResponse, InfoSectionKeys, InfoSectionProps, TransactionCategory } from '../../model/types';
import { AmountInfo, CashReceiptReceiptDownloadParams, CashReceiptReceiptDownloadResponse, CustomerInfo, InfoSectionKeys, InfoSectionProps, IssueInfo, MerchantInfo, ProductInfo, TransactionCategory, TransactionInfo } from '../../model/types';
import { SlideDown } from 'react-slidedown';
import 'react-slidedown/lib/slidedown.css';
import { snackBar } from '@/shared/lib';
import { useCashReceiptReceiptDownloadMutation } from '../../api/use-cash-receipt-receipt-download-mutation';
import { useState } from 'react';
import { DownloadBottomSheet, DownloadSelectedMode } from '@/entities/common/ui/download-bottom-sheet';
import { TaxInvoiceSample } from '@/entities/common/ui/tax-invoice-sample';
import { CashReceiptSample } from '@/entities/common/ui/cash-receipt-sample';
export const AmountInfoSection = ({
transactionCategory,
@@ -23,7 +23,14 @@ export const AmountInfoSection = ({
const { mutateAsync: cashReceiptReceiptDownload } = useCashReceiptReceiptDownloadMutation();
const [downloadBottomSheetOn, setDownloadBottomSheetOn] = useState<boolean>(false);
const [taxInvoiceOn, setTaxInvoiceOn] = useState<boolean>(false);
const [cashReceiptSampleOn, setCashReceiptSampleOn] = useState<boolean>(false);
const [receiptIssueInfo, setReceiptIssueInfo] = useState<IssueInfo>({});
const [receiptMerchantInfo, setReceiptMerchantInfo] = useState<MerchantInfo>({});
const [receiptTransactionInfo, setReceiptTransactionInfo] = useState<TransactionInfo>({});
const [receiptAmountInfo, setReceiptAmountInfo] = useState<AmountInfo>({});
const [receiptCustomerInfo, setReceiptCustomerInfo] = useState<CustomerInfo>();
const [receiptProductInfo, setReceiptProductInfo] = useState<ProductInfo>();
let newAmountInfo: Record<string, any> | undefined = amountInfo;
const subItems: Record<string, Record<string, string>> = {
@@ -196,15 +203,24 @@ export const AmountInfoSection = ({
selectedMode: DownloadSelectedMode,
userEmail?: string
) => {
console.log(selectedMode, userEmail, tid)
if(!!tid){
let params: CashReceiptReceiptDownloadParams = {
tid: tid
};
cashReceiptReceiptDownload(params).then((rs: CashReceiptReceiptDownloadResponse) => {
console.log(rs);
/*
if(rs?.merchantInfo){
rs.merchantInfo.merchantName = '나이스페이먼츠';
}
*/
setReceiptIssueInfo(rs?.issueInfo);
setReceiptMerchantInfo(rs?.merchantInfo);
setReceiptTransactionInfo(rs?.transactionInfo);
setReceiptAmountInfo(rs?.amountInfo);
setReceiptCustomerInfo(rs?.customerInfo);
setReceiptProductInfo(rs?.productInfo);
if(selectedMode === DownloadSelectedMode.IMAGE){
setTaxInvoiceOn(true);
setCashReceiptSampleOn(true);
}
else if(selectedMode === DownloadSelectedMode.EMAIL){
snackBar('거래확인서 다운 성공');
@@ -412,7 +428,7 @@ export const AmountInfoSection = ({
}
<div className="txn-doc">
{ (transactionCategory === TransactionCategory.CashReceipt) &&
// !!canDownloadReceipt &&
!!canDownloadReceipt &&
<button
className="doc-btn"
type="button"
@@ -430,11 +446,17 @@ export const AmountInfoSection = ({
sendRequest={ onRequestDownload }
></DownloadBottomSheet>
}
{
<TaxInvoiceSample
taxInvoiceOn={ taxInvoiceOn }
setTaxInvoiceOn={ setTaxInvoiceOn }
></TaxInvoiceSample>
{ !!cashReceiptSampleOn &&
<CashReceiptSample
cashReceiptSampleOn={ cashReceiptSampleOn }
setCashReceiptSampleOn={ setCashReceiptSampleOn }
merchantInfo={ receiptMerchantInfo }
issueInfo={ receiptIssueInfo }
amountInfo={ receiptAmountInfo }
transactionInfo={ receiptTransactionInfo }
customerInfo={ receiptCustomerInfo }
productInfo={ receiptProductInfo }
></CashReceiptSample>
}
</>
);

View File

@@ -53,13 +53,6 @@ export const TaxInvoiceDetail = ({
const [detail, setDetail] = useState<VatReturnDetailResponse>({});
const [breakdown, setBreakdown] = useState<Array<Breakdown>>([]);
useSetHeaderTitle(t('vatReturn.taxInvoiceDetail'));
useSetHeaderType(HeaderType.RightClose);
useSetOnBack(() => {
navigate(PATHS.vatReturn.list);
});
useSetFooterMode(false);
const { mutateAsync: vatReturnTaxInvoice } = useVatReturnTaxInvoiceMutation();
const { mutateAsync: vatReturnDetail } = useVatReturnDetailMutation();
const { mutateAsync: vatReturnBreakdown } = useVatReturnBreakdownMutation();
@@ -121,7 +114,7 @@ export const TaxInvoiceDetail = ({
>
<div className="full-menu-container pdw-16">
<div className="full-menu-header">
<div className="full-menu-title center">{ t('transaction.detailTitle') }</div>
<div className="full-menu-title center">{ t('vatReturn.taxInvoiceDetail') }</div>
<div className="full-menu-actions">
<FullMenuClose
addClass="full-menu-close"
@@ -129,7 +122,7 @@ export const TaxInvoiceDetail = ({
></FullMenuClose>
</div>
</div>
<div className="option-list">
<div className="option-list pb-86">
<div className="txn-detail">
<AmountSection
detail={ detail }

View File

@@ -22,6 +22,7 @@ import useIntersectionObserver from '@/widgets/intersection-observer';
import { TaxInvoiceDetail } from './detail/tax-invoice-detail';
import { showAlert } from '@/widgets/show-alert';
import { checkGrant } from '@/shared/lib/check-grant';
import { TaxInvoiceSample } from '@/entities/common/ui/tax-invoice-sample';
/* 세금계산서 48 */
const menuId = 48;
@@ -41,6 +42,7 @@ export const ListWrap = () => {
const [targetType, setTargetType] = useState<VatReturnTargetType>(VatReturnTargetType.ALL);
const [downloadBottomSheetOn, setDownloadBottomSheetOn] = useState<boolean>(false);
const [taxInvoiceSampleOn, setTaxInvoiceSampleOn] = useState<boolean>(false);
const [detailOn, setDetailOn] = useState<boolean>(false);
const [detailTaxInvoiceNumber, setDetailTaxInvoiceNumber] = useState<string>('');
@@ -130,7 +132,9 @@ export const ListWrap = () => {
selectedMode: DownloadSelectedMode,
userEmail?: string
) => {
if(selectedMode === DownloadSelectedMode.IMAGE){
setTaxInvoiceSampleOn(true);
}
};
useEffect(() => {
@@ -248,8 +252,7 @@ export const ListWrap = () => {
detailOn={ detailOn }
setDetailOn={ setDetailOn }
taxInvoiceNumber={ detailTaxInvoiceNumber }
>
</TaxInvoiceDetail>
></TaxInvoiceDetail>
{ !!downloadBottomSheetOn &&
<DownloadBottomSheet
bottomSheetOn={ downloadBottomSheetOn }
@@ -259,6 +262,13 @@ export const ListWrap = () => {
sendRequest={ onRequestDownloadExcel }
></DownloadBottomSheet>
}
{ !!taxInvoiceSampleOn &&
<TaxInvoiceSample
taxInvoiceSampleOn={ taxInvoiceSampleOn }
setTaxInvoiceSampleOn={ setTaxInvoiceSampleOn }
></TaxInvoiceSample>
}
</>
);
};

View File

@@ -57,6 +57,10 @@ export const API_URL_TRANSACTION = {
// POST: 현금영수증 거래확인서 다운로드
return `${API_BASE_URL}/api/v1/${API_URL_KEY}/cash-receipt/receipt/download`;
},
cashReceiptExcelDownload: () => {
// POST: 현금영수증 엑셀 다운로드
return `${API_BASE_URL}/api/v1/${API_URL_KEY}/cash-receipt/excel/download`;
},
/* Escro Management - 에스크로 API */
escrowList: () => {
@@ -71,6 +75,10 @@ export const API_URL_TRANSACTION = {
// POST: 에스크로 메일 재발송
return `${API_BASE_URL}/api/v1/${API_URL_KEY}/escrows/mail/resend`;
},
escrowDownloadExcel: () => {
// POST: 에스크로 거래내역 엑셀 다운로드
return `${API_BASE_URL}/api/v1/${API_URL_KEY}/escrows/download/excel`;
},
/* Billing Management - 빌링 API */
billingList: () => {

View File

@@ -1,4 +1,4 @@
body { margin: 0; padding: 0; background: #FAFAFA; }
/* body { margin: 0; padding: 0; background: #FAFAFA; }*/
.mail-page { min-height: 100vh; display: flex; align-items: flex-start; justify-content: center; padding: 0 0 30px 0; }
.mail-page .wrap { width: 100%; min-width: 320px; background: #FFFFFF; padding: 0 20px; box-sizing: border-box; }
/* header */
@@ -39,9 +39,10 @@ body { margin: 0; padding: 0; background: #FAFAFA; }
.mail-page .notice { padding: 16px; background: #F3F3F3; border-radius: 6px; margin: 30px 0 0 0; font-family: Pretendard, Arial, Helvetica, sans-serif; font-size: 14px; line-height: 1.5; color: #2D3436; }
.mail-page #image-section{
padding: 0 20px 20px 20px;
padding: 0 0 20px 0;
background-color: #ffffff;
}
.mail-page #image-section .section{
background-color: #ffffff;
}