|
- import { clientAuthFetch } from "@/app/utils/clientAuthFetch";
- import { NEXT_PUBLIC_API_URL } from "@/config/api";
-
- const BASE = `${NEXT_PUBLIC_API_URL}/chart`;
-
- function buildParams(params: Record<string, string | number | undefined>) {
- const p = new URLSearchParams();
- Object.entries(params).forEach(([k, v]) => {
- if (v !== undefined && v !== "") p.set(k, String(v));
- });
- return p.toString();
- }
-
- export interface StockTransactionsByDateRow {
- date: string;
- inQty: number;
- outQty: number;
- totalQty: number;
- }
-
- export interface DeliveryOrderByDateRow {
- date: string;
- orderCount: number;
- totalQty: number;
- }
-
- export interface PurchaseOrderByStatusRow {
- status: string;
- count: number;
- }
-
- /** Multi-select filters for purchase charts (repeated `supplierId` / `itemCode` / `purchaseOrderNo` query params). */
- export type PurchaseOrderChartFilters = {
- supplierIds?: number[];
- itemCodes?: string[];
- purchaseOrderNos?: string[];
- /** Single supplier code (drill when row has no supplier id); not used with `supplierIds`. */
- supplierCode?: string;
- };
-
- function appendPurchaseOrderListParams(p: URLSearchParams, filters?: PurchaseOrderChartFilters) {
- (filters?.supplierIds ?? []).forEach((id) => {
- if (Number.isFinite(id) && id > 0) p.append("supplierId", String(id));
- });
- (filters?.itemCodes ?? []).forEach((c) => {
- const t = String(c).trim();
- if (t) p.append("itemCode", t);
- });
- (filters?.purchaseOrderNos ?? []).forEach((n) => {
- const t = String(n).trim();
- if (t) p.append("purchaseOrderNo", t);
- });
- const sc = filters?.supplierCode?.trim();
- if (sc) p.set("supplierCode", sc);
- }
-
- export interface PoFilterSupplierOption {
- supplierId: number;
- code: string;
- name: string;
- }
-
- export interface PoFilterItemOption {
- itemCode: string;
- itemName: string;
- }
-
- export interface PoFilterPoNoOption {
- poNo: string;
- }
-
- export interface PurchaseOrderFilterOptions {
- suppliers: PoFilterSupplierOption[];
- items: PoFilterItemOption[];
- poNos: PoFilterPoNoOption[];
- }
-
- export interface PurchaseOrderEstimatedArrivalRow {
- bucket: string;
- count: number;
- }
-
- export interface PurchaseOrderDetailByStatusRow {
- purchaseOrderId: number;
- purchaseOrderNo: string;
- status: string;
- orderDate: string;
- estimatedArrivalDate: string;
- /** Shop / supplier FK; use for grouping when code is blank */
- supplierId: number | null;
- supplierCode: string;
- supplierName: string;
- itemCount: number;
- totalQty: number;
- }
-
- export interface PurchaseOrderItemRow {
- purchaseOrderLineId: number;
- itemCode: string;
- itemName: string;
- orderedQty: number;
- uom: string;
- receivedQty: number;
- pendingQty: number;
- }
-
- export interface StockInOutByDateRow {
- date: string;
- inQty: number;
- outQty: number;
- }
-
- export interface TopDeliveryItemsRow {
- itemCode: string;
- itemName: string;
- totalQty: number;
- }
-
- export interface StockBalanceTrendRow {
- date: string;
- balance: number;
- }
-
- export interface ConsumptionTrendByMonthRow {
- month: string;
- outQty: number;
- }
-
- export interface StaffDeliveryPerformanceRow {
- date: string;
- staffName: string;
- orderCount: number;
- totalMinutes: number;
- }
-
- export interface StaffOption {
- staffNo: string;
- name: string;
- }
-
- export async function fetchStaffDeliveryPerformanceHandlers(): Promise<StaffOption[]> {
- const res = await clientAuthFetch(`${BASE}/staff-delivery-performance-handlers`);
- if (!res.ok) throw new Error("Failed to fetch staff list");
- const data = await res.json();
- if (!Array.isArray(data)) return [];
- return (data as Record<string, unknown>[]).map((r: Record<string, unknown>) => ({
- staffNo: String(r.staffNo ?? ""),
- name: String(r.name ?? ""),
- }));
- }
-
- // Job order
- export interface JobOrderByStatusRow {
- status: string;
- count: number;
- }
-
- export interface JobOrderCountByDateRow {
- date: string;
- orderCount: number;
- }
-
- export interface JobOrderCreatedCompletedRow {
- date: string;
- createdCount: number;
- completedCount: number;
- }
-
- export interface ProductionScheduleByDateRow {
- date: string;
- scheduledItemCount: number;
- totalEstProdCount: number;
- }
-
- export interface PlannedDailyOutputRow {
- itemCode: string;
- itemName: string;
- dailyQty: number;
- }
-
- export async function fetchJobOrderByStatus(
- targetDate?: string
- ): Promise<JobOrderByStatusRow[]> {
- const q = targetDate ? buildParams({ targetDate }) : "";
- const res = await clientAuthFetch(
- q ? `${BASE}/job-order-by-status?${q}` : `${BASE}/job-order-by-status`
- );
- if (!res.ok) throw new Error("Failed to fetch job order by status");
- const data = await res.json();
- return ((Array.isArray(data) ? data : []) as Record<string, unknown>[]).map((r: Record<string, unknown>) => ({
- status: String(r.status ?? ""),
- count: Number(r.count ?? 0),
- }));
- }
-
- export async function fetchJobOrderCountByDate(
- startDate?: string,
- endDate?: string
- ): Promise<JobOrderCountByDateRow[]> {
- const q = buildParams({ startDate: startDate ?? "", endDate: endDate ?? "" });
- const res = await clientAuthFetch(`${BASE}/job-order-count-by-date?${q}`);
- if (!res.ok) throw new Error("Failed to fetch job order count by date");
- const data = await res.json();
- return normalizeChartRows(data, "date", ["orderCount"]);
- }
-
- export async function fetchJobOrderCreatedCompletedByDate(
- startDate?: string,
- endDate?: string
- ): Promise<JobOrderCreatedCompletedRow[]> {
- const q = buildParams({ startDate: startDate ?? "", endDate: endDate ?? "" });
- const res = await clientAuthFetch(
- `${BASE}/job-order-created-completed-by-date?${q}`
- );
- if (!res.ok) throw new Error("Failed to fetch job order created/completed");
- const data = await res.json();
- return ((Array.isArray(data) ? data : []) as Record<string, unknown>[]).map((r: Record<string, unknown>) => ({
- date: String(r.date ?? ""),
- createdCount: Number(r.createdCount ?? 0),
- completedCount: Number(r.completedCount ?? 0),
- }));
- }
-
- export interface JobMaterialPendingPickedRow {
- date: string;
- pendingCount: number;
- pickedCount: number;
- }
-
- export async function fetchJobMaterialPendingPickedByDate(
- startDate?: string,
- endDate?: string
- ): Promise<JobMaterialPendingPickedRow[]> {
- const q = buildParams({ startDate: startDate ?? "", endDate: endDate ?? "" });
- const res = await clientAuthFetch(`${BASE}/job-material-pending-picked-by-date?${q}`);
- if (!res.ok) throw new Error("Failed to fetch job material pending/picked");
- const data = await res.json();
- return ((Array.isArray(data) ? data : []) as Record<string, unknown>[]).map((r: Record<string, unknown>) => ({
- date: String(r.date ?? ""),
- pendingCount: Number(r.pendingCount ?? 0),
- pickedCount: Number(r.pickedCount ?? 0),
- }));
- }
-
- export interface JobProcessPendingCompletedRow {
- date: string;
- pendingCount: number;
- completedCount: number;
- }
-
- export async function fetchJobProcessPendingCompletedByDate(
- startDate?: string,
- endDate?: string
- ): Promise<JobProcessPendingCompletedRow[]> {
- const q = buildParams({ startDate: startDate ?? "", endDate: endDate ?? "" });
- const res = await clientAuthFetch(`${BASE}/job-process-pending-completed-by-date?${q}`);
- if (!res.ok) throw new Error("Failed to fetch job process pending/completed");
- const data = await res.json();
- return ((Array.isArray(data) ? data : []) as Record<string, unknown>[]).map((r: Record<string, unknown>) => ({
- date: String(r.date ?? ""),
- pendingCount: Number(r.pendingCount ?? 0),
- completedCount: Number(r.completedCount ?? 0),
- }));
- }
-
- export interface JobEquipmentWorkingWorkedRow {
- date: string;
- workingCount: number;
- workedCount: number;
- }
-
- export async function fetchJobEquipmentWorkingWorkedByDate(
- startDate?: string,
- endDate?: string
- ): Promise<JobEquipmentWorkingWorkedRow[]> {
- const q = buildParams({ startDate: startDate ?? "", endDate: endDate ?? "" });
- const res = await clientAuthFetch(`${BASE}/job-equipment-working-worked-by-date?${q}`);
- if (!res.ok) throw new Error("Failed to fetch job equipment working/worked");
- const data = await res.json();
- return ((Array.isArray(data) ? data : []) as Record<string, unknown>[]).map((r: Record<string, unknown>) => ({
- date: String(r.date ?? ""),
- workingCount: Number(r.workingCount ?? 0),
- workedCount: Number(r.workedCount ?? 0),
- }));
- }
-
- export async function fetchProductionScheduleByDate(
- startDate?: string,
- endDate?: string
- ): Promise<ProductionScheduleByDateRow[]> {
- const q = buildParams({ startDate: startDate ?? "", endDate: endDate ?? "" });
- const res = await clientAuthFetch(
- `${BASE}/production-schedule-by-date?${q}`
- );
- if (!res.ok) throw new Error("Failed to fetch production schedule by date");
- const data = await res.json();
- return ((Array.isArray(data) ? data : []) as Record<string, unknown>[]).map((r: Record<string, unknown>) => ({
- date: String(r.date ?? ""),
- scheduledItemCount: Number(r.scheduledItemCount ?? r.scheduleCount ?? 0),
- totalEstProdCount: Number(r.totalEstProdCount ?? 0),
- }));
- }
-
- export async function fetchPlannedDailyOutputByItem(
- limit = 20
- ): Promise<PlannedDailyOutputRow[]> {
- const res = await clientAuthFetch(
- `${BASE}/planned-daily-output-by-item?limit=${limit}`
- );
- if (!res.ok) throw new Error("Failed to fetch planned daily output");
- const data = await res.json();
- return ((Array.isArray(data) ? data : []) as Record<string, unknown>[]).map((r: Record<string, unknown>) => ({
- itemCode: String(r.itemCode ?? ""),
- itemName: String(r.itemName ?? ""),
- dailyQty: Number(r.dailyQty ?? 0),
- }));
- }
-
- /** Planned production by date and by item (production_schedule). */
- export interface PlannedOutputByDateAndItemRow {
- date: string;
- itemCode: string;
- itemName: string;
- qty: number;
- }
-
- export async function fetchPlannedOutputByDateAndItem(
- startDate?: string,
- endDate?: string
- ): Promise<PlannedOutputByDateAndItemRow[]> {
- const q = buildParams({ startDate: startDate ?? "", endDate: endDate ?? "" });
- const res = await clientAuthFetch(
- q ? `${BASE}/planned-output-by-date-and-item?${q}` : `${BASE}/planned-output-by-date-and-item`
- );
- if (!res.ok) throw new Error("Failed to fetch planned output by date and item");
- const data = await res.json();
- return ((Array.isArray(data) ? data : []) as Record<string, unknown>[]).map((r: Record<string, unknown>) => ({
- date: String(r.date ?? ""),
- itemCode: String(r.itemCode ?? ""),
- itemName: String(r.itemName ?? ""),
- qty: Number(r.qty ?? 0),
- }));
- }
-
- export async function fetchStaffDeliveryPerformance(
- startDate?: string,
- endDate?: string,
- staffNos?: string[]
- ): Promise<StaffDeliveryPerformanceRow[]> {
- const p = new URLSearchParams();
- if (startDate) p.set("startDate", startDate);
- if (endDate) p.set("endDate", endDate);
- (staffNos ?? []).forEach((no) => p.append("staffNo", no));
- const q = p.toString();
- const res = await clientAuthFetch(
- q ? `${BASE}/staff-delivery-performance?${q}` : `${BASE}/staff-delivery-performance`
- );
- if (!res.ok) throw new Error("Failed to fetch staff delivery performance");
- const data = await res.json();
- return ((Array.isArray(data) ? data : []) as Record<string, unknown>[]).map((r: Record<string, unknown>) => {
- // Accept camelCase or lowercase keys (JDBC/DB may return different casing)
- const row = r as Record<string, unknown>;
- return {
- date: String(row.date ?? row.Date ?? ""),
- staffName: String(row.staffName ?? row.staffname ?? ""),
- orderCount: Number(row.orderCount ?? row.ordercount ?? 0),
- totalMinutes: Number(row.totalMinutes ?? row.totalminutes ?? 0),
- };
- });
- }
-
- export async function fetchStockTransactionsByDate(
- startDate?: string,
- endDate?: string
- ): Promise<StockTransactionsByDateRow[]> {
- const q = buildParams({ startDate: startDate ?? "", endDate: endDate ?? "" });
- const res = await clientAuthFetch(`${BASE}/stock-transactions-by-date?${q}`);
- if (!res.ok) throw new Error("Failed to fetch stock transactions by date");
- const data = await res.json();
- return normalizeChartRows(data, "date", ["inQty", "outQty", "totalQty"]);
- }
-
- export async function fetchDeliveryOrderByDate(
- startDate?: string,
- endDate?: string
- ): Promise<DeliveryOrderByDateRow[]> {
- const q = buildParams({ startDate: startDate ?? "", endDate: endDate ?? "" });
- const res = await clientAuthFetch(`${BASE}/delivery-order-by-date?${q}`);
- if (!res.ok) throw new Error("Failed to fetch delivery order by date");
- const data = await res.json();
- return normalizeChartRows(data, "date", ["orderCount", "totalQty"]);
- }
-
- export async function fetchPurchaseOrderByStatus(
- targetDate?: string,
- filters?: PurchaseOrderChartFilters
- ): Promise<PurchaseOrderByStatusRow[]> {
- const p = new URLSearchParams();
- if (targetDate) p.set("targetDate", targetDate);
- appendPurchaseOrderListParams(p, filters);
- const q = p.toString();
- const res = await clientAuthFetch(
- q ? `${BASE}/purchase-order-by-status?${q}` : `${BASE}/purchase-order-by-status`
- );
- if (!res.ok) throw new Error("Failed to fetch purchase order by status");
- const data = await res.json();
- return ((Array.isArray(data) ? data : []) as Record<string, unknown>[]).map((r: Record<string, unknown>) => ({
- status: String(r.status ?? ""),
- count: Number(r.count ?? 0),
- }));
- }
-
- export async function fetchPurchaseOrderFilterOptions(
- targetDate?: string
- ): Promise<PurchaseOrderFilterOptions> {
- const p = new URLSearchParams();
- if (targetDate) p.set("targetDate", targetDate);
- const q = p.toString();
- const res = await clientAuthFetch(
- q ? `${BASE}/purchase-order-filter-options?${q}` : `${BASE}/purchase-order-filter-options`
- );
- if (!res.ok) throw new Error("Failed to fetch purchase order filter options");
- const data = await res.json();
- const row = (data ?? {}) as Record<string, unknown>;
- const suppliers = (Array.isArray(row.suppliers) ? row.suppliers : []) as Record<string, unknown>[];
- const items = (Array.isArray(row.items) ? row.items : []) as Record<string, unknown>[];
- const poNos = (Array.isArray(row.poNos) ? row.poNos : []) as Record<string, unknown>[];
- return {
- suppliers: suppliers.map((r) => ({
- supplierId: Number(r.supplierId ?? r.supplierid ?? 0),
- code: String(r.code ?? ""),
- name: String(r.name ?? ""),
- })),
- items: items.map((r) => ({
- itemCode: String(r.itemCode ?? r.itemcode ?? ""),
- itemName: String(r.itemName ?? r.itemname ?? ""),
- })),
- poNos: poNos.map((r) => ({
- poNo: String(r.poNo ?? r.pono ?? ""),
- })),
- };
- }
-
- export async function fetchPurchaseOrderEstimatedArrivalSummary(
- targetDate?: string,
- filters?: PurchaseOrderChartFilters
- ): Promise<PurchaseOrderEstimatedArrivalRow[]> {
- const p = new URLSearchParams();
- if (targetDate) p.set("targetDate", targetDate);
- appendPurchaseOrderListParams(p, filters);
- const q = p.toString();
- const res = await clientAuthFetch(
- q
- ? `${BASE}/purchase-order-estimated-arrival-summary?${q}`
- : `${BASE}/purchase-order-estimated-arrival-summary`
- );
- if (!res.ok) throw new Error("Failed to fetch estimated arrival summary");
- const data = await res.json();
- return ((Array.isArray(data) ? data : []) as Record<string, unknown>[]).map((r: Record<string, unknown>) => ({
- bucket: String(r.bucket ?? ""),
- count: Number(r.count ?? 0),
- }));
- }
-
- export interface EstimatedArrivalBreakdownSupplierRow {
- supplierId: number | null;
- supplierCode: string;
- supplierName: string;
- poCount: number;
- }
-
- export interface EstimatedArrivalBreakdownItemRow {
- itemCode: string;
- itemName: string;
- poCount: number;
- totalQty: number;
- }
-
- export interface EstimatedArrivalBreakdownPoRow {
- purchaseOrderId: number;
- purchaseOrderNo: string;
- status: string;
- orderDate: string;
- supplierId: number | null;
- supplierCode: string;
- supplierName: string;
- }
-
- export interface PurchaseOrderEstimatedArrivalBreakdown {
- suppliers: EstimatedArrivalBreakdownSupplierRow[];
- items: EstimatedArrivalBreakdownItemRow[];
- purchaseOrders: EstimatedArrivalBreakdownPoRow[];
- }
-
- /** Related suppliers / items / POs for one 預計送貨 bucket (same bar filters as the donut). */
- export async function fetchPurchaseOrderEstimatedArrivalBreakdown(
- targetDate: string,
- estimatedArrivalBucket: string,
- filters?: PurchaseOrderChartFilters
- ): Promise<PurchaseOrderEstimatedArrivalBreakdown> {
- const p = new URLSearchParams();
- p.set("targetDate", targetDate);
- p.set("estimatedArrivalBucket", estimatedArrivalBucket.trim().toLowerCase());
- appendPurchaseOrderListParams(p, filters);
- const res = await clientAuthFetch(`${BASE}/purchase-order-estimated-arrival-breakdown?${p.toString()}`);
- if (!res.ok) throw new Error("Failed to fetch estimated arrival breakdown");
- const data = await res.json();
- const row = (data ?? {}) as Record<string, unknown>;
- const suppliers = (Array.isArray(row.suppliers) ? row.suppliers : []) as Record<string, unknown>[];
- const items = (Array.isArray(row.items) ? row.items : []) as Record<string, unknown>[];
- const purchaseOrders = (Array.isArray(row.purchaseOrders) ? row.purchaseOrders : []) as Record<string, unknown>[];
- return {
- suppliers: suppliers.map((r) => ({
- supplierId: (() => {
- const v = r.supplierId ?? r.supplierid;
- if (v == null || v === "") return null;
- const n = Number(v);
- return Number.isFinite(n) ? n : null;
- })(),
- supplierCode: String(r.supplierCode ?? r.suppliercode ?? ""),
- supplierName: String(r.supplierName ?? r.suppliername ?? ""),
- poCount: Number(r.poCount ?? r.pocount ?? 0),
- })),
- items: items.map((r) => ({
- itemCode: String(r.itemCode ?? r.itemcode ?? ""),
- itemName: String(r.itemName ?? r.itemname ?? ""),
- poCount: Number(r.poCount ?? r.pocount ?? 0),
- totalQty: Number(r.totalQty ?? r.totalqty ?? 0),
- })),
- purchaseOrders: purchaseOrders.map((r) => ({
- purchaseOrderId: Number(r.purchaseOrderId ?? r.purchaseorderid ?? 0),
- purchaseOrderNo: String(r.purchaseOrderNo ?? r.purchaseorderno ?? ""),
- status: String(r.status ?? ""),
- orderDate: String(r.orderDate ?? r.orderdate ?? ""),
- supplierId: (() => {
- const v = r.supplierId ?? r.supplierid;
- if (v == null || v === "") return null;
- const n = Number(v);
- return Number.isFinite(n) ? n : null;
- })(),
- supplierCode: String(r.supplierCode ?? r.suppliercode ?? ""),
- supplierName: String(r.supplierName ?? r.suppliername ?? ""),
- })),
- };
- }
-
- export type PurchaseOrderDrillQuery = PurchaseOrderChartFilters & {
- /** order = PO order date; complete = PO complete date (for received/completed on a day) */
- dateFilter?: "order" | "complete";
- /** delivered | not_delivered | cancelled | other — same as 預計送貨 donut buckets */
- estimatedArrivalBucket?: string;
- };
-
- export async function fetchPurchaseOrderDetailsByStatus(
- status: string,
- targetDate?: string,
- opts?: PurchaseOrderDrillQuery
- ): Promise<PurchaseOrderDetailByStatusRow[]> {
- const p = new URLSearchParams();
- p.set("status", status.trim().toLowerCase());
- if (targetDate) p.set("targetDate", targetDate);
- if (opts?.dateFilter) p.set("dateFilter", opts.dateFilter);
- if (opts?.estimatedArrivalBucket?.trim()) {
- p.set("estimatedArrivalBucket", opts.estimatedArrivalBucket.trim().toLowerCase());
- }
- appendPurchaseOrderListParams(p, opts);
- const q = p.toString();
- const res = await clientAuthFetch(`${BASE}/purchase-order-details-by-status?${q}`);
- if (!res.ok) throw new Error("Failed to fetch purchase order details by status");
- const data = await res.json();
- return ((Array.isArray(data) ? data : []) as Record<string, unknown>[]).map((r: Record<string, unknown>) => ({
- purchaseOrderId: Number(r.purchaseOrderId ?? 0),
- purchaseOrderNo: String(r.purchaseOrderNo ?? ""),
- status: String(r.status ?? ""),
- orderDate: String(r.orderDate ?? ""),
- estimatedArrivalDate: String(r.estimatedArrivalDate ?? ""),
- supplierId: (() => {
- const v = r.supplierId;
- if (v == null || v === "") return null;
- const n = Number(v);
- return Number.isFinite(n) && n > 0 ? n : null;
- })(),
- supplierCode: String(r.supplierCode ?? ""),
- supplierName: String(r.supplierName ?? ""),
- itemCount: Number(r.itemCount ?? 0),
- totalQty: Number(r.totalQty ?? 0),
- }));
- }
-
- export async function fetchPurchaseOrderItems(
- purchaseOrderId: number
- ): Promise<PurchaseOrderItemRow[]> {
- const q = buildParams({ purchaseOrderId });
- const res = await clientAuthFetch(`${BASE}/purchase-order-items?${q}`);
- if (!res.ok) throw new Error("Failed to fetch purchase order items");
- const data = await res.json();
- return ((Array.isArray(data) ? data : []) as Record<string, unknown>[]).map((r: Record<string, unknown>) => ({
- purchaseOrderLineId: Number(r.purchaseOrderLineId ?? 0),
- itemCode: String(r.itemCode ?? ""),
- itemName: String(r.itemName ?? ""),
- orderedQty: Number(r.orderedQty ?? 0),
- uom: String(r.uom ?? ""),
- receivedQty: Number(r.receivedQty ?? 0),
- pendingQty: Number(r.pendingQty ?? 0),
- }));
- }
-
- export async function fetchPurchaseOrderItemsByStatus(
- status: string,
- targetDate?: string,
- opts?: PurchaseOrderDrillQuery
- ): Promise<PurchaseOrderItemRow[]> {
- const p = new URLSearchParams();
- p.set("status", status.trim().toLowerCase());
- if (targetDate) p.set("targetDate", targetDate);
- if (opts?.dateFilter) p.set("dateFilter", opts.dateFilter);
- if (opts?.estimatedArrivalBucket?.trim()) {
- p.set("estimatedArrivalBucket", opts.estimatedArrivalBucket.trim().toLowerCase());
- }
- appendPurchaseOrderListParams(p, opts);
- const q = p.toString();
- const res = await clientAuthFetch(`${BASE}/purchase-order-items-by-status?${q}`);
- if (!res.ok) throw new Error("Failed to fetch purchase order items by status");
- const data = await res.json();
- return ((Array.isArray(data) ? data : []) as Record<string, unknown>[]).map((r: Record<string, unknown>) => ({
- purchaseOrderLineId: 0,
- itemCode: String(r.itemCode ?? ""),
- itemName: String(r.itemName ?? ""),
- orderedQty: Number(r.orderedQty ?? 0),
- uom: String(r.uom ?? ""),
- receivedQty: Number(r.receivedQty ?? 0),
- pendingQty: Number(r.pendingQty ?? 0),
- }));
- }
-
- export async function fetchStockInOutByDate(
- startDate?: string,
- endDate?: string
- ): Promise<StockInOutByDateRow[]> {
- const q = buildParams({ startDate: startDate ?? "", endDate: endDate ?? "" });
- const res = await clientAuthFetch(`${BASE}/stock-in-out-by-date?${q}`);
- if (!res.ok) throw new Error("Failed to fetch stock in/out by date");
- const data = await res.json();
- return normalizeChartRows(data, "date", ["inQty", "outQty"]);
- }
-
- export interface TopDeliveryItemOption {
- itemCode: string;
- itemName: string;
- }
-
- export async function fetchTopDeliveryItemsItemOptions(
- startDate?: string,
- endDate?: string
- ): Promise<TopDeliveryItemOption[]> {
- const q = buildParams({ startDate: startDate ?? "", endDate: endDate ?? "" });
- const res = await clientAuthFetch(
- q ? `${BASE}/top-delivery-items-item-options?${q}` : `${BASE}/top-delivery-items-item-options`
- );
- if (!res.ok) throw new Error("Failed to fetch item options");
- const data = await res.json();
- return ((Array.isArray(data) ? data : []) as Record<string, unknown>[]).map((r: Record<string, unknown>) => ({
- itemCode: String(r.itemCode ?? ""),
- itemName: String(r.itemName ?? ""),
- }));
- }
-
- export async function fetchTopDeliveryItems(
- startDate?: string,
- endDate?: string,
- limit = 10,
- itemCodes?: string[]
- ): Promise<TopDeliveryItemsRow[]> {
- const p = new URLSearchParams();
- if (startDate) p.set("startDate", startDate);
- if (endDate) p.set("endDate", endDate);
- p.set("limit", String(limit));
- (itemCodes ?? []).forEach((code) => p.append("itemCode", code));
- const q = p.toString();
- const res = await clientAuthFetch(`${BASE}/top-delivery-items?${q}`);
- if (!res.ok) throw new Error("Failed to fetch top delivery items");
- const data = await res.json();
- return ((Array.isArray(data) ? data : []) as Record<string, unknown>[]).map((r: Record<string, unknown>) => ({
- itemCode: String(r.itemCode ?? ""),
- itemName: String(r.itemName ?? ""),
- totalQty: Number(r.totalQty ?? 0),
- }));
- }
-
- export async function fetchStockBalanceTrend(
- startDate?: string,
- endDate?: string,
- itemCode?: string
- ): Promise<StockBalanceTrendRow[]> {
- const q = buildParams({
- startDate: startDate ?? "",
- endDate: endDate ?? "",
- itemCode: itemCode ?? "",
- });
- const res = await clientAuthFetch(`${BASE}/stock-balance-trend?${q}`);
- if (!res.ok) throw new Error("Failed to fetch stock balance trend");
- const data = await res.json();
- return normalizeChartRows(data, "date", ["balance"]);
- }
-
- export async function fetchConsumptionTrendByMonth(
- year?: number,
- startDate?: string,
- endDate?: string,
- itemCode?: string
- ): Promise<ConsumptionTrendByMonthRow[]> {
- const q = buildParams({
- year: year ?? "",
- startDate: startDate ?? "",
- endDate: endDate ?? "",
- itemCode: itemCode ?? "",
- });
- const res = await clientAuthFetch(`${BASE}/consumption-trend-by-month?${q}`);
- if (!res.ok) throw new Error("Failed to fetch consumption trend");
- const data = await res.json();
- return ((Array.isArray(data) ? data : []) as Record<string, unknown>[]).map((r: Record<string, unknown>) => ({
- month: String(r.month ?? ""),
- outQty: Number(r.outQty ?? 0),
- }));
- }
-
- /** Normalize rows: ensure date key is string and numeric keys are numbers (backend may return BigDecimal/Long). */
- function normalizeChartRows<T>(
- rows: unknown[],
- dateKey: string,
- numberKeys: string[]
- ): T[] {
- if (!Array.isArray(rows)) return [];
- return rows.map((r: unknown) => {
- const row = r as Record<string, unknown>;
- const out: Record<string, unknown> = {};
- out[dateKey] = row[dateKey] != null ? String(row[dateKey]) : "";
- numberKeys.forEach((k) => {
- out[k] = Number(row[k]) || 0;
- });
- return out as T;
- });
- }
|