Implementation Changes: Inventory v00
Precise code modifications for Inventory v00, keyed to the phases in specification.md and the requirements in requirements.md. Paths are repo-relative. Line numbers are from the branch baseline and are indicative — confirm against current code.
operations
Section titled “operations”Naming: DB columns total_inventory_count_amount, total_inventory_count_unit,
last_count_date_timestamp, last_count_date_time_zone. Kotlin fields
totalInventoryCount: Quantity.Value?, lastCountDate: DateTime?.
1. Entity + serializer — src/main/kotlin/.../reference/item/business/Item.kt
Section titled “1. Entity + serializer — src/main/kotlin/.../reference/item/business/Item.kt”- Add to the
Itemsealed interface (nearminQuantity, ~line 81) and theEntitydata class (near ~line 135), with= nulldefaults:val totalInventoryCount: Quantity.Value?val lastCountDate: DateTime?(importcards.arda.common.lib.domain.general.time.DateTime)
- Add one
element<...>("totalInventoryCount", isOptional = true)and oneelement<...>("lastCountDate", isOptional = true)to the hand-writtenItemSerializerSerialDescriptor(~lines 28–52), and read/write them in the serializer’s encode/decode blocks alongside the existing fields. This is the step most easily missed. - No
validate()change (REQ-INV-007).
2. DTO — src/main/kotlin/.../reference/item/api/Model.kt
Section titled “2. DTO — src/main/kotlin/.../reference/item/api/Model.kt”- In
ItemInput(~lines 36–106), addval totalInventoryCount: Quantity.Value? = nullandval lastCountDate: DateTime? = null(mirrorminQuantityat ~line 45). - In
toItem()(~lines 60–105), apply the REQ-INV-006 normalization when constructingtotalInventoryCount:- if
amount != null && unit == null→unit = "each" - if
unit != null && amount == null→amount = 0.0 - if both null →
nullPasslastCountDatethrough directly.
- if
- Keep the normalization in a small pure helper so
ItemInputModelTestcan exercise it directly.
3. Persistence — src/main/kotlin/.../reference/item/persistence/ItemPersistence.kt
Section titled “3. Persistence — src/main/kotlin/.../reference/item/persistence/ItemPersistence.kt”- In
ITEM_TABLE(~lines 29–52) add:val totalInventoryCount = root.quantityComponent<...>("total_inventory_count")(copymin_quantity)val lastCountDate = root.dateTimeComponent<...>("last_count_date")(copyOrder.deliverByinOrderPersistence.kt:53)
- In
ItemRecord(~lines 54–131): addvardelegates for both; add them to the companion insert lambda (~82–105); set them infromEntity(~107–130); rebuild them infillPayload()(~133–163).
4. Migration — src/main/resources/reference/item/database/migrations/V022__inventory_count.sql (new)
Section titled “4. Migration — src/main/resources/reference/item/database/migrations/V022__inventory_count.sql (new)”-- Inventory v00: add totalInventoryCount (Quantity) and lastCountDate (DateTime) to item.begin;alter table item add column total_inventory_count_amount DOUBLE PRECISION NULL;alter table item add column total_inventory_count_unit VARCHAR(255) NULL;alter table item add column last_count_date_timestamp TIMESTAMP NULL;alter table item add column last_count_date_time_zone VARCHAR(255) NULL;commit;Confirm the exact DateTime component column suffixes/types against
DateTimeComponent.kt (forName) before finalizing.
5. Tests
Section titled “5. Tests”ItemDDLTest— assert the four new columns.ItemInputModelTest— round-trip both fields + normalization cases (amount-only ⇒ unit “each”; unit-only ⇒ amount 0; both null ⇒ null).ItemUniverseTestData— add values to shared fixtures.AddItemTest/ItemUniverseTest/ItemEndpointTest— create, read (null when unset), update, and clear-to-null via omission.
arda-frontend-app
Section titled “arda-frontend-app”Domain carries the date as an ISO string (Decision D3); the mapper converts
to/from the ARDA DateTime shape. Grid paths: totalInventoryCountAmount,
totalInventoryCountUnit, lastCountDate.
DateTime interchange contract (from common-module DateTime /
TzIana): JSON is { timestamp: number /* epoch millis */, tz: string /* IANA name via @SerialName, e.g. "America/New_York" */ }. The frontend has no existing
precedent for this shape, so define an ArdaDateTime DTO type and two helpers:
- to domain:
dt => new Date(dt.timestamp).toISOString()(carry ISO string; keepdt.tzif the display needs the original zone). - from domain (Detail panel, full datetime): parse the local datetime-local
value in the browser tz →
{ timestamp: <millis>, tz: <browser IANA> }. - from domain (inline, date only — REQ-INV-012): given the picked date and
the hinted tz, compute the epoch millis of 12:00:00 that date in that tz
(noon avoids date roll under tz/DST) →
{ timestamp, tz }.
1. Types
Section titled “1. Types”src/types/items.ts(Item, ~lines 213–237): addtotalInventoryCount?: QuantityandlastCountDate?: string.src/types/arda-api.ts: add both toArdaItemPayloadandArdaCreateItemRequest(count as amount/unit; date as the ARDADateTimerepresentation used byOrder.deliverBy).src/constants/types.ts: add toItemFormState(count amount asnumber | '', unit asstring, date as ISOstring) and toItemCard.
2. Mappers — src/lib/mappers/ardaMappers.ts
Section titled “2. Mappers — src/lib/mappers/ardaMappers.ts”mapArdaItemToItem(~299–389): read both fields into the domain shape (convert DTODateTime→ ISO string).mapItemToArdaCreateRequest(~395+) andmapItemToArdaUpdateRequest(~597–643): write both; use the existingemptyToNull/nullToUndefinedhelpers so an empty value is omitted (clear-to-null, REQ-INV-005/022). Convert ISO string → ARDADateTime.
3. Grid columns — src/components/table/columnPresets.itemColumns.detail.tsx
Section titled “3. Grid columns — src/components/table/columnPresets.itemColumns.detail.tsx”- Add three colDefs (copy the
Min Qty/Min Unitshape atcolumnPresets.itemColumns.tsx:265–286and theCreateddate column atcolumnPresets.itemColumns.detail.tsx:285–293):{ headerName: 'Total Inventory Count', field: 'totalInventoryCount.amount', colId: 'totalInventoryCountAmount', hide: true, valueFormatter: p => String(p.value ?? '-') }{ headerName: 'Count Unit', field: 'totalInventoryCount.unit', colId: 'totalInventoryCountUnit', hide: true, cellRenderer: … ?? '-' }{ headerName: 'Last Count Date', field: 'lastCountDate', colId: 'lastCountDate', hide: true, valueFormatter: p => formatDate(p.value) }
- Include them in
buildItemsColumnDefs(columnPresets.itemColumns.tsx:414–423). - Register in
src/app/items/itemTableConfig.ts(VIEW_KEY_TO_FIELD) and add toggles insrc/app/items/sections/ColumnVisibilityMenu.tsx.
4. Inline edit — src/app/items/gridColumnEnhancers.tsx
Section titled “4. Inline edit — src/app/items/gridColumnEnhancers.tsx”getEditableCellValue(~115): addif (path === 'totalInventoryCountAmount') return d.totalInventoryCount?.amount != null ? String(d.totalInventoryCount.amount) : '';if (path === 'totalInventoryCountUnit') return d.totalInventoryCount?.unit ?? '';if (path === 'lastCountDate') return d.lastCountDate ?? '';
applyEditableCellValue(~156): add (note amount default0, not1):- amount:
const current = originalData.totalInventoryCount || { amount: 0, unit: 'each' }; const n = parseFloat(String(v ?? '')); setNested(d, 'totalInventoryCount', { ...current, amount: isNaN(n) ? 0 : n }); - unit:
const current = originalData.totalInventoryCount || { amount: 0, unit: 'each' }; setNested(d, 'totalInventoryCount', { ...current, unit: String(v ?? '').trim() || 'each' }); - date:
setNested(d, 'lastCountDate', v == null || v === '' ? undefined : String(v));
- amount:
- In the enhancer’s per-column spread (~312–322): attach
cellEditor: UnitCellEditorfortotalInventoryCountUnit, and a date cell editor (DScreateDateCellEditor()/DateCellEditor) forlastCountDate. Amount uses the default editor.
5. Detail View — src/components/items/ItemDetailsPanel.tsx
Section titled “5. Detail View — src/components/items/ItemDetailsPanel.tsx”src/constants/types.tsItemCard: add both fields;src/app/items/hooks/itemsPageUtils.tsconvertItemToItemCard(~10–25): populate them.- In the field stack (~1242–1271), add a titled “Inventory Count” group at the
end with two
ReadOnlyFieldrows (count formatted asamount unit; date viaformatDate/formatDateTime),fallback= the existing placeholder.
6. Detail Edit — src/components/items/ItemFormPanel.tsx
Section titled “6. Detail Edit — src/components/items/ItemFormPanel.tsx”- Add an “Inventory Count”
<div className={sectionClassName}>after the “Additional Info” section (~after line 2542), before the footer. - Add a
tocSectionsentry (~1222–1229) with aref. - Prefill in the item→form
useEffect(~567–674): count amount/unit and date ISO (default''/ date pre-seeds to now when empty, REQ-INV-023). - In the submit builder
newItem(~880–1122): emittotalInventoryCountonly when amount/unit present (mirrorminQuantityat ~905–911); emitlastCountDatewhen set, elseundefined(clear-to-null). - Count: number input + unit input (reuse the Min Qty pattern). Date: clearable
date-time input (native
<input type="datetime-local">viacomponents/ui/input.tsx).
7. Tests / mocks / e2e
Section titled “7. Tests / mocks / e2e”src/mocks/data/mockItems.ts: one item with both set, one with both null.- Component tests:
ItemDetailsPanel.test.tsx,ItemFormPanel.test.tsx(+.toc), column/renderer tests,ardaMappersmapper tests. - Playwright:
e2e/pages/items.page.ts,e2e/specs/items/edit-item.spec.ts,item-details.spec.ts,items-grid.spec.ts— view, inline-edit, detail-edit, clear-to-null.
documentation
Section titled “documentation”- Update the Item current-system functional reference to document both fields
(type, nullability, normalization/default behavior). Do not edit
CHANGELOG.md(PR-body changelog).
Copyright: (c) Arda Systems 2025-2026, All rights reserved
Copyright: © Arda Systems 2025-2026, All rights reserved