Skip to content

Pallet Working Release - #4

Open
roncodes wants to merge 36 commits into
mainfrom
dev-v0.0.2
Open

Pallet Working Release#4
roncodes wants to merge 36 commits into
mainfrom
dev-v0.0.2

Conversation

@roncodes

@roncodes roncodes commented Jul 19, 2024

Copy link
Copy Markdown
Member

Pallet v0.0.2 working release — inventory & warehouse management extension.

Completion progress (auto-maintained)

Last updated: 2026-08-17 (iter 3) · phased roadmap per Ron's direction · autonomous completion loop

Phase A — Functional & visual completion (PRIORITY 1) — in progress

  • Dev toolchain runnable (frozen install, pest bootstrap, lint green) — c33317b, 39fae16
  • Backend test harness (Testbench + SQLite lane; production-like save paths) — 43885fe, b0f7957
  • Frontend boot (prod build green; suite executes on Ember 5.4) — 8d46cc9
  • Stock correctness 7/7: transfer-cancel loss · ledger repair · cycle-count atomicity · PO receive locks · adjustment lock · reservation txn/locks · storefront guards + link uniqueness · inventory create txn — b0f7957, c1e4c4b, b3c8a65
  • Lifecycle gaps: wave→pick-list generation · picking moves stock · expiration sweep · status guards
  • Schema/model integrity: sales-order customer columns · warehouse hierarchy relations · link uniqueness
  • Per-screen visual verification (catalog · facilities · inventory · POs · SOs · operations · analytics)
  • Frontend defect list closed (dead buttons, missing controller/re-exports, i18n, console.*)
  • Storefront integration correct (14 endpoints, idempotency, expiration)

Phase B — Warehouse layout designer — not started

  • Interactive editor (zones/aisles/racks/bins/docks on floor plan, persisted)
  • Viewer (utilization overlays, click-through to inventory)

Phase C — User testing guide — not started

Phase D — Public consumable API (Fleetbase v1 conventions) — not started

Phase E — Postman collection (~/Development/fleetbase/postman) — not started

Phase F — 100% coverage · CI · codecov · README badge (LAST) — not started

@roncodes roncodes changed the title upgraded dependencies v0.0.2 Jul 19, 2024
roncodes and others added 5 commits October 2, 2024 18:32
This comprehensive refactoring transforms Pallet into a complete enterprise-grade
Warehouse Management System following the Fleetops architecture patterns.

Backend Enhancements:
- Added 11 new models for enterprise WMS features:
  * PickList & PickListItem for warehouse picking operations
  * Wave for wave-based picking management
  * CycleCount & CycleCountItem for inventory accuracy
  * StockTransfer & StockTransferItem for inter-warehouse transfers
  * BinLocation for detailed location tracking
  * WarehouseZone for zone management
  * InventoryReservation for order allocations
  * ProductKitComponent for kit/bundle management

- Enhanced existing models:
  * Product: Added tracking flags, reorder points, shelf life, kit support
  * Inventory: Added lot/serial tracking, multi-UOM, reserved quantities
  * Warehouse: Added zones, bins, capacity tracking, utilization metrics

Frontend Refactoring:
- Refactored Product components to modular Fleetops pattern:
  * product/form.hbs & .js - Comprehensive form component
  * product/details.hbs & .js - Read-only detail view
  * product/panel-header.hbs & .js - Panel header component
  * product/pill.hbs & .js - Compact display component

- Modernized templates:
  * Updated products/index to use Layout::Resource::Tabular
  * Updated products/index/details to use Layout::Resource::Panel
  * Updated products/index/edit to use Layout::Resource::Panel

Database Schema:
- Created comprehensive migration for all new tables and enhanced columns
- Added proper indexes and foreign key constraints
- Supports lot/serial tracking, reservations, and advanced WMS operations

Features Implemented:
✓ Lot/batch and serial number tracking
✓ Inventory reservations (soft/hard)
✓ Pick list management with multiple strategies
✓ Wave-based picking
✓ Cycle counting with variance tracking
✓ Inter-warehouse stock transfers
✓ Bin location and zone management
✓ Kit/bundle product support
✓ Expiry date tracking and alerts
✓ Reorder point management

This establishes the foundation for a complete, scalable, enterprise-grade WMS.
Refactored all remaining resources to follow Fleetops architecture pattern:
- Inventory: form, details, panel-header, pill components
- Warehouse: form, details, panel-header, pill components
- Supplier: form, details, panel-header, pill components
- Purchase Order: form, details, panel-header, pill components
- Sales Order: form, details, panel-header, pill components
- Batch: form, details, panel-header, pill components

Updated all templates to use modern Layout components:
- All index templates now use Layout::Resource::Tabular
- All details templates now use Layout::Resource::Panel with TabNavigation
- All edit templates now use Layout::Resource::Panel with form integration

This completes the frontend modernization across all 7 resources:
✓ Product (previously completed)
✓ Inventory
✓ Warehouse
✓ Supplier
✓ Purchase Order
✓ Sales Order
✓ Batch

