d6f80c187b
- 입력 input의 min={1} 제거, 0만 막고 음수는 허용
- 입고 라인 표시: 양수 +N(에메랄드) / 음수 -N(로즈) 색상 구분
- API: ln.qty<=0 → ln.qty===0 만 skip. move_type 을 qty 부호로 IN/OUT 분기
(qty 컬럼은 부호 그대로 — 기존 stock_moves 컨벤션 일치)
- 음수 라인은 cost_price 미갱신
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
62 lines
2.5 KiB
TypeScript
62 lines
2.5 KiB
TypeScript
// 매입 입고 (단순) — 창고/품목/수량 입력하면 재고 + 이력
|
|
import { NextRequest, NextResponse } from "next/server";
|
|
import { pool } from "@/lib/db";
|
|
import { createObjectId } from "@/lib/utils";
|
|
import { requireMomoAdmin } from "@/lib/momo-guard";
|
|
|
|
interface Line {
|
|
itemObjid: string;
|
|
qty: number;
|
|
costPrice?: number;
|
|
}
|
|
|
|
export async function POST(req: NextRequest) {
|
|
const g = await requireMomoAdmin();
|
|
if (g instanceof NextResponse) return g;
|
|
const userId = g.user.objid || g.user.userId;
|
|
|
|
const { whObjid, lines, memo } = await req.json() as { whObjid: string; lines: Line[]; memo?: string };
|
|
if (!whObjid || !Array.isArray(lines) || lines.length === 0) {
|
|
return NextResponse.json({ success: false, message: "창고와 품목 라인을 입력하세요." }, { status: 400 });
|
|
}
|
|
|
|
const client = await pool.connect();
|
|
try {
|
|
await client.query("BEGIN");
|
|
for (const ln of lines) {
|
|
// 0 만 skip — 음수는 차감(재고 조정 출고)로 허용
|
|
if (!ln.itemObjid || !ln.qty || ln.qty === 0) continue;
|
|
|
|
// upsert stock — 음수면 자연스럽게 차감 (qty + EXCLUDED.qty)
|
|
await client.query(
|
|
`INSERT INTO momo_stocks (objid, wh_objid, item_objid, qty, update_date)
|
|
VALUES ($1, $2, $3, $4, NOW())
|
|
ON CONFLICT (wh_objid, item_objid) DO UPDATE
|
|
SET qty = momo_stocks.qty + EXCLUDED.qty, update_date = NOW()`,
|
|
[createObjectId(), whObjid, ln.itemObjid, ln.qty]
|
|
);
|
|
|
|
// log — 부호로 IN/OUT 분기. qty 컬럼은 부호 그대로(OUT 은 음수, IN 은 양수)
|
|
const moveType = ln.qty > 0 ? "IN" : "OUT";
|
|
await client.query(
|
|
`INSERT INTO momo_stock_moves (objid, wh_objid, item_objid, move_type, qty, ref_type, memo, regdate, regid)
|
|
VALUES ($1, $2, $3, $4, $5, 'PROCUREMENT', $6, NOW(), $7)`,
|
|
[createObjectId(), whObjid, ln.itemObjid, moveType, ln.qty, memo ?? null, userId]
|
|
);
|
|
|
|
// 매입가가 들어오면 items.cost_price 도 갱신 (입고 시에만)
|
|
if (ln.qty > 0 && ln.costPrice && ln.costPrice > 0) {
|
|
await client.query(`UPDATE momo_items SET cost_price = $2 WHERE objid = $1`, [ln.itemObjid, ln.costPrice]);
|
|
}
|
|
}
|
|
await client.query("COMMIT");
|
|
return NextResponse.json({ success: true });
|
|
} catch (err) {
|
|
await client.query("ROLLBACK");
|
|
console.error("[inbound]", err);
|
|
return NextResponse.json({ success: false, message: "입고 처리 중 오류가 발생했습니다." }, { status: 500 });
|
|
} finally {
|
|
client.release();
|
|
}
|
|
}
|