diff --git a/src/components/inventory/inventory-summary-table.tsx b/src/components/inventory/inventory-summary-table.tsx
index 49b8353..73d970a 100644
--- a/src/components/inventory/inventory-summary-table.tsx
+++ b/src/components/inventory/inventory-summary-table.tsx
@@ -40,10 +40,7 @@ export function InventorySummaryTable({
'Description',
'Plant',
'Warehouse',
- 'On Hand',
- 'Allocated',
- 'Available',
- 'UM',
+ 'On Hand Qty',
];
const rows = filteredData.map((row) => [
row.part_num,
@@ -51,9 +48,6 @@ export function InventorySummaryTable({
row.plant,
row.warehouse,
row.on_hand_qty,
- row.allocated_qty || 0,
- row.available_qty || 0,
- row.um,
]);
const csvContent = [headers, ...rows]
@@ -105,10 +99,7 @@ export function InventorySummaryTable({
Description
Plant
Warehouse
- On Hand
- Allocated
- Available
- UM
+ On Hand Qty
@@ -116,7 +107,7 @@ export function InventorySummaryTable({
{row.part_num}
@@ -128,15 +119,8 @@ export function InventorySummaryTable({
{row.plant}
{row.warehouse}
- {row.on_hand_qty.toLocaleString()}
+ {Number(row.on_hand_qty).toLocaleString()}
-
- {(row.allocated_qty || 0).toLocaleString()}
-
-
- {(row.available_qty || 0).toLocaleString()}
-
- {row.um}
))}
diff --git a/src/components/orders/orders-table.tsx b/src/components/orders/orders-table.tsx
index cff9ec6..ba63fb7 100644
--- a/src/components/orders/orders-table.tsx
+++ b/src/components/orders/orders-table.tsx
@@ -1,7 +1,6 @@
'use client';
import { useState } from 'react';
-import Link from 'next/link';
import type { OrderRow } from '@/services/orders';
import {
Card,
@@ -33,39 +32,37 @@ export function OrdersTable({ data }: Props) {
const searchLower = searchTerm.toLowerCase();
return (
row.order_num?.toString().includes(searchLower) ||
- row.po_num?.toLowerCase().includes(searchLower) ||
- row.customer_part?.toLowerCase().includes(searchLower) ||
- row.vorteq_part?.toLowerCase().includes(searchLower)
+ row.customer_po?.toLowerCase().includes(searchLower) ||
+ row.vorteq_part?.toLowerCase().includes(searchLower) ||
+ row.job_num?.toLowerCase().includes(searchLower)
);
});
const handleExportCSV = () => {
const headers = [
'Order #',
- 'PO #',
- 'Order Date',
- 'Need By',
- 'Customer Part',
+ 'Customer PO',
'Vorteq Part',
- 'Order Qty',
- 'Shipped',
- 'Remaining',
- 'UM',
- 'Status',
+ 'Description',
+ 'Plant',
+ 'Warehouse',
+ 'Qty Completed',
+ 'Completion Date',
+ 'Job #',
];
const rows = filteredData.map((row) => [
row.order_num?.toString() || '',
- row.po_num || '',
- row.order_date ? new Date(row.order_date).toLocaleDateString() : '',
- row.need_by_date ? new Date(row.need_by_date).toLocaleDateString() : '',
- row.customer_part || '',
+ row.customer_po || '',
row.vorteq_part || '',
- row.order_qty?.toString() || '0',
- row.shipped_qty?.toString() || '0',
- row.remaining_qty?.toString() || '0',
- row.um || '',
- row.status || '',
+ row.part_description || '',
+ row.plant || '',
+ row.warehouse || '',
+ row.qty_completed?.toString() || '0',
+ row.completion_date
+ ? new Date(row.completion_date).toLocaleDateString()
+ : '',
+ row.job_num || '',
]);
const csvContent = [headers, ...rows]
@@ -92,7 +89,7 @@ export function OrdersTable({ data }: Props) {
setSearchTerm(e.target.value)}
className="pl-8"
@@ -109,22 +106,21 @@ export function OrdersTable({ data }: Props) {
Order #
- PO #
- Order Date
- Need By
- Customer Part
+ Customer PO
Vorteq Part
- Qty
- Shipped
- Remaining
- Status
+ Description
+ Plant
+ Warehouse
+ Qty Completed
+ Completion Date
+ Job #
{filteredData.length === 0 ? (
No orders found
@@ -133,49 +129,27 @@ export function OrdersTable({ data }: Props) {
) : (
filteredData.map((row, i) => (
-
-
- {row.order_num}
-
+
+ {row.order_num}
- {row.po_num || '-'}
-
- {row.order_date
- ? new Date(row.order_date).toLocaleDateString()
- : '-'}
-
-
- {row.need_by_date
- ? new Date(row.need_by_date).toLocaleDateString()
- : '-'}
-
- {row.customer_part || '-'}
+ {row.customer_po || '-'}
{row.vorteq_part || '-'}
-
- {row.order_qty?.toFixed(0) || '0'}
+
+ {row.part_description || '-'}
+ {row.plant || '-'}
+ {row.warehouse || '-'}
- {row.shipped_qty?.toFixed(0) || '0'}
-
-
- {row.remaining_qty?.toFixed(0) || '0'}
+ {Number(row.qty_completed || 0).toLocaleString()}
-
- {row.status}
-
+ {row.completion_date
+ ? new Date(row.completion_date).toLocaleDateString()
+ : '-'}
+ {row.job_num || '-'}
))
)}
diff --git a/src/services/inventory.ts b/src/services/inventory.ts
index 1504183..5dc18e2 100644
--- a/src/services/inventory.ts
+++ b/src/services/inventory.ts
@@ -1,7 +1,8 @@
/**
* Inventory Service
*
- * Handles inventory data retrieval from Epicor stored procedures
+ * Handles inventory data retrieval from Epicor stored procedures.
+ * SP columns are PascalCase; we map them to snake_case for the UI.
*/
import { execStoredProc } from '@/lib/epicor';
@@ -10,11 +11,11 @@ export type InventorySummaryRow = {
part_num: string;
description: string;
plant: string;
+ plant_key: string;
warehouse: string;
on_hand_qty: number;
- allocated_qty: number;
- available_qty: number;
- um: string; // Unit of measure
+ paint_code?: string;
+ cust_part_num?: string;
};
export type InventoryDetailRow = {
@@ -35,9 +36,25 @@ export type InventoryDetailRow = {
uom?: string;
receipt_date?: Date;
paint_code?: string;
- [key: string]: unknown; // Allow additional fields from stored procedures
+ [key: string]: unknown;
};
+/**
+ * Map raw Epicor summary SP row to our normalized type
+ */
+function mapSummaryRow(raw: Record): InventorySummaryRow {
+ return {
+ part_num: String(raw.VorteqPartNum ?? ''),
+ description: String(raw.VorteqPartDesc ?? ''),
+ plant: String(raw.Plant ?? ''),
+ plant_key: String(raw.PlantKey ?? ''),
+ warehouse: String(raw.Warehouse ?? ''),
+ on_hand_qty: Number(raw.OnHandQty ?? 0),
+ paint_code: raw.PaintCode ? String(raw.PaintCode) : undefined,
+ cust_part_num: raw.CustPartNum ? String(raw.CustPartNum) : undefined,
+ };
+}
+
export type InventoryCategory =
| 'wip'
| 'finished-goods'
@@ -54,7 +71,7 @@ export async function getWorkInProgressSummary(
dbName: string,
sub: number
): Promise {
- const result = await execStoredProc(
+ const result = await execStoredProc[]>(
'PortalWorkInProgressInventorySummaryV6',
{
Customer: custId,
@@ -63,7 +80,7 @@ export async function getWorkInProgressSummary(
}
);
- return result;
+ return result.map(mapSummaryRow);
}
/**
@@ -74,7 +91,7 @@ export async function getFinishedGoodsSummary(
dbName: string,
sub: number
): Promise {
- const result = await execStoredProc(
+ const result = await execStoredProc[]>(
'PortalFinishedGoodsInventorySummaryV6',
{
Customer: custId,
@@ -83,7 +100,7 @@ export async function getFinishedGoodsSummary(
}
);
- return result;
+ return result.map(mapSummaryRow);
}
/**
@@ -94,7 +111,7 @@ export async function getProcessedOtherSummary(
dbName: string,
sub: number
): Promise {
- const result = await execStoredProc(
+ const result = await execStoredProc[]>(
'PortalProcessedOtherInventorySummaryV6',
{
Customer: custId,
@@ -103,12 +120,13 @@ export async function getProcessedOtherSummary(
}
);
- return result;
+ return result.map(mapSummaryRow);
}
/**
* Get Unprocessed inventory summary
- * Note: Sub-users are blocked from accessing this category
+ * Note: Sub-users are blocked from accessing this category.
+ * Non-V6 SPs use @CUSTID parameter.
*/
export async function getUnprocessedSummary(
custId: string,
@@ -116,18 +134,18 @@ export async function getUnprocessedSummary(
isSubUser: boolean
): Promise {
if (isSubUser) {
- return []; // Sub-users cannot access unprocessed inventory
+ return [];
}
- const result = await execStoredProc(
+ const result = await execStoredProc[]>(
'PortalUnprocessedInventorySummary',
{
- Customer: custId,
+ CUSTID: custId,
DBNAME: dbName,
}
);
- return result;
+ return result.map(mapSummaryRow);
}
/**
@@ -139,18 +157,18 @@ export async function getUnprocessedRRSummary(
isSubUser: boolean
): Promise {
if (isSubUser) {
- return []; // Sub-users cannot access R&R inventory
+ return [];
}
- const result = await execStoredProc(
+ const result = await execStoredProc[]>(
'PortalUnprocessedInventoryRejectsAndReturnsSummary',
{
- Customer: custId,
+ CUSTID: custId,
DBNAME: dbName,
}
);
- return result;
+ return result.map(mapSummaryRow);
}
/**
@@ -162,18 +180,18 @@ export async function getProcessedRRSummary(
isSubUser: boolean
): Promise {
if (isSubUser) {
- return []; // Sub-users cannot access R&R inventory
+ return [];
}
- const result = await execStoredProc(
+ const result = await execStoredProc[]>(
'PortalProcessedInventoryRejectsAndReturnsSummary',
{
- Customer: custId,
+ CUSTID: custId,
DBNAME: dbName,
}
);
- return result;
+ return result.map(mapSummaryRow);
}
/**
@@ -236,10 +254,12 @@ export async function getInventoryDetails(
DBNAME: dbName,
};
- // V6 procedures use Customer, non-V6 also use Customer
- params.Customer = custId;
+ // V6 procedures use @Customer, non-V6 use @CUSTID
if (procConfig.isV6) {
+ params.Customer = custId;
params.SUBUSER = sub;
+ } else {
+ params.CUSTID = custId;
}
// Add filters if provided (only for filtered variant)
diff --git a/src/services/orders.ts b/src/services/orders.ts
index 7d32878..2b34807 100644
--- a/src/services/orders.ts
+++ b/src/services/orders.ts
@@ -1,70 +1,60 @@
/**
* Orders Service
- * Handles order data retrieval from Epicor
+ * Handles order data retrieval from Epicor using the portal_Orders view
*/
import { execQuery } from '@/lib/epicor';
export type OrderRow = {
order_num: number;
- po_num: string;
- order_date: Date;
- need_by_date: Date;
- customer_part: string;
+ customer_po: string;
vorteq_part: string;
- order_qty: number;
- shipped_qty: number;
- remaining_qty: number;
- um: string;
- open_order: boolean;
- status: string;
- ship_to_name?: string;
+ part_description: string;
+ plant: string;
+ warehouse: string;
+ qty_completed: number;
+ completion_date: Date;
+ job_num: string;
[key: string]: unknown;
};
/**
- * Get top 100 orders for a customer
- * Special handling for HDC customer
+ * Map raw portal_Orders view row to our normalized type
+ */
+function mapOrderRow(raw: Record): OrderRow {
+ return {
+ order_num: Number(raw.VorteqSalesOrderNum ?? 0),
+ customer_po: String(raw.CustomerPO ?? ''),
+ vorteq_part: String(raw.VorteqPartNum ?? ''),
+ part_description: String(raw.PartDescription ?? ''),
+ plant: String(raw.Plant ?? ''),
+ warehouse: String(raw.Warehouse ?? ''),
+ qty_completed: Number(raw.QuantityCompleted ?? 0),
+ completion_date: raw.CompletionDate as Date,
+ job_num: String(raw.VorteqJobNum ?? ''),
+ };
+}
+
+/**
+ * Get top 100 orders for a customer using the portal_Orders view.
+ * HDC exception: uses portal_OrdersHDC view and maps to CustID 'HDM'.
*/
export async function getTop100Orders(custId: string): Promise {
- // HDC exception: use different customer ID for second parameter
- const cust2 = custId === 'HDC' ? 'HDM' : custId;
+ const viewName = custId === 'HDC' ? 'portal_OrdersHDC' : 'portal_Orders';
+ const queryCustId = custId === 'HDC' ? 'HDM' : custId;
- // Query Epicor OrderHed and OrderDtl tables
- // This is a simplified version - the actual portal_Orders.sql may have more complex logic
- const sql = `
- SELECT TOP 100
- oh.OrderNum AS order_num,
- oh.PONum AS po_num,
- oh.OrderDate AS order_date,
- oh.NeedByDate AS need_by_date,
- od.XPartNum AS customer_part,
- od.PartNum AS vorteq_part,
- od.OrderQty AS order_qty,
- od.ShippedQty AS shipped_qty,
- (od.OrderQty - od.ShippedQty) AS remaining_qty,
- od.IUM AS um,
- oh.OpenOrder AS open_order,
- CASE
- WHEN oh.OpenOrder = 1 THEN 'Open'
- ELSE 'Closed'
- END AS status,
- st.Name AS ship_to_name
- FROM Erp.OrderHed oh
- INNER JOIN Erp.OrderDtl od ON oh.Company = od.Company AND oh.OrderNum = od.OrderNum
- INNER JOIN Erp.Customer c ON oh.Company = c.Company AND oh.CustNum = c.CustNum
- LEFT JOIN Erp.ShipTo st ON oh.Company = st.Company AND oh.ShipToNum = st.ShipToNum
- WHERE c.CustID = @Cust1
- OR c.CustID = @Cust2
- ORDER BY oh.OrderDate DESC, oh.OrderNum DESC
+ const query = `
+ SELECT TOP 100 *
+ FROM dbo.${viewName}
+ WHERE CustomerID = @CustID
+ ORDER BY CompletionDate DESC
`;
- const result = await execQuery(sql, {
- Cust1: custId,
- Cust2: cust2,
+ const result = await execQuery[]>(query, {
+ CustID: queryCustId,
});
- return result;
+ return result.map(mapOrderRow);
}
/**