feat(orders): admin 출고관리 인라인 수기 발주 + 품목 추가 + 거래처 변경
Deploy momo-erp / deploy (push) Successful in 2m19s
Deploy momo-erp / deploy (push) Successful in 2m19s
매입 발주서 작성 패턴처럼 출고관리 안에서 직접 빈 발주 → 거래처 → 품목 채워가는 흐름. 신규 API: - /api/m/orders/create-empty (admin) — 빈 발주 INSERT · status='REQUESTED', customer 임시 admin, HQ 기본 supplier snapshot - /api/m/orders/update-customer (admin) — 발주의 거래처 변경 · 변경 시 새 거래처 statement_branch 기반 supplier snapshot 재계산 · REQUESTED/APPROVED 만 변경 허용 (입금 후 잠금) UI (/m/admin/orders): - '수기 발주' 버튼 → 즉시 create-empty 호출 → 리스트 새로고침 + 새 row 자동 활성화 (모달/redirect 제거) - detail 의 거래명세서 안 '귀하' 줄 → editable 시 CustomerEditor (select) - 액션바에 '+ 품목 추가' 버튼 → AdminItemPickerModal (재고 있는 품목 검색) · items/add API 호출, ITEM 라인 일괄 INSERT
This commit is contained in:
@@ -4,8 +4,7 @@ import { useEffect, useMemo, useState, useCallback, useRef } from "react";
|
||||
import { Check, Download, X, RefreshCcw, Truck, AlertCircle, Package, PhoneCall } from "lucide-react";
|
||||
import Swal from "sweetalert2";
|
||||
import { captureAndShare } from "@/lib/capture-share";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { SearchableSelect } from "@/components/ui/searchable-select";
|
||||
// SearchableSelect/useRouter 는 이전 수기 발주 모달용 — 단순 인라인 흐름으로 변경되어 제거
|
||||
|
||||
interface Order {
|
||||
OBJID: string; ORDER_NO: string; ORDER_DATE: string;
|
||||
@@ -16,6 +15,7 @@ interface Order {
|
||||
interface DetailOrder extends Order {
|
||||
CEO_NAME?: string; BIZ_NO?: string; PHONE?: string; ADDRESS?: string;
|
||||
MEMO?: string; APPROVE_DATE?: string;
|
||||
CUSTOMER_OBJID?: string;
|
||||
}
|
||||
interface DetailLine {
|
||||
OBJID: string;
|
||||
@@ -249,7 +249,7 @@ export default function AdminOrdersPage() {
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<ManualOrderButton />
|
||||
<ManualOrderButton onCreated={async (newObjid) => { await load(); setActiveId(newObjid); }} />
|
||||
<button
|
||||
onClick={bulkShip}
|
||||
disabled={busy || requestedSelectedIds.length === 0}
|
||||
@@ -521,6 +521,20 @@ function StatementPreview({
|
||||
: { unitPrice: 5000, qty: 1, label: "용차" };
|
||||
upsertExtra({ kind, ...defaults });
|
||||
};
|
||||
|
||||
// ITEM 라인 추가 (피커 모달)
|
||||
const [pickerOpen, setPickerOpen] = useState(false);
|
||||
const addItemLines = async (selected: { itemObjid: string; qty: number }[]) => {
|
||||
if (selected.length === 0) return;
|
||||
const res = await fetch("/api/m/orders/items/add", {
|
||||
method: "POST", headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ orderObjid: order.OBJID, items: selected }),
|
||||
});
|
||||
const j = await res.json();
|
||||
if (j.success) { setPickerOpen(false); onReload(); onReloadList(); }
|
||||
else Swal.fire({ icon: "error", title: "품목 추가 실패", text: j.message });
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="text-[12px] text-slate-800 space-y-3">
|
||||
{/* 공유/캡처/엑셀/출고 버튼 — 캡처 영역 밖에 배치 */}
|
||||
@@ -593,13 +607,19 @@ function StatementPreview({
|
||||
</div>
|
||||
|
||||
<div className="border border-slate-200 rounded p-2 bg-slate-50/60 mt-3">
|
||||
<div className="font-semibold text-slate-900">{order.COMPANY_NAME} <span className="text-slate-500 font-normal">귀하</span></div>
|
||||
<div className="text-[11px] text-slate-600 mt-0.5 leading-relaxed">
|
||||
{order.CEO_NAME && <>대표: {order.CEO_NAME} · </>}
|
||||
{order.BIZ_NO && <>사업자번호: {order.BIZ_NO} · </>}
|
||||
{order.PHONE && <>전화: {order.PHONE}</>}
|
||||
{order.ADDRESS && <div>주소: {order.ADDRESS}</div>}
|
||||
</div>
|
||||
{editable ? (
|
||||
<CustomerEditor order={order} onReload={onReload} onReloadList={onReloadList} />
|
||||
) : (
|
||||
<>
|
||||
<div className="font-semibold text-slate-900">{order.COMPANY_NAME} <span className="text-slate-500 font-normal">귀하</span></div>
|
||||
<div className="text-[11px] text-slate-600 mt-0.5 leading-relaxed">
|
||||
{order.CEO_NAME && <>대표: {order.CEO_NAME} · </>}
|
||||
{order.BIZ_NO && <>사업자번호: {order.BIZ_NO} · </>}
|
||||
{order.PHONE && <>전화: {order.PHONE}</>}
|
||||
{order.ADDRESS && <div>주소: {order.ADDRESS}</div>}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{lowStock.length > 0 && (
|
||||
@@ -618,7 +638,14 @@ function StatementPreview({
|
||||
|
||||
{editable && (
|
||||
<div className="flex flex-wrap items-center gap-2 js-no-export">
|
||||
<span className="text-[11px] text-slate-500">택배/용차 라인 추가:</span>
|
||||
<span className="text-[11px] text-slate-500">라인 추가:</span>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setPickerOpen(true)}
|
||||
className="inline-flex items-center gap-1 h-7 px-2.5 rounded bg-emerald-100 text-emerald-800 text-[11px] font-bold hover:bg-emerald-200"
|
||||
>
|
||||
+ 품목 추가
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => addNewExtra("DELIVERY")}
|
||||
@@ -746,6 +773,104 @@ function StatementPreview({
|
||||
<IssueEinvoiceButton order={order} />
|
||||
</div>
|
||||
)}
|
||||
|
||||
{pickerOpen && (
|
||||
<AdminItemPickerModal
|
||||
onClose={() => setPickerOpen(false)}
|
||||
onConfirm={addItemLines}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function AdminItemPickerModal({ onClose, onConfirm }: {
|
||||
onClose: () => void;
|
||||
onConfirm: (selected: { itemObjid: string; qty: number }[]) => void;
|
||||
}) {
|
||||
const [items, setItems] = useState<Array<{ OBJID: string; ITEM_CODE: string; ITEM_NAME: string; UNIT_PRICE: number; STOCK_QTY: number; IS_TAX_FREE: string; UNIT: string }>>([]);
|
||||
const [keyword, setKeyword] = useState("");
|
||||
const [cart, setCart] = useState<Record<string, number>>({});
|
||||
|
||||
useEffect(() => {
|
||||
fetch("/api/m/items/list", {
|
||||
method: "POST", headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ stockFilter: "AVAILABLE" }),
|
||||
})
|
||||
.then((r) => r.json())
|
||||
.then((j) => setItems(j.RESULTLIST ?? []))
|
||||
.catch(() => {});
|
||||
}, []);
|
||||
|
||||
const filtered = useMemo(() => {
|
||||
const kw = keyword.trim().toLowerCase();
|
||||
if (!kw) return items;
|
||||
return items.filter((it) => it.ITEM_NAME?.toLowerCase().includes(kw) || it.ITEM_CODE?.toLowerCase().includes(kw));
|
||||
}, [items, keyword]);
|
||||
|
||||
const confirm = () => {
|
||||
const selected = Object.entries(cart)
|
||||
.filter(([, q]) => q > 0)
|
||||
.map(([itemObjid, qty]) => ({ itemObjid, qty }));
|
||||
onConfirm(selected);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="fixed inset-0 bg-slate-900/60 z-[60] flex items-center justify-center p-3" onClick={onClose}>
|
||||
<div className="bg-white rounded-xl shadow-xl max-w-2xl w-full max-h-[85vh] overflow-hidden flex flex-col" onClick={(e) => e.stopPropagation()}>
|
||||
<div className="flex items-center justify-between px-4 py-3 border-b border-slate-200">
|
||||
<h3 className="font-bold">품목 추가</h3>
|
||||
<button onClick={onClose} className="text-slate-400 hover:text-slate-700"><X size={18} /></button>
|
||||
</div>
|
||||
<div className="px-4 py-2 border-b border-slate-200">
|
||||
<input value={keyword} onChange={(e) => setKeyword(e.target.value)}
|
||||
placeholder="품목명/코드 검색"
|
||||
className="w-full h-9 px-3 rounded border border-slate-300 text-sm" />
|
||||
</div>
|
||||
<div className="flex-1 overflow-y-auto">
|
||||
<table className="w-full text-xs">
|
||||
<thead className="bg-slate-50 text-slate-600 sticky top-0">
|
||||
<tr>
|
||||
<th className="text-left p-2">품목</th>
|
||||
<th className="text-right p-2 w-16">재고</th>
|
||||
<th className="text-right p-2 w-20">단가</th>
|
||||
<th className="text-center p-2 w-20">수량</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{filtered.length === 0 ? (
|
||||
<tr><td colSpan={4} className="text-center py-8 text-slate-400">검색 결과 없음</td></tr>
|
||||
) : filtered.slice(0, 100).map((it) => (
|
||||
<tr key={it.OBJID} className="border-t border-slate-100 hover:bg-slate-50">
|
||||
<td className="p-2">
|
||||
<div className="font-semibold">{it.ITEM_NAME}</div>
|
||||
<div className="text-[10px] text-slate-400">{it.ITEM_CODE} · {it.IS_TAX_FREE === "Y" ? "면세" : "과세"}</div>
|
||||
</td>
|
||||
<td className="p-2 text-right tabular-nums">{Number(it.STOCK_QTY).toLocaleString()}</td>
|
||||
<td className="p-2 text-right tabular-nums">{Number(it.UNIT_PRICE).toLocaleString()}</td>
|
||||
<td className="p-2 text-center">
|
||||
<input type="number" min={0} max={Number(it.STOCK_QTY)}
|
||||
value={cart[it.OBJID] ?? 0}
|
||||
onChange={(e) => {
|
||||
const v = Math.min(Number(it.STOCK_QTY), Math.max(0, Number(e.target.value) || 0));
|
||||
setCart((p) => ({ ...p, [it.OBJID]: v }));
|
||||
}}
|
||||
className="w-16 h-7 px-1 border border-slate-200 rounded text-right tabular-nums" />
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div className="flex justify-end gap-2 px-4 py-3 border-t border-slate-200">
|
||||
<button onClick={onClose} className="h-9 px-4 rounded border border-slate-200 text-sm font-semibold">취소</button>
|
||||
<button onClick={confirm}
|
||||
disabled={Object.values(cart).every((q) => !q)}
|
||||
className="h-9 px-5 rounded bg-emerald-700 text-white text-sm font-bold hover:bg-emerald-800 disabled:opacity-40">
|
||||
추가
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -942,60 +1067,80 @@ function QtyInput({ initial, onSave }: { initial: number; onSave: (q: number) =>
|
||||
}
|
||||
|
||||
// 수기 발주 작성 — admin 이 전화 요청 등을 받아 거래처 대신 발주 등록
|
||||
function ManualOrderButton() {
|
||||
const router = useRouter();
|
||||
const [open, setOpen] = useState(false);
|
||||
// 거래처 변경 — admin 이 출고관리 detail 안에서 거래처 직접 select
|
||||
function CustomerEditor({ order, onReload, onReloadList }: {
|
||||
order: DetailOrder; onReload: () => void; onReloadList: () => void;
|
||||
}) {
|
||||
const [customers, setCustomers] = useState<{ USER_ID: string; USER_NAME: string }[]>([]);
|
||||
const [selected, setSelected] = useState("");
|
||||
const [busy, setBusy] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (!open || customers.length > 0) return;
|
||||
fetch("/api/m/customers/list", {
|
||||
method: "POST", headers: { "Content-Type": "application/json" }, body: "{}",
|
||||
})
|
||||
fetch("/api/m/customers/list", { method: "POST", headers: { "Content-Type": "application/json" }, body: "{}" })
|
||||
.then((r) => r.json())
|
||||
.then((j) => setCustomers(j.RESULTLIST ?? []))
|
||||
.catch(() => {});
|
||||
}, [open, customers.length]);
|
||||
}, []);
|
||||
|
||||
const onProceed = () => {
|
||||
if (!selected) { Swal.fire({ icon: "warning", title: "거래처를 선택하세요." }); return; }
|
||||
setOpen(false);
|
||||
router.push(`/m/orders/new?customerObjid=${encodeURIComponent(selected)}`);
|
||||
const change = async (newId: string) => {
|
||||
if (!newId || newId === order.CUSTOMER_OBJID) return;
|
||||
setBusy(true);
|
||||
try {
|
||||
const res = await fetch("/api/m/orders/update-customer", {
|
||||
method: "POST", headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ objid: order.OBJID, customerObjid: newId }),
|
||||
});
|
||||
const j = await res.json();
|
||||
if (j.success) { onReload(); onReloadList(); }
|
||||
else Swal.fire({ icon: "error", title: "거래처 변경 실패", text: j.message });
|
||||
} finally { setBusy(false); }
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setOpen(true)}
|
||||
className="h-9 px-3 rounded-lg bg-white border border-amber-300 text-amber-700 text-sm font-bold hover:bg-amber-50 inline-flex items-center gap-1.5"
|
||||
title="전화 요청 등 수기로 거래처 대신 발주 작성"
|
||||
<div className="flex items-center gap-2 flex-wrap">
|
||||
<span className="text-[11px] text-slate-500 font-semibold">거래처</span>
|
||||
<select
|
||||
value={order.CUSTOMER_OBJID ?? ""}
|
||||
onChange={(e) => change(e.target.value)}
|
||||
disabled={busy}
|
||||
className="h-7 px-2 rounded border border-slate-300 text-[11px] bg-white max-w-[280px]"
|
||||
>
|
||||
<PhoneCall size={14} /> 수기 발주
|
||||
</button>
|
||||
{open && (
|
||||
<div className="fixed inset-0 bg-slate-900/60 z-50 flex items-center justify-center p-3" onClick={() => setOpen(false)}>
|
||||
<div className="bg-white rounded-xl shadow-xl max-w-md w-full p-5" onClick={(e) => e.stopPropagation()}>
|
||||
<h3 className="font-bold text-lg mb-3">수기 발주 — 거래처 선택</h3>
|
||||
<p className="text-xs text-slate-500 mb-4">전화 요청 등 거래처를 대신해 발주를 작성합니다. 선택한 거래처 명의로 발주가 등록되며, 그 거래처의 기준 명세표가 적용됩니다.</p>
|
||||
<SearchableSelect
|
||||
value={selected}
|
||||
onChange={setSelected}
|
||||
options={customers.map((c) => ({ value: c.USER_ID, label: `${c.USER_NAME} (${c.USER_ID})` }))}
|
||||
placeholder="거래처 검색/선택"
|
||||
/>
|
||||
<div className="flex gap-2 justify-end mt-5">
|
||||
<button type="button" onClick={() => setOpen(false)}
|
||||
className="h-10 px-4 rounded-lg border border-slate-200 text-sm font-semibold">취소</button>
|
||||
<button type="button" onClick={onProceed}
|
||||
className="h-10 px-5 rounded-lg bg-emerald-700 text-white text-sm font-bold hover:bg-emerald-800">
|
||||
작성하기
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<option value="">-- 선택 --</option>
|
||||
{customers.map((c) => (
|
||||
<option key={c.USER_ID} value={c.USER_ID}>{c.USER_NAME} ({c.USER_ID})</option>
|
||||
))}
|
||||
</select>
|
||||
<span className="text-slate-500 font-normal">귀하</span>
|
||||
{order.COMPANY_NAME && (
|
||||
<span className="text-[10px] text-slate-400 ml-1">
|
||||
{order.CEO_NAME && <>대표 {order.CEO_NAME} · </>}
|
||||
{order.PHONE && <>{order.PHONE}</>}
|
||||
</span>
|
||||
)}
|
||||
</>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// 수기 발주 — 클릭하면 빈 발주 즉시 생성 → 자동 활성화. 거래처/품목은 detail 에서 채움.
|
||||
function ManualOrderButton({ onCreated }: { onCreated: (newObjid: string) => void }) {
|
||||
const [busy, setBusy] = useState(false);
|
||||
const onClick = async () => {
|
||||
setBusy(true);
|
||||
try {
|
||||
const res = await fetch("/api/m/orders/create-empty", { method: "POST" });
|
||||
const j = await res.json();
|
||||
if (j.success) onCreated(j.objId);
|
||||
else Swal.fire({ icon: "error", title: "생성 실패", text: j.message });
|
||||
} finally { setBusy(false); }
|
||||
};
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
onClick={onClick}
|
||||
disabled={busy}
|
||||
className="h-9 px-3 rounded-lg bg-white border border-amber-300 text-amber-700 text-sm font-bold hover:bg-amber-50 disabled:opacity-50 inline-flex items-center gap-1.5"
|
||||
title="빈 발주를 생성하여 오른쪽에서 거래처/품목 채워가는 흐름"
|
||||
>
|
||||
<PhoneCall size={14} /> 수기 발주
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
// 빈 발주 생성 — admin 수기 작성용. 즉시 출고관리 화면 안에서 거래처/품목 채워가는 흐름.
|
||||
// status='REQUESTED', customer 는 admin 본인으로 임시 박음. detail 에서 변경 가능.
|
||||
import { NextResponse } from "next/server";
|
||||
import { pool, queryOne } from "@/lib/db";
|
||||
import { createObjectId } from "@/lib/utils";
|
||||
import { requireMomoAdmin } from "@/lib/momo-guard";
|
||||
import { getSupplierByBranch } from "@/lib/momo-branches";
|
||||
|
||||
export async function POST() {
|
||||
const g = await requireMomoAdmin();
|
||||
if (g instanceof NextResponse) return g;
|
||||
const adminId = g.user.objid || g.user.userId;
|
||||
|
||||
const orderObjid = createObjectId();
|
||||
const orderNo = await genOrderNo();
|
||||
|
||||
// 기본 supplier — admin 본인 사용자(또는 HQ) 의 기준명세표 snapshot
|
||||
// customer 가 바뀌면 별도 API (update-customer) 가 supplier 재계산
|
||||
const supplier = await getSupplierByBranch("HQ");
|
||||
|
||||
const client = await pool.connect();
|
||||
try {
|
||||
await client.query("BEGIN");
|
||||
await client.query(
|
||||
`INSERT INTO momo_orders (
|
||||
objid, order_no, customer_objid, order_date, status,
|
||||
total_supply, total_vat, total_amount, total_taxfree, total_taxable,
|
||||
total_delivery, total_charter, memo, regdate, regid,
|
||||
supplier_branch, supplier_name, supplier_ceo, supplier_bank_account,
|
||||
supplier_phone, supplier_email, supplier_biz_no, supplier_address
|
||||
) VALUES ($1, $2, $3, CURRENT_DATE, 'REQUESTED',
|
||||
0, 0, 0, 0, 0, 0, 0, NULL, NOW(), $3,
|
||||
$4, $5, $6, $7, $8, $9, $10, $11)`,
|
||||
[orderObjid, orderNo, adminId,
|
||||
supplier.CODE, supplier.NAME, supplier.CEO, supplier.BANK_ACCOUNT,
|
||||
supplier.PHONE, supplier.EMAIL, supplier.BIZ_NO, supplier.ADDRESS]
|
||||
);
|
||||
await client.query("COMMIT");
|
||||
return NextResponse.json({ success: true, objId: orderObjid, orderNo });
|
||||
} catch (err) {
|
||||
await client.query("ROLLBACK");
|
||||
console.error("[orders/create-empty]", err);
|
||||
return NextResponse.json({ success: false, message: err instanceof Error ? err.message : "오류" }, { status: 500 });
|
||||
} finally {
|
||||
client.release();
|
||||
}
|
||||
}
|
||||
|
||||
async function genOrderNo(): Promise<string> {
|
||||
const today = new Date();
|
||||
const ymd = `${today.getFullYear()}${String(today.getMonth() + 1).padStart(2, "0")}${String(today.getDate()).padStart(2, "0")}`;
|
||||
const prefix = `ORD-${ymd}-`;
|
||||
const row = await queryOne<{ MAX_NO: string }>(
|
||||
`SELECT COALESCE(MAX(order_no), '') AS "MAX_NO" FROM momo_orders WHERE order_no LIKE $1 || '%'`,
|
||||
[prefix]
|
||||
);
|
||||
const last = row?.MAX_NO ?? "";
|
||||
const lastNum = last ? Number(last.replace(prefix, "")) || 0 : 0;
|
||||
return prefix + String(lastNum + 1).padStart(4, "0");
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
// 발주의 거래처 변경 — admin 전용, REQUESTED 또는 APPROVED 상태만.
|
||||
// 변경 시 supplier_branch snapshot 도 새 거래처의 기준 명세표로 재계산.
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
import { pool, queryOne } from "@/lib/db";
|
||||
import { requireMomoAdmin } from "@/lib/momo-guard";
|
||||
import { getSupplierByBranch } from "@/lib/momo-branches";
|
||||
|
||||
export async function POST(req: NextRequest) {
|
||||
const g = await requireMomoAdmin();
|
||||
if (g instanceof NextResponse) return g;
|
||||
|
||||
const { objid, customerObjid } = await req.json() as { objid?: string; customerObjid?: string };
|
||||
if (!objid || !customerObjid) {
|
||||
return NextResponse.json({ success: false, message: "필수 항목 누락" }, { status: 400 });
|
||||
}
|
||||
|
||||
// 거래처 존재 + statement_branch 조회
|
||||
const cust = await queryOne<{ statement_branch: string | null }>(
|
||||
`SELECT COALESCE(statement_branch, 'HQ') AS statement_branch FROM user_info WHERE user_id = $1`,
|
||||
[customerObjid]
|
||||
);
|
||||
if (!cust) {
|
||||
return NextResponse.json({ success: false, message: "거래처를 찾을 수 없습니다." }, { status: 404 });
|
||||
}
|
||||
const supplier = await getSupplierByBranch(cust.statement_branch ?? "HQ");
|
||||
|
||||
const cur = await pool.query(`SELECT status FROM momo_orders WHERE objid = $1`, [objid]);
|
||||
if (cur.rowCount === 0) return NextResponse.json({ success: false, message: "발주 없음" }, { status: 404 });
|
||||
if (!["REQUESTED", "APPROVED"].includes(cur.rows[0].status)) {
|
||||
return NextResponse.json({ success: false, message: "입금 전 발주만 거래처 변경 가능합니다." }, { status: 400 });
|
||||
}
|
||||
|
||||
await pool.query(
|
||||
`UPDATE momo_orders SET
|
||||
customer_objid = $2,
|
||||
supplier_branch = $3, supplier_name = $4, supplier_ceo = $5, supplier_bank_account = $6,
|
||||
supplier_phone = $7, supplier_email = $8, supplier_biz_no = $9, supplier_address = $10,
|
||||
update_date = NOW()
|
||||
WHERE objid = $1`,
|
||||
[objid, customerObjid,
|
||||
supplier.CODE, supplier.NAME, supplier.CEO, supplier.BANK_ACCOUNT,
|
||||
supplier.PHONE, supplier.EMAIL, supplier.BIZ_NO, supplier.ADDRESS]
|
||||
);
|
||||
return NextResponse.json({ success: true });
|
||||
}
|
||||
Reference in New Issue
Block a user