Total: 48 new component files + 84 app exports + 18 template updates
Enterprise WMS Refactoring with Fleetops Architecture
@roncodes roncodes changed the title v0.0.2 Working Pallet Release Feb 28, 2026
roncodes and others added 7 commits February 28, 2026 02:37
Backend fixes:
- [B1] Fix WarehouseDockController class name (was WarehouseSectionController)
- [B2] Fix PurchaseOrder resource orde_date_at typo -> order_date_at; add order_number alias for public_id
- [B3] Fix SalesOrder resource orde_date_at typo -> order_date_at; add order_number alias for public_id
- [B4] Rename WarehouesDock.php -> WarehouseDock.php (filename typo fix)
- [B5] Rename SuplierFilter.php -> SupplierFilter.php (filename typo fix)
- [B6] Fix PurchaseOrderController payload key: purchaseOrder -> purchase_order
- [B7] Fix SalesOrderController payload key: salesOrder -> sales_order
- [B8] Fix Audit model User namespace: App\Models\User -> Fleetbase\Models\User
       Add HasPublicId trait, SoftDeletes, align fillable with actual DB schema
- [B9] Add all missing WMS fields to Inventory resource (status, bin_location_uuid,
       zone_uuid, lot_number, serial_number, uom, reserved_quantity, available_quantity,
       max_quantity, reorder_point, unit_cost, received_at, last_counted_at)
- [B10] Fix PalletServiceProvider error messages (Storefront -> Pallet)
- [B11] Fix InventoryController::createRecord - explicitly set batch_uuid on inventory
        record; add received_at timestamp; improve batch_number generation
- [B12] Fix StockAdjustment resource - use incrementing_id; add missing fields
        (inventory_uuid, warehouse_uuid, reason, notes, adjustment_type)
- [B13] Create missing Audit HTTP Resource class

Frontend fixes:
- [F1] Build out Audit model with all attributes, relationships, and computed properties
- [F2] Build out AuditsIndexController with columns, search, queryParams, and tracking
- [F3] Fix routes.js - add index sub-routes to audits, reports, and batch parent routes
- [F4] Build out audits/index.hbs template with Layout::Resource::Tabular
- [F5] Fix inventory model - add all missing WMS fields (bin_location_uuid, zone_uuid,
       lot_number, serial_number, uom, reserved_quantity, available_quantity, max_quantity,
       reorder_point, unit_cost, received_at, last_counted_at, isLowStock, isExpired)
       Fix supplier relationship type (vendor -> supplier)
- [F6] Fix purchase-order model - add order_number, order_date_at, currency, meta attrs
       Fix supplier relationship type (vendor -> supplier)
- [F7] Fix sales-order model - add order_number, currency, meta attrs
       Fix supplier relationship type (vendor -> supplier)
- [F8] Fix inventory-form-panel.hbs - fix @onchange on Supplier ModelSelect
       (was passing string value, now correctly uses fn (mut ...))
- [F9] Fix @isResizeble typo -> @isResizable across ALL form panel components:
       inventory, warehouse, supplier, purchase-order, sales-order, batch,
       product, stock-adjustment, warehouse-editor
- [F10] Create addon/serializers/audit.js
- [F11] Complete translations/en-us.yaml - add all ~120 missing translation keys
        across resource, common, product, inventory, warehouse, supplier,
        purchase-order, sales-order, batch, and audit namespaces
## What changed

### Backend

**Migration (modified, not new)**
- Refactored pallet_audits table: added event_type column (indexed), renamed
  auditable_uuid/type to subject_uuid/subject_type for clarity, added
  scheduled_at/completed_at for time-bounded events, added composite indexes
  on (company_uuid, event_type) and (subject_uuid, subject_type)

**New: AuditEventType constants class**
- Defines all machine-readable event type keys: stock_adjustment, cycle_count,
  po_received, so_fulfilled, stock_transfer, inventory_created, batch_created, etc.

**New: HasOperationalAuditTrail trait**
- Reusable trait any Pallet model can use to call logAuditEvent()
- Automatically captures company_uuid, performed_by_uuid, subject, and meta

**New: AuditService**
- Centralised service for programmatic audit logging from controllers
- Provides log() and logForModel() helpers

**Refactored: Audit model**
- Now immutable (no direct create/update/delete via API)
- Added event_type, subject_uuid/type, scopes (byEventType, bySubject, recent)
- Added SoftDeletes, HasPublicId, correct Fleetbase User namespace

**Refactored: AuditController**
- Now read-only: index() and show() only
- Added eventTypes() endpoint: GET /pallet/v1/audits/event-types
- Filters by event_type, subject_type, performed_by_uuid, date range

**Refactored: Audit HTTP Resource**
- Returns event_type, subject_label, action, reason, meta, performedBy

**Refactored: routes.php**
- Replaced generic fleetbaseRoutes('audits') with explicit read-only routes
- Added GET /pallet/v1/audits/event-types endpoint

