Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 | 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 6x 5x 5x 5x 3x 2x 6x 6x 3x 3x 3x 1x 6x 6x 6x 2x 2x 1x 6x 6x 6x 2x 6x 6x 6x 6x 6x 6x 2x 1x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x | /**
* @file normalizers.ts
* @module api/inventory/normalizers
*
* @summary
* Data normalization utilities for inventory API responses.
* Converts tolerant backend responses into typed frontend models.
*
* @enterprise
* - Tolerant parsing: handles missing/unknown fields gracefully
* - Type narrowing via guards before unsafe operations
* - No throwing: returns null for invalid data instead of errors
* - Defensive field mapping with fallback chains
*/
import type { InventoryRow } from './types';
import {
isRecord,
pickString,
pickNumber,
pickNumberFromList,
pickStringFromList,
} from './utils';
/**
* Normalize raw backend response into typed InventoryRow.
* Returns null if required fields (id) are missing.
* Uses defensive field picking with fallback chains for flexibility.
*
* @param raw - Unknown backend response data
* @returns Normalized InventoryRow or null if validation fails
*
* @enterprise
* - Accepts multiple field name variations from backend
* - Gracefully handles missing optional fields
* - Never throws; returns null for invalid data
*/
export function normalizeInventoryRow(raw: unknown): InventoryRow | null {
if (!isRecord(raw)) return null;
const id =
pickString(raw, 'id') ??
pickString(raw, 'itemId') ??
pickString(raw, 'item_id');
if (!id) return null;
const name =
pickString(raw, 'name') ??
pickString(raw, 'itemName') ??
pickString(raw, 'title') ??
'—';
const code =
pickString(raw, 'code') ??
pickString(raw, 'sku') ??
pickString(raw, 'itemCode') ??
null;
const supplierIdRaw =
pickString(raw, 'supplierId') ??
pickString(raw, 'supplier_id');
const supplierIdNum = pickNumber(raw, 'supplierId');
const supplierId: string | number | null =
supplierIdRaw ?? (typeof supplierIdNum === 'number' ? supplierIdNum : null);
const supplierName =
pickString(raw, 'supplierName') ??
pickString(raw, 'supplier') ??
null;
const onHand = pickNumberFromList(raw, [
'quantity',
'onHand',
'availableQuantity',
'stockQuantity',
'stockQty',
'qty',
'currentQuantity',
'currentQty',
'quantityOnHand',
'onHandQuantity',
'stock',
]) ?? 0;
const minQty =
pickNumberFromList(raw, [
'minimumQuantity',
'minQty',
'min_quantity',
'minimum',
'reorderLevel',
]) ?? null;
// Backend list payload only has createdAt; surface it as updatedAt for the grid.
const updatedAt = pickStringFromList(raw, [
'updatedAt',
'updated_at',
'lastUpdate',
'lastModified',
'lastModifiedDate',
'modifiedAt',
'modified_at',
'createdAt',
'created_at',
'createdDate',
'created_date',
'created',
]) ?? null;
return {
id: String(id),
name,
code,
supplierId,
supplierName,
onHand,
minQty,
updatedAt,
};
}
|