import { NextResponse } from "next/server"; import { ensureSystemUser } from "@/features/purchases/lib/system-user"; import { generateReceiptNo } from "@/features/receipts/lib/generate-receipt-no"; import { serializeReceiptDetail, serializeReceiptListItem } from "@/features/receipts/lib/serialize-receipt"; import { receiptInputSchema } from "@/features/receipts/schemas/receipt.schema"; import { createAuditTrailSafe } from "@/lib/audit-trail"; import { prisma } from "@/lib/prisma"; import { requireApiAccess } from "@/lib/authorization"; const receiptDetailInclude = { purchase: true, lines: { include: { grade: true, unit: true, warehouse: true, warehouseLocation: true } }, lots: { include: { grade: true, unit: true, warehouse: true, warehouseLocation: true } } } as const; export async function GET(request: Request) { const auth = requireApiAccess(request); if (!auth.ok) return auth.response; const data = await prisma.receipt.findMany({ include: { purchase: true, lines: true, lots: true }, orderBy: [{ createdAt: "desc" }] }); return NextResponse.json({ data: data.map(serializeReceiptListItem) }); } export async function POST(request: Request) { const auth = requireApiAccess(request); if (!auth.ok) return auth.response; const parsed = receiptInputSchema.safeParse(await request.json()); if (!parsed.success) { return NextResponse.json( { message: "Validasi gagal", errors: parsed.error.flatten().fieldErrors }, { status: 400 } ); } const systemUser = await ensureSystemUser(); const receiptDate = new Date(`${parsed.data.receipt_date}T00:00:00.000Z`); const receiptNo = await generateReceiptNo(receiptDate); const purchaseId = BigInt(parsed.data.purchase_id); const purchase = await prisma.purchase.findUnique({ where: { id: purchaseId }, include: { receipts: { select: { id: true, receiptNo: true } }, lots: { select: { id: true } } } }); if (!purchase) { return NextResponse.json({ message: "Purchase not found" }, { status: 404 }); } if (purchase.status === "CANCELLED") { return NextResponse.json({ message: "Purchase yang dibatalkan tidak bisa dibuatkan receipt" }, { status: 422 }); } if (purchase.receipts.length > 0 || purchase.lots.length > 0) { return NextResponse.json( { message: `Purchase sudah memiliki receipt/lot ${purchase.receipts[0]?.receiptNo ?? ""}`.trim() }, { status: 409 } ); } const receipt = await prisma.receipt.create({ data: { receiptNo, purchaseId, receiptDate, status: "DRAFT", notes: parsed.data.notes || null, receivedById: systemUser.id, lines: { create: parsed.data.lines.map((line) => ({ purchaseLineId: BigInt(line.purchase_line_id), gradeId: line.grade_id ? BigInt(line.grade_id) : null, qtyReceived: line.qty_received, qtyAccepted: line.qty_accepted, qtyRejected: line.qty_rejected, unitId: BigInt(line.unit_id), unitCost: line.unit_cost, warehouseId: BigInt(line.warehouse_id), warehouseLocationId: line.warehouse_location_id ? BigInt(line.warehouse_location_id) : null, notes: line.notes || null })) } }, include: receiptDetailInclude }); await createAuditTrailSafe({ userId: auth.user.id, action: "RECEIPT_CREATED", entityType: "RECEIPT", entityId: receipt.id, method: request.method, pathname: new URL(request.url).pathname, statusCode: 201, summary: `Receipt ${receipt.receiptNo} dibuat`, metadata: { receipt_no: receipt.receiptNo, purchase_no: receipt.purchase.purchaseNo, line_count: receipt.lines.length } }); return NextResponse.json({ data: serializeReceiptDetail(receipt) }, { status: 201 }); }