fixes
This commit is contained in:
104
lib/actions.ts
104
lib/actions.ts
@@ -22,6 +22,11 @@ type ParsedFulfillmentLine = {
|
||||
quantity: number;
|
||||
};
|
||||
|
||||
type RelationalFulfillmentLine = {
|
||||
lineId: number;
|
||||
quantity: number;
|
||||
};
|
||||
|
||||
function db() {
|
||||
return getDb();
|
||||
}
|
||||
@@ -121,6 +126,32 @@ function parseFulfillmentLines(raw: string): ParsedFulfillmentLine[] {
|
||||
});
|
||||
}
|
||||
|
||||
function parseRelationalFulfillmentLines(raw: string): RelationalFulfillmentLine[] {
|
||||
try {
|
||||
const parsed = JSON.parse(raw) as Array<Record<string, unknown>>;
|
||||
if (!Array.isArray(parsed) || parsed.length === 0) {
|
||||
throw new Error("No fulfillment lines selected.");
|
||||
}
|
||||
|
||||
return parsed.map((line) => {
|
||||
const lineId = Number(line.lineId);
|
||||
const quantity = Number(line.quantity);
|
||||
|
||||
if (!Number.isInteger(lineId) || lineId <= 0) {
|
||||
throw new Error("Invalid line selection.");
|
||||
}
|
||||
|
||||
if (!Number.isFinite(quantity) || quantity <= 0) {
|
||||
throw new Error("Invalid fulfillment quantity.");
|
||||
}
|
||||
|
||||
return { lineId, quantity };
|
||||
});
|
||||
} catch {
|
||||
throw new Error("Invalid relational fulfillment payload.");
|
||||
}
|
||||
}
|
||||
|
||||
function getPartIdBySku(sku: string) {
|
||||
const row = db().prepare(`SELECT id FROM parts WHERE sku = ?`).get(sku) as { id: number } | undefined;
|
||||
if (!row) {
|
||||
@@ -274,8 +305,12 @@ export async function buildAssembly(formData: FormData) {
|
||||
const assemblySku = getText(formData, "assemblySku");
|
||||
const buildQuantity = getNumber(formData, "quantity");
|
||||
|
||||
if (!assemblySku) {
|
||||
redirect("/assemblies?error=Select an assembly before building.");
|
||||
}
|
||||
|
||||
if (buildQuantity <= 0) {
|
||||
throw new Error("Build quantity must be greater than zero.");
|
||||
redirect("/assemblies?error=Build quantity must be greater than zero.");
|
||||
}
|
||||
|
||||
const assemblyId = getPartIdBySku(assemblySku);
|
||||
@@ -304,13 +339,13 @@ export async function buildAssembly(formData: FormData) {
|
||||
}>;
|
||||
|
||||
if (components.length === 0) {
|
||||
throw new Error("Assembly has no bill of materials defined.");
|
||||
redirect("/assemblies?error=Assembly has no bill of materials defined.");
|
||||
}
|
||||
|
||||
for (const component of components) {
|
||||
const needed = component.componentQuantity * buildQuantity;
|
||||
if (component.quantityOnHand < needed) {
|
||||
throw new Error(`Not enough stock for component ${component.sku}. Need ${needed}, have ${component.quantityOnHand}.`);
|
||||
redirect(`/assemblies?error=${encodeURIComponent(`Not enough stock for component ${component.sku}. Need ${needed}, have ${component.quantityOnHand}.`)}`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -354,6 +389,7 @@ export async function buildAssembly(formData: FormData) {
|
||||
revalidatePath("/");
|
||||
revalidatePath("/parts");
|
||||
revalidatePath("/assemblies");
|
||||
redirect(`/assemblies?success=${encodeURIComponent(`Built ${buildQuantity} of ${assemblySku}.`)}`);
|
||||
}
|
||||
|
||||
export async function recordAdjustment(formData: FormData) {
|
||||
@@ -548,18 +584,18 @@ export async function shipSalesOrder(formData: FormData) {
|
||||
throw new Error("Sales order has no lines.");
|
||||
}
|
||||
|
||||
const requestedLines = parseFulfillmentLines(getText(formData, "lines"));
|
||||
const fulfilledLines = requestedLines.length
|
||||
? requestedLines.map((request) => {
|
||||
const matchingLine = orderLines.find((line) => line.sku === request.sku);
|
||||
const relationalPayload = getText(formData, "fulfillmentLines");
|
||||
const fulfilledLines = relationalPayload
|
||||
? parseRelationalFulfillmentLines(relationalPayload).map((request) => {
|
||||
const matchingLine = orderLines.find((line) => line.lineId === request.lineId);
|
||||
|
||||
if (!matchingLine) {
|
||||
throw new Error(`SKU ${request.sku} is not on this sales order.`);
|
||||
throw new Error("Selected sales order line is invalid.");
|
||||
}
|
||||
|
||||
const remaining = matchingLine.quantity - matchingLine.shippedQuantity;
|
||||
if (request.quantity > remaining) {
|
||||
throw new Error(`Cannot ship ${request.quantity} of ${request.sku}; only ${remaining} remain.`);
|
||||
throw new Error(`Cannot ship ${request.quantity} of ${matchingLine.sku}; only ${remaining} remain.`);
|
||||
}
|
||||
|
||||
if (matchingLine.quantityOnHand < request.quantity) {
|
||||
@@ -568,7 +604,26 @@ export async function shipSalesOrder(formData: FormData) {
|
||||
|
||||
return { ...matchingLine, shipQuantity: request.quantity };
|
||||
})
|
||||
: orderLines
|
||||
: parseFulfillmentLines(getText(formData, "lines")).length
|
||||
? parseFulfillmentLines(getText(formData, "lines")).map((request) => {
|
||||
const matchingLine = orderLines.find((line) => line.sku === request.sku);
|
||||
|
||||
if (!matchingLine) {
|
||||
throw new Error(`SKU ${request.sku} is not on this sales order.`);
|
||||
}
|
||||
|
||||
const remaining = matchingLine.quantity - matchingLine.shippedQuantity;
|
||||
if (request.quantity > remaining) {
|
||||
throw new Error(`Cannot ship ${request.quantity} of ${request.sku}; only ${remaining} remain.`);
|
||||
}
|
||||
|
||||
if (matchingLine.quantityOnHand < request.quantity) {
|
||||
throw new Error(`Insufficient stock for ${matchingLine.sku}. Need ${request.quantity}, have ${matchingLine.quantityOnHand}.`);
|
||||
}
|
||||
|
||||
return { ...matchingLine, shipQuantity: request.quantity };
|
||||
})
|
||||
: orderLines
|
||||
.map((line) => {
|
||||
const remaining = line.quantity - line.shippedQuantity;
|
||||
return remaining > 0 ? { ...line, shipQuantity: remaining } : null;
|
||||
@@ -718,23 +773,38 @@ export async function receivePurchaseOrder(formData: FormData) {
|
||||
throw new Error("Purchase order has no lines.");
|
||||
}
|
||||
|
||||
const requestedLines = parseFulfillmentLines(getText(formData, "lines"));
|
||||
const fulfilledLines = requestedLines.length
|
||||
? requestedLines.map((request) => {
|
||||
const matchingLine = lines.find((line) => line.sku === request.sku);
|
||||
const relationalPayload = getText(formData, "fulfillmentLines");
|
||||
const fulfilledLines = relationalPayload
|
||||
? parseRelationalFulfillmentLines(relationalPayload).map((request) => {
|
||||
const matchingLine = lines.find((line) => line.lineId === request.lineId);
|
||||
|
||||
if (!matchingLine) {
|
||||
throw new Error(`SKU ${request.sku} is not on this purchase order.`);
|
||||
throw new Error("Selected purchase order line is invalid.");
|
||||
}
|
||||
|
||||
const remaining = matchingLine.quantity - matchingLine.receivedQuantity;
|
||||
if (request.quantity > remaining) {
|
||||
throw new Error(`Cannot receive ${request.quantity} of ${request.sku}; only ${remaining} remain.`);
|
||||
throw new Error(`Cannot receive ${request.quantity} of ${matchingLine.sku}; only ${remaining} remain.`);
|
||||
}
|
||||
|
||||
return { ...matchingLine, receiveQuantity: request.quantity };
|
||||
})
|
||||
: lines
|
||||
: parseFulfillmentLines(getText(formData, "lines")).length
|
||||
? parseFulfillmentLines(getText(formData, "lines")).map((request) => {
|
||||
const matchingLine = lines.find((line) => line.sku === request.sku);
|
||||
|
||||
if (!matchingLine) {
|
||||
throw new Error(`SKU ${request.sku} is not on this purchase order.`);
|
||||
}
|
||||
|
||||
const remaining = matchingLine.quantity - matchingLine.receivedQuantity;
|
||||
if (request.quantity > remaining) {
|
||||
throw new Error(`Cannot receive ${request.quantity} of ${request.sku}; only ${remaining} remain.`);
|
||||
}
|
||||
|
||||
return { ...matchingLine, receiveQuantity: request.quantity };
|
||||
})
|
||||
: lines
|
||||
.map((line) => {
|
||||
const remaining = line.quantity - line.receivedQuantity;
|
||||
return remaining > 0 ? { ...line, receiveQuantity: remaining } : null;
|
||||
|
||||
Reference in New Issue
Block a user