세금계산서 리스트 및 상세

This commit is contained in:
focp212@naver.com
2025-09-25 14:01:03 +09:00
parent ba0b119e39
commit d5c12f4b69
34 changed files with 518 additions and 586 deletions

View File

@@ -26,10 +26,10 @@ export const EscrowInfoWrap = ({
const [isOpen, setIsOpen] = useState<boolean>(false); const [isOpen, setIsOpen] = useState<boolean>(false);
const opeSection = () => { const openSection = () => {
const staus = !isOpen; const status = !isOpen;
setIsOpen(staus); setIsOpen(status);
if(!!staus){ if(!!status){
setOpenChild(type); setOpenChild(type);
} }
else { else {
@@ -47,7 +47,7 @@ export const EscrowInfoWrap = ({
<> <>
<div className="section"> <div className="section">
<div className="section-title" <div className="section-title"
onClick={ () => opeSection() } onClick={ () => openSection() }
> >
{ title } <SectionTitleArrow isOpen={ isOpen }></SectionTitleArrow> { title } <SectionTitleArrow isOpen={ isOpen }></SectionTitleArrow>
</div> </div>

View File

@@ -1,29 +0,0 @@
import axios from 'axios';
import { API_URL_TAX } from '@/shared/api/api-url-tax';
import { resultify } from '@/shared/lib/resultify';
import { CBDCAxiosError } from '@/shared/@types/error';
import {
InvoiceDetailParams,
InvoiceDetailResponse
} from '../model/types';
import {
useMutation,
UseMutationOptions
} from '@tanstack/react-query';
export const invoiceDetail = (params: InvoiceDetailParams) => {
return resultify(
axios.post<InvoiceDetailResponse>(API_URL_TAX.invoiceDetail(), params),
);
};
export const useInvoiceDetailMutation = (options?: UseMutationOptions<InvoiceDetailResponse, CBDCAxiosError, InvoiceDetailParams>) => {
const mutation = useMutation<InvoiceDetailResponse, CBDCAxiosError, InvoiceDetailParams>({
...options,
mutationFn: (params: InvoiceDetailParams) => invoiceDetail(params),
});
return {
...mutation,
};
};

View File

@@ -1,29 +0,0 @@
import axios from 'axios';
import { API_URL_TAX } from '@/shared/api/api-url-tax';
import { resultify } from '@/shared/lib/resultify';
import { CBDCAxiosError } from '@/shared/@types/error';
import {
InvoiceListResponse,
InvoiceListParams,
} from '../model/types';
import {
useMutation,
UseMutationOptions
} from '@tanstack/react-query';
export const invoiceList = (params: InvoiceListParams) => {
return resultify(
axios.post<InvoiceListResponse>(API_URL_TAX.invoiceList(), params),
);
};
export const useInvoiceListMutation = (options?: UseMutationOptions<InvoiceListResponse, CBDCAxiosError, InvoiceListParams>) => {
const mutation = useMutation<InvoiceListResponse, CBDCAxiosError, InvoiceListParams>({
...options,
mutationFn: (params: InvoiceListParams) => invoiceList(params),
});
return {
...mutation,
};
};

View File

@@ -1,96 +0,0 @@
import { DefaulResponsePagination, DefaultRequestPagination } from '@/entities/common/model/types';
export enum TaxTabKeys {
InvoiceList = 'InvoiceList',
VatReference = 'VatReference',
};
export interface TaxTabProps {
activeTab: TaxTabKeys;
};
export interface InvoiceFilterProps {
filterOn: boolean;
setFilterOn: (filterOn: boolean) => void;
mid: string;
startDate: string;
endDate: string;
receiptType: string;
targetType: string;
setMid: (mid: string) => void;
setStartDate: (date: string) => void;
setEndDate: (date: string) => void;
setReceiptType: (type: string) => void;
setTargetType: (type: string) => void;
};
export enum DetailInfoSectionKeys {
Amount = 'Amount',
Publish = 'Publish',
Receiver = 'Receiver',
Supplier = 'Supplier',
};
export interface InvoiceListParams {
mid: string;
startDate?: string;
endDate?: string;
receiptType?: string;
targetType?: string;
};
export interface InvoiceListResponse extends DefaulResponsePagination {
content: Array<ListItemProps>;
}
export interface ListItemProps extends InvoiceListItem {
};
export interface InvoiceListItem {
id: number
companyName: string;
mid: string;
issueDate: string;
paymentMethod: string;
amount: number;
};
export interface ListDateGroupProps {
date: string;
items: Array<ListItemProps>
}
export interface InvoiceListProps {
listItems: Record<string, Array<ListItemProps>>
};
export interface InvoiceDetailParams {
svcCd?: string;
tid: string;
};
export interface InvoiceDetailResponse {
};
export interface DetailAmountInfoProps {
};
export interface DetailPublishInfoProps {
};
export interface DetailReceiverInfoProps {
};
export interface DetailSupplierInfoProps {
};
export interface DetailResponse {
amountInfo?: DetailAmountInfoProps;
publishInfo?: DetailPublishInfoProps;
receiverInfo?: DetailReceiverInfoProps;
supplierInfo?: DetailSupplierInfoProps;
};
export interface DetailInfoSectionProps extends DetailResponse {
isOpen?: boolean;
tid?: string;
onClickToShowInfo?: (info: DetailInfoSectionKeys) => void;
};

View File

@@ -1,47 +0,0 @@
import { DetailInfoSectionProps } from '../model/types';
import { SectionTitleArrow } from '@/entities/common/ui/section-title-arrow';
export const DetailAmountInfoSection = ({
amountInfo,
isOpen,
onClickToShowInfo
}: DetailInfoSectionProps) => {
return (
<>
<div className="txn-num-group">
<div className="txn-amount">
<div className="value">48,125,100<span className="unit"></span></div>
<button
className="chip-btn"
type="button"
>
<span></span> <SectionTitleArrow isOpen={ isOpen }></SectionTitleArrow>
</button>
</div>
<div className="amount-expand">
<ul className="amount-list">
<li className="amount-item">
<span className="label">·&nbsp;&nbsp;</span>
<span className="value">43,750,000 </span>
</li>
<li className="amount-item">
<span className="label">·&nbsp;&nbsp;VAT</span>
<span className="value">4,375,100 </span>
</li>
</ul>
</div>
<div className="txn-mid">
<span className="value">croquis01m</span>
</div>
<div className="txn-mid">
<span className="value">2025.08.19</span>
</div>
<div className="txn-doc">
<button className="doc-btn" type="button"></button>
<button className="doc-btn" type="button"></button>
</div>
</div>
</>
);
};

View File

@@ -1,26 +0,0 @@
import { DetailInfoSectionProps } from '../model/types';
export const DetailSupplierInfoSection = ({
supplierInfo,
isOpen,
onClickToShowInfo
}: DetailInfoSectionProps) => {
return (
<>
<div className="txn-section">
<div className="section-title"> </div>
</div>
<ul className="kv-list">
<li className="kv-row">
<span className="k"></span>
<span className="v">222-22-22222</span>
</li>
<li className="kv-row">
<span className="k"></span>
<span className="v"></span>
</li>
</ul>
</>
);
};

View File

@@ -1,29 +0,0 @@
import { InvoiceListProps } from '../model/types';
import { ListDateGroup } from './list-date-group';
export const InvoiceList = ({
listItems
}: InvoiceListProps) => {
const getListDateGroup = () => {
let rs = [];
for (const [key, value] of Object.entries(listItems)) {
rs.push(
<ListDateGroup
key={ key }
date={ key }
items={ value }
></ListDateGroup>
);
}
return rs;
};
return (
<>
<div className="transaction-list">
{ getListDateGroup() }
</div>
</>
)
};

View File

@@ -1,37 +0,0 @@
import { PATHS } from '@/shared/constants/paths';
import { useNavigate } from '@/shared/lib/hooks/use-navigate';
import {
TaxTabKeys,
TaxTabProps
} from '../model/types';
export const TaxTab = ({
activeTab
}: TaxTabProps) => {
const { navigate } = useNavigate();
const onClickToNavigation = (tab: TaxTabKeys) => {
if(activeTab !== tab){
if(tab === TaxTabKeys.InvoiceList){
navigate(PATHS.tax.invoice.list);
}
else if(tab === TaxTabKeys.VatReference){
navigate(PATHS.tax.vatReference);
}
}
};
return(
<>
<div className="subTab">
<button
className={`subtab-btn ${(activeTab === TaxTabKeys.InvoiceList)? 'active': ''}` }
onClick={ () => onClickToNavigation(TaxTabKeys.InvoiceList) }
> </button>
<button
className={`subtab-btn ${(activeTab === TaxTabKeys.VatReference)? 'active': ''}` }
onClick={ () => onClickToNavigation(TaxTabKeys.VatReference) }
> </button>
</div>
</>
);
};

View File

@@ -0,0 +1,29 @@
import axios from 'axios';
import { API_URL_VAT_RETURN } from '@/shared/api/api-url-vat-return';
import { resultify } from '@/shared/lib/resultify';
import { CBDCAxiosError } from '@/shared/@types/error';
import {
VatReturnDetailParams,
VatReturnDetailResponse
} from '../model/types';
import {
useMutation,
UseMutationOptions
} from '@tanstack/react-query';
export const vatReturnDetail = (params: VatReturnDetailParams) => {
return resultify(
axios.post<VatReturnDetailResponse>(API_URL_VAT_RETURN.vatReturnDetail(), params),
);
};
export const useVatReturnDetailMutation = (options?: UseMutationOptions<VatReturnDetailResponse, CBDCAxiosError, VatReturnDetailParams>) => {
const mutation = useMutation<VatReturnDetailResponse, CBDCAxiosError, VatReturnDetailParams>({
...options,
mutationFn: (params: VatReturnDetailParams) => vatReturnDetail(params),
});
return {
...mutation,
};
};

View File

@@ -0,0 +1,29 @@
import axios from 'axios';
import { API_URL_VAT_RETURN } from '@/shared/api/api-url-vat-return';
import { resultify } from '@/shared/lib/resultify';
import { CBDCAxiosError } from '@/shared/@types/error';
import {
VatReturnListResponse,
VatReturnListParams,
} from '../model/types';
import {
useMutation,
UseMutationOptions
} from '@tanstack/react-query';
export const vatReturnList = (params: VatReturnListParams) => {
return resultify(
axios.post<VatReturnListResponse>(API_URL_VAT_RETURN.vatReturnList(), params),
);
};
export const useVatReturnListMutation = (options?: UseMutationOptions<VatReturnListResponse, CBDCAxiosError, VatReturnListParams>) => {
const mutation = useMutation<VatReturnListResponse, CBDCAxiosError, VatReturnListParams>({
...options,
mutationFn: (params: VatReturnListParams) => vatReturnList(params),
});
return {
...mutation,
};
};

View File

@@ -0,0 +1,81 @@
import { DefaulResponsePagination, DefaultRequestPagination } from '@/entities/common/model/types';
export enum VatReturnTabKeys {
List = 'List',
VatReference = 'VatReference',
};
export interface VatReturnTabProps {
activeTab: VatReturnTabKeys;
};
export enum VatReturnReceiptType {
ALL = 'ALL',
RECEIPT = 'RECEIPT',
BILL = 'BILL '
};
export enum VatReturnTargetType {
ALL = 'ALL',
GENERAL = 'GENERAL',
DIFFERENCE_COLLECTION = 'DIFFERENCE_COLLECTION',
REFUND_SETTLEMENT = 'REFUND_SETTLEMENT',
};
export interface VatReturnListParams {
mid: string;
startDate?: string;
endDate?: string;
receiptType?: VatReturnReceiptType;
targetType?: VatReturnTargetType;
pagination?: DefaultRequestPagination;
};
export interface VatReturnListResponse extends DefaulResponsePagination {
content: Array<VatReturnListContent>;
};
export interface VatReturnListContent {
id?: number
companyName?: string;
mid?: string;
issueDate?: string;
paymentMethod?: string;
amount?: number;
};
export interface ListDateGroupProps {
date?: string;
items?: Array<VatReturnListContent>
}
export interface ListItemProps {
id?: number;
companyName?: string;
mid?: string;
issueDate?: string;
paymentMethod?: string;
amount?: number;
}
export interface VatReturnDetailParams {
taxInvoiceNumber?: string;
};
export interface VatReturnDetailResponse {
id?: number;
totalAmount?: number;
supplyAmount?: number;
vatAmount?: number;
mid?: string;
targetBusinessStartDate?: string;
targetBusinessEndDate?: string;
issueDate?: string;
subject?: string;
targetType?: VatReturnTargetType;
receiptType?: VatReturnReceiptType;
email?: string;
receiverBusinessRegistrationNumber?: string;
receiverCompanyName?: string;
receiverCeoName?: string;
supplierBusinessRegistrationNumber?: string;
supplierCompanyName?: string;
supplierCeoName?: string;
};

View File

@@ -1,8 +1,24 @@
import { motion } from 'framer-motion'; import { motion } from 'framer-motion';
import { IMAGE_ROOT } from '@/shared/constants/common'; import { IMAGE_ROOT } from '@/shared/constants/common';
import { InvoiceFilterProps } from '../model/types'; import { VatReturnReceiptType, VatReturnTargetType } from '../model/types';
import { FilterMotionDuration, FilterMotionStyle, FilterMotionVariants } from '@/entities/common/model/constant';
export const InvoiceFilter = ({ export interface ListFilterProps {
filterOn: boolean;
setFilterOn: (filterOn: boolean) => void;
mid: string;
startDate: string;
endDate: string;
receiptType: VatReturnReceiptType;
targetType: VatReturnTargetType;
setMid: (mid: string) => void;
setStartDate: (date: string) => void;
setEndDate: (date: string) => void;
setReceiptType: (receiptType: VatReturnReceiptType) => void;
setTargetType: (targetType: VatReturnTargetType) => void;
};
export const ListFilter = ({
filterOn, filterOn,
setFilterOn, setFilterOn,
mid, mid,
@@ -15,7 +31,7 @@ export const InvoiceFilter = ({
setEndDate, setEndDate,
setReceiptType, setReceiptType,
setTargetType setTargetType
}: InvoiceFilterProps) => { }: ListFilterProps) => {
const variants = { const variants = {
hidden: { x: '100%' }, hidden: { x: '100%' },
visible: { x: '0%' }, visible: { x: '0%' },
@@ -31,12 +47,9 @@ export const InvoiceFilter = ({
className="full-menu-modal" className="full-menu-modal"
initial="hidden" initial="hidden"
animate={ (filterOn)? 'visible': 'hidden' } animate={ (filterOn)? 'visible': 'hidden' }
variants={ variants } variants={ FilterMotionVariants }
transition={{ duration: 0.3 }} transition={ FilterMotionDuration }
style={{ style={ FilterMotionStyle }
width: '100%',
height: '100%',
}}
> >
<div className="full-menu-container"> <div className="full-menu-container">
<div className="full-menu-header"> <div className="full-menu-header">

View File

@@ -15,9 +15,9 @@ export const ListItem = ({
const { navigate } = useNavigate(); const { navigate } = useNavigate();
const onClickToNavigate = () => { const onClickToNavigate = () => {
navigate(PATHS.tax.invoice.detail, { navigate(PATHS.vatReturn.detail, {
state: { state: {
id: id taxInvoiceNumber: id
} }
}); });
}; };
@@ -44,7 +44,7 @@ export const ListItem = ({
value={ amount } value={ amount }
thousandSeparator thousandSeparator
displayType="text" displayType="text"
suffix={ '원' } suffix='원'
></NumericFormat> ></NumericFormat>
</div> </div>
</div> </div>

View File

@@ -2,43 +2,44 @@ import moment from 'moment';
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { IMAGE_ROOT } from '@/shared/constants/common'; import { IMAGE_ROOT } from '@/shared/constants/common';
import { InvoiceFilter } from './invoice-filter'; import { ListFilter } from './list-filter';
import { SortOptionsBox } from '@/entities/common/ui/sort-options-box'; import { SortOptionsBox } from '@/entities/common/ui/sort-options-box';
import { SortByKeys } from '@/entities/common/model/types'; import { SortByKeys } from '@/entities/common/model/types';
import { InvoiceList } from './invoice-list';
import { useInvoiceListMutation } from '../api/use-invoice-list-mutation';
import { DEFAULT_PAGE_PARAM } from '@/entities/common/model/constant'; import { DEFAULT_PAGE_PARAM } from '@/entities/common/model/constant';
import { ListItemProps } from '../model/types'; import {
VatReturnListContent,
VatReturnListParams,
VatReturnReceiptType,
VatReturnTargetType
} from '../model/types';
import { useVatReturnListMutation } from '../api/use-vat-return-list-mutation';
import { ListDateGroup } from './list-date-group';
export const InvoiceListWrap = () => { export const ListWrap = () => {
const [filterOn, setFilterOn] = useState<boolean>(false); const [filterOn, setFilterOn] = useState<boolean>(false);
const [sortBy, setSortBy] = useState<SortByKeys>(SortByKeys.New); const [sortBy, setSortBy] = useState<SortByKeys>(SortByKeys.New);
const [listItems, setListItems] = useState({}); const [listItems, setListItems] = useState<Record<string, Array<VatReturnListContent>>>({});
const [pageParam, setPageParam] = useState(DEFAULT_PAGE_PARAM); const [pageParam, setPageParam] = useState(DEFAULT_PAGE_PARAM);
const [mid, setMid] = useState<string>('nictest00m'); const [mid, setMid] = useState<string>('nictest00m');
const [startDate, setStartDate] = useState(moment().subtract(1, 'month').format('YYYY-MM-DD')); const [startDate, setStartDate] = useState(moment().subtract(1, 'month').format('YYYY-MM-DD'));
const [endDate, setEndDate] = useState(moment().format('YYYY-MM-DD')); const [endDate, setEndDate] = useState(moment().format('YYYY-MM-DD'));
const [receiptType, setReceiptType] = useState<string>(''); const [receiptType, setReceiptType] = useState<VatReturnReceiptType>(VatReturnReceiptType.ALL);
const [targetType, setTargetType] = useState<string>(''); const [targetType, setTargetType] = useState<VatReturnTargetType>(VatReturnTargetType.ALL);
const { mutateAsync: invoiceList } = useInvoiceListMutation();
const { mutateAsync: vatReturnList } = useVatReturnListMutation();
const callList = () => { const callList = () => {
let listParams = { let params: VatReturnListParams = {
mid: mid, mid: mid,
startDate: startDate, startDate: startDate,
endDate: endDate, endDate: endDate,
receiptType: receiptType, receiptType: receiptType,
targetType: targetType, targetType: targetType,
}; };
invoiceList(listParams).then((rs) => { vatReturnList(params).then((rs) => {
setListItems(assembleData(rs.content)); setListItems(assembleData(rs.content));
}); });
// setListItems({
// '20250601': [
// {tid: 'a1234567', pname: '와와디디', userid: 'nictest01m', idate: '20250630', kind: 'PG수수료', amount: 48125100},
// {tid: 'b34534534', pname: '와와디디', userid: 'wadizcop@m', idate: '20250630', kind: 'PG수수료', amount: 23845000},
// ]
// })
}; };
const onClickToOpenFIlter = () => { const onClickToOpenFIlter = () => {
@@ -52,7 +53,23 @@ export const InvoiceListWrap = () => {
callList(); callList();
}, []); }, []);
const assembleData = (content: Array<ListItemProps>) => { const getListDateGroup = () => {
let rs = [];
if(Object.keys(listItems).length > 0){
for (const [key, value] of Object.entries(listItems)) {
rs.push(
<ListDateGroup
key={ key }
date={ key }
items={ value }
></ListDateGroup>
);
}
}
return rs;
};
const assembleData = (content: Array<VatReturnListContent>) => {
let data: any = {}; let data: any = {};
if(content && content.length > 0){ if(content && content.length > 0){
for(let i=0;i<content?.length;i++){ for(let i=0;i<content?.length;i++){
@@ -65,7 +82,6 @@ export const InvoiceListWrap = () => {
} }
} }
} }
console.log('Data : ', data)
return data; return data;
}; };
return ( return (
@@ -103,52 +119,13 @@ export const InvoiceListWrap = () => {
onClickToSort={ onClickToSort } onClickToSort={ onClickToSort }
></SortOptionsBox> ></SortOptionsBox>
</div> </div>
<InvoiceList
listItems={ listItems }
></InvoiceList>
{/*
<div className="transaction-list"> <div className="transaction-list">
<div className="date-group"> { getListDateGroup() }
<div className="date-header">25.06</div>
<div className="transaction-item approved">
<div className="transaction-status">
<div className="status-dot blue"></div>
</div> </div>
<div className="transaction-content">
<div className="transaction-title"> (nictest01m)</div>
<div className="transaction-details">
<span>2025.06.30</span>
<span className="separator">|</span>
<span>PG수수료</span>
</div>
</div>
<div className="transaction-amount">48,125,100</div>
</div>
<div className="transaction-item refund">
<div className="transaction-status">
<div className="status-dot gray"></div>
</div>
<div className="transaction-content">
<div className="transaction-title"> (wadizcop@m)</div>
<div className="transaction-details">
<span>2025.06.30</span>
<span className="separator">|</span>
<span>PG수수료</span>
</div>
</div>
<div className="transaction-amount">23,845,000</div>
</div>
</div>
</div>
*/
}
<div className="apply-row bottom-padding"> <div className="apply-row bottom-padding">
<button className="btn-50 btn-blue flex-1"></button> <button className="btn-50 btn-blue flex-1"></button>
</div> </div>
<InvoiceFilter <ListFilter
filterOn={ filterOn } filterOn={ filterOn }
setFilterOn={ setFilterOn } setFilterOn={ setFilterOn }
mid={ mid } mid={ mid }
@@ -161,7 +138,7 @@ export const InvoiceListWrap = () => {
setEndDate={ setEndDate } setEndDate={ setEndDate }
setReceiptType={ setReceiptType } setReceiptType={ setReceiptType }
setTargetType={ setTargetType } setTargetType={ setTargetType }
></InvoiceFilter> ></ListFilter>
</> </>
); );
}; };

View File

@@ -0,0 +1,85 @@
import moment from 'moment';
import { VatReturnDetailResponse } from '../../model/types';
import { SectionTitleArrow } from '@/entities/common/ui/section-title-arrow';
import { NumericFormat } from 'react-number-format';
import SlideDown from 'react-slidedown';
import 'react-slidedown/lib/slidedown.css';
import { useState } from 'react';
export interface AmountSectionProps {
detail: VatReturnDetailResponse;
};
export const AmountSection = ({
detail
}: AmountSectionProps) => {
const [isOpen, setIsOpen] = useState<boolean>(false);
const openSection = () => {
const status = !isOpen;
setIsOpen(status);
};
return (
<>
<div className="txn-num-group">
<div className="txn-amount">
<div className="value">
<NumericFormat
value={ detail.totalAmount }
thousandSeparator
displayType="text"
></NumericFormat>
<span className="unit"></span>
</div>
<button
className="chip-btn"
type="button"
onClick={ () => openSection() }
>
<span></span> <SectionTitleArrow isOpen={ isOpen }></SectionTitleArrow>
</button>
</div>
<SlideDown className={'my-dropdown-slidedown'}>
{ isOpen &&
<div className="amount-expand">
<ul className="amount-list">
<li className="amount-item">
<span className="label">·&nbsp;&nbsp;</span>
<span className="value">
<NumericFormat
value={ detail.supplyAmount }
thousandSeparator
displayType="text"
suffix='원'
></NumericFormat>
</span>
</li>
<li className="amount-item">
<span className="label">·&nbsp;&nbsp;VAT</span>
<span className="value">
<NumericFormat
value={ detail.vatAmount }
thousandSeparator
displayType="text"
suffix='원'
></NumericFormat>
</span>
</li>
</ul>
</div>
}
</SlideDown>
<div className="txn-mid">
<span className="value">{ detail.mid }</span>
</div>
<div className="txn-mid">
<span className="value">{ moment(detail.issueDate).format('YYYY.MM.DD') }</span>
</div>
<div className="txn-doc">
<button className="doc-btn" type="button"></button>
<button className="doc-btn" type="button"></button>
</div>
</div>
</>
);
};

View File

@@ -1,10 +1,13 @@
import { DetailInfoSectionProps } from '../model/types'; import moment from 'moment';
import { VatReturnDetailResponse } from '../../model/types';
export const DetailPublishInfoSection = ({ export interface IssueSectionProps {
publishInfo, detail: VatReturnDetailResponse
isOpen, };
onClickToShowInfo
}: DetailInfoSectionProps) => { export const IssueSection = ({
detail
}: IssueSectionProps) => {
return ( return (
<> <>
@@ -13,31 +16,31 @@ export const DetailPublishInfoSection = ({
<ul className="kv-list"> <ul className="kv-list">
<li className="kv-row"> <li className="kv-row">
<span className="k">MID</span> <span className="k">MID</span>
<span className="v">nictest01m</span> <span className="v">{ detail.mid }</span>
</li> </li>
<li className="kv-row"> <li className="kv-row">
<span className="k"></span> <span className="k"></span>
<span className="v">2025.06.01 ~ 2025.06.30</span> <span className="v">{ 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">2025.06.30</span> <span className="v">{ 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>
<span className="v">PG </span> <span className="v">{ detail.subject }</span>
</li> </li>
<li className="kv-row"> <li className="kv-row">
<span className="k"></span> <span className="k"></span>
<span className="v"></span> <span className="v">{ detail.targetType }</span>
</li> </li>
<li className="kv-row"> <li className="kv-row">
<span className="k"></span> <span className="k"></span>
<span className="v"></span> <span className="v">{ detail.receiptType }</span>
</li> </li>
<li className="kv-row"> <li className="kv-row">
<span className="k">Email</span> <span className="k">Email</span>
<span className="v">test@nicepay.co.kr</span> <span className="v">{ detail.email }</span>
</li> </li>
</ul> </ul>
</div> </div>

View File

@@ -1,10 +1,12 @@
import { DetailInfoSectionProps } from '../model/types'; import { VatReturnDetailResponse } from '../../model/types';
export const DetailReceiverInfoSection = ({ export interface ReceiverSectionProps {
receiverInfo, detail: VatReturnDetailResponse
isOpen, };
onClickToShowInfo
}: DetailInfoSectionProps) => { export const ReceiverSection = ({
detail
}: ReceiverSectionProps) => {
return ( return (
<> <>
@@ -14,15 +16,15 @@ export const DetailReceiverInfoSection = ({
<ul className="kv-list"> <ul className="kv-list">
<li className="kv-row"> <li className="kv-row">
<span className="k"></span> <span className="k"></span>
<span className="v">123-12-12345</span> <span className="v">{ detail.receiverBusinessRegistrationNumber }</span>
</li> </li>
<li className="kv-row"> <li className="kv-row">
<span className="k"></span> <span className="k"></span>
<span className="v"></span> <span className="v">{ detail.receiverCompanyName }</span>
</li> </li>
<li className="kv-row"> <li className="kv-row">
<span className="k"></span> <span className="k"></span>
<span className="v"></span> <span className="v">{ detail.receiverCeoName }</span>
</li> </li>
</ul> </ul>
</> </>

View File

@@ -0,0 +1,32 @@
import { VatReturnDetailResponse } from '../../model/types';
export interface SupplierSectionProps {
detail: VatReturnDetailResponse
};
export const SupplierSection = ({
detail
}: SupplierSectionProps) => {
return (
<>
<div className="txn-section">
<div className="section-title"> </div>
</div>
<ul className="kv-list">
<li className="kv-row">
<span className="k"></span>
<span className="v">{ detail.supplierBusinessRegistrationNumber }</span>
</li>
<li className="kv-row">
<span className="k"></span>
<span className="v">{ detail.supplierCompanyName }</span>
</li>
<li className="kv-row">
<span className="k"></span>
<span className="v">{ detail.supplierCeoName }</span>
</li>
</ul>
</>
);
};

View File

@@ -0,0 +1,37 @@
import { PATHS } from '@/shared/constants/paths';
import { useNavigate } from '@/shared/lib/hooks/use-navigate';
import {
VatReturnTabKeys,
VatReturnTabProps
} from '../model/types';
export const VatReturnTab = ({
activeTab
}: VatReturnTabProps) => {
const { navigate } = useNavigate();
const onClickToNavigation = (tab: VatReturnTabKeys) => {
if(activeTab !== tab){
if(tab === VatReturnTabKeys.List){
navigate(PATHS.vatReturn.list);
}
else if(tab === VatReturnTabKeys.VatReference){
navigate(PATHS.vatReturn.vatReference);
}
}
};
return(
<>
<div className="subTab">
<button
className={`subtab-btn ${(activeTab === VatReturnTabKeys.List)? 'active': ''}` }
onClick={ () => onClickToNavigation(VatReturnTabKeys.List) }
> </button>
<button
className={`subtab-btn ${(activeTab === VatReturnTabKeys.VatReference)? 'active': ''}` }
onClick={ () => onClickToNavigation(VatReturnTabKeys.VatReference) }
> </button>
</div>
</>
);
};

View File

@@ -1,133 +0,0 @@
import { useEffect, useState } from 'react';
import { PATHS } from '@/shared/constants/paths';
import { useLocation } from 'react-router';
import { useNavigate } from '@/shared/lib/hooks/use-navigate';
import { useInvoiceDetailMutation } from '@/entities/tax/api/use-invoice-detail-mutation';
import { DetailAmountInfoSection } from '@/entities/tax/ui/detail-amount-info-section';
import { DetailPublishInfoSection } from '@/entities/tax/ui/detail-publish-info-section';
import { DetailReceiverInfoSection } from '@/entities/tax/ui/detail-receiver-info-section';
import { DetailSupplierInfoSection } from '@/entities/tax/ui/detail-supplier-info-section';
import { HeaderType } from '@/entities/common/model/types';
import {
DetailResponse,
InvoiceDetailParams,
DetailAmountInfoProps,
DetailInfoSectionKeys,
DetailPublishInfoProps,
DetailReceiverInfoProps,
DetailSupplierInfoProps
} from '@/entities/tax/model/types';
import {
useSetOnBack,
useSetHeaderTitle,
useSetHeaderType,
useSetFooterMode
} from '@/widgets/sub-layout/use-sub-layout';
export const InvoiceDetailPage = () => {
const { navigate } = useNavigate();
const location = useLocation();
const [tid, setTid] = useState<string>(location?.state.tid);
const [amountInfo, setAmountInfo] = useState<DetailAmountInfoProps>();
const [publishInfo, setPublishInfo] = useState<DetailPublishInfoProps>();
const [receiverInfo, setReceiverInfo] = useState<DetailReceiverInfoProps>();
const [supplierInfo, setSupplierInfo] = useState<DetailSupplierInfoProps>();
const [showAmount, setShowAmount] = useState<boolean>(false);
const [showPublish, setShowPublish] = useState<boolean>(false);
const [showReceiver, setShowReceiver] = useState<boolean>(false);
const [showSupplier, setShowSupplier] = useState<boolean>(false);
useSetHeaderTitle('세금계산서 상세');
useSetHeaderType(HeaderType.RightClose);
useSetOnBack(() => {
navigate(PATHS.tax.invoice.list);
});
useSetFooterMode(false);
const { mutateAsync: invoiceDetail } = useInvoiceDetailMutation();
const onClickToShowInfo = (info: DetailInfoSectionKeys) => {
if(info === DetailInfoSectionKeys.Amount){
setShowAmount(!showAmount);
}
else if(info === DetailInfoSectionKeys.Publish){
setShowPublish(!showReceiver);
}
else if(info === DetailInfoSectionKeys.Receiver){
setShowReceiver(!showReceiver);
}
else if(info === DetailInfoSectionKeys.Supplier){
setShowSupplier(!showSupplier);
}
};
const callDetail = () => {
let invoiceDetailParams: InvoiceDetailParams = {
svcCd: 'st',
tid: tid
};
invoiceDetail(invoiceDetailParams).then((rs: DetailResponse) => {
setAmountInfo(rs.amountInfo);
setPublishInfo(rs.publishInfo);
setReceiverInfo(rs.receiverInfo);
setSupplierInfo(rs.supplierInfo);
});
};
useEffect(() => {
// callDetail();
}, []);
return (
<>
<main className="full-height">
<div className="tab-content">
<div className="tab-pane sub active">
<div className="option-list">
<div className="txn-detail">
<DetailAmountInfoSection
amountInfo={ amountInfo }
isOpen={ showAmount }
tid={ tid }
onClickToShowInfo={ (info) => onClickToShowInfo(info) }
></DetailAmountInfoSection>
<div className="txn-divider minus"></div>
<DetailPublishInfoSection
publishInfo={ publishInfo }
isOpen={ showAmount }
tid={ tid }
onClickToShowInfo={ (info) => onClickToShowInfo(info) }
></DetailPublishInfoSection>
<div className="txn-divider minus"></div>
<DetailReceiverInfoSection
receiverInfo={ receiverInfo }
isOpen={ showAmount }
tid={ tid }
onClickToShowInfo={ (info) => onClickToShowInfo(info) }
></DetailReceiverInfoSection>
<div className="txn-divider"></div>
<DetailSupplierInfoSection
supplierInfo={ supplierInfo }
isOpen={ showAmount }
tid={ tid }
onClickToShowInfo={ (info) => onClickToShowInfo(info) }
></DetailSupplierInfoSection>
</div>
</div>
</div>
</div>
</main>
</>
)
};

View File

@@ -1,20 +0,0 @@
import { Route } from 'react-router-dom';
import { SentryRoutes } from '@/shared/configs/sentry';
import { ROUTE_NAMES } from '@/shared/constants/route-names';
import { InvoiceListPage } from './invoice/list-page';
import { InvoiceDetailPage } from './invoice/detail-page';
import { VatReferencePage } from './vat-reference/vat-reference-page';
export const TaxPages = () => {
return (
<>
<SentryRoutes>
<Route path={ROUTE_NAMES.tax.invoice.base}>
<Route path={ROUTE_NAMES.tax.invoice.list} element={<InvoiceListPage />} />
<Route path={ROUTE_NAMES.tax.invoice.detail} element={<InvoiceDetailPage />} />
</Route>
<Route path={ROUTE_NAMES.tax.vatReference} element={<VatReferencePage />} />
</SentryRoutes>
</>
);
};

View File

@@ -0,0 +1,82 @@
import { useEffect, useState } from 'react';
import { PATHS } from '@/shared/constants/paths';
import { useLocation } from 'react-router';
import { useNavigate } from '@/shared/lib/hooks/use-navigate';
import { useVatReturnDetailMutation } from '@/entities/vat-return/api/use-vat-return-detail-mutation';
import { HeaderType } from '@/entities/common/model/types';
import {
VatReturnDetailParams,
VatReturnDetailResponse
} from '@/entities/vat-return/model/types';
import {
useSetOnBack,
useSetHeaderTitle,
useSetHeaderType,
useSetFooterMode
} from '@/widgets/sub-layout/use-sub-layout';
import { SupplierSection } from '@/entities/vat-return/ui/section/supplier-section';
import { ReceiverSection } from '@/entities/vat-return/ui/section/receiver-section';
import { IssueSection } from '@/entities/vat-return/ui/section/issue-section';
import { AmountSection } from '@/entities/vat-return/ui/section/amount-section';
export const DetailPage = () => {
const { navigate } = useNavigate();
const location = useLocation();
let taxInvoiceNumber = location?.state.taxInvoiceNumber;
taxInvoiceNumber = 'TAX202506300001';
const [openAmount, setOpenAmount] = useState<boolean>(false);
const [detail, setDetail] = useState<VatReturnDetailResponse>({});
useSetHeaderTitle('세금계산서 상세');
useSetHeaderType(HeaderType.RightClose);
useSetOnBack(() => {
navigate(PATHS.vatReturn.list);
});
useSetFooterMode(false);
const { mutateAsync: vatReturnDetail } = useVatReturnDetailMutation();
const callDetail = () => {
let params: VatReturnDetailParams = {
taxInvoiceNumber: taxInvoiceNumber,
};
vatReturnDetail(params).then((rs: VatReturnDetailResponse) => {
setDetail(rs);
});
};
useEffect(() => {
callDetail();
}, []);
return (
<>
<main className="full-height">
<div className="tab-content">
<div className="tab-pane sub active">
<div className="option-list">
<div className="txn-detail">
<AmountSection
detail={ detail }
></AmountSection>
<div className="txn-divider minus"></div>
<IssueSection
detail={ detail }
></IssueSection>
<div className="txn-divider minus"></div>
<ReceiverSection
detail={ detail }
></ReceiverSection>
<div className="txn-divider"></div>
<SupplierSection
detail={ detail }
></SupplierSection>
</div>
</div>
</div>
</div>
</main>
</>
)
};

View File

@@ -1,9 +1,9 @@
import { useState } from 'react'; import { useState } from 'react';
import { PATHS } from '@/shared/constants/paths'; import { PATHS } from '@/shared/constants/paths';
import { useNavigate } from '@/shared/lib/hooks/use-navigate'; import { useNavigate } from '@/shared/lib/hooks/use-navigate';
import { TaxTab } from '@/entities/tax/ui/tax-tab'; import { VatReturnTab } from '@/entities/vat-return/ui/vat-return-tab';
import { InvoiceListWrap } from '@/entities/tax/ui/invoice-list-wrap'; import { ListWrap } from '@/entities/vat-return/ui/list-wrap';
import { TaxTabKeys } from '@/entities/tax/model/types'; import { VatReturnTabKeys } from '@/entities/vat-return/model/types';
import { HeaderType } from '@/entities/common/model/types'; import { HeaderType } from '@/entities/common/model/types';
import { import {
useSetHeaderTitle, useSetHeaderTitle,
@@ -12,9 +12,9 @@ import {
useSetOnBack useSetOnBack
} from '@/widgets/sub-layout/use-sub-layout'; } from '@/widgets/sub-layout/use-sub-layout';
export const InvoiceListPage = () => { export const ListPage = () => {
const { navigate } = useNavigate(); const { navigate } = useNavigate();
const [activeTab, setActiveTab] = useState<TaxTabKeys>(TaxTabKeys.InvoiceList); const [activeTab, setActiveTab] = useState<VatReturnTabKeys>(VatReturnTabKeys.List);
useSetHeaderTitle('부가세 신고 자료'); useSetHeaderTitle('부가세 신고 자료');
useSetHeaderType(HeaderType.LeftArrow); useSetHeaderType(HeaderType.LeftArrow);
useSetFooterMode(true); useSetFooterMode(true);
@@ -27,8 +27,8 @@ export const InvoiceListPage = () => {
<main> <main>
<div className="tab-content"> <div className="tab-content">
<div className="tab-pane pt-46 active"> <div className="tab-pane pt-46 active">
<TaxTab activeTab={ activeTab }></TaxTab> <VatReturnTab activeTab={ activeTab }></VatReturnTab>
<InvoiceListWrap></InvoiceListWrap> <ListWrap></ListWrap>
</div> </div>
</div> </div>
</main> </main>

View File

@@ -1,9 +1,9 @@
import { useState } from 'react'; import { useState } from 'react';
import { PATHS } from '@/shared/constants/paths'; import { PATHS } from '@/shared/constants/paths';
import { useNavigate } from '@/shared/lib/hooks/use-navigate'; import { useNavigate } from '@/shared/lib/hooks/use-navigate';
import { TaxTab } from '@/entities/tax/ui/tax-tab'; import { VatReturnTab } from '@/entities/vat-return/ui/vat-return-tab';
import { VatReferenceWrap } from '@/entities/tax/ui/vat-reference-wrap'; import { VatReferenceWrap } from '@/entities/vat-return/ui/vat-reference-wrap';
import { TaxTabKeys } from '@/entities/tax/model/types'; import { VatReturnTabKeys } from '@/entities/vat-return/model/types';
import { HeaderType } from '@/entities/common/model/types'; import { HeaderType } from '@/entities/common/model/types';
import { import {
useSetHeaderTitle, useSetHeaderTitle,
@@ -16,7 +16,7 @@ import {
export const VatReferencePage = () => { export const VatReferencePage = () => {
const { navigate } = useNavigate(); const { navigate } = useNavigate();
const [activeTab, setActiveTab] = useState<TaxTabKeys>(TaxTabKeys.VatReference); const [activeTab, setActiveTab] = useState<VatReturnTabKeys>(VatReturnTabKeys.VatReference);
useSetHeaderTitle('부가세 신고 자료'); useSetHeaderTitle('부가세 신고 자료');
useSetHeaderType(HeaderType.LeftArrow); useSetHeaderType(HeaderType.LeftArrow);
useSetFooterMode(true); useSetFooterMode(true);
@@ -29,7 +29,7 @@ export const VatReferencePage = () => {
<main> <main>
<div className="tab-content"> <div className="tab-content">
<div className="tab-pane sub active"> <div className="tab-pane sub active">
<TaxTab activeTab={ activeTab }></TaxTab> <VatReturnTab activeTab={ activeTab }></VatReturnTab>
<VatReferenceWrap></VatReferenceWrap> <VatReferenceWrap></VatReferenceWrap>
</div> </div>
</div> </div>

View File

@@ -0,0 +1,18 @@
import { Route } from 'react-router-dom';
import { SentryRoutes } from '@/shared/configs/sentry';
import { ROUTE_NAMES } from '@/shared/constants/route-names';
import { ListPage } from './list-page';
import { DetailPage } from './detail-page';
import { VatReferencePage } from './vat-reference-page';
export const VatReturnPages = () => {
return (
<>
<SentryRoutes>
<Route path={ROUTE_NAMES.vatReturn.list} element={<ListPage />} />
<Route path={ROUTE_NAMES.vatReturn.detail} element={<DetailPage />} />
<Route path={ROUTE_NAMES.vatReturn.vatReference} element={<VatReferencePage />} />
</SentryRoutes>
</>
);
};

View File

@@ -3,11 +3,13 @@ import {
API_URL_KEY, API_URL_KEY,
} from './../constants/url'; } from './../constants/url';
export const API_URL_TAX = { export const API_URL_VAT_RETURN = {
invoiceList: () => { vatReturnList: () => {
// POST: 세금계산서 목록 조회
return `${API_BASE_URL}/api/v1/${API_URL_KEY}/vat-return/list`; return `${API_BASE_URL}/api/v1/${API_URL_KEY}/vat-return/list`;
}, },
invoiceDetail: () => { vatReturnDetail: () => {
// POST: 세금계산서 상세 조회
return `${API_BASE_URL}/api/v1/${API_URL_KEY}/vat-return/detail`; return `${API_BASE_URL}/api/v1/${API_URL_KEY}/vat-return/detail`;
}, },
}; };

View File

@@ -79,7 +79,7 @@ const SettlementPages = lazyLoad('/src/pages/settlement/settlement-pages
const MerchantPages = lazyLoad('/src/pages/merchant/merchant-pages'); const MerchantPages = lazyLoad('/src/pages/merchant/merchant-pages');
const PaymentPages = lazyLoad('/src/pages/payment/payment-pages'); const PaymentPages = lazyLoad('/src/pages/payment/payment-pages');
const AccountPages = lazyLoad('/src/pages/account/account-pages'); const AccountPages = lazyLoad('/src/pages/account/account-pages');
const TaxPages = lazyLoad('/src/pages/tax/tax-pages'); const VatReturnPages = lazyLoad('/src/pages/vat-return/vat-return-pages');
const AdditionalServicePages = lazyLoad('/src/pages/additional-service/additional-service-pages'); const AdditionalServicePages = lazyLoad('/src/pages/additional-service/additional-service-pages');
const SupportPages = lazyLoad('/src/pages/support/support-pages'); const SupportPages = lazyLoad('/src/pages/support/support-pages');
const SettingPage = lazyLoad('/src/pages/setting/setting-page'); const SettingPage = lazyLoad('/src/pages/setting/setting-page');
@@ -125,7 +125,7 @@ const Pages = () => {
<Route path={ROUTE_NAMES.merchant.base} element={<MerchantPages />} /> <Route path={ROUTE_NAMES.merchant.base} element={<MerchantPages />} />
<Route path={ROUTE_NAMES.payment.base} element={<PaymentPages />} /> <Route path={ROUTE_NAMES.payment.base} element={<PaymentPages />} />
<Route path={ROUTE_NAMES.account.base} element={<AccountPages />} /> <Route path={ROUTE_NAMES.account.base} element={<AccountPages />} />
<Route path={ROUTE_NAMES.tax.base} element={<TaxPages />} /> <Route path={ROUTE_NAMES.vatReturn.base} element={<VatReturnPages />} />
<Route path={ROUTE_NAMES.additionalService.base} element={<AdditionalServicePages />} /> <Route path={ROUTE_NAMES.additionalService.base} element={<AdditionalServicePages />} />
<Route path={ROUTE_NAMES.support.base} element={<SupportPages />} /> <Route path={ROUTE_NAMES.support.base} element={<SupportPages />} />
<Route path={ROUTE_NAMES.setting} element={<SettingPage />} /> <Route path={ROUTE_NAMES.setting} element={<SettingPage />} />

View File

@@ -124,20 +124,11 @@ export const PATHS: RouteNamesType = {
), ),
} }
}, },
tax: { vatReturn: {
base: generatePath(ROUTE_NAMES.tax.base), base: generatePath(ROUTE_NAMES.vatReturn.base),
invoice: { list: generatePath(ROUTE_NAMES.vatReturn.base, ROUTE_NAMES.vatReturn.list),
base: generatePath(`${ROUTE_NAMES.tax.base}${ROUTE_NAMES.tax.invoice.base}`), detail: generatePath(ROUTE_NAMES.vatReturn.base, ROUTE_NAMES.vatReturn.detail),
list: generatePath( vatReference: generatePath(ROUTE_NAMES.vatReturn.base, ROUTE_NAMES.vatReturn.vatReference),
`${ROUTE_NAMES.tax.base}${ROUTE_NAMES.tax.invoice.base}`,
ROUTE_NAMES.tax.invoice.list,
),
detail: generatePath(
`${ROUTE_NAMES.tax.base}${ROUTE_NAMES.tax.invoice.base}`,
ROUTE_NAMES.tax.invoice.detail,
),
},
vatReference: generatePath(ROUTE_NAMES.tax.base, ROUTE_NAMES.tax.vatReference),
}, },
additionalService: { additionalService: {
base: generatePath(ROUTE_NAMES.additionalService.base), base: generatePath(ROUTE_NAMES.additionalService.base),

View File

@@ -58,13 +58,10 @@ export const ROUTE_NAMES = {
modifyLoginPassword: 'modifyLoginPassword' modifyLoginPassword: 'modifyLoginPassword'
} }
}, },
tax: { vatReturn: {
base: '/tax/*', base: '/vat-return/*',
invoice: {
base: '/invoice/*',
list: 'list', list: 'list',
detail: 'detail', detail: 'detail',
},
vatReference: 'vatReference' vatReference: 'vatReference'
}, },
additionalService: { additionalService: {

View File

@@ -73,8 +73,8 @@ export const Menu = ({
category: '부가세 신고 자료', category: '부가세 신고 자료',
categoryIcon: 'vat-icon', categoryIcon: 'vat-icon',
items: [ items: [
{title: '세금계산서', path: PATHS.tax.invoice.list}, {title: '세금계산서', path: PATHS.vatReturn.list},
{title: '부가세 참고', path: PATHS.tax.vatReference}, {title: '부가세 참고', path: PATHS.vatReturn.vatReference},
] ]
}, },
additionalService: { additionalService: {