fixed
This commit is contained in:
@@ -11,6 +11,12 @@ type ParsedLine = {
|
||||
amount: number;
|
||||
};
|
||||
|
||||
type RelationalOrderLine = {
|
||||
partId: number;
|
||||
quantity: number;
|
||||
amount: number;
|
||||
};
|
||||
|
||||
type ParsedFulfillmentLine = {
|
||||
sku: string;
|
||||
quantity: number;
|
||||
@@ -55,6 +61,44 @@ function parseLines(raw: string): ParsedLine[] {
|
||||
});
|
||||
}
|
||||
|
||||
function parseRelationalOrderLines(raw: string): RelationalOrderLine[] {
|
||||
try {
|
||||
const parsed = JSON.parse(raw) as Array<Record<string, unknown>>;
|
||||
if (!Array.isArray(parsed) || parsed.length === 0) {
|
||||
throw new Error("Order must contain at least one line.");
|
||||
}
|
||||
|
||||
const lines = parsed.map((line) => {
|
||||
const partId = Number(line.partId);
|
||||
const quantity = Number(line.quantity);
|
||||
const amount = Number(line.amount);
|
||||
|
||||
if (!Number.isInteger(partId) || partId <= 0) {
|
||||
throw new Error("Invalid item selection.");
|
||||
}
|
||||
|
||||
if (!Number.isFinite(quantity) || quantity <= 0) {
|
||||
throw new Error("Invalid line quantity.");
|
||||
}
|
||||
|
||||
if (!Number.isFinite(amount) || amount < 0) {
|
||||
throw new Error("Invalid line amount.");
|
||||
}
|
||||
|
||||
return { partId, quantity, amount };
|
||||
});
|
||||
|
||||
const uniquePartIds = new Set(lines.map((line) => line.partId));
|
||||
if (uniquePartIds.size !== lines.length) {
|
||||
throw new Error("Each inventory item can only appear once per order.");
|
||||
}
|
||||
|
||||
return lines;
|
||||
} catch {
|
||||
throw new Error("Invalid relational order payload.");
|
||||
}
|
||||
}
|
||||
|
||||
function parseFulfillmentLines(raw: string): ParsedFulfillmentLine[] {
|
||||
return raw
|
||||
.split(/\r?\n/)
|
||||
@@ -85,6 +129,18 @@ function getPartIdBySku(sku: string) {
|
||||
return row.id;
|
||||
}
|
||||
|
||||
function getExistingPart(partId: number) {
|
||||
const row = db()
|
||||
.prepare(`SELECT id FROM parts WHERE id = ?`)
|
||||
.get(partId) as { id: number } | undefined;
|
||||
|
||||
if (!row) {
|
||||
throw new Error(`Selected inventory item ${partId} does not exist.`);
|
||||
}
|
||||
|
||||
return row.id;
|
||||
}
|
||||
|
||||
function getOrderNumber(prefix: string, table: "sales_orders" | "purchase_orders") {
|
||||
const row = db().prepare(`SELECT COUNT(*) AS count FROM ${table}`).get() as { count: number };
|
||||
return `${prefix}-${String((row.count ?? 0) + 1).padStart(5, "0")}`;
|
||||
@@ -399,12 +455,19 @@ export async function createVendor(formData: FormData) {
|
||||
}
|
||||
|
||||
export async function createSalesOrder(formData: FormData) {
|
||||
const customerCode = getText(formData, "customerCode");
|
||||
const lines = parseLines(getText(formData, "lines"));
|
||||
const customerRow = db().prepare(`SELECT id FROM customers WHERE code = ?`).get(customerCode) as { id: number } | undefined;
|
||||
const customerId = Number(getText(formData, "customerId"));
|
||||
const relationalLinesPayload = getText(formData, "lineItems");
|
||||
const lines = relationalLinesPayload
|
||||
? parseRelationalOrderLines(relationalLinesPayload)
|
||||
: parseLines(getText(formData, "lines")).map((line) => ({
|
||||
partId: getPartIdBySku(line.sku),
|
||||
quantity: line.quantity,
|
||||
amount: line.amount
|
||||
}));
|
||||
const customerRow = db().prepare(`SELECT id FROM customers WHERE id = ?`).get(customerId) as { id: number } | undefined;
|
||||
|
||||
if (!customerRow) {
|
||||
throw new Error(`Customer "${customerCode}" does not exist.`);
|
||||
throw new Error("Selected customer does not exist.");
|
||||
}
|
||||
|
||||
const tx = db().transaction(() => {
|
||||
@@ -426,7 +489,7 @@ export async function createSalesOrder(formData: FormData) {
|
||||
);
|
||||
|
||||
for (const line of lines) {
|
||||
insertLine.run(orderId, getPartIdBySku(line.sku), line.quantity, line.amount);
|
||||
insertLine.run(orderId, getExistingPart(line.partId), line.quantity, line.amount);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -567,12 +630,19 @@ export async function shipSalesOrder(formData: FormData) {
|
||||
}
|
||||
|
||||
export async function createPurchaseOrder(formData: FormData) {
|
||||
const vendorCode = getText(formData, "vendorCode");
|
||||
const lines = parseLines(getText(formData, "lines"));
|
||||
const vendorRow = db().prepare(`SELECT id FROM vendors WHERE code = ?`).get(vendorCode) as { id: number } | undefined;
|
||||
const vendorId = Number(getText(formData, "vendorId"));
|
||||
const relationalLinesPayload = getText(formData, "lineItems");
|
||||
const lines = relationalLinesPayload
|
||||
? parseRelationalOrderLines(relationalLinesPayload)
|
||||
: parseLines(getText(formData, "lines")).map((line) => ({
|
||||
partId: getPartIdBySku(line.sku),
|
||||
quantity: line.quantity,
|
||||
amount: line.amount
|
||||
}));
|
||||
const vendorRow = db().prepare(`SELECT id FROM vendors WHERE id = ?`).get(vendorId) as { id: number } | undefined;
|
||||
|
||||
if (!vendorRow) {
|
||||
throw new Error(`Vendor "${vendorCode}" does not exist.`);
|
||||
throw new Error("Selected vendor does not exist.");
|
||||
}
|
||||
|
||||
const tx = db().transaction(() => {
|
||||
@@ -594,7 +664,7 @@ export async function createPurchaseOrder(formData: FormData) {
|
||||
);
|
||||
|
||||
for (const line of lines) {
|
||||
insertLine.run(orderId, getPartIdBySku(line.sku), line.quantity, line.amount);
|
||||
insertLine.run(orderId, getExistingPart(line.partId), line.quantity, line.amount);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ import type {
|
||||
JournalEntryRow,
|
||||
KitRow,
|
||||
LowStockRow,
|
||||
OrderItemOption,
|
||||
PartRow,
|
||||
PurchaseOrderListRow,
|
||||
SalesOrderListRow,
|
||||
@@ -94,6 +95,28 @@ export function getParts(): PartRow[] {
|
||||
.all() as PartRow[];
|
||||
}
|
||||
|
||||
export function getOrderItemOptions(): OrderItemOption[] {
|
||||
return db()
|
||||
.prepare(
|
||||
`
|
||||
SELECT
|
||||
p.id,
|
||||
p.sku,
|
||||
p.name,
|
||||
p.kind,
|
||||
COALESCE(ib.quantity_on_hand, 0) AS quantityOnHand,
|
||||
p.sale_price AS salePrice,
|
||||
p.unit_cost AS unitCost,
|
||||
p.unit_of_measure AS unitOfMeasure
|
||||
FROM parts p
|
||||
LEFT JOIN inventory_balances ib ON ib.part_id = p.id
|
||||
WHERE p.is_active = 1
|
||||
ORDER BY p.kind DESC, p.sku ASC
|
||||
`
|
||||
)
|
||||
.all() as OrderItemOption[];
|
||||
}
|
||||
|
||||
export function getAssembliesWithComponents(): KitRow[] {
|
||||
return db()
|
||||
.prepare(
|
||||
|
||||
11
lib/types.ts
11
lib/types.ts
@@ -41,6 +41,17 @@ export type ContactRow = {
|
||||
phone: string | null;
|
||||
};
|
||||
|
||||
export type OrderItemOption = {
|
||||
id: number;
|
||||
sku: string;
|
||||
name: string;
|
||||
kind: "part" | "assembly";
|
||||
quantityOnHand: number;
|
||||
salePrice: number;
|
||||
unitCost: number;
|
||||
unitOfMeasure: string;
|
||||
};
|
||||
|
||||
export type SalesOrderListRow = {
|
||||
id: number;
|
||||
orderNumber: string;
|
||||
|
||||
Reference in New Issue
Block a user