From 44146a4a222e97796c7e7d12a64c7eee3e3c8ec6 Mon Sep 17 00:00:00 2001 From: Wira Irawan Date: Tue, 26 May 2026 12:39:20 +0700 Subject: [PATCH] Split purchase submit from receipt finalization --- src/app/api/v1/purchases/[id]/route.ts | 26 +- src/app/api/v1/purchases/[id]/submit/route.ts | 340 ++++-------------- src/app/api/v1/purchases/route.ts | 6 + .../v1/receipts/[id]/generate-lots/route.ts | 220 ++++++++---- src/app/api/v1/receipts/route.ts | 33 +- .../purchases/components/purchases-client.tsx | 4 +- .../purchases/lib/serialize-purchase.ts | 6 + src/types/purchase.ts | 2 + 8 files changed, 301 insertions(+), 336 deletions(-) diff --git a/src/app/api/v1/purchases/[id]/route.ts b/src/app/api/v1/purchases/[id]/route.ts index 2ca9c04..9e06bf0 100644 --- a/src/app/api/v1/purchases/[id]/route.ts +++ b/src/app/api/v1/purchases/[id]/route.ts @@ -130,12 +130,27 @@ export async function PUT(request: Request, context: RouteContext) { const existing = await prisma.purchase.findUnique({ where: { id: parsedId }, include: { - lines: true + lines: true, + _count: { + select: { + receipts: true, + lots: true + } + } } }); if (!existing) { return NextResponse.json({ message: "Purchase not found" }, { status: 404 }); } + if (existing._count.receipts > 0 || existing._count.lots > 0) { + return NextResponse.json( + { + message: + "Purchase sudah memiliki receipt/lot, sehingga baris pembelian tidak bisa diubah dari form purchase. Gunakan koreksi stok atau buat purchase baru untuk perubahan setelah barang diterima." + }, + { status: 409 } + ); + } const purchaseDate = toDateOrThrow(`${payload.purchase_date}T00:00:00.000Z`, "Tanggal pembelian"); const receivedAt = toDateOrThrow(payload.received_at, "Waktu diterima"); @@ -268,6 +283,15 @@ export async function PUT(request: Request, context: RouteContext) { return NextResponse.json({ data: serializePurchaseDetail(purchase) }); } catch (error) { + if (error instanceof Prisma.PrismaClientKnownRequestError && error.code === "P2003") { + return NextResponse.json( + { + message: + "Purchase masih terkait receipt/lot, sehingga baris pembelian tidak bisa diubah dari form purchase." + }, + { status: 409 } + ); + } if (error instanceof Prisma.PrismaClientKnownRequestError || error instanceof Prisma.PrismaClientValidationError) { return NextResponse.json({ message: error.message }, { status: 400 }); } diff --git a/src/app/api/v1/purchases/[id]/submit/route.ts b/src/app/api/v1/purchases/[id]/submit/route.ts index 5aa4e80..01ed2f3 100644 --- a/src/app/api/v1/purchases/[id]/submit/route.ts +++ b/src/app/api/v1/purchases/[id]/submit/route.ts @@ -1,20 +1,12 @@ import { NextResponse } from "next/server"; -import { Prisma } from "@prisma/client"; import { createAuditTrailSafe } from "@/lib/audit-trail"; import { buildAuditChangeMetadata } from "@/lib/audit-trail-diff"; import { requireApiAccess } from "@/lib/authorization"; import { prisma } from "@/lib/prisma"; -import { recalculatePurchaseRealizationSummary } from "@/features/purchase-realization/lib/recalculate-purchase-realization-summary"; -import { generateLotCode } from "@/features/receipts/lib/generate-lot-code"; -import { generateReceiptNo } from "@/features/receipts/lib/generate-receipt-no"; type RouteContext = { params: Promise<{ id: string }> }; -type SubmitTx = Prisma.TransactionClient & { - lotPurchaseAllocation: typeof prisma.lotPurchaseAllocation; - purchaseRealizationEntry: typeof prisma.purchaseRealizationEntry; - purchaseRealizationSummary: typeof prisma.purchaseRealizationSummary; -}; + const parseId = (id: string) => { try { return BigInt(id); @@ -30,271 +22,75 @@ export async function POST(request: Request, context: RouteContext) { const parsedId = parseId((await context.params).id); if (parsedId === null) return NextResponse.json({ message: "Invalid id" }, { status: 400 }); - try { - const purchaseId = parsedId; - const purchase = await prisma.purchase.findUnique({ - where: { id: purchaseId }, - include: { - agent: { select: { id: true, name: true } }, - profitShareScheme: { select: { id: true, shareAgent: true } }, - receipts: { - select: { - id: true, - receiptNo: true - } - }, - lines: { - include: { - grade: true, - unit: true, - warehouse: true, - warehouseLocation: true - } + const purchase = await prisma.purchase.findUnique({ + where: { id: parsedId }, + include: { + _count: { + select: { + receipts: true, + lots: true } - } - }); - if (!purchase) return NextResponse.json({ message: "Purchase not found" }, { status: 404 }); - - if (purchase.status === "SUBMITTED") { - return NextResponse.json({ message: "Purchase sudah disubmit" }, { status: 409 }); - } - - if (purchase.receipts.length > 0) { - return NextResponse.json( - { message: `Purchase sudah memiliki receipt ${purchase.receipts[0]?.receiptNo ?? ""}`.trim() }, - { status: 409 } - ); - } - - if (!purchase.receivedByEmployeeId) { - return NextResponse.json({ message: "Penerima belum dipilih" }, { status: 400 }); - } - - if (!purchase.receivedAt) { - return NextResponse.json({ message: "Waktu diterima belum diisi" }, { status: 400 }); - } - - const receivedAt = purchase.receivedAt; - const receiptDate = new Date(receivedAt.toISOString().slice(0, 10)); - const receiptNo = await generateReceiptNo(receiptDate); - const sourceCode = purchase.agent?.name || purchase.purchaseNo; - const enteredQtyLines = purchase.lines.filter((line) => Number(line.qtyAccepted.toNumber()) > 0); - if (enteredQtyLines.length === 0) { - return NextResponse.json( - { message: "Tidak ada baris dengan qty yang masuk > 0 untuk generate lot" }, - { status: 400 } - ); - } - const missingWarehouseLine = enteredQtyLines.find((line) => !line.warehouseId); - if (missingWarehouseLine) { - return NextResponse.json( - { message: "Warehouse belum diisi pada salah satu baris pembelian" }, - { status: 400 } - ); - } - - const generated = await prisma.$transaction(async (rawTx) => { - const tx = rawTx as SubmitTx; - const baseLotCode = await generateLotCode(receivedAt, sourceCode); - const codeMatch = baseLotCode.match(/-(\d+)$/); - if (!codeMatch) { - throw new Error("Gagal generate kode lot"); - } - const lotPrefix = baseLotCode.replace(/-\d+$/, ""); - let lotSuffix = Number(codeMatch[1] ?? "0") || 1; - - const availableLotCount = await tx.inventoryLot.count({ where: { purchaseId } }); - if (availableLotCount > 0) { - throw new Error("Lot untuk purchase ini sudah pernah digenerate"); - } - - const existingReceiptCount = await tx.receipt.count({ where: { purchaseId } }); - if (existingReceiptCount > 0) { - throw new Error("Receipt untuk purchase ini sudah pernah dibuat"); - } - - const receipt = await tx.receipt.create({ - data: { - receiptNo, - purchaseId: purchase.id, - receiptDate, - status: "FINALIZED", - notes: purchase.notes || null, - receivedById: BigInt(auth.user.id) - } - }); - - await tx.receiptLine.createMany({ - data: enteredQtyLines.map((line) => ({ - receiptId: receipt.id, - purchaseLineId: line.id, - gradeId: line.gradeId, - qtyReceived: line.qtyReceived, - qtyAccepted: line.qtyAccepted, - qtyRejected: line.qtyRejected, - unitId: line.unitId, - unitCost: line.unitCost, - warehouseId: line.warehouseId!, - warehouseLocationId: line.warehouseLocationId, - notes: line.notes || null - })) - }); - - const receiptLines = await tx.receiptLine.findMany({ - where: { receiptId: receipt.id }, - orderBy: { id: "asc" } - }); - - const lots = []; - for (const line of receiptLines) { - if (!line.warehouseId) { - throw new Error("Warehouse belum diisi pada salah satu baris pembelian"); - } - const availableQty = Number(line.qtyAccepted.toNumber()); - const lotCode = `${lotPrefix}-${String(lotSuffix).padStart(3, "0")}`; - lotSuffix += 1; - - const lot = await tx.inventoryLot.create({ - data: { - lotCode, - sourceType: "PURCHASE", - sourceRefId: purchaseId, - purchaseId: purchase.id, - purchaseLineId: line.purchaseLineId, - receiptId: receipt.id, - receiptLineId: line.id, - gradeId: line.gradeId, - warehouseId: line.warehouseId, - warehouseLocationId: line.warehouseLocationId, - originalQty: availableQty, - availableQty, - unitId: line.unitId, - unitCost: line.unitCost, - receivedAt, - status: "ACTIVE", - qrCodeValue: lotCode, - barcodeValue: lotCode - } - }); - - const costTotalAllocated = Number( - (availableQty * line.unitCost.toNumber()).toFixed(2) - ); - const allocation = await tx.lotPurchaseAllocation.create({ - data: { - lotId: lot.id, - purchaseId: purchase.id, - purchaseLineId: line.id, - sourceType: "PURCHASE", - sourceRefId: purchase.id, - agentIdSnapshot: purchase.agentId, - profitShareSchemeIdSnapshot: purchase.profitShareSchemeId, - qtyAllocated: new Prisma.Decimal(availableQty), - costTotalAllocated: new Prisma.Decimal(costTotalAllocated), - unitCostSnapshot: line.unitCost - } - }); - - await tx.purchaseRealizationEntry.create({ - data: { - purchaseId: purchase.id, - lotId: lot.id, - allocationId: allocation.id, - eventType: "OPENING_COST", - referenceType: "PURCHASE", - referenceId: purchase.id, - occurredAt: receivedAt, - qtyIn: new Prisma.Decimal(availableQty), - qtyOut: new Prisma.Decimal(0), - qtyShrinkage: new Prisma.Decimal(0), - amountCost: new Prisma.Decimal(costTotalAllocated), - amountRevenue: new Prisma.Decimal(0), - amountExpense: new Prisma.Decimal(0), - amountProfit: new Prisma.Decimal(0), - agentSharePercentSnapshot: purchase.profitShareScheme?.shareAgent ?? null, - agentAmount: new Prisma.Decimal(0), - notes: `Opening realization dari purchase line ${line.purchaseLineId.toString()}` - } - }); - - lots.push({ - id: lot.id.toString(), - lot_code: lot.lotCode - }); - } - - const updated = await tx.purchase.update({ - where: { id: purchaseId }, - data: { status: "SUBMITTED" } - }); - - await recalculatePurchaseRealizationSummary( - tx, - purchase.id, - purchase.profitShareScheme?.shareAgent.toNumber() ?? null - ); - - return { - purchase: updated, - receipt: { - id: receipt.id.toString(), - receipt_no: receipt.receiptNo - }, - lots - }; - }); - - await createAuditTrailSafe({ - userId: auth.user.id, - action: "PURCHASE_SUBMITTED", - entityType: "PURCHASE", - entityId: generated.purchase.id, - method: request.method, - pathname: new URL(request.url).pathname, - statusCode: 200, - summary: `Purchase ${purchase.purchaseNo} disubmit + receipt + lot dibuat`, - metadata: buildAuditChangeMetadata( - { status: purchase.status, lot_count: 0, receipt_no: null }, - { - status: generated.purchase.status, - lot_count: generated.lots.length, - receipt_no: generated.receipt.receipt_no - } - ) - }); - - if (generated.lots.length === 0) { - return NextResponse.json({ message: "Lot untuk purchase ini sudah pernah digenerate" }, { status: 409 }); - } - - return NextResponse.json( - { - success: true, - status: generated.purchase.status, - lot_count: generated.lots.length, - receipt: generated.receipt }, - { status: 200 } + lines: true + } + }); + + if (!purchase) return NextResponse.json({ message: "Purchase not found" }, { status: 404 }); + + if (purchase._count.receipts > 0 || purchase._count.lots > 0) { + return NextResponse.json( + { message: "Purchase sudah diterima dan memiliki receipt/lot." }, + { status: 409 } ); - } catch (error) { - if (error instanceof Prisma.PrismaClientKnownRequestError && error.code === "P2002") { - return NextResponse.json( - { message: "Kode lot duplikat saat submit. Coba lagi." }, - { status: 409 } - ); - } - if (error instanceof Error) { - console.error("PURCHASE_SUBMIT_ERROR", { - purchaseId: parsedId?.toString(), - message: error.message, - stack: error.stack - }); - return NextResponse.json({ message: error.message }, { status: 400 }); - } - console.error("PURCHASE_SUBMIT_ERROR", { - purchaseId: parsedId?.toString(), - message: "Unknown error" - }); - return NextResponse.json({ message: "Submit gagal karena kesalahan internal" }, { status: 500 }); } + + if (!purchase.receivedByEmployeeId) { + return NextResponse.json({ message: "Penerima belum dipilih" }, { status: 400 }); + } + + if (!purchase.receivedAt) { + return NextResponse.json({ message: "Waktu diterima belum diisi" }, { status: 400 }); + } + + const enteredQtyLines = purchase.lines.filter((line) => Number(line.qtyAccepted.toNumber()) > 0); + if (enteredQtyLines.length === 0) { + return NextResponse.json( + { message: "Tidak ada baris dengan qty yang masuk > 0 untuk diajukan" }, + { status: 400 } + ); + } + + if (purchase.status === "SUBMITTED") { + return NextResponse.json({ + success: true, + status: purchase.status, + message: "Purchase sudah diajukan dan menunggu penerimaan." + }); + } + + const updated = await prisma.purchase.update({ + where: { id: parsedId }, + data: { status: "SUBMITTED" } + }); + + await createAuditTrailSafe({ + userId: auth.user.id, + action: "PURCHASE_SUBMITTED", + entityType: "PURCHASE", + entityId: updated.id, + method: request.method, + pathname: new URL(request.url).pathname, + statusCode: 200, + summary: `Purchase ${updated.purchaseNo} diajukan untuk penerimaan`, + metadata: buildAuditChangeMetadata( + { status: purchase.status }, + { status: updated.status } + ) + }); + + return NextResponse.json({ + success: true, + status: updated.status, + message: "Purchase diajukan. Buat receipt dan lot dari menu Penerimaan." + }); } diff --git a/src/app/api/v1/purchases/route.ts b/src/app/api/v1/purchases/route.ts index da22899..9dcc042 100644 --- a/src/app/api/v1/purchases/route.ts +++ b/src/app/api/v1/purchases/route.ts @@ -84,6 +84,12 @@ export async function GET(request: Request) { select: { subtotal: true } + }, + _count: { + select: { + receipts: true, + lots: true + } } }, orderBy: [{ createdAt: "desc" }] diff --git a/src/app/api/v1/receipts/[id]/generate-lots/route.ts b/src/app/api/v1/receipts/[id]/generate-lots/route.ts index 4e5604c..feb726f 100644 --- a/src/app/api/v1/receipts/[id]/generate-lots/route.ts +++ b/src/app/api/v1/receipts/[id]/generate-lots/route.ts @@ -1,11 +1,20 @@ import { NextResponse } from "next/server"; +import { Prisma } from "@prisma/client"; +import { recalculatePurchaseRealizationSummary } from "@/features/purchase-realization/lib/recalculate-purchase-realization-summary"; import { generateLotCode } from "@/features/receipts/lib/generate-lot-code"; import { createAuditTrailSafe } from "@/lib/audit-trail"; import { requireApiAccess } from "@/lib/authorization"; import { prisma } from "@/lib/prisma"; type RouteContext = { params: Promise<{ id: string }> }; + +type GenerateLotsTx = Prisma.TransactionClient & { + lotPurchaseAllocation: typeof prisma.lotPurchaseAllocation; + purchaseRealizationEntry: typeof prisma.purchaseRealizationEntry; + purchaseRealizationSummary: typeof prisma.purchaseRealizationSummary; +}; + const parseId = (id: string) => { try { return BigInt(id); @@ -26,7 +35,12 @@ export async function POST(request: Request, context: RouteContext) { const existingReceipt = await prisma.receipt.findUnique({ where: { id: parsedId }, include: { - purchase: true, + purchase: { + include: { + agent: { select: { id: true, name: true } }, + profitShareScheme: { select: { id: true, shareAgent: true } } + } + }, lines: true, lots: true } @@ -43,72 +57,156 @@ export async function POST(request: Request, context: RouteContext) { ); } - const generated = await prisma.$transaction(async (tx) => { - const lots = []; - for (const line of existingReceipt.lines) { - if (Number(line.qtyAccepted) <= 0) { - continue; + const acceptedLines = existingReceipt.lines.filter((line) => Number(line.qtyAccepted.toNumber()) > 0); + if (acceptedLines.length === 0) { + return NextResponse.json( + { message: "Tidak ada baris receipt dengan qty accepted > 0." }, + { status: 400 } + ); + } + + try { + const generated = await prisma.$transaction(async (rawTx) => { + const tx = rawTx as GenerateLotsTx; + const sourceCode = existingReceipt.purchase.agent?.name || existingReceipt.purchase.purchaseNo; + const baseLotCode = await generateLotCode(existingReceipt.receiptDate, sourceCode); + const codeMatch = baseLotCode.match(/-(\d+)$/); + if (!codeMatch) { + throw new Error("Gagal generate kode lot"); + } + const lotPrefix = baseLotCode.replace(/-\d+$/, ""); + let lotSuffix = Number(codeMatch[1] ?? "0") || 1; + + const existingLotCount = await tx.inventoryLot.count({ + where: { receiptId: existingReceipt.id } + }); + if (existingLotCount > 0) { + throw new Error("Lot untuk receipt ini sudah pernah dibuat"); } - const lotCode = await generateLotCode( - existingReceipt.receiptDate, - existingReceipt.purchaseId.toString() + const lots = []; + for (const line of acceptedLines) { + const availableQty = Number(line.qtyAccepted.toNumber()); + const lotCode = `${lotPrefix}-${String(lotSuffix).padStart(3, "0")}`; + lotSuffix += 1; + + const lot = await tx.inventoryLot.create({ + data: { + lotCode, + sourceType: "RECEIPT", + sourceRefId: line.id, + purchaseId: existingReceipt.purchaseId, + purchaseLineId: line.purchaseLineId, + receiptId: existingReceipt.id, + receiptLineId: line.id, + gradeId: line.gradeId, + warehouseId: line.warehouseId, + warehouseLocationId: line.warehouseLocationId, + originalQty: line.qtyAccepted, + availableQty: line.qtyAccepted, + unitId: line.unitId, + unitCost: line.unitCost, + receivedAt: existingReceipt.receiptDate, + status: "ACTIVE", + qrCodeValue: lotCode, + barcodeValue: lotCode + } + }); + + const costTotalAllocated = Number((availableQty * line.unitCost.toNumber()).toFixed(2)); + const allocation = await tx.lotPurchaseAllocation.create({ + data: { + lotId: lot.id, + purchaseId: existingReceipt.purchaseId, + purchaseLineId: line.purchaseLineId, + sourceType: "RECEIPT", + sourceRefId: existingReceipt.id, + agentIdSnapshot: existingReceipt.purchase.agentId, + profitShareSchemeIdSnapshot: existingReceipt.purchase.profitShareSchemeId, + qtyAllocated: new Prisma.Decimal(availableQty), + costTotalAllocated: new Prisma.Decimal(costTotalAllocated), + unitCostSnapshot: line.unitCost + } + }); + + await tx.purchaseRealizationEntry.create({ + data: { + purchaseId: existingReceipt.purchaseId, + lotId: lot.id, + allocationId: allocation.id, + eventType: "OPENING_COST", + referenceType: "RECEIPT", + referenceId: existingReceipt.id, + occurredAt: existingReceipt.receiptDate, + qtyIn: new Prisma.Decimal(availableQty), + qtyOut: new Prisma.Decimal(0), + qtyShrinkage: new Prisma.Decimal(0), + amountCost: new Prisma.Decimal(costTotalAllocated), + amountRevenue: new Prisma.Decimal(0), + amountExpense: new Prisma.Decimal(0), + amountProfit: new Prisma.Decimal(0), + agentSharePercentSnapshot: existingReceipt.purchase.profitShareScheme?.shareAgent ?? null, + agentAmount: new Prisma.Decimal(0), + notes: `Opening realization dari receipt line ${line.id.toString()}` + } + }); + + lots.push(lot); + } + + await tx.receipt.update({ + where: { id: existingReceipt.id }, + data: { status: "FINALIZED" } + }); + + await tx.purchase.update({ + where: { id: existingReceipt.purchaseId }, + data: { status: "SUBMITTED" } + }); + + await recalculatePurchaseRealizationSummary( + tx, + existingReceipt.purchaseId, + existingReceipt.purchase.profitShareScheme?.shareAgent.toNumber() ?? null ); - const lot = await tx.inventoryLot.create({ - data: { - lotCode, - sourceType: "RECEIPT", - sourceRefId: line.id, - purchaseId: existingReceipt.purchaseId, - purchaseLineId: line.purchaseLineId, - receiptId: existingReceipt.id, - receiptLineId: line.id, - gradeId: line.gradeId, - warehouseId: line.warehouseId, - warehouseLocationId: line.warehouseLocationId, - originalQty: line.qtyAccepted, - availableQty: line.qtyAccepted, - unitId: line.unitId, - unitCost: line.unitCost, - receivedAt: existingReceipt.receiptDate, - status: "ACTIVE", - qrCodeValue: lotCode, - barcodeValue: lotCode - } - }); - lots.push(lot); - } - - await tx.receipt.update({ - where: { id: existingReceipt.id }, - data: { status: "FINALIZED" } + return lots; }); - return lots; - }); + await createAuditTrailSafe({ + userId: auth.user.id, + action: "LOTS_GENERATED", + entityType: "RECEIPT", + entityId: existingReceipt.id, + method: request.method, + pathname: new URL(request.url).pathname, + statusCode: 200, + summary: `${generated.length} lot dibuat dari receipt ${existingReceipt.receiptNo}`, + metadata: { + receipt_no: existingReceipt.receiptNo, + purchase_no: existingReceipt.purchase.purchaseNo, + lot_codes: generated.map((lot) => lot.lotCode) + } + }); - await createAuditTrailSafe({ - userId: auth.user.id, - action: "LOTS_GENERATED", - entityType: "RECEIPT", - entityId: existingReceipt.id, - method: request.method, - pathname: new URL(request.url).pathname, - statusCode: 200, - summary: `${generated.length} lot dibuat dari receipt ${existingReceipt.receiptNo}`, - metadata: { - receipt_no: existingReceipt.receiptNo, - lot_codes: generated.map((lot) => lot.lotCode) + return NextResponse.json({ + receipt_id: existingReceipt.id.toString(), + lots: generated.map((lot) => ({ + id: lot.id.toString(), + lot_code: lot.lotCode, + qr_code_value: lot.qrCodeValue + })) + }); + } catch (error) { + if (error instanceof Prisma.PrismaClientKnownRequestError && error.code === "P2002") { + return NextResponse.json( + { message: "Kode lot duplikat saat membuat lot. Coba lagi." }, + { status: 409 } + ); } - }); - - return NextResponse.json({ - receipt_id: existingReceipt.id.toString(), - lots: generated.map((lot) => ({ - id: lot.id.toString(), - lot_code: lot.lotCode, - qr_code_value: lot.qrCodeValue - })) - }); + if (error instanceof Error) { + return NextResponse.json({ message: error.message }, { status: 400 }); + } + throw error; + } } diff --git a/src/app/api/v1/receipts/route.ts b/src/app/api/v1/receipts/route.ts index e0729af..d010bb4 100644 --- a/src/app/api/v1/receipts/route.ts +++ b/src/app/api/v1/receipts/route.ts @@ -55,11 +55,42 @@ export async function POST(request: Request) { 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: { + _count: { + select: { + receipts: true, + lots: true + } + } + } + }); + + if (!purchase) { + return NextResponse.json({ message: "Purchase not found" }, { status: 404 }); + } + + if (purchase.status !== "SUBMITTED") { + return NextResponse.json( + { message: "Purchase harus diajukan sebelum dibuatkan receipt." }, + { status: 409 } + ); + } + + if (purchase._count.receipts > 0 || purchase._count.lots > 0) { + return NextResponse.json( + { message: "Purchase sudah memiliki receipt/lot." }, + { status: 409 } + ); + } const receipt = await prisma.receipt.create({ data: { receiptNo, - purchaseId: BigInt(parsed.data.purchase_id), + purchaseId, receiptDate, status: "DRAFT", notes: parsed.data.notes || null, diff --git a/src/features/purchases/components/purchases-client.tsx b/src/features/purchases/components/purchases-client.tsx index 2f3a7e6..a4b99c9 100644 --- a/src/features/purchases/components/purchases-client.tsx +++ b/src/features/purchases/components/purchases-client.tsx @@ -1348,7 +1348,9 @@ export function PurchasesClient({ currencyCode }: PurchasesClientProps) {
- + {item.receipt_count === 0 && item.lot_count === 0 ? ( + + ) : null} {(item.status === "DRAFT" || item.status === "CANCELLED") ? (