chore: upload parte 1 - logica
DevSecOps Enterprise Pipeline / security-gate-and-deploy (push) Failing after 41s
DevSecOps Enterprise Pipeline / security-gate-and-deploy (push) Failing after 41s
This commit is contained in:
@@ -0,0 +1,92 @@
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
import { getDb } from "@/lib/db";
|
||||
|
||||
export async function GET(
|
||||
_request: NextRequest,
|
||||
{ params }: { params: Promise<{ id: string }> }
|
||||
) {
|
||||
try {
|
||||
const sql = getDb();
|
||||
const { id } = await params;
|
||||
|
||||
const plRows = await sql(
|
||||
`SELECT pl.*,
|
||||
po.po_number,
|
||||
po.customer_ref,
|
||||
po.order_date,
|
||||
c.name AS customer_name,
|
||||
c.code AS customer_code,
|
||||
c.email AS customer_email,
|
||||
c.phone AS customer_phone,
|
||||
c.address AS customer_address,
|
||||
c.contact_person AS customer_contact,
|
||||
p.name AS product_name,
|
||||
p.sku AS product_sku
|
||||
FROM packing_lists pl
|
||||
JOIN purchase_orders po ON po.id = pl.purchase_order_id
|
||||
JOIN customers c ON c.id = po.customer_id
|
||||
JOIN purchase_order_items poi ON poi.id = pl.po_item_id
|
||||
JOIN products p ON p.id = poi.product_id
|
||||
WHERE pl.id = $1`,
|
||||
[Number(id)]
|
||||
);
|
||||
|
||||
if (plRows.length === 0) {
|
||||
return NextResponse.json({ error: "Packing list not found" }, { status: 404 });
|
||||
}
|
||||
|
||||
const serials = await sql(
|
||||
`SELECT * FROM packing_list_serials WHERE packing_list_id = $1 ORDER BY id ASC`,
|
||||
[Number(id)]
|
||||
);
|
||||
|
||||
return NextResponse.json({ ...plRows[0], serials });
|
||||
} catch (error) {
|
||||
console.error("[v0] Packing list detail GET error:", error);
|
||||
return NextResponse.json(
|
||||
{ error: error instanceof Error ? error.message : "Failed to fetch packing list" },
|
||||
{ status: 500 }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export async function DELETE(
|
||||
_request: NextRequest,
|
||||
{ params }: { params: Promise<{ id: string }> }
|
||||
) {
|
||||
try {
|
||||
const sql = getDb();
|
||||
const { id } = await params;
|
||||
|
||||
// Get the packing list details to reverse shipped_quantity
|
||||
const plRows = await sql(
|
||||
`SELECT * FROM packing_lists WHERE id = $1`,
|
||||
[Number(id)]
|
||||
);
|
||||
if (plRows.length === 0) {
|
||||
return NextResponse.json({ error: "Packing list not found" }, { status: 404 });
|
||||
}
|
||||
|
||||
const pl = plRows[0];
|
||||
|
||||
// Delete serials
|
||||
await sql(`DELETE FROM packing_list_serials WHERE packing_list_id = $1`, [Number(id)]);
|
||||
|
||||
// Delete packing list
|
||||
await sql(`DELETE FROM packing_lists WHERE id = $1`, [Number(id)]);
|
||||
|
||||
// Reverse the shipped_quantity
|
||||
await sql(
|
||||
`UPDATE purchase_order_items SET shipped_quantity = GREATEST(0, COALESCE(shipped_quantity, 0) - $1) WHERE id = $2`,
|
||||
[Number(pl.quantity), Number(pl.po_item_id)]
|
||||
);
|
||||
|
||||
return NextResponse.json({ success: true });
|
||||
} catch (error) {
|
||||
console.error("[v0] Packing list DELETE error:", error);
|
||||
return NextResponse.json(
|
||||
{ error: error instanceof Error ? error.message : "Failed to delete packing list" },
|
||||
{ status: 500 }
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,174 @@
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
import { getDb } from "@/lib/db";
|
||||
|
||||
export async function GET(request: NextRequest) {
|
||||
try {
|
||||
const sql = getDb();
|
||||
const { searchParams } = new URL(request.url);
|
||||
const search = searchParams.get("search");
|
||||
const orderId = searchParams.get("orderId");
|
||||
|
||||
let query = `
|
||||
SELECT pl.*,
|
||||
po.po_number,
|
||||
c.name AS customer_name,
|
||||
c.code AS customer_code,
|
||||
c.email AS customer_email,
|
||||
c.phone AS customer_phone,
|
||||
c.address AS customer_address,
|
||||
p.name AS product_name,
|
||||
p.sku AS product_sku,
|
||||
(SELECT COUNT(*) FROM packing_list_serials pls WHERE pls.packing_list_id = pl.id) AS serial_count
|
||||
FROM packing_lists pl
|
||||
JOIN purchase_orders po ON po.id = pl.purchase_order_id
|
||||
JOIN customers c ON c.id = po.customer_id
|
||||
JOIN purchase_order_items poi ON poi.id = pl.po_item_id
|
||||
JOIN products p ON p.id = poi.product_id
|
||||
`;
|
||||
const conditions: string[] = [];
|
||||
const params: (string | number)[] = [];
|
||||
|
||||
if (orderId) {
|
||||
params.push(Number(orderId));
|
||||
conditions.push(`pl.purchase_order_id = $${params.length}`);
|
||||
}
|
||||
if (search) {
|
||||
params.push(`%${search}%`);
|
||||
const i = params.length;
|
||||
conditions.push(
|
||||
`(pl.packing_number ILIKE $${i} OR po.po_number ILIKE $${i} OR c.name ILIKE $${i} OR p.name ILIKE $${i})`
|
||||
);
|
||||
}
|
||||
|
||||
if (conditions.length > 0) {
|
||||
query += ` WHERE ${conditions.join(" AND ")}`;
|
||||
}
|
||||
query += ` ORDER BY pl.created_at DESC`;
|
||||
|
||||
const rows = await sql(query, params);
|
||||
return NextResponse.json(rows);
|
||||
} catch (error) {
|
||||
console.error("[v0] Packing list GET error:", error);
|
||||
return NextResponse.json(
|
||||
{ error: error instanceof Error ? error.message : "Failed to fetch packing lists" },
|
||||
{ status: 500 }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export async function POST(request: NextRequest) {
|
||||
try {
|
||||
const sql = getDb();
|
||||
const body = await request.json();
|
||||
const { purchase_order_id, po_item_id, serial_numbers, notes } = body;
|
||||
|
||||
if (!purchase_order_id || !po_item_id || !serial_numbers?.length) {
|
||||
return NextResponse.json(
|
||||
{ error: "purchase_order_id, po_item_id, and serial_numbers are required" },
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
|
||||
const quantity = serial_numbers.length;
|
||||
|
||||
// Fetch customer name for packing number generation
|
||||
const custRows = await sql(
|
||||
`SELECT c.name FROM purchase_orders po JOIN customers c ON c.id = po.customer_id WHERE po.id = $1`,
|
||||
[purchase_order_id]
|
||||
);
|
||||
if (custRows.length === 0) {
|
||||
return NextResponse.json({ error: "Purchase order not found" }, { status: 404 });
|
||||
}
|
||||
const customerName = String(custRows[0].name).replace(/[^a-zA-Z0-9]/g, "").toUpperCase();
|
||||
|
||||
// Generate packing number: PL-EACH-YYYY_MM_DD-RR-CUSTOMERNAME
|
||||
const now = new Date();
|
||||
const yyyy = now.getFullYear();
|
||||
const mm = String(now.getMonth() + 1).padStart(2, "0");
|
||||
const dd = String(now.getDate()).padStart(2, "0");
|
||||
const datePrefix = `PL-EACH-${yyyy}_${mm}_${dd}`;
|
||||
const dateSuffix = `-${customerName}`;
|
||||
const pattern = `${datePrefix}-__-${customerName}`;
|
||||
|
||||
// Count existing packing lists with same date + customer to determine revision
|
||||
const existingRows = await sql(
|
||||
`SELECT COUNT(*) AS cnt FROM packing_lists WHERE packing_number LIKE $1`,
|
||||
[`${datePrefix}-%${dateSuffix}`]
|
||||
);
|
||||
const revision = Number(existingRows[0].cnt) + 1;
|
||||
const packing_number = `${datePrefix}-${String(revision).padStart(2, "0")}${dateSuffix}`;
|
||||
|
||||
// Check remaining capacity on the PO item
|
||||
const poItemRows = await sql(
|
||||
`SELECT poi.quantity, COALESCE(poi.shipped_quantity, 0) AS shipped_quantity, poi.product_id, p.name AS product_name
|
||||
FROM purchase_order_items poi
|
||||
JOIN products p ON p.id = poi.product_id
|
||||
WHERE poi.id = $1`,
|
||||
[po_item_id]
|
||||
);
|
||||
if (poItemRows.length === 0) {
|
||||
return NextResponse.json({ error: "PO item not found" }, { status: 404 });
|
||||
}
|
||||
const poItem = poItemRows[0];
|
||||
const productId = poItemRows[0].product_id;
|
||||
const remaining = Number(poItem.quantity) - Number(poItem.shipped_quantity);
|
||||
if (quantity > remaining) {
|
||||
return NextResponse.json(
|
||||
{ error: `Only ${remaining} units remaining to ship for ${poItem.product_name}` },
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
|
||||
// Check for duplicate serial numbers within the same Purchase Order
|
||||
if (serial_numbers.length > 0) {
|
||||
const placeholders = serial_numbers.map((_: string, i: number) => `$${i + 2}`).join(", ");
|
||||
const dupes = await sql(
|
||||
`SELECT pls.serial_number
|
||||
FROM packing_list_serials pls
|
||||
JOIN packing_lists pl ON pl.id = pls.packing_list_id
|
||||
WHERE pl.purchase_order_id = $1
|
||||
AND pls.serial_number IN (${placeholders})`,
|
||||
[purchase_order_id, ...serial_numbers]
|
||||
);
|
||||
if (dupes.length > 0) {
|
||||
const dupeList = dupes.map((d: { serial_number: string }) => d.serial_number).join(", ");
|
||||
return NextResponse.json(
|
||||
{ error: `Duplicate serial number(s) in this PO: ${dupeList}` },
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Create packing list
|
||||
const plRows = await sql(
|
||||
`INSERT INTO packing_lists (purchase_order_id, po_item_id, packing_number, quantity, notes)
|
||||
VALUES ($1, $2, $3, $4, $5)
|
||||
RETURNING *`,
|
||||
[purchase_order_id, po_item_id, packing_number, quantity, notes || null]
|
||||
);
|
||||
const packingList = plRows[0];
|
||||
|
||||
// Insert serial numbers
|
||||
for (const sn of serial_numbers) {
|
||||
await sql(
|
||||
`INSERT INTO packing_list_serials (packing_list_id, serial_number)
|
||||
VALUES ($1, $2)`,
|
||||
[packingList.id, sn]
|
||||
);
|
||||
}
|
||||
|
||||
// Update shipped_quantity on the PO item
|
||||
await sql(
|
||||
`UPDATE purchase_order_items SET shipped_quantity = COALESCE(shipped_quantity, 0) + $1 WHERE id = $2`,
|
||||
[quantity, po_item_id]
|
||||
);
|
||||
|
||||
return NextResponse.json(packingList, { status: 201 });
|
||||
} catch (error) {
|
||||
console.error("[v0] Packing list POST error:", error);
|
||||
return NextResponse.json(
|
||||
{ error: error instanceof Error ? error.message : "Failed to create packing list" },
|
||||
{ status: 500 }
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user