**WMS model integrations**
- StockAdjustment: logs STOCK_ADJUSTMENT event on created()
- CycleCount: logs CYCLE_COUNT event on complete() and approve()
- PurchaseOrder: markAsReceived() logs PO_RECEIVED event
- SalesOrder: markAsFulfilled() logs SO_FULFILLED event
- StockTransfer: logs STOCK_TRANSFER event on ship() and receive()

**Spatie LogsActivity added to 8 primary models**
- Product, Inventory, Warehouse, Supplier, Batch, PurchaseOrder, SalesOrder,
  StockAdjustment now all use LogsActivity with logOnly() + logOnlyDirty()
- Consistent with how core-api handles User, Alert, File, etc.

### Frontend

**Audit model**
- Updated to use event_type, subject_uuid/type instead of auditable_uuid/type
- Added eventTypeLabel, subjectLabel, eventTypeBadgeClass computed properties
- Added createdAgo with addSuffix option

**Audits/index controller**
- queryParams updated: event_type + subject_type replace auditable_type
- Columns updated: Event Type, Action, Subject, Subject ID, Reason, Performed By, Date
- Added eventTypeOptions array for dropdown filter
- Added filterByEventType() and clearFilters() actions

**Audits/index template**
- Added event type filter dropdown in subheader slot
- Added clear filters button (shown when any filter is active)
- Set @Cancreate=false and @canDelete=false (immutable trail)

**Audit serializer**
- Removed createdBy embedded relation (no longer in schema)

**Translations**
- Expanded audit section with event-types, filter labels, search placeholder
- Added common.clear_filters and common.no-records keys
Backend:
- Add migration: purchase_order_items and sales_order_items tables
  with full schema: product_uuid, warehouse_uuid, quantity, quantity_received/
  quantity_fulfilled, outstanding_quantity, unit_price, unit_cost, total_price,
  currency, sku, lot_number, serial_number, expiry_date, unit_of_measure,
  status, notes, meta, received_at/fulfilled_at
- Add PurchaseOrderItem model with recalculateTotalPrice(), relationships to
  Product, Warehouse, PurchaseOrder; LogsActivity trait
- Add SalesOrderItem model with recalculateTotalPrice(), relationships to
  Product, Warehouse, Inventory, SalesOrder; LogsActivity trait
- Add hasMany items() + item_count/total_value aggregates to PurchaseOrder model
- Add hasMany items() + item_count/total_value aggregates to SalesOrder model
- Add PurchaseOrderItemController (index, store, update, destroy)
- Add SalesOrderItemController (index, store, update, destroy)
- Add PurchaseOrderItem and SalesOrderItem HTTP Resources
- Update PurchaseOrder resource to include items, item_count, total_value
- Update SalesOrder resource to include items, item_count, total_value
- Add nested item routes: GET/POST/PUT/DELETE for both PO and SO items

Frontend:
- Add purchase-order-item Ember model with all attributes + computed helpers
- Add sales-order-item Ember model with all attributes + computed helpers
- Add hasMany items + item_count/total_value to purchase-order Ember model
- Add hasMany items + item_count/total_value to sales-order Ember model
- Add purchase-order-item and sales-order-item serializers
- Update purchase-order and sales-order serializers to embed items
- Add purchase-order-panel/items tab component (HBS + JS) with:
  - Inline add row with product ModelSelect, SKU, quantity, unit price
  - Inline edit row per item
  - Read-only rows showing product, SKU, qty, qty received, unit price, total
  - Status badge per item
  - Delete per item
  - Disabled when order is received/cancelled
- Add sales-order-panel/items tab component (HBS + JS) with same pattern
  (qty fulfilled instead of qty received)
- Wire Items tab into purchase-order-panel and sales-order-panel components
- Expand translations: purchase-order.line-items.* and sales-order.line-items.*
Backend:
- PurchaseOrderController: full receive() action with DB transaction,
  line-item iteration, inventory create/increment, lot/serial/expiry/bin
  tracking, PO status transition (partial/received), audit trail logging
- SalesOrderController: full fulfill() action with pre-flight stock check,
  FEFO inventory selection, available_quantity deduction, SO item status
  tracking, SO status transition (partial/fulfilled), audit trail logging
- routes.php: POST purchase-orders/{id}/receive and sales-orders/{id}/fulfill

Frontend:
- receive-purchase-order-form-panel: order summary, per-item receipt rows
  with ordered/received/outstanding quantities, lot/expiry/notes inputs,
  submits to API, calls onReceived callback on success
- fulfill-sales-order-form-panel: order summary, FEFO notice, per-item
  fulfillment rows with ordered/fulfilled/outstanding quantities, notes
  input, submits to API, calls onFulfilled callback on success
- context-panel.js: registered receiving intent for purchaseOrder and
  fulfilling intent for salesOrder
- purchase-orders/index.js: receivePurchaseOrder() action, improved columns
- sales-orders/index.js: fulfillSalesOrder() action, improved columns
- translations/en-us.yaml: added receive and fulfill translation keys
…emplates

