147 lines
4.0 KiB
TypeScript
147 lines
4.0 KiB
TypeScript
import { NextRequest, NextResponse } from "next/server";
|
|
import { getDb } from "@/lib/db";
|
|
|
|
export async function GET(request: NextRequest) {
|
|
const sql = getDb();
|
|
const { searchParams } = new URL(request.url);
|
|
const componentId = searchParams.get("componentId");
|
|
const search = searchParams.get("search");
|
|
const type = searchParams.get("type");
|
|
const limit = Math.min(Number(searchParams.get("limit") || 50), 200);
|
|
const offset = Number(searchParams.get("offset") || 0);
|
|
|
|
let query = `
|
|
SELECT
|
|
st.*,
|
|
c.name AS component_name,
|
|
c.part_number,
|
|
cat.name AS category_name
|
|
FROM stock_transactions st
|
|
JOIN components c ON st.component_id = c.id
|
|
LEFT JOIN categories cat ON c.category_id = cat.id
|
|
WHERE 1=1
|
|
`;
|
|
const params: (string | number)[] = [];
|
|
let paramIdx = 1;
|
|
|
|
if (componentId) {
|
|
query += ` AND st.component_id = $${paramIdx++}`;
|
|
params.push(Number(componentId));
|
|
}
|
|
|
|
if (search) {
|
|
query += ` AND (c.name ILIKE $${paramIdx} OR c.part_number ILIKE $${paramIdx})`;
|
|
params.push(`%${search}%`);
|
|
paramIdx++;
|
|
}
|
|
|
|
if (type && type !== "all") {
|
|
query += ` AND st.transaction_type = $${paramIdx++}`;
|
|
params.push(type);
|
|
}
|
|
|
|
query += ` ORDER BY st.created_at DESC LIMIT $${paramIdx++} OFFSET $${paramIdx++}`;
|
|
params.push(limit, offset);
|
|
|
|
const rows = await sql(query, params);
|
|
|
|
// Also get total count for pagination
|
|
let countQuery = `
|
|
SELECT COUNT(*) as total
|
|
FROM stock_transactions st
|
|
JOIN components c ON st.component_id = c.id
|
|
WHERE 1=1
|
|
`;
|
|
const countParams: (string | number)[] = [];
|
|
let countIdx = 1;
|
|
|
|
if (componentId) {
|
|
countQuery += ` AND st.component_id = $${countIdx++}`;
|
|
countParams.push(Number(componentId));
|
|
}
|
|
if (search) {
|
|
countQuery += ` AND (c.name ILIKE $${countIdx} OR c.part_number ILIKE $${countIdx})`;
|
|
countParams.push(`%${search}%`);
|
|
countIdx++;
|
|
}
|
|
if (type && type !== "all") {
|
|
countQuery += ` AND st.transaction_type = $${countIdx++}`;
|
|
countParams.push(type);
|
|
}
|
|
|
|
const countResult = await sql(countQuery, countParams);
|
|
|
|
return NextResponse.json({
|
|
transactions: rows,
|
|
total: Number(countResult[0].total),
|
|
});
|
|
}
|
|
|
|
export async function POST(request: NextRequest) {
|
|
const sql = getDb();
|
|
const body = await request.json();
|
|
const {
|
|
component_id,
|
|
transaction_type,
|
|
quantity,
|
|
unit_cost,
|
|
supplier,
|
|
supplier_order_ref,
|
|
notes,
|
|
} = body;
|
|
|
|
if (!component_id || !transaction_type || !quantity) {
|
|
return NextResponse.json(
|
|
{ error: "component_id, transaction_type, and quantity are required" },
|
|
{ status: 400 }
|
|
);
|
|
}
|
|
|
|
// Validate supplier_order_ref if provided - must match an existing PO
|
|
if (supplier_order_ref && supplier_order_ref.trim()) {
|
|
const poCheck = await sql(
|
|
`SELECT id FROM supplier_pos WHERE po_number = $1`,
|
|
[supplier_order_ref.trim()]
|
|
);
|
|
if (poCheck.length === 0) {
|
|
return NextResponse.json(
|
|
{ error: `Purchase order "${supplier_order_ref}" not found. Please select a valid PO or leave the field empty.` },
|
|
{ status: 400 }
|
|
);
|
|
}
|
|
}
|
|
|
|
const total_cost = unit_cost ? Number(unit_cost) * Number(quantity) : null;
|
|
|
|
// Insert the stock transaction record
|
|
const insertParams = [
|
|
component_id,
|
|
transaction_type,
|
|
Number(quantity),
|
|
unit_cost ? Number(unit_cost) : null,
|
|
total_cost,
|
|
supplier || null,
|
|
supplier_order_ref || null,
|
|
notes || null,
|
|
];
|
|
const rows = await sql(
|
|
`INSERT INTO stock_transactions (component_id, transaction_type, quantity, unit_cost, total_cost, supplier, supplier_order_ref, notes)
|
|
VALUES ($1, $2, $3, $4, $5, $6, $7, $8)
|
|
RETURNING *`,
|
|
insertParams
|
|
);
|
|
|
|
// Update the component's stock_quantity
|
|
const qtyChange =
|
|
transaction_type === "consumption"
|
|
? -Math.abs(Number(quantity))
|
|
: Math.abs(Number(quantity));
|
|
|
|
await sql(
|
|
`UPDATE components SET stock_quantity = stock_quantity + $1, updated_at = NOW() WHERE id = $2`,
|
|
[qtyChange, component_id]
|
|
);
|
|
|
|
return NextResponse.json(rows[0], { status: 201 });
|
|
}
|