현금영수증 완료

This commit is contained in:
focp212@naver.com
2025-10-25 21:48:10 +09:00
parent 2f13c29d1b
commit 9cffa264e7
12 changed files with 167 additions and 51 deletions

View File

@@ -3,7 +3,7 @@ import { SortTypeKeys } from "./types";
export const DEFAULT_PAGE_PARAM = { export const DEFAULT_PAGE_PARAM = {
cursor: null, cursor: null,
size: 0, size: 20,
sortType: SortTypeKeys.LATEST, sortType: SortTypeKeys.LATEST,
limit: 0 limit: 0
}; };

View File

@@ -176,6 +176,7 @@ export interface CashReceiptListItem {
transactionTime?: string; transactionTime?: string;
customerName?: string; customerName?: string;
issueNumber?: string; issueNumber?: string;
tid?: string;
approvalNumber?: string; approvalNumber?: string;
amount?: number; amount?: number;
processResult?: string; processResult?: string;
@@ -310,10 +311,10 @@ export interface AllTransactionDetailParams {
tid?: string; tid?: string;
}; };
export interface CashReceiptDetailParams { export interface CashReceiptDetailParams {
approvalNumber?: string; tid?: string;
}; };
export interface EscrowDetailParams { export interface EscrowDetailParams {
issueNumber?: number; tid?: string;
}; };
export interface BillingDetailParams { export interface BillingDetailParams {
billKey?: string; billKey?: string;
@@ -333,6 +334,12 @@ export interface AmountInfo {
multiCouponAmount?: number; multiCouponAmount?: number;
receiptAmount?: number; receiptAmount?: number;
cupDepositAmount?: number; cupDepositAmount?: number;
amount?: number;
supplyAmount?: number;
vat?: number;
serviceAmount?: number;
taxFreeAmount?: number;
}; };
export interface ImportantInfo { export interface ImportantInfo {
moid?: string; moid?: string;
@@ -478,6 +485,7 @@ export interface MerchantInfo {
} }
export interface DetailResponse { export interface DetailResponse {
amountDetail?: AmountInfo;
amountInfo?: AmountInfo; amountInfo?: AmountInfo;
importantInfo?: ImportantInfo; importantInfo?: ImportantInfo;
paymentInfo?: PaymentInfo; paymentInfo?: PaymentInfo;
@@ -501,6 +509,7 @@ export interface InfoSectionProps extends DetailResponse {
serviceCode?: string; serviceCode?: string;
purposeType?: CashReceiptPurposeType | string; purposeType?: CashReceiptPurposeType | string;
onClickToOpenInfo?: (info: InfoSectionKeys) => void; onClickToOpenInfo?: (info: InfoSectionKeys) => void;
canDownloadReceipt?: boolean;
} }
@@ -675,7 +684,7 @@ export interface BillingFilterProps extends FilterProps {
}; };
export interface CashReceiptPurposeUpdateParams { export interface CashReceiptPurposeUpdateParams {
approvalNumber: string; tid: string;
newPurpose: string; newPurpose: string;
}; };
export interface CashReceiptPurposeUpdateResponse { export interface CashReceiptPurposeUpdateResponse {

View File

@@ -9,16 +9,11 @@ import { FilterButtonGroups } from '@/shared/ui/filter/button-groups';
import { FilterRangeAmount } from '@/shared/ui/filter/range-amount'; import { FilterRangeAmount } from '@/shared/ui/filter/range-amount';
import { import {
AllTransactionMoidTidOptionsGroup, AllTransactionMoidTidOptionsGroup,
AllTransactionStatusCodeBtnGroup,
AllTransactionServiceCodeOptionsGroup,
AllTransactionCardBankCodeOptionsGroup,
AllTracsactionStatusCode, AllTracsactionStatusCode,
} from '@/entities/transaction/model/contant'; } from '@/entities/transaction/model/contant';
import { import {
AllTransactionFilterProps, AllTransactionFilterProps,
AllTransactionSearchCl, AllTransactionSearchCl,
AllTransactionStatusCode,
AllTransactionServiceCode,
AllTransactionMoidTid AllTransactionMoidTid
} from '../../model/types'; } from '../../model/types';
import { import {

View File

@@ -58,14 +58,14 @@ export const ListItem = ({
else if(transactionCategory === TransactionCategory.CashReceipt){ else if(transactionCategory === TransactionCategory.CashReceipt){
navigate(PATHS.transaction.cashReceipt.detail, { navigate(PATHS.transaction.cashReceipt.detail, {
state: { state: {
approvalNumber: approvalNumber tid: tid
} }
}); });
} }
else if(transactionCategory === TransactionCategory.Escrow){ else if(transactionCategory === TransactionCategory.Escrow){
navigate(PATHS.transaction.escrow.detail, { navigate(PATHS.transaction.escrow.detail, {
state: { state: {
issueNumber: issueNumber tid: tid
} }
}); });
} }

View File

@@ -12,7 +12,8 @@ export const AmountInfoSection = ({
isOpen, isOpen,
tid, tid,
serviceCode, serviceCode,
onClickToOpenInfo onClickToOpenInfo,
canDownloadReceipt
}: InfoSectionProps) => { }: InfoSectionProps) => {
const { mutateAsync: downloadConfirmation } = useDownloadConfirmationMutation(); const { mutateAsync: downloadConfirmation } = useDownloadConfirmationMutation();
let newAmountInfo: Record<string, any> | undefined = amountInfo; let newAmountInfo: Record<string, any> | undefined = amountInfo;
@@ -186,7 +187,7 @@ export const AmountInfoSection = ({
<div className="txn-num-group"> <div className="txn-num-group">
<div className="txn-amount"> <div className="txn-amount">
<div className="value"> <div className="value">
{ { (transactionCategory === TransactionCategory.AllTransaction) &&
(serviceCode === '01' || serviceCode === '02' || serviceCode === '03' || serviceCode === '26') && (serviceCode === '01' || serviceCode === '02' || serviceCode === '03' || serviceCode === '26') &&
<NumericFormat <NumericFormat
value={ amountInfo?.transactionRequestAmount } value={ amountInfo?.transactionRequestAmount }
@@ -194,11 +195,18 @@ export const AmountInfoSection = ({
displayType="text" displayType="text"
></NumericFormat> ></NumericFormat>
} }
{ { (transactionCategory === TransactionCategory.AllTransaction) &&
(serviceCode === '05' || serviceCode === '14' || serviceCode === '21' (serviceCode === '05' || serviceCode === '14' || serviceCode === '21'
|| serviceCode === '24' || serviceCode === '31') && || serviceCode === '24' || serviceCode === '31') &&
<NumericFormat <NumericFormat
value={ amountInfo?.transactionAmount } value={ amountInfo?.transactionAmount}
thousandSeparator
displayType="text"
></NumericFormat>
}
{ (transactionCategory === TransactionCategory.CashReceipt) &&
<NumericFormat
value={ amountInfo?.amount}
thousandSeparator thousandSeparator
displayType="text" displayType="text"
></NumericFormat> ></NumericFormat>
@@ -222,7 +230,48 @@ export const AmountInfoSection = ({
subLi() subLi()
} }
{ (transactionCategory === TransactionCategory.CashReceipt) && { (transactionCategory === TransactionCategory.CashReceipt) &&
<></> <>
<li className="amount-item">
<span className="label">·&nbsp;&nbsp;</span>
<span className="value">
<NumericFormat
value={ amountInfo?.supplyAmount }
thousandSeparator
displayType="text"
></NumericFormat>
</span>
</li>
<li className="amount-item">
<span className="label">·&nbsp;&nbsp;VAT</span>
<span className="value">
<NumericFormat
value={ amountInfo?.vat }
thousandSeparator
displayType="text"
></NumericFormat>
</span>
</li>
<li className="amount-item">
<span className="label">·&nbsp;&nbsp;</span>
<span className="value">
<NumericFormat
value={ amountInfo?.serviceAmount }
thousandSeparator
displayType="text"
></NumericFormat>
</span>
</li>
<li className="amount-item">
<span className="label">·&nbsp;&nbsp;</span>
<span className="value">
<NumericFormat
value={ amountInfo?.taxFreeAmount }
thousandSeparator
displayType="text"
></NumericFormat>
</span>
</li>
</>
} }
{ (transactionCategory === TransactionCategory.Escrow) && { (transactionCategory === TransactionCategory.Escrow) &&
<></> <></>
@@ -231,21 +280,24 @@ export const AmountInfoSection = ({
</div> </div>
} }
</SlideDown> </SlideDown>
<div className="txn-mid"> { (transactionCategory === TransactionCategory.AllTransaction) &&
<span className="value">{ amountInfo?.mid }</span> <div className="txn-mid">
</div> <span className="value">{ amountInfo?.mid }</span>
{ </div>
((transactionCategory === TransactionCategory.CashReceipt) || }
(transactionCategory === TransactionCategory.Escrow) || <div className="txn-doc">
(transactionCategory === TransactionCategory.Billing)) && {
<div className="txn-doc"> ((transactionCategory === TransactionCategory.CashReceipt) ||
(transactionCategory === TransactionCategory.Escrow) ||
(transactionCategory === TransactionCategory.Billing)) &&
!!canDownloadReceipt &&
<button <button
className="doc-btn" className="doc-btn"
type="button" type="button"
onClick={ () => onClickToDownloadConfirmation() } onClick={ () => onClickToDownloadConfirmation() }
> </button> > </button>
</div> }
} </div>
</div> </div>
</> </>
); );

View File

@@ -84,7 +84,11 @@ export const AmountSection = ({
<span className="value">{ detail.mid }</span> <span className="value">{ detail.mid }</span>
</div> </div>
<div className="txn-mid"> <div className="txn-mid">
<span className="value">{ moment(detail.issueDate).format('YYYY.MM.DD') }</span> <span className="value">
{ !!detail.issueDate &&
moment(detail.issueDate).format('YYYY.MM.DD')
}
</span>
</div> </div>
<div className="txn-doc"> <div className="txn-doc">
<button <button

View File

@@ -20,11 +20,23 @@ export const IssueSection = ({
</li> </li>
<li className="kv-row"> <li className="kv-row">
<span className="k"></span> <span className="k"></span>
<span className="v">{ moment(detail.targetBusinessStartDate).format('YYYY.MM.DD') } ~ { moment(detail.targetBusinessEndDate).format('YYYY.MM.DD') }</span> <span className="v">
{ !!detail.targetBusinessStartDate && !!detail.targetBusinessEndDate &&
moment(detail.targetBusinessStartDate).format('YYYY.MM.DD')
+
' ~ '
+
moment(detail.targetBusinessEndDate).format('YYYY.MM.DD')
}
</span>
</li> </li>
<li className="kv-row"> <li className="kv-row">
<span className="k"></span> <span className="k"></span>
<span className="v">{ moment(detail.issueDate).format('YYYY.MM.DD') }</span> <span className="v">
{ !!detail.issueDate &&
moment(detail.issueDate).format('YYYY.MM.DD')
}
</span>
</li> </li>
<li className="kv-row"> <li className="kv-row">
<span className="k"></span> <span className="k"></span>

View File

@@ -94,6 +94,7 @@ export const AllTransactionListPage = () => {
}); });
const callList = (type?: string) => { const callList = (type?: string) => {
setOnActionIntersect(false);
let listSummaryParams: AllTransactionListSummaryParams = { let listSummaryParams: AllTransactionListSummaryParams = {
moid: moid, moid: moid,
tid: tid, tid: tid,

View File

@@ -14,7 +14,9 @@ import {
DetailInfo, DetailInfo,
InfoSectionKeys, InfoSectionKeys,
CashReceiptPurposeType, CashReceiptPurposeType,
AmountInfo AmountInfo,
CashReceiptPurposeUpdateParams,
CashReceiptTransactionType
} from '@/entities/transaction/model/types'; } from '@/entities/transaction/model/types';
import { import {
useSetOnBack, useSetOnBack,
@@ -25,17 +27,21 @@ import {
import { CashReceitPurposeUpdateBottomSheet } from '@/entities/transaction/ui/cash-receit-purpose-update-bottom-sheet'; import { CashReceitPurposeUpdateBottomSheet } from '@/entities/transaction/ui/cash-receit-purpose-update-bottom-sheet';
import { useCashReceiptPurposeUpdateMutation } from '@/entities/transaction/api/use-cash-receipt-purpose-update-mutation'; import { useCashReceiptPurposeUpdateMutation } from '@/entities/transaction/api/use-cash-receipt-purpose-update-mutation';
import { AmountInfoSection } from '@/entities/transaction/ui/section/amount-info-section'; import { AmountInfoSection } from '@/entities/transaction/ui/section/amount-info-section';
import { snackBar } from '@/shared/lib';
export const CashReceiptDetailPage = () => { export const CashReceiptDetailPage = () => {
const { navigate } = useNavigate(); const { navigate, reload } = useNavigate();
const location = useLocation(); const location = useLocation();
const [amountInfo, setAmountInfo] = useState<AmountInfo>(); const [amountInfo, setAmountInfo] = useState<AmountInfo>();
const [issueInfo, setIssueInfo] = useState<IssueInfo>(); const [issueInfo, setIssueInfo] = useState<IssueInfo>();
const [detailInfo, setDetailInfo] = useState<DetailInfo>(); const [detailInfo, setDetailInfo] = useState<DetailInfo>();
const [showAmountInfo, setShowAmountInfo] = useState<boolean>(false);
const [showDetailInfo, setShowDetailInfo] = useState<boolean>(false); const [showDetailInfo, setShowDetailInfo] = useState<boolean>(false);
const [bottomSheetOn, setBottomSheetOn] = useState<boolean>(false); const [bottomSheetOn, setBottomSheetOn] = useState<boolean>(false);
const [purposeType, setPurposeType] = useState<string>(); const [purposeType, setPurposeType] = useState<string>();
const [canDownloadReceipt, setCanDownloadReceipt] = useState<boolean>(false);
useSetHeaderTitle('현금영수증 상세'); useSetHeaderTitle('현금영수증 상세');
useSetHeaderType(HeaderType.RightClose); useSetHeaderType(HeaderType.RightClose);
@@ -44,7 +50,7 @@ export const CashReceiptDetailPage = () => {
}); });
useSetFooterMode(false); useSetFooterMode(false);
const approvalNumber = location?.state.approvalNumber const tid = location?.state.tid
const { mutateAsync: cashReceiptDetail } = useCashReceiptDetailMutation(); const { mutateAsync: cashReceiptDetail } = useCashReceiptDetailMutation();
const { mutateAsync: cashReceiptPurposeUpdate } = useCashReceiptPurposeUpdateMutation(); const { mutateAsync: cashReceiptPurposeUpdate } = useCashReceiptPurposeUpdateMutation();
@@ -52,25 +58,28 @@ export const CashReceiptDetailPage = () => {
const callPurposeUpdate = () => { const callPurposeUpdate = () => {
let newPurpose = (purposeType === CashReceiptPurposeType.EXPENSE_PROOF) let newPurpose = (purposeType === CashReceiptPurposeType.EXPENSE_PROOF)
? CashReceiptPurposeType.INCOME_DEDUCTION: CashReceiptPurposeType.EXPENSE_PROOF; ? CashReceiptPurposeType.INCOME_DEDUCTION: CashReceiptPurposeType.EXPENSE_PROOF;
let params = { let params: CashReceiptPurposeUpdateParams = {
approvalNumber: approvalNumber, tid: tid,
newPurpose: newPurpose newPurpose: newPurpose
}; };
cashReceiptPurposeUpdate(params).then((rs) => { cashReceiptPurposeUpdate(params).then((rs) => {
setPurposeType(rs.afterPurposeType); setPurposeType(rs.afterPurposeType);
setBottomSheetOn(false); setBottomSheetOn(false);
alert('toast : 용도 변경을 성공하였습니다.') snackBar('용도 변경을 성공하였습니다.', function(){
reload();
}, 2000);
}); });
}; };
const callDetail = () => { const callDetail = () => {
let cashReceitDetailParams: CashReceiptDetailParams = { let cashReceitDetailParams: CashReceiptDetailParams = {
approvalNumber: approvalNumber tid: tid
}; };
cashReceiptDetail(cashReceitDetailParams).then((rs: DetailResponse) => { cashReceiptDetail(cashReceitDetailParams).then((rs: DetailResponse) => {
setAmountInfo(rs.amountInfo || {}); setAmountInfo(rs.amountDetail || {});
setIssueInfo(rs.issueInfo || {}); setIssueInfo(rs.issueInfo || {});
setDetailInfo(rs.detailInfo || {}); setDetailInfo(rs.detailInfo || {});
setCanDownloadReceipt(rs.detailInfo?.canDownloadReceipt || false);
if(rs.issueInfo){ if(rs.issueInfo){
setPurposeType(rs.issueInfo.purpose); setPurposeType(rs.issueInfo.purpose);
} }
@@ -81,7 +90,10 @@ export const CashReceiptDetailPage = () => {
}, []); }, []);
const onClickToOpenInfo = (infoSectionKey: InfoSectionKeys) => { const onClickToOpenInfo = (infoSectionKey: InfoSectionKeys) => {
if(infoSectionKey === InfoSectionKeys.Detail){ if(infoSectionKey === InfoSectionKeys.Amount){
setShowAmountInfo(!showAmountInfo);
}
else if(infoSectionKey === InfoSectionKeys.Detail){
setShowDetailInfo(!showDetailInfo); setShowDetailInfo(!showDetailInfo);
} }
}; };
@@ -100,7 +112,10 @@ export const CashReceiptDetailPage = () => {
<AmountInfoSection <AmountInfoSection
transactionCategory={ TransactionCategory.CashReceipt } transactionCategory={ TransactionCategory.CashReceipt }
amountInfo={ amountInfo } amountInfo={ amountInfo }
isOpen={ showAmountInfo }
onClickToOpenInfo={ (infoSectionKey) => onClickToOpenInfo(infoSectionKey) }
purposeType={ purposeType } purposeType={ purposeType }
canDownloadReceipt={ canDownloadReceipt }
></AmountInfoSection> ></AmountInfoSection>
<div className="txn-divider"></div> <div className="txn-divider"></div>
<IssueInfoSection <IssueInfoSection
@@ -117,13 +132,14 @@ export const CashReceiptDetailPage = () => {
></DetailInfoSection> ></DetailInfoSection>
</div> </div>
</div> </div>
{ !!detailInfo?.canDownloadReceipt && { (issueInfo?.transactionType === CashReceiptTransactionType.APPROVAL) &&
<div className="apply-row"> (issueInfo?.processResult === '발급완료') &&
<button <div className="apply-row">
className="btn-50 btn-blue flex-1" <button
onClick={ () => onClickToPurposeUpdate() } className="btn-50 btn-blue flex-1"
> </button> onClick={ () => onClickToPurposeUpdate() }
</div> > </button>
</div>
} }
</div> </div>
</div> </div>

View File

@@ -34,6 +34,7 @@ import { MerchantInfoSection } from '@/entities/transaction/ui/section/merchant-
export const EscrowDetailPage = () => { export const EscrowDetailPage = () => {
const { navigate } = useNavigate(); const { navigate } = useNavigate();
const location = useLocation(); const location = useLocation();
const paramTid = location?.state.tid;
const [amountInfo, setAmountInfo] = useState<ImportantInfo>(); const [amountInfo, setAmountInfo] = useState<ImportantInfo>();
const [importantInfo, setImportantInfo] = useState<ImportantInfo>(); const [importantInfo, setImportantInfo] = useState<ImportantInfo>();
@@ -54,7 +55,7 @@ export const EscrowDetailPage = () => {
const [bottomSheetOn, setBottomSheetOn] = useState<boolean>(false); const [bottomSheetOn, setBottomSheetOn] = useState<boolean>(false);
const [orderNumber, setOrderNumber] = useState<string>(); const [orderNumber, setOrderNumber] = useState<string>();
const [tid, setTid] = useState<string>(); const [tid, setTid] = useState<string | undefined>(paramTid);
useSetHeaderTitle('에스크로 상세'); useSetHeaderTitle('에스크로 상세');
useSetHeaderType(HeaderType.RightClose); useSetHeaderType(HeaderType.RightClose);
@@ -68,7 +69,7 @@ export const EscrowDetailPage = () => {
const callDetail = () => { const callDetail = () => {
let escroDetailParams: EscrowDetailParams = { let escroDetailParams: EscrowDetailParams = {
issueNumber: location?.state.issueNumber, tid: tid || paramTid,
}; };
escrowDetail(escroDetailParams).then((rs: DetailResponse) => { escrowDetail(escroDetailParams).then((rs: DetailResponse) => {
setImportantInfo(rs.importantInfo || {}); setImportantInfo(rs.importantInfo || {});

View File

@@ -64,7 +64,7 @@ export const DetailPage = () => {
return ( return (
<> <>
<main className="full-height"> <main className="full-height">
<div className="tab-content"> <div className="tab-content pb-86">
<div className="tab-pane sub active"> <div className="tab-pane sub active">
<div className="option-list"> <div className="option-list">
<div className="txn-detail"> <div className="txn-detail">

View File

@@ -29,6 +29,7 @@ import { useShortcutDefaultMutation } from '@/entities/user/api/use-shortcut-det
import { useBusinessPropertyMutation } from '@/entities/user/api/use-business-property-mutation'; import { useBusinessPropertyMutation } from '@/entities/user/api/use-business-property-mutation';
import { useUserFindAuthMethodMutation } from '@/entities/user/api/use-user-find-authmethod-mutation'; import { useUserFindAuthMethodMutation } from '@/entities/user/api/use-user-find-authmethod-mutation';
import { useCodesSelectMutation } from '@/entities/common/api/use-codes-select-mutation'; import { useCodesSelectMutation } from '@/entities/common/api/use-codes-select-mutation';
import { MenuItems } from '@/entities/common/model/constant';
export interface ContextType { export interface ContextType {
setOnBack: (onBack: () => void) => void; setOnBack: (onBack: () => void) => void;
@@ -127,18 +128,41 @@ export const SubLayout = () => {
}; };
shortcutUser(params).then((rs: ShortcutUserResponse) => { shortcutUser(params).then((rs: ShortcutUserResponse) => {
// Modify iconFilePath to use menu_icon_{menuId}.svg format // Modify iconFilePath to use menu_icon_{menuId}.svg format
console.log("============================rs", rs)
const modifiedShortcuts = rs.shortcuts.map(shortcut => ({ const modifiedShortcuts = rs.shortcuts.map(shortcut => ({
...shortcut, ...shortcut,
iconFilePath: `menu_icon_${shortcut.menuId}.svg` iconFilePath: `menu_icon_${shortcut.menuId}.svg`
})); }));
console.log("============================modifiedShortcuts", modifiedShortcuts)
useStore.getState().UserStore.setUserFavorite(modifiedShortcuts); useStore.getState().UserStore.setUserFavorite(modifiedShortcuts);
}); });
} }
}; };
// 맵 형태로 상수로 만들어 놓을 필요가 있다. 무의미한 반복문 너무 많이 사용
const getProgramPath = (menuId: number) => {
let programPath: string = '';
Loop1:
for(let i=0;i<MenuItems.length;i++){
let menuItem = MenuItems[i];
if(menuItem){
let subMenu = menuItem.subMenu;
if(subMenu){
Loop2:
for(let j=0;j<subMenu.length;j++){
let subMenuItem = subMenu[j];
if(subMenuItem){
if(subMenuItem.menuId === menuId){
programPath = subMenuItem.programPath;
break Loop1;
}
}
}
}
}
}
return programPath;
};
const callShortcutUser = () => { const callShortcutUser = () => {
console.log("============================callShortcutUser") console.log("============================callShortcutUser")
let userInfo = useStore.getState().UserStore.userInfo; let userInfo = useStore.getState().UserStore.userInfo;
@@ -152,8 +176,10 @@ export const SubLayout = () => {
if(rs.shortcuts.length > 0){ if(rs.shortcuts.length > 0){
const modifiedShortcuts = rs.shortcuts.map(shortcut => ({ const modifiedShortcuts = rs.shortcuts.map(shortcut => ({
...shortcut, ...shortcut,
iconFilePath: `/images/menu_icon_${shortcut.menuId}.svg` iconFilePath: `/images/menu_icon_${shortcut.menuId}.svg`,
programPath: getProgramPath(shortcut.menuId)
})); }));
console.log(modifiedShortcuts)
console.log("============================modifiedShortcuts", modifiedShortcuts) console.log("============================modifiedShortcuts", modifiedShortcuts)
useStore.getState().UserStore.setUserFavorite(modifiedShortcuts); useStore.getState().UserStore.setUserFavorite(modifiedShortcuts);