Initial import of AbelBirdNest Stock
This commit is contained in:
104
src/app/api/v1/receipts/route.ts
Normal file
104
src/app/api/v1/receipts/route.ts
Normal file
@ -0,0 +1,104 @@
|
||||
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: 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 receipt = await prisma.receipt.create({
|
||||
data: {
|
||||
receiptNo,
|
||||
purchaseId: BigInt(parsed.data.purchase_id),
|
||||
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 });
|
||||
}
|
||||
Reference in New Issue
Block a user