diff --git a/db/migrations/014_menu_reorder.sql b/db/migrations/014_menu_reorder.sql new file mode 100644 index 0000000..901df8f --- /dev/null +++ b/db/migrations/014_menu_reorder.sql @@ -0,0 +1,33 @@ +-- 014_menu_reorder.sql +-- v0.6 (2026-05-07) +-- 메뉴 순서 재배치 + 대시보드 위치 이동 +-- +-- 변경 후 순서: +-- 600 거래처 주문 (9000100) +-- 700 매입/입고 (9000300) +-- 750 출고/정산 (9000400) +-- 800 통계 (9000500) ← 대시보드 자식으로 포함 +-- 900 마스터 관리 (9000200) ← 마지막 +-- +-- 대시보드(9000001) 는 [DASHBOARD] 대메뉴(1837127121) → 통계(9000500) 자식으로 이동 +-- [DASHBOARD] 대메뉴 자체는 비활성화 + +BEGIN; + +-- 1. 대메뉴 seq 재조정 — 마스터 관리를 맨 뒤로 +UPDATE menu_info SET seq = 900 WHERE objid = 9000200; -- 마스터 관리 +-- 거래처 주문(600), 매입/입고(700), 출고/정산(750), 통계(800)는 그대로 + +-- 2. 대시보드(9000001) 를 통계(9000500) 의 첫 자식으로 이동 +UPDATE menu_info + SET parent_obj_id = 9000500, + seq = 5, + menu_name_kor = '대시보드' + WHERE objid = 9000001; + +-- 3. 빈 [DASHBOARD] 대메뉴(1837127121) 비활성화 +UPDATE menu_info + SET status = 'inactive' + WHERE objid = 1837127121; + +COMMIT; diff --git a/src/app/(auth)/signup/page.tsx b/src/app/(auth)/signup/page.tsx index 7bdaa4d..70e6e99 100644 --- a/src/app/(auth)/signup/page.tsx +++ b/src/app/(auth)/signup/page.tsx @@ -61,7 +61,8 @@ export default function SignupPage() { text: "이제 발주를 시작하실 수 있습니다.", confirmButtonColor: "#0f766e", }); - router.push("/m/dashboard"); + // 거래처 가입 직후 — 출고 요청 화면으로 + router.push("/m/orders/new"); } else { Swal.fire({ icon: "error", title: "가입 실패", text: data.message }); } diff --git a/src/app/(main)/m/admin/orders/page.tsx b/src/app/(main)/m/admin/orders/page.tsx index 18ae740..0f7f9ec 100644 --- a/src/app/(main)/m/admin/orders/page.tsx +++ b/src/app/(main)/m/admin/orders/page.tsx @@ -441,7 +441,8 @@ function StatementPreview({ - {items.map((it) => { + {items.map((it, idx) => { + const displaySeq = idx + 1; const isExtra = it.KIND === "DELIVERY" || it.KIND === "CHARTER"; const lack = !isExtra && Number(it.STOCK_QTY) < Number(it.QTY); const kindBadge = it.KIND === "DELIVERY" ? "택배" : it.KIND === "CHARTER" ? "용차" : null; @@ -452,6 +453,7 @@ function StatementPreview({ upsertExtra({ objid: it.OBJID, kind: it.KIND as "DELIVERY" | "CHARTER", ...updated })} onDelete={() => deleteExtra(it.OBJID)} /> @@ -460,7 +462,7 @@ function StatementPreview({ return ( - {it.SEQ} + {displaySeq} {kindBadge && {kindBadge}} {it.ITEM_NAME} @@ -524,8 +526,9 @@ function StatementPreview({ ); } -function ExtraRow({ line, onSave, onDelete }: { +function ExtraRow({ line, displaySeq, onSave, onDelete }: { line: DetailLine; + displaySeq: number; onSave: (data: { label: string; unitPrice: number; qty: number }) => void; onDelete: () => void; }) { @@ -542,7 +545,7 @@ function ExtraRow({ line, onSave, onDelete }: { return ( - {line.SEQ} + {displaySeq} {isDelivery ? "택배" : "용차"} diff --git a/src/app/api/auth/login/route.ts b/src/app/api/auth/login/route.ts index e96731d..9291a63 100644 --- a/src/app/api/auth/login/route.ts +++ b/src/app/api/auth/login/route.ts @@ -18,12 +18,20 @@ export async function POST(request: NextRequest) { // 이메일 형태이면 MOMO 사용자 우선 시도, 그 외에는 FITO 우선 시도 const looksLikeEmail = /@/.test(userId); + // 역할별 진입 페이지: + // ADMIN(모모유통 임직원/시스템 관리자) → /m/admin/orders (발주서 관리·출고처리) + // USER (거래처) → /m/orders/new (출고 요청) + const landingFor = (u: User): string => + (u.isAdmin || u.role === "ADMIN" || u.userType === "A") + ? "/m/admin/orders" + : "/m/orders/new"; + if (looksLikeEmail) { const momo = await verifyMomoCredentials(userId, password); if (momo.success && momo.user) { const sessionUser: User = momoToSessionUser(momo.user); await createSession(sessionUser); - return NextResponse.json({ success: true, user: sessionUser, redirectTo: "/m/dashboard" }); + return NextResponse.json({ success: true, user: sessionUser, redirectTo: landingFor(sessionUser) }); } // MOMO 실패 시 FITO 폴백 시도 (관리자 마이그레이션 케이스) } @@ -31,8 +39,7 @@ export async function POST(request: NextRequest) { const fito = await verifyCredentials(userId, password); if (fito.success && fito.user) { await createSession(fito.user); - // 모모유통 도메인은 모두 모모 대시보드로 (FITO admin 도 /m/dashboard 로 진입) - return NextResponse.json({ success: true, user: fito.user, redirectTo: "/m/dashboard" }); + return NextResponse.json({ success: true, user: fito.user, redirectTo: landingFor(fito.user) }); } // FITO 도 실패하면 MOMO를 한 번 더 시도 (이메일 형태가 아니지만 MOMO 계정인 경우) @@ -41,7 +48,7 @@ export async function POST(request: NextRequest) { if (momo.success && momo.user) { const sessionUser: User = momoToSessionUser(momo.user); await createSession(sessionUser); - return NextResponse.json({ success: true, user: sessionUser, redirectTo: "/m/dashboard" }); + return NextResponse.json({ success: true, user: sessionUser, redirectTo: landingFor(sessionUser) }); } } diff --git a/src/app/api/m/orders/detail/route.ts b/src/app/api/m/orders/detail/route.ts index 9695d9c..4bc05b5 100644 --- a/src/app/api/m/orders/detail/route.ts +++ b/src/app/api/m/orders/detail/route.ts @@ -64,7 +64,13 @@ export async function POST(req: NextRequest) { FROM momo_order_items OI LEFT JOIN momo_items I ON OI.item_objid = I.objid WHERE OI.order_objid = $1 - ORDER BY OI.seq ASC`, + ORDER BY + CASE COALESCE(OI.kind,'ITEM') + WHEN 'DELIVERY' THEN 0 + WHEN 'CHARTER' THEN 1 + ELSE 2 + END, + OI.seq ASC`, [objid] ); diff --git a/src/app/api/m/orders/statement/[id]/route.ts b/src/app/api/m/orders/statement/[id]/route.ts index 2f340a6..615cef4 100644 --- a/src/app/api/m/orders/statement/[id]/route.ts +++ b/src/app/api/m/orders/statement/[id]/route.ts @@ -26,8 +26,16 @@ export async function GET(req: NextRequest, ctx: { params: Promise<{ id: string const items = await queryRows>( `SELECT seq, item_name_snap, unit_price, qty, is_tax_free, supply_amount, vat_amount, total_amount, + COALESCE(kind, 'ITEM') AS kind, extra_label, (SELECT unit FROM momo_items WHERE objid = OI.item_objid) AS unit - FROM momo_order_items OI WHERE order_objid = $1 ORDER BY seq`, + FROM momo_order_items OI WHERE order_objid = $1 + ORDER BY + CASE COALESCE(kind,'ITEM') + WHEN 'DELIVERY' THEN 0 + WHEN 'CHARTER' THEN 1 + ELSE 2 + END, + seq ASC`, [id] ); @@ -46,8 +54,8 @@ export async function GET(req: NextRequest, ctx: { params: Promise<{ id: string phone: process.env.MOMO_PHONE ?? "010-6624-5315", email: process.env.SMTP_FROM ?? "momo8443@daum.net", }, - items: items.map((it) => ({ - seq: Number(it.seq), + items: items.map((it, idx) => ({ + seq: idx + 1, itemName: String(it.item_name_snap), unit: String(it.unit ?? "EA"), qty: Number(it.qty),