diff --git a/src/app/(main)/m/admin/einvoices/page.tsx b/src/app/(main)/m/admin/einvoices/page.tsx index 08f6395..aa0036b 100644 --- a/src/app/(main)/m/admin/einvoices/page.tsx +++ b/src/app/(main)/m/admin/einvoices/page.tsx @@ -1,9 +1,10 @@ "use client"; -import { useEffect, useState, useCallback } from "react"; +import { useEffect, useState, useCallback, useMemo } from "react"; import { FileText, Send, Download, RefreshCcw, AlertCircle } from "lucide-react"; import Swal from "sweetalert2"; import { downloadXlsx } from "@/lib/xlsx-export"; +import { SearchableSelect } from "@/components/ui/searchable-select"; interface Einvoice { OBJID: string; @@ -53,9 +54,13 @@ function defaultRange() { return [s.toISOString().slice(0, 10), e.toISOString().slice(0, 10)]; } +interface Customer { USER_ID: string; USER_NAME: string } + export default function EinvoicesPage() { const [[from, to], setRange] = useState(defaultRange()); const [statusFilter, setStatusFilter] = useState(""); + const [customerFilter, setCustomerFilter] = useState(""); + const [customers, setCustomers] = useState([]); const [list, setList] = useState([]); const [pending, setPending] = useState([]); const [busy, setBusy] = useState(false); @@ -63,10 +68,37 @@ export default function EinvoicesPage() { const load = useCallback(async () => { const res = await fetch("/api/m/einvoices/list", { method: "POST", headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ dateFrom: from, dateTo: to, status: statusFilter || undefined }), + body: JSON.stringify({ + dateFrom: from, + dateTo: to, + status: statusFilter || undefined, + customerObjid: customerFilter || undefined, + }), }); setList((await res.json()).RESULTLIST ?? []); - }, [from, to, statusFilter]); + }, [from, to, statusFilter, customerFilter]); + + const loadCustomers = useCallback(async () => { + const res = await fetch("/api/m/customers/list", { + method: "POST", headers: { "Content-Type": "application/json" }, body: "{}", + }); + setCustomers((await res.json()).RESULTLIST ?? []); + }, []); + + // 면세/과세/합계 합산 + const summary = useMemo(() => { + let taxFreeAmount = 0, taxableSupply = 0, taxableVat = 0, total = 0; + for (const e of list) { + total += Number(e.TOTAL_AMOUNT) || 0; + if (e.INVOICE_KIND === "TAXFREE") { + taxFreeAmount += Number(e.TOTAL_SUPPLY) || 0; + } else { + taxableSupply += Number(e.TOTAL_SUPPLY) || 0; + taxableVat += Number(e.TOTAL_VAT) || 0; + } + } + return { taxFreeAmount, taxableSupply, taxableVat, taxableTotal: taxableSupply + taxableVat, total }; + }, [list]); const loadPending = useCallback(async () => { // 발주 + 발행이력 동시 조회 후 이미 발행된 건은 제외 @@ -88,6 +120,7 @@ export default function EinvoicesPage() { }, []); useEffect(() => { load(); loadPending(); }, [load, loadPending]); + useEffect(() => { loadCustomers(); }, [loadCustomers]); const issueFromOrder = async (orderObjid: string, kind: "TAX" | "TAXFREE" = "TAX") => { const ok = await Swal.fire({ @@ -217,12 +250,20 @@ export default function EinvoicesPage() {
발행 이력 ({list.length}건) -
+
setRange([e.target.value, to])} className="h-8 px-2 rounded border border-slate-200" /> ~ setRange([from, e.target.value])} className="h-8 px-2 rounded border border-slate-200" /> +
+ ({ value: c.USER_ID, label: c.USER_NAME }))]} + value={customerFilter} + onChange={setCustomerFilter} + placeholder="거래처" + /> +
setFilterStatus(e.target.value)} - className="h-10 px-3 rounded-lg border border-slate-200 text-sm focus:border-emerald-500 outline-none bg-white" - > - - - - +
+ ({ value: v.OBJID, label: v.VENDOR_NAME }))]} + value={filterVendor} + onChange={setFilterVendor} + placeholder="공급업체" + /> +