import { useMemo, useState } from 'react' import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query' import dayjs from 'dayjs' import { Button, Card, Typography, message } from 'antd' import CareTable from '../../components/CareTable' import { CalculatorOutlined, DownloadOutlined, LeftOutlined, RightOutlined } from '@ant-design/icons' import client, { apiPrefix, unwrap, type ApiResponse } from '../../api/client' import { money } from './homesShared' type PublishRow = { id?: number | null employee: string yearMonth: string internetCount?: number internetAmount?: number homeCount?: number homeAmount?: number balanceCount?: number balanceAmount?: number settleAmount?: number issued?: boolean status?: string consent?: string taxInvoice?: string withdrawStatus?: string mismatched?: boolean } type PublishResult = { yearMonth: string total: number list: PublishRow[] } const num = (v: unknown) => { const n = Number(v ?? 0) return Number.isFinite(n) ? n : 0 } const cell = (v: unknown) => (num(v) === 0 ? '' : num(v).toLocaleString()) const moneyCell = (v: unknown, mismatched?: boolean) => { if (num(v) === 0) return '' return {money(v)} } export default function InvoicePublishPage() { const qc = useQueryClient() const [cursor, setCursor] = useState(() => dayjs().startOf('month')) const yearMonth = cursor.format('YYYY-MM') const query = useQuery({ queryKey: ['homes-invoice-publish', yearMonth], queryFn: async () => { const { data } = await client.get>(`${apiPrefix()}/homes/invoices/publish`, { params: { yearMonth } }) return unwrap(data) } }) const issue = useMutation({ mutationFn: async (row: PublishRow) => { const { data } = await client.post(`${apiPrefix()}/homes/invoices/publish/issue`, { yearMonth: row.yearMonth || yearMonth, employee: row.employee }) return unwrap(data as ApiResponse) }, onSuccess: () => { message.success('정산서를 발행했습니다.') qc.invalidateQueries({ queryKey: ['homes-invoice-publish'] }) }, onError: (e: Error) => message.error(e.message || '발행에 실패했습니다.') }) const download = async () => { try { const { data } = await client.get(`${apiPrefix()}/homes/invoices/publish/export`, { params: { yearMonth }, responseType: 'blob' }) const url = URL.createObjectURL(data) const a = document.createElement('a') a.href = url a.download = `정산서발행_${yearMonth}.csv` a.click() URL.revokeObjectURL(url) } catch { message.error('엑셀 다운로드에 실패했습니다.') } } const columns = useMemo(() => [ { title: '직원', dataIndex: 'employee', width: 100, align: 'center' as const, render: (v: string, row: PublishRow) => ( {v} ) }, { title: '인터넷', dataIndex: 'internetCount', width: 80, align: 'center' as const, render: (v: unknown, row: PublishRow) => ( {cell(v)} ) }, { title: '인터넷금액', dataIndex: 'internetAmount', width: 110, align: 'center' as const, render: (v: unknown, row: PublishRow) => moneyCell(v, row.mismatched) }, { title: '홈렌탈', dataIndex: 'homeCount', width: 80, align: 'center' as const, render: (v: unknown, row: PublishRow) => ( {cell(v)} ) }, { title: '홈렌탈금액', dataIndex: 'homeAmount', width: 110, align: 'center' as const, render: (v: unknown, row: PublishRow) => moneyCell(v, row.mismatched) }, { title: '후정산', dataIndex: 'balanceCount', width: 80, align: 'center' as const, render: (v: unknown, row: PublishRow) => ( {cell(v)} ) }, { title: '후정산금액', dataIndex: 'balanceAmount', width: 110, align: 'center' as const, render: (v: unknown, row: PublishRow) => moneyCell(v, row.mismatched) }, { title: '정산금액', dataIndex: 'settleAmount', width: 110, align: 'center' as const, render: (v: unknown, row: PublishRow) => moneyCell(v, row.mismatched) }, { title: '정산서발행', width: 100, align: 'center' as const, render: (_: unknown, row: PublishRow) => ( row.issued ? ( ) : ( ) ) }, { title: '동의', dataIndex: 'consent', width: 80, align: 'center' as const, render: (v: string) => v || '-' }, { title: '계산서', dataIndex: 'taxInvoice', width: 80, align: 'center' as const, render: (v: string) => v || '-' }, { title: '출금상태', dataIndex: 'withdrawStatus', width: 90, align: 'center' as const, render: (v: string) => v || '-' }, ], [issue]) return (

정산서발행

직원 및 하부점에 정산서를 발행 및 관리하실 수 있습니다. (계약 진행상태가 완료인 자료만 정산서를 발행하실 수 있습니다.)

{cursor.format('YYYY년 M월')}
`${row.employee}-${row.yearMonth}`} size="small" loading={query.isLoading} dataSource={query.data?.list} columns={columns} pagination={false} scroll={{ x: 1200 }} locale={{ emptyText: '등록된 자료가 없습니다.' }} rowClassName={(row) => (row.mismatched ? 'invoice-row-mismatch' : '')} />
* 정산서발행 시 자료의 양에 따라 처리시간이 오래 걸릴 수 있습니다. * 붉은색 표기는 추가/수정으로 인해 발행정산서와 일치하지 않는 내용입니다. (필요한 경우 재발행하시면 해결됩니다.)
) }