Files
distribution_erp/src/app/api/m/inventory/inbound/route.ts
T
chpark d6f80c187b feat(inventory): 매입 입고에서 음수 수량 허용 — 재고 차감 가능
- 입력 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>
2026-05-31 22:20:04 +09:00

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();
}
}