Both purchase-order-panel/items.hbs and sales-order-panel/items.hbs had
the {{#if (eq this.editingItem.id item.id)}} block incorrectly closed
with {{/each}} instead of {{/if}}, with the {{/each}} for the outer
each loop also missing. This caused a Babel build error:
  'if doesn't match each - 28:23'

Fixed both files:
  - {{/each}} on line 89 replaced with {{/if}}
  - {{/each}} added on line 90 to correctly close the outer each loop
@roncodes roncodes changed the title Working Pallet Release Pallet Working Release Feb 28, 2026
roncodes and others added 12 commits March 1, 2026 15:29
- extension.js: register 'pallet' dashboard via widgetService.registerDashboard()
  and 8 widgets via widgetService.registerWidgets() using correct Widget +
  ExtensionComponent pattern (Widget/ExtensionComponent from @fleetbase/ember-core/contracts)
  Removed unused Hook import. Default widgets: inventory-summary, low-stock,
  po-status, so-status, recent-activity. Optional: stock-value, expiring-stock,
  top-products.

- templates/home.hbs: replaced bare {{outlet}} with <Dashboard> component using
  @defaultDashboardId='pallet', @defaultDashboardName='Pallet Dashboard',
  @extension='pallet' inside <Layout::Section::Body> with overflow scroll.

Frontend widget components (widget/ namespace):
- widget/inventory-summary: 5-KPI banner (SKUs, units, value, warehouses, low-stock)
- widget/low-stock: table of products at/below min_stock_level
- widget/po-status: 4-status badge grid + recent PO list
- widget/so-status: 4-status badge grid + recent SO list
- widget/recent-activity: scrollable audit trail feed with event icons
- widget/stock-value: horizontal bar chart of value per warehouse
- widget/expiring-stock: table of batches expiring within 30 days
- widget/top-products: ranked bar chart of most-moved products

Backend:
- MetricsController: 7 read-only endpoints (inventory-summary, low-stock,
  po-status, so-status, stock-value, expiring-stock, top-products), all scoped
  to session company_uuid
- routes.php: added metrics prefix group with all 7 endpoints under
  fleetbase.protected middleware
PHP fatal error: 'Cannot declare class CreateOrderItemsTables, because the
name is already in use' was caused by a naming conflict with another package
that registers a migration class with the same name.

All 14 named-class migrations have been converted to the anonymous class
pattern (return new class extends Migration { ... };) which is the modern
Laravel standard and completely eliminates cross-package class name conflicts.

The 2024_11_06_create_wms_tables.php and 2024_11_07_create_order_items_tables.php
files were already using the anonymous pattern and were left unchanged.
pallet_purchase_order_items and pallet_sales_order_items were referencing
'pallet_products' and 'pallet_warehouses' which do not exist as standalone
tables. The correct backing tables are:

  - pallet_products  → 'entities'  (Product extends FleetOps Entity)
  - pallet_warehouses → 'places'   (Warehouse extends Fleetbase Place)

The 'pallet_inventory' reference on sales_order_items is correct and unchanged.

All other Pallet migrations already use the correct 'entities' and 'places'
table names consistently.
pallet_sales_order_items referenced 'pallet_inventory' but the actual
table created by the inventory migration is 'pallet_inventories' (plural).

Fixed: ->on('pallet_inventory') → ->on('pallet_inventories')
…et_warehouses table

- Create new pallet_warehouses migration with WMS-specific fields:
  code, type, status, capacity, current_utilization, floor_area_sqm,
  operating_hours, timezone, phone, email, manager_uuid, total_docks,
  is_active, is_default, meta, place_uuid (FK to places table)

- Update 8 existing migrations to reference pallet_warehouses instead
  of places for warehouse_uuid foreign keys

- Rewrite Warehouse PHP model to extend Model (not Place) with:
  - place_uuid belongsTo(Place) for geographic/address data
  - company(), createdBy(), manager() relationships
  - All WMS hasMany relationships preserved
  - getAddressAttribute() proxy to linked Place
  - getTotalInventoryValue() using entities table

- Update WarehouseController to create/update linked Place from
  address fields on create/update operations

- Rewrite WarehouseResource to proxy address fields from linked Place
  and include new WMS-specific fields (code, type, status, capacity,
  utilization_percentage, floor_area_sqm, operating_hours, etc.)

- Update WarehouseFilter to remove type=pallet-warehouse constraint
  and add proper type/status/isActive filter methods

- Update 9 Pallet models to use Warehouse class instead of
  FleetOps\Place for warehouse_uuid relationships:
  Inventory, WarehouseSection, WarehouseDock, WarehouseZone,
  BinLocation, CycleCount, StockTransfer, PickList, Wave,
  InventoryReservation

- Rewrite Ember warehouse model to extend Model (not PlaceModel)
  with all WMS attributes, place belongsTo, and computed properties

- Update warehouse serializer to embed place, sections, docks, zones

- Update warehouse-form-panel with two content panels:
  'Warehouse Details' (name, code, type, status, capacity, phone,
  email, is_active, is_default) and 'Address' (street, city, etc.)

- Update warehouse/details.hbs to show new WMS fields (code, type,
  status, is_active) replacing the old is_3pl field

- Expand translations with new warehouse field keys (code, type,
  status, is-active, is-default, email, phone, floor-area, timezone,
  total-bins, total-docks, total-zones, utilization)
  and add common.active/inactive keys
@ukane-philemon

Copy link
Copy Markdown

Hello @roncodes!

Well done!

- align ember-concurrency to ^4.0.6: committed pnpm-lock.yaml resolved 4.0.6
  against specifier ^3.1.1, so 'pnpm install --frozen-lockfile' (the CI install
  step) failed on this branch; lockfile regenerated, frozen install verified
- composer test:unit now self-heals a vendor -> server_vendor symlink: pest's
  binary resolves vendor/autoload.php relative to itself, so the renamed
  vendor-dir made 'composer test:unit' unrunnable as shipped
- run pest with E_DEPRECATED suppressed so the EOL pest v1 stack boots on
  PHP 8.4 (CI's PHP 8.2 unaffected)
- ignore /vendor (compatibility symlink)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@roncodes

Copy link
Copy Markdown
Member Author

🔄 Completion loop — iteration 1 (discovery + toolchain repair)

Landed: c33317b — the branch's dev toolchain was unrunnable and is now fixed:

  • pnpm install --frozen-lockfile (CI's install step) failed: the committed lockfile resolved ember-concurrency@4.0.6 against specifier ^3.1.1. Aligned to ^4.0.6, lockfile regenerated, frozen install verified green.
  • composer test:unit could never run as shipped: Pest v1's binary resolves vendor/autoload.php relative to itself, which the renamed server_vendor dir defeats. The script now self-heals a vendor → server_vendor symlink and suppresses E_DEPRECATED so the EOL Pest v1 stack boots on PHP 8.4 (CI's 8.2 unaffected).

Baselines measured (fresh, honest numbers):

  • Backend: 1 test file (server/tests/Feature.php, 18 unit tests). Suite currently fatals before running: tests subclass controllers whose parent chain needs Illuminate\Foundation, which no installed package provides (composer resolves illuminate/* components, not the full framework). Orchestra Testbench is now justified and is the next unit of work.
  • Frontend: 91 test files, 58 are unmodified generator placeholders, 22 rendering tests assert components render empty. Suite currently fails at boot with Class extends value [object Object] in vendor.js — diagnosis in progress.
  • Coverage: none measured or enforced on either side today.

Next: backend Testbench harness → stock-correctness defect fixes with regression tests (see findings comment below) → frontend boot fix → coverage rigs → CI overhaul.

Full contract matrix and risk ledger maintained in the loop's working state; progress checklist in the PR description will stay current.

@roncodes

Copy link
Copy Markdown
Member Author

🔎 Issues uncovered — discovery sweep (iteration 1)

Consolidated findings from a full backend/frontend/tests/CI audit of this branch. Each will be fixed in this PR with a regression test, or explicitly flagged for a decision. Severity-ordered.

Critical — stock correctness

  1. [critical] Cancelling an in-transit stock transfer permanently loses stockserver/src/Models/StockTransfer.php:296: cancel() only sets status; stock deducted by ship() (:193) is never restored.
  2. [critical] Cycle-count approval can half-applyserver/src/Models/CycleCount.php:283: approve() loops applyAdjustment() per item with no outer transaction; a mid-loop failure (e.g. the reserved-stock guard) leaves the count partially applied with status completed.
  3. [high] PO receiving has an over-receive raceserver/src/Http/Controllers/PurchaseOrderController.php:151-276: transaction but no lockForUpdate on the inventory row or PO line item; concurrent receipts can exceed outstanding_quantity.
  4. [high] Reservation endpoint commits on failureserver/src/Http/Controllers/InventoryReservationController.php:81: returns response()->error() from inside the DB::transaction closure, so the transaction commits instead of rolling back; release/fulfill (:103,:114) also take no row lock (double-release race).
  5. [high] Storefront single-id release/commit are unguardedStorefrontInventoryController.php:616,:627: no transaction, no lock (unlike their -context counterparts); transitionReservationBatch (:509) opens a txn but doesn't lock rows; unlink (:91) is non-atomic.
  6. [high] Stock-adjustment raceStockAdjustmentController.php:81,:159: read-modify-write of before_quantity inside txn without lockForUpdate.
  7. [high] Expired reservations hold stock forever — expiry is only swept lazily on same-key retry or releaseContext; no scheduled job reclaims abandoned-checkout stock.

High — schema/model integrity

  1. [high] SalesOrder references dropped columnscustomer_uuid/customer_type are in $fillable and searchableColumns, and SalesOrderController:66 queries them, but migration 2023_12_05_084834 dropped both.
  2. [high] Stock ledger is unreadableInventory::transactions() joins on inventory_uuid, which the pallet_stock_transactions table doesn't have; recordStockTransaction writes the inventory UUID into source_uuid instead → the relation always returns empty. StockTransaction model also has no casts, no relations, no HasPublicId (its Ember model is completely empty too, with no serializer).
  3. [high] Broken warehouse-hierarchy relationsWarehouse::aisles()/racks() and WarehouseZone::aisles() join on warehouse_uuid/zone_uuid columns that don't exist in pallet_warehouse_aisles/_racks. The 5 legacy hierarchy controllers (sections/aisles/racks/bins/docks) are also unrouted, 5 models are missing HasPublicId (rack's $publicIdType is copy-pasted as 'warehouse_aisle'), and warehouse-dock.warehouse on the Ember side points at model place. Decision needed: complete or retire the legacy section→aisle→rack→bin hierarchy (a newer zone→bin-location hierarchy coexists).
  4. [med] Storefront link columns not unique in DB2026_06_06_000001 adds storefront_product_uuid/storefront_variant_uuid indexed but not unique; uniqueness only enforced in PHP → concurrent links can double-bind.
  5. [med] Two migrations share timestamp 2026_06_17_000001 — execution order non-deterministic in intent.

Lifecycle gaps

  1. [high] Wave::release() doesn't create pick listsserver/src/Models/Wave.php:167: docblock says "create pick lists"; body only flips status.
  2. [high] Picking never touches stockPickListItem::markPicked() (:139) updates bookkeeping only; no reservation/deduction/ledger linkage.
  3. [med] CycleCountItemController::recordCount doesn't verify the parent count is in_progress; StockTransferItemController allows item creation on non-pending transfers.

Frontend defects

  1. [med] Dead buttonsaddon/components/warehouse-editor.hbs:127,141: "Add Racks"/"Add Bins" wired to undefined this.addRacks/this.addBins; warehouse-form-panel.hbs:65 PhoneInput bound to undefined this.phone.
  2. [med] app/templates/application.js re-export missing (engine application template may not resolve in the host); addon/controllers/inventory/batches.js missing (route+template exist); analytics/reports/index/details/{index,result} declared in routes.js with templates but no route/controller files.
  3. [med] addon/utils/geojson/geo-json.js imports ./calculate-bounds which doesn't exist (currently dead code — would fail if ever imported).
  4. [med] admin/product-category async add/delete actions have no loading/disabled guards (double-submit risk); 6 console.log/console.error debug/swallow sites.
  5. [low] i18n: only 23% of templates translated; 30 of 42 controllers (all operations/inventory/orders/audits + sidebar nav) hardcode English despite 322 translation keys existing.

Process/infra

  1. [high] CI never runs on this branch — both workflows trigger only on main/tags, so all six WMS milestones landed unverified. Will extend triggers alongside the coverage/MySQL overhaul.
  2. [med] No composer.lock (removed in 671c6d7) — backend deps unpinned; CI can't do a locked install. Flagging for a lock-policy decision with the CI work.
  3. [med] pnpm version contract unpinnedpnpm-workspace.yaml uses pnpm-10-only settings, but no packageManager field and pnpm/action-setup@v4 pins nothing; pnpm 9 fails on this repo.
  4. [low] Config driftserver/config/pallet.php says version 0.0.1; README structure tree lists nonexistent dirs; ember-try still set to yarn; index.js isDevelopingAddon() always true.

Items 1–9 are the current fix queue, in order. Fixes will reply to this comment with commit SHAs.

roncodes and others added 4 commits August 11, 2026 21:45
- add orchestra/testbench ^8.36 (blocker: core-api controllers need
  Illuminate\Foundation, which only laravel/framework provides; no
  container existed for request()/session()/config() so the suite could
  not run at all)
- upgrade pest ^1 -> ^2.33 + collision ^7 (matches core-api's own dev
  harness; the EOL pest 1 stack breaks on PHP 8.4 once Laravel's
  error handler re-enables deprecations)
- server/tests/TestCase.php: Testbench app on a shared per-process SQLite
  file; the mysql connection aliases to it because core models pin
  $connection = 'mysql'; shims for core tables (companies, users, places,
  files, ...) whose real migrations are MySQL-only; runs Pallet's own
  migrations. MySQL-only contracts go to the MySQL lane.
- server/tests/Pest.php binds the TestCase; runner passes
  --test-directory server/tests (pest resolves Pest.php from there)
- rename Feature.php -> FeatureTest.php and tighten phpunit discovery to
  suffix Test.php (bare .php ingested Pest.php/TestCase.php as tests);
  migrate phpunit.xml.dist to the PHPUnit 10 schema
- make migration up/down paths SQLite-safe: split multi-column drops into
  one call each, skip dropForeign on sqlite (identical behavior on MySQL);
  rollback now runs green every test teardown
- pin runner flags: xdebug.mode=off (9x faster), memory_limit=512M,
  E_DEPRECATED suppressed

composer test:unit: 18 passed (64 assertions) in ~30s — first green
backend suite on this package.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
getDisplayNameAttribute() declares : string but fell through to
public_id, which is null on unsaved/partial records — TypeError.
Terminal fallback is now an empty string. Caught by the existing
resource-contract test once the suite became runnable.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Three changes, on this working branch rather than a separate one as requested. Only
these three files are staged — the in-progress addon edits are left alone.

1. release.yml delegates to the reusable workflow in fleetbase/fleetbase, which
   validates and pushes the tag when a dev-v* branch merges to main. The version is
   never retyped: it comes from the branch name, and the tag is refused unless
   composer.json, package.json, extension.json and RELEASE.md already agree with it.

2. create-release.yml is NEW. pallet was the only module without one, so tagging it
   published to npm and GitHub Packages and created no GitHub Release at all. Copied
   from the other modules, with body_path: RELEASE.md so the notes are the release
   body, matching fleetbase/fleetbase.

3. RELEASE.md is seeded, because the tag workflow refuses to release without notes
   whose first line names the version. Both placeholder markers must be replaced
   before a release will go through — that guard exists so the template itself can
   never be published as the release notes.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Comment on lines +9 to +22
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Publish GitHub Release
uses: softprops/action-gh-release@v2
with:
tag_name: ${{ github.ref_name }}
name: ${{ github.ref_name }}
body_path: RELEASE.md
generate_release_notes: true
draft: false
prerelease: false
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
roncodes and others added 4 commits August 17, 2026 14:22
…guard

- add @ember/legacy-built-in-components ^0.4.2 (devDep): ember-engines
  0.9's LinkToExternal extends @ember/routing LinkTo, which is not a
  constructible class on Ember 5.4 — the whole test suite failed at boot
  with 'Class extends value [object Object]'. With the package present
  (same peer resolution as fleetops and the console) ember-engines takes
  its legacy LinkComponent branch and the suite executes.
- pallet-engine.css: split two multi-layer background shorthands into
  longhands — clean-css v3 (ember-engines css pipeline) crashes parsing
  'gradient position / size, color' shorthand, which broke the
  production build. Identical rendering.
- tests/test-helper.js: QUnit testTimeout 30s — a hung await previously
  wedged the suite for 90+ minutes with no output.

Production build verified green; test suite now runs (failures are
pre-existing placeholder tests, tracked for the coverage phase).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…port

Three production defects, each with regression coverage:

- StockTransfer::cancel() on an in_transit transfer only flipped status —
  stock deducted by ship() was permanently lost. Cancel now restores
  source-warehouse stock in a transaction with row locks and records a
  transfer_cancelled ledger entry + audit event. (StockTransferTest)
- the stock ledger was unwritable and unreadable: recordStockTransaction()
  inserts transaction_date_at but the table never had the column (every
  ledger insert failed), and Inventory::transactions() joins on
  inventory_uuid which also didn't exist. Repair migration adds both plus
  a transaction_type index; StockTransaction gains real casts, relations,
  and HasPublicId; the writer now populates inventory_uuid.
- Warehouse + WarehouseController imported Fleetbase\Models\Place, which
  doesn't exist (Place lives in Fleetbase\FleetOps\Models) — the place()
  relation and address accessors fatally errored whenever touched.

Harness: TestCase now registers responsecache/activitylog providers
(inert in tests) and applies core-api's class expansions (Str::humanize
etc.) so model save paths run like production.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
composer test:lint now passes in check mode.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@roncodes

Copy link
Copy Markdown
Member Author

🔄 Completion loop — replanned roadmap + first stock-correctness fixes

Roadmap replanned per Ron's direction — new phase order: A functional + visual completion (console at localhost:4200 as ground truth) → B interactive warehouse layout designer → C user testing guide → D public consumable API (Fleetbase v1 conventions) → E Postman collection → F coverage/CI/codecov last. The PR checklist below will track these phases.

Landed this iteration:

  • 8d46cc94 — frontend boot fixed: the engine now builds for production (clean-css v3 crash in pallet-engine.css) and the test suite executes on Ember 5.4 (@ember/legacy-built-in-components for ember-engines' LinkToExternal, matching fleetops/console; plus a QUnit hang guard).
  • b0f79578three production defects fixed, with regression tests:
    1. Stock-loss bug: cancelling an in-transit stock transfer permanently lost the shipped stock — cancel now restores source-warehouse stock transactionally with row locks, a transfer_cancelled ledger entry, and an audit event.
    2. The stock ledger never worked: every recordStockTransaction() insert failed (transaction_date_at column was never created), and Inventory::transactions() joined on a nonexistent inventory_uuid. Repair migration + model overhaul (casts, relations, HasPublicId) — the ledger now writes and reads.
    3. Fatal import: Warehouse/WarehouseController imported Fleetbase\Models\Place (doesn't exist — Place is in Fleetbase\FleetOps\Models); the place() relation and address accessors crashed whenever touched.
  • 39fae164 — php-cs-fixer sweep: composer test:lint passes in check mode for the first time on this branch.
  • Merged 29fa6866 (release automation) from upstream.

Backend suite: 21/21 green. Test harness now boots model save paths like production (responsecache/activitylog providers, core-api class expansions).

Next: remaining stock-correctness queue — cycle-count atomic approve, PO receive locks, stock-adjustment lock, reservation error-in-txn rollback + locks, storefront single release/commit guards, inventory create transaction — then per-screen visual verification begins.

Waiting on Ron (non-blocking): ① commit/push the WIP in the main checkout (product/form.hbs, pallet-engine.css) so the console can fast-forward to these fixes; ② one-time console login in the Claude browser pane for visual verification.

@roncodes

Copy link
Copy Markdown
Member Author

↳ Findings update (original findings comment): item 1 (transfer-cancel stock loss) and item 9 (unreadable stock ledger) are fixed in b0f7957 with regression tests. Newly uncovered beyond the original list, also fixed in the same commit: the ledger write path never worked (missing transaction_date_at column → every insert failed) and a fatal Fleetbase\Models\Place mis-import in Warehouse/WarehouseController.

Stock-correctness fixes, each with regression tests:

- CycleCount::approve() applied per-item adjustments in independent
  transactions; a mid-loop failure (reserved-stock guard) left earlier
  adjustments committed with the count still 'completed'. The whole
  approval — every adjustment plus the status change — is now one
  transaction. CycleCountItem::recordCount() also now requires the parent
  count to be in_progress.
- PurchaseOrderController::receive() read the PO line item and inventory
  row without locks inside its transaction — concurrent receipts could
  exceed outstanding_quantity. Both reads now lockForUpdate.
- StockAdjustmentController::resolveInventory() same unlocked
  read-modify-write — now locked.
- InventoryReservation::release()/fulfill() checked status on the
  in-memory instance only; a stale instance could release or commit
  stock twice. Both now re-read the row under lock inside the
  transaction. InventoryReservationController::createRecord() returned
  an error response from inside its transaction closure (committing
  instead of rolling back) — it now throws.
- reordered catch blocks in Warehouse/Inventory/SalesOrder/PurchaseOrder
  controllers where catch \Exception preceded the specific handlers,
  making them unreachable.

Test harness: the default DB connection is now the same connection NAME
models pin ('mysql') — connection names get separate PDO handles, so
DB::transaction() on a different default never covered model writes;
atomicity tests were silently meaningless before this.

Suite: 31 passed (110 assertions), stable across 3 consecutive runs.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@roncodes

Copy link
Copy Markdown
Member Author

🔄 Iteration update — stock-correctness queue 5/7 done

c1e4c4b3 — four more defect clusters fixed with regression tests (backend suite 31/31, stable across 3 runs):

  1. Cycle-count approval is now atomic — a mid-loop failure previously left counts half-applied with earlier inventory adjustments committed; count recording now also requires the count to be in progress.
  2. PO receiving locks the line item + inventory row (over-receive race closed); stock adjustments lock their inventory read too.
  3. Reservation double-release/double-fulfill closed — status is re-checked under a row lock inside the transaction; the create endpoint no longer commits on failure (throw instead of returning an error from inside the transaction closure).
  4. Unreachable catch ordering fixed in four controllers.

Also a notable harness fix: Fleetbase models pin the mysql connection name, so the test default connection must be the same name — otherwise DB::transaction() runs on a different PDO handle and covers nothing. Atomicity tests are now actually meaningful.

Next: storefront single release/commit + unlink transaction guards, inventory-create transaction — then the per-screen visual pass begins (still waiting on the main-checkout WIP commit + a one-time console login).

…, link uniqueness

- StorefrontInventoryController::unlink() saved the product and cleared
  variant links non-atomically — now one transaction
- InventoryController::createRecord() created a Batch then an Inventory
  with no transaction — a failed inventory save orphaned the batch
- storefront link uniqueness was PHP-only; unique indexes on
  pallet_products.storefront_product_uuid and
  pallet_product_variants.storefront_variant_uuid now backstop the race
  (single-id storefront release/commit and batch transitions were already
  covered by the locked in-transaction re-check added to
  InventoryReservation::release/fulfill)

Suite: 34 passed (118 assertions).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@roncodes

Copy link
Copy Markdown
Member Author

✅ Stock-correctness queue complete (7/7)

b3c8a65f closes the final cluster: atomic storefront unlink, transactional inventory+batch creation (no more orphaned batches), and database-level uniqueness for storefront links (unique indexes as the race backstop behind the existing PHP check). Backend suite: 34/34.

All seven clusters from the findings audit are now fixed with regression tests: transfer-cancel stock loss, unwritable ledger, cycle-count atomicity, PO over-receive races, adjustment locks, reservation double-release/fulfill, and the link/create transaction gaps.

Next: lifecycle gaps — wave release generating pick lists, picking actually moving stock, an expired-reservation sweep — then the sales-order customer-column repair and the per-screen visual pass.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants