Book 5, Chapter 20 — Tyre Management¶
Part of the Fleet & Logistics book. Sibling chapters: Fuel (Ch19), Vehicle Register (Ch18), Service (Ch21), Inbound Logistics (Ch22). This chapter documents the complete tyre sub-ERP: a purpose-built lifecycle system with its own procurement, inventory, operations, driver workflow, and retreading, running parallel to but separate from the main inventory (Book 2) and procurement/AP (Book 3).
1. Purpose (plain language)¶
A logistics company that runs a large fleet of trucks and trailers spends an enormous amount of money on tyres. Tyres are not like ordinary consumables: each individual tyre is a serialised, high-value asset that is bought, stored, fitted to a specific wheel position on a specific vehicle, worn down over tens of thousands of kilometres, sent out for retreading (re-capping the rubber so it can be reused), fitted again, and eventually disposed of when it can no longer safely carry a load. Because a single tyre can be worth as much as a small appliance and can travel between depots, vehicles and outside suppliers many times in its life, the business needs to track each one by serial number from cradle to grave.
The Tyre Management module is that cradle-to-grave system. It mirrors the shapes you already know from the rest of Bizwiz — you procure tyres with purchase orders and approvals, you receive them into a store with a goods-received note (GRN), you pay suppliers through advance payments and supplier invoices, you hold inventory in named locations, you run stock takes, and you transfer stock between depots — but it does all of this in a set of tables that belong to tyres alone. It never touches the main wa_stock / general-inventory tables. The only place it reaches back into the shared financial spine is at the very end of the procurement chain, where a tyre supplier invoice posts a real creditor transaction and general-ledger entries so the finance team sees tyre spend alongside everything else.
Layered on top of the procure-store-fit-dispose cycle is the most distinctive piece: a driver-facing workflow. Drivers, from a mobile/API surface, report the tyres on their vehicle (their serials, condition, mileage). The office then runs a two-track maker-checker process — one track handles mismatches (the tyre a driver reports doesn't match what the system thought was on that wheel, which may mean a driver swapped or lost a tyre and should be charged), and the other track handles replacement requests (the driver needs a new tyre, which triggers verification, fitting, and an incentive calculation that rewards or penalises the driver based on how much mileage they actually got out of the old tyre versus its design life).
So the module answers, at any moment: where is every tyre, what condition is it in, how much did it cost, which vehicle and wheel position is it on, how many kilometres has it done, and is any driver owed an incentive or liable for a charge because of it.
2. Users & roles (and the many permission strings)¶
There is no single "tyre manager" role; access is gated model-by-model with can('view', '<model-string>') checks in the sidebar and (by inference) policy/gate checks in controllers. The top-level gate is can('view', 'tyre-management') (fleet.blade.php:302), which reveals the whole Tyres Management treeview. Within it, five sub-groups each show only if the user holds at least one child permission.
The permission strings, grouped exactly as the sidebar groups them (fleet.blade.php:304-346):
Procurement group ($tyreProcurementModels, line 304):
- tyre-purchase-orders — raise/edit tyre LPOs
- tyre-purchase-order-approvals — approve/reject LPOs (checker)
- tyre-advance-payments — supplier advance payments & allocations
- tyre-receipts — receive tyres (GRN maker)
- tyre-grn-approvals — approve GRNs (checker)
- tyre-supplier-invoices — book supplier invoices (the AP/GL seam)
Inventory group ($tyreInventoryModels, line 305):
- tyres-listing — the full tyre register (route admin.tyres.all)
- vehicle-tyres — tyres currently fitted to vehicles (route admin.tyres.index)
- tyre-store-status-report, tyre-store-movement-report — store analytics
- tyre-stock-take — stock takes (note: sidebar var checks tyre-stock-take, singular, at line 323)
- tyre-transfers — inter-location transfers
Operations group ($tyreOperationsModels, line 306):
- bulk-tyre-movements — the bulk-action console (fit, retread, dispose, rotate, remove, status-change)
- tyre-movement-approvals — approve movement requests (checker)
- tyre-disposals — approve disposals
- driver-tyre-puncture — puncture reports (permission string differs from model slug puncture-reports; see line 330)
Driver-workflow group ($tyreDriverWorkflowModels, line 307):
- driver-tyre-requests — office view of driver-submitted requests
- request-verify, mismatch-verify, mismatch-approve — the maker-checker tracks
- tyre-fitting-workflow — fit the approved replacement
- driver-tyre-incentives, incentive-approve — incentive queue and its approver
Retreading group ($tyreRetreadingModels, line 308):
- tyre-retread-requests, tyre-retread-lpos
Plus tyres-configuration (line 346) for the master-data admin (brands, series, statuses, locations, positions, patterns, and the "cleanup" tabs).
The maker-checker pattern recurs throughout: the maker permission (e.g. tyre-receipts, tyre-purchase-orders, driver-tyre-requests) is separate from the checker permission (tyre-grn-approvals, tyre-purchase-order-approvals, mismatch-approve, incentive-approve). This is a deliberate segregation-of-duties design and it is enforced at the permission-string level, so the same person can be granted both in a small depot.
3. Processes¶
All admin routes live under one of two prefixes, both aliased to the admin.tyres.* name space:
- prefix => 'tyres-management' (routes/web.php:2360) — procurement, inventory, operations, retreading, configuration.
- prefix => 'tyre-workflow' (routes/web.php:2848) — the driver maker-checker tracks (mismatch-verify, mismatch-approve, request-verify, incentive-approve, fitting, puncture-reports).
There is also a legacy set of Route::resource definitions immediately after the tyre-workflow block (routes/web.php:2909+): tyre_fitting, tyre_removal, tyre_transfer, tyre_retreading, tyre_inventories, tyreposition, receive-tyre-purchase-order, and tyre-purchase-orders. These bind to controllers in app/Http/Controllers/Admin/ (not the Admin/Fleet/ set) — TyreFittingController, TyreRemovalController, ReceiveTyrePurchasedOrderController, etc. They correspond to the older tyre implementation (see the 2023_09_08_* migrations for tyre_fittings, tyre_inventories, wa_tyre_purchase_orders). The current module is the Admin/Fleet/* controllers on the two prefixed groups; the legacy Admin/* controllers are treated here as a variant (§6).
3.1 Procurement (procure-to-pay for tyres)¶
Trigger → states → routes → outcome.
- Raise LPO. A buyer creates a tyre purchase order.
tyre_purchase_ordersstartsstatus = 'draft'and moves throughpending_approval → approved | rejected → received | cancelled(enum,2026_03_02_100000_create_tyre_purchase_orders_table.php:18). Routes:purchase-orders.create/store/edit/update, thenpurchase-orders.submitto send for approval,purchase-orders.cancel, andpurchase-orders.toggle-advance-payment(web.php:2489-2502). The LPO carrieslpo_number(unique),supplier_id,order_date,expected_quantity,estimated_amount, and fullapproved_by/approved_at/rejected_by/rejected_at/rejection_reasonaudit columns. - Approve LPO (checker).
TyrePurchaseOrderApprovalController—purchase-order-approvals.approve/.reject(web.php:2505-2513). Separatedata.approved/data.rejecteddatatables. - Advance payment (optional). If the LPO is flagged for advance payment,
TyreAdvancePaymentControllermanages a mini-lifecycle of its own:store → submit-for-approval → mark-complete → final-approve → archive, plus proforma upload, WHT-voucher attach, invoice allocation/deallocation andsettle-invoices(web.php:2523-2545). Tables:tyre_advance_paymentsandtyre_advance_payment_allocations. - Receive (GRN maker).
TyreReceiptController—receipts.create/store/submit/reopen(web.php:2461-2472). Atyre_receiptsrow isstatus = 'draft'until submitted (enum draft|completed,2025_12_16_235816_create_tyre_receipts_table.php). Receiving is where individualtyresrows are created against a brand/series with serial numbers. - GRN approval (checker).
TyreGrnApprovalController—grn-approvals.approve/reject(web.php:2475-2486). - Supplier invoice (AP/GL seam).
TyreSupplierInvoiceController.store(web.php:2516) books the invoice against a completed receipt — see §5 for the accounting detail.
3.2 Inventory¶
- Register / listing.
admin.tyres.all→TyreController@allTyres(full register, exportable,web.php:2448).admin.tyres.indexis "Vehicle Tyres" (fitted set,web.php:2447).tyres.create/store, bulk-upload and Excel import/validate flows also live here (web.php:2437-2460). - Store status & movement reports.
store-status-report,store-movement-report(+.details/.batch/.export) (web.php:2449-2455). - Stock take.
TyreStockTakeController— a full count/reconcile/approve cycle.tyre_stock_takes.statusenum =in_progress → submitted → approved | rejected(2026_02_11_100000_create_tyre_stock_takes_table.php). Routes includecount,submit-counts,reconcile,submit,approve/reject, per-itemserialsandassign-charge(a stock-take shortage can raise a charge). Supporting tables:tyre_stock_take_items,tyre_stock_take_serials,tyre_stock_take_movements. - Transfers.
TyreTransferController— inter-location transfer with its own maker-checker plus a dispatch/receive handshake:store → submit → approve/reject → dispatch-all (or per-item dispatch) → request-receive → receive-approvals.approve/reject(web.php:2632-2660). Tables:tyre_transfer_requests,tyre_transfer_request_items,tyre_transfer_receive_requests.
3.3 Operations — the bulk console¶
BulkTyreMovementController (route group bulk, web.php:2554+) is the operational heart. It exposes one screen per movement type, each a form + AJAX tyre-picker + POST:
- Fit to vehicle (fit-form → vehicle-positions/{vehicle} → fit)
- Send to retread / Return from retread (retread-form/retread, return-form/return)
- Request disposal (disposal-form/disposal)
- Rotate (rotate-form → rotate-vehicle/{vehicle} → rotate)
- Remove from vehicle (remove-form → remove)
- Status change (status-change-form/status-change)
- Bulk approve/reject disposals inline.
Each action writes a tyre_movements row (from_location_id/to_location_id, from_position_id/to_position_id, movement_date, mileage_at_movement, depth_at_movement, reason, notes — 2025_09_01_145037_create_tyre_movements_table.php) and updates the tyre's tyre_status_id / tyre_location_id. Movements that require sign-off are staged as tyre_movement_requests (status pending → approved | rejected, 2026_02_15_140308) and cleared through TyreMovementApprovalController (movement-approvals.approve/reject, bulk variants, web.php:2688-2701).
Disposals are a distinct request table (tyre_disposal_requests: tyre_id, reason, status default pending, requested/approved/rejected audit, 2025_12_16_130412), cleared by TyreDisposalApprovalController (disposals.approve/reject, web.php:2547-2551).
Puncture reports (PunctureReportController, workflow prefix) capture a puncture event with per-item exemption and approve/reject (web.php:2892-2900). Tables: tyre_puncture_reports, tyre_puncture_report_items, tyre_puncture_charges.
Mermaid — tyre lifecycle (procure → store → fit → wear → retread → dispose)¶
flowchart TD
PO["Tyre LPO<br/>tyre_purchase_orders<br/>draft→approved"] --> GRN["Tyre Receipt / GRN<br/>tyre_receipts<br/>draft→completed"]
GRN --> INV["Tyre created<br/>tyres row<br/>serial_number, status=in-store"]
INV --> INVOICE["Supplier Invoice<br/>tyre_supplier_invoices<br/>→ WaSuppTran + GL (§5)"]
INV --> STORE["In store<br/>tyre_location_id"]
STORE -->|Fit to vehicle| FIT["Fitted<br/>tyre_fits (is_active=1)<br/>on vehicle+position"]
FIT -->|Rotate / Remove| STORE
FIT -->|Wear over km| FIT
STORE -->|Send to retread| RETREAD["At supplier<br/>transit_status_id<br/>tyre_retread_requests"]
RETREAD -->|Return from retread| STORE
RETREAD --> RLPO["Retread LPO<br/>tyre_retread_lpos<br/>→ supplier invoice"]
STORE -->|Request disposal| DISP["Disposal request<br/>tyre_disposal_requests<br/>pending→approved"]
FIT -->|Puncture| PUNC["Puncture report<br/>tyre_puncture_reports"]
DISP --> DEAD["Disposed<br/>end of life"]
3.4 Driver workflow (the maker-checker centrepiece)¶
A driver, via the API surface (app/Http/Controllers/Api/DriverTyreRequestController.php), submits a driver tyre request describing their vehicle's tyres. This creates a driver_tyre_requests header (document_no, driver_id, cab_vehicle_id, mileage_value, odometer_photo, status, driver_confirmed) and one driver_tyre_request_items row per reported wheel position.
The request header moves through the enum submitted → mismatch_verification → request_verification → fitting_pending → resolved (2026_03_05_100100_create_driver_tyre_requests_table.php:19; fitting_complete added later by 2026_03_09_151436_add_fitting_complete_to_driver_tyre_requests_status_enum.php). Each item carries the driver's reported serial/code/brand/size/pattern/status plus the system's matched_tyre_id, and the booleans is_mismatch, mismatch_verified, request_verified, is_driver_fault, fitting_confirmed (2026_03_05_100200). The issue enum per item is okay | replacement | damage.
Two parallel tracks then run:
Track 1 — Mismatch (fault) handling. When the reported tyre does not match the system's record (is_mismatch = true), the item routes to:
- Step 1a Verify — MismatchVerifyController (mismatch-verify, web.php:2850-2855). Verifier posts verify per item, setting mismatch_verified.
- Step 1b Approve — MismatchApproveController (mismatch-approve, web.php:2858-2864). Approver decides fault and charge. A driver_tyre_mismatch_charges row records expected_tyre_id, tyre_cost, charge_amount, proposed_action (charge | no_charge), optional new_status_id, and status pending → approved | rejected (2026_03_05_100300). An approved charge makes the driver liable for the value of the missing/wrong tyre.
Track 2 — Replacement request + incentive. When the driver needs a replacement (issue = replacement):
- Step 2a Verify — RequestVerifyController (request-verify, web.php:2867-2872) sets request_verified.
- Step 3 Fitting — TyreFittingWorkflowController (fitting, web.php:2876-2881) fits the approved new tyre. fit records new_tyre_id, fitting_confirmed, fitting_confirmed_at on the item, writes a tyre_fits row (is_active, fitted_at, fitted_mileage, unique on active_tyre_id — a tyre can only be actively fitted once, 2025_12_16_225404), and creates a tyre_movements row.
- Incentive calc. When the old tyre is removed/replaced, a driver_tyre_incentives row is computed comparing actual life to design life: it stores design_mileage, mileage_at_fitting, reported_mileage, mileage_used, mileage_threshold, mileage_difference, cost_per_km, amount, and type (positive | negative) — positive rewards a driver who exceeded the threshold, negative penalises early wear (2026_03_05_100400).
- Step 2b Approve — IncentiveApproveController (incentive-approve, web.php:2884-2890) approves/rejects; the incentive then carries incentive_status unpaid → paid. Payment hand-off to payroll is by inference (§7).
Mermaid — driver-request maker-checker¶
flowchart TD
D["Driver submits request (API)<br/>driver_tyre_requests: submitted"] --> ITEMS["Per-position items<br/>driver_tyre_request_items<br/>issue: okay/replacement/damage"]
ITEMS -->|is_mismatch=true| MV["Step 1a MismatchVerify<br/>verify → mismatch_verified"]
MV --> MA["Step 1b MismatchApprove<br/>approve/reject"]
MA --> CHG["driver_tyre_mismatch_charges<br/>proposed_action charge/no_charge<br/>pending→approved → driver liable"]
ITEMS -->|issue=replacement| RV["Step 2a RequestVerify<br/>verify → request_verified"]
RV --> FIT["Step 3 Fitting<br/>fit new_tyre_id<br/>tyre_fits + tyre_movements"]
FIT --> INC["driver_tyre_incentives<br/>compute mileage_used vs threshold<br/>type positive/negative, amount"]
INC --> IA["Step 2b IncentiveApprove<br/>approve/reject"]
IA --> PAID["incentive_status unpaid→paid<br/>(payroll hand-off — inference)"]
MA --> RESOLVED["request resolved / fitting_complete"]
IA --> RESOLVED
3.5 Retreading¶
A retread request (TyreRetreadRequestController, web.php:2703+) selects in-store tyres eligible by filter_status_id, assigns a transit_status_id (worn while at supplier) and a return_location_id, and runs a long maker-checker with a send leg and a receive leg (tyre_retread_requests has full submitted/send_approved/send_rejected/receive_submitted/receive_approved/receive_rejected/cancelled audit columns, 2026_02_15_160100):
draft → submit → approve-send / reject-send → (tyres dispatched) → receive → submit-receive → approve-receive / reject-receive → completed | cancelled.
It also supports uploading a signed request document and a supplier invoice, per-tyre add/remove, and printing. A retread LPO (TyreRetreadLpoController, tyre_retread_lpos: lpo_number, retread_request_id, cost_per_tyre, tyre_count, sub_total, vat_rate default 16.00, total_amount, is_invoiced, 2026_03_03_100000) is generated to pay the retreader, and feeds the same supplier-invoice path (tyre_supplier_invoices.retread_lpo_id, added 2026_03_03_100200). A retread reversal tool (TyreRetreadReversalController, web.php:2793+) exists to undo an erroneous retread.
4. Tables touched & key data (verbatim column names)¶
Master data / configuration:
- tyres — the register. serial_number (unique), tyre_code, tyre_brand_id, tyre_series_id, tyre_status_id, tyre_location_id, initial_depth, current_depth (2025_08_21_122906). Later columns added: purchase_cost (2026_03_02_100050), pattern_id (2026_02_15_160300), tyre_cost (2026_03_05_100000).
- tyre_brands, tyre_series, tyre_statuses, tyre_locations (has location_type default store, 2025_12_16_130412), tyre_positions, tyre_patterns, tyre_series_positions, tyre_status_positions.
- vehicle_axle_tyre_mappings (2023_11_29_151241) — links vehicle axles to tyre positions.
Procurement / AP:
- tyre_purchase_orders, tyre_purchase_order_items
- tyre_receipts (draft|completed), receipt procurement fields added 2026_03_02_100100
- tyre_advance_payments, tyre_advance_payment_allocations
- tyre_supplier_invoices — invoice_number (unique), supplier_invoice_number, supplier_id, tyre_receipt_id, wa_supp_trans_id, invoice_date, sub_total, vat_amount, amount, withholding_amount, is_paid (2026_03_02_100200); invoice_photo (..100543), cu_invoice_number (..104745), retread_lpo_id (2026_03_03_100200).
- tyre_supplier_invoice_items, tyre_supplier_invoice_amendments
Inventory / operations:
- tyre_movements (see §3.3), tyre_movement_requests
- tyre_fits (fitting register; unique active fitting per tyre)
- tyre_stock_takes, tyre_stock_take_items, tyre_stock_take_serials, tyre_stock_take_movements
- tyre_transfer_requests, tyre_transfer_request_items, tyre_transfer_receive_requests
- tyre_disposal_requests
- tyre_verifications, tyre_verification_items (verification feature is present in models/migrations but its route group is commented out, web.php:2664-2685 — see §6)
- tyre_rotation_requests, tyre_rotation_request_items
- tyre_puncture_reports, tyre_puncture_report_items, tyre_puncture_charges
Retreading:
- tyre_retread_requests, tyre_retread_request_items, tyre_retread_lpos, tyre_retread_lpo_items
Driver workflow:
- driver_tyre_requests, driver_tyre_request_items, driver_tyre_mismatch_charges, driver_tyre_incentives
Verbatim misspellings / oddities to preserve (do not "correct" these in code):
- Controller ReceiveTyrePurchasedOrderController ("Purchased", not "Purchase").
- Legacy route names TyrePurchaseOrderController@getInventryItemDetails and .getInventryItemDetails ("Inventry", web.php legacy block).
- Column wa_supp_trans_id on tyre_supplier_invoices (singular trans), while the model class is WaSuppTran (singular) mapping to table wa_supp_trans (plural). The GL foreign key is wa_supp_tran_id (singular tran) in the GL row array (TyreSupplierInvoiceController.php:547).
- Permission-string vs model-slug drift: sidebar checks tyre-stock-take (line 323) though the model slug is tyre-stock-takes; and driver-tyre-puncture (line 330) though the route/model is puncture-reports.
5. Interactions with other modules¶
Accounts Payable & General Ledger (the one hard seam). The tyre sub-ERP is otherwise self-contained, but TyreSupplierInvoiceController.store (web.php:2516) reaches into the shared financial spine. On booking an invoice it:
1. Creates a WaSuppTran (app/Model/WaSuppTran) — the shared supplier/creditor transaction ledger used by the main AP module — and stamps its id back onto tyre_supplier_invoices.wa_supp_trans_id (TyreSupplierInvoiceController.php:248, 286). cu_invoice_number is validated unique across wa_supp_trans (line 173), i.e. the fiscal-device (CU) number shares the company-wide namespace.
2. Calls postGLEntries() (defined at TyreSupplierInvoiceController.php:511), which:
- Dr the Tyre Asset GL account with sub_total (companyPreference->tyreAssetGlAccount, line 526/553) — tyres capitalise to a dedicated asset account, not general stock.
- Dr the VAT Input Tax GL account with vat_amount when VAT applies (resolved from TaxManager, lines 528-557).
- Cr the Creditors Control GL account with the gross amount (from SupplierChartOfAccountService::getCreditorsAccountCodeForPosting($supplier), lines 523/561).
- GL rows are tagged with wa_supp_tran_id and the supplier-transaction transaction_no so the ledger lines carry the same document number as the creditor entry (lines 542-547).
The method throws if the Creditors Control, Tyre Asset, or VAT Input GL accounts are not configured in Company Preferences (lines 516/520/533) — so the seam is guarded, not silent.
The retread path reuses the same seam via storeRetreadInvoice() (TyreSupplierInvoiceController.php:567), posting an equivalent WaSuppTran + GL set for retread costs (lines 609-673).
Vehicle register (Ch18). Fitting binds a tyre to a vehicle_id and tyre_position_id via tyre_fits; driver_tyre_requests.cab_vehicle_id and driver_tyre_request_items.vehicle_id reference the fleet vehicles. vehicle_axle_tyre_mappings describes the wheel layout per vehicle. Mileage readings (mileage_value, mileage_at_movement, fitted_mileage) tie into the same odometer data the fuel/service modules use, but tyres store their own snapshots rather than reading live.
Supplier master. supplier_id throughout points at the shared wa_supplier master (also used by main procurement, Book 3).
Payroll / driver charges. Approved driver_tyre_mismatch_charges (driver liability) and approved driver_tyre_incentives (incentive_status unpaid→paid) are the interface points to driver compensation; the actual posting to payroll is not proven in the files read (§7).
Deliberately separate from Book 2 & Book 3. Nothing here writes to the main wa_stock, wa_purchase_orders, or generic GRN tables. Tyre POs, receipts, stock takes and transfers are entirely parallel implementations on tyre_* tables. The only convergence is WaSuppTran + GL at invoice time. This is a purpose-built vertical, not a configuration of the general modules.
6. Alternatives & variants¶
- Legacy vs current implementation. Two generations coexist. The current module is the
Admin/Fleet/*controllers behind thetyres-managementandtyre-workflowprefixes withtyres/tyre_*tables dated 2025-2026. The legacy module is theAdmin/*controllers (TyreFittingController,TyreRemovalController,TyreInventoriesController,ReceiveTyrePurchasedOrderController,TyrePurchaseOrderControllerinAdmin/) bound by plainRoute::resource(web.php:2909+) against the 2023 tables (tyre_fittings,tyre_inventories,wa_tyre_purchase_orders,wa_tyre_purchase_order_items). The commented-out block inroutes/modules/fleet.php:180-224shows an intermediate wiring attempt that was abandoned in favour ofroutes/web.php. - Tyre verification is a fully-modelled but disabled feature:
TyreVerificationController,tyre_verifications/tyre_verification_itemstables and migrations exist, but the entireverificationroute group is commented out (web.php:2664-2685). Treat as dormant. - Lab tyres (
LabTyre,LabTyreCategory,LabTyreControllerunderAdmin/Lab/, routes inroutes/modules/lab.php:138-144) are a different concept — tyres as laboratory test samples — and belong to the Lab module, not this chapter. - Advance payment path is optional per LPO (
toggle-advance-payment); an LPO can go straight receipt→invoice without an advance. - Configuration "cleanup" tools — pattern-fix and movement-fix (
web.php:2379-2386) and brand/series/status merge (merge.preview/merge) are admin data-repair variants for when duplicate master records accumulate.
7. Open questions — CODE-PROVEN vs INFERENCE¶
CODE-PROVEN (cited above):
- Route topology under tyres-management and tyre-workflow, both aliased admin.tyres.* (web.php:2360, 2848).
- All table columns quoted in §4 come directly from the named migration files.
- The AP/GL seam: WaSuppTran creation, wa_supp_trans_id stamping, Dr Tyre Asset / Dr VAT / Cr Creditors postings, and the guard exceptions (TyreSupplierInvoiceController.php:173,248,286,511-561).
- Driver-request status enums, item flags, mismatch-charge and incentive column sets (2026_03_05_100100/100200/100300/100400).
- Maker-checker controller split for every approval track (distinct verify vs approve controllers/routes).
- Permission strings and sidebar grouping (fleet.blade.php:302-346).
INFERENCE (not verified in files read):
- The exact incentive formula (how mileage_used, mileage_threshold, cost_per_km and amount/type are computed) — I read the incentive table columns and the fitting controller's reference to items.incentive, but did not open the calculation method. The threshold-vs-design-life reward/penalty description is inferred from column names.
- Whether approved driver_tyre_incentives.amount and driver_tyre_mismatch_charges.charge_amount post to payroll or the ledger automatically, or are merely flagged (incentive_status paid) for a manual payroll run. No posting code was read for these.
- The precise moment a tyres row's tyre_status_id flips at each movement (fit/retread/dispose) — inferred from the movement request from_status_id/to_status_id columns and the bulk console actions, not from reading each controller's update logic.
- How matched_tyre_id is resolved (serial lookup vs position lookup) in the driver-request API — inferred from column semantics.
- Whether GRN approval creates the individual tyres rows or the receipt-submit step does — receipt clearly owns creation, but the approval's side-effects were not read.
- Controller-level permission enforcement (I confirmed sidebar can() gates; controller middleware/policy names were not opened).
- The legacy Admin/* controllers may still be reachable in production or may be fully superseded — routes exist but usage is not proven.
8. Source references (file:line)¶
Navigation / permissions:
- resources/views/admin/includes/sidebar_includes/fleet.blade.php:302 — can('view','tyre-management') top gate
- .../fleet.blade.php:304-346 — permission-string groups ($tyreProcurementModels … $tyreRetreadingModels, tyres-configuration)
- .../fleet.blade.php:348-613 — treeview markup and route links (admin.tyres.purchase-orders.index, .all, .index, .driver-requests.index, .fitting.index, .retread-requests.index, .configurations.index, …)
Routes:
- routes/web.php:2360 — prefix 'tyres-management' as 'admin.tyres.'
- routes/web.php:2362-2434 — configurations (series/status positions, pattern-fix, movement-fix, brands/series/statuses/locations merge, positions, patterns)
- routes/web.php:2437-2460 — tyre register, bulk-upload, import, reports
- routes/web.php:2461-2472 — receipts
- routes/web.php:2475-2486 — grn-approvals
- routes/web.php:2489-2502 — purchase-orders
- routes/web.php:2505-2513 — purchase-order-approvals
- routes/web.php:2516 — supplier-invoices
- routes/web.php:2523-2545 — advance-payments
- routes/web.php:2547-2551 — disposals
- routes/web.php:2554+ — bulk console (fit/retread/return/disposal/rotate/remove/status-change)
- routes/web.php:2600+ — stock-take
- routes/web.php:2632-2660 — transfers (+ receive-approvals)
- routes/web.php:2664-2685 — verification (COMMENTED OUT)
- routes/web.php:2688-2701 — movement-approvals
- routes/web.php:2703-2790 — retread-requests
- routes/web.php:2793-2800 — retread-reversals
- routes/web.php:2803-2810 — retread-lpos
- routes/web.php:2812-2833 — driver-requests (admin)
- routes/web.php:2848 — prefix 'tyre-workflow' as 'admin.tyres.'
- routes/web.php:2850-2864 — mismatch-verify / mismatch-approve
- routes/web.php:2867-2872 — request-verify
- routes/web.php:2876-2881 — fitting
- routes/web.php:2884-2890 — incentive-approve
- routes/web.php:2892-2900 — puncture-reports
- routes/web.php:2909+ — legacy Route::resource (tyre_fitting, tyre_removal, tyre_transfer, tyre_retreading, tyre_inventories, tyre-purchase-orders, receive-tyre-purchase-order)
- routes/modules/fleet.php:180-224 — abandoned/commented tyre route wiring
Controllers (all app/Http/Controllers/Admin/Fleet/ unless noted):
- TyrePurchaseOrderController, TyrePurchaseOrderApprovalController, TyreReceiptController, TyreGrnApprovalController, TyreAdvancePaymentController
- TyreSupplierInvoiceController.php — store() :162; WaSuppTran :12,248; cu_invoice_number unique :173; create :282; postGLEntries() :511-561; retread invoice :567-673
- TyreController, BulkTyreMovementController, TyreStockTakeController, TyreTransferController, TyreMovementApprovalController, TyreDisposalApprovalController
- TyreRetreadRequestController, TyreRetreadLpoController, TyreRetreadReversalController
- DriverTyreRequestAdminController, MismatchVerifyController, MismatchApproveController, RequestVerifyController, IncentiveApproveController, TyreFittingWorkflowController, PunctureReportController, DriverTyreIncentiveController
- app/Http/Controllers/Api/DriverTyreRequestController.php — driver submission surface
- Legacy: app/Http/Controllers/Admin/{TyreFittingController,TyreRemovalController,TyreInventoriesController,ReceiveTyrePurchasedOrderController,TyrePurchaseOrderController,TyreRetreadingController}.php
Models (app/Models/ unless noted): Tyre, TyreBrand, TyreSeries, TyreStatus, TyreLocation, TyrePosition, TyrePattern, TyreSeriesPosition, TyreStatusPosition, TyreMovement, TyreMovementRequest, TyreFit, TyrePurchaseOrder(+Item), TyreReceipt, TyreSupplierInvoice(+Item,+Amendment), TyreAdvancePayment(+Allocation), TyreStockTake(+Item,+Serial,+Movement), TyreTransferRequest(+Item), TyreTransferReceiveRequest, TyreDisposalRequest, TyreVerification(+Item), TyreRotationRequest(+Item), TyrePunctureReport(+Item), TyrePunctureCharge, TyreCharge, TyreRetreadRequest(+Item), TyreRetreadLpo(+Item), DriverTyreRequest, DriverTyreRequestItem, DriverTyreMismatchCharge, DriverTyreIncentive; app/VehicleAxleTyreMapping.php; app/Model/WaSuppTran.
Migrations (database/migrations/): 2025_08_21_122906_create_tyres_table, 2025_09_01_145037_create_tyre_movements_table, 2025_09_01_204956_modify_sometables_add_tyre_fitting_table, 2025_12_16_130412_create_tyre_disposal_requests_table, 2025_12_16_225404_fix_tyre_fits_unique_constraint, 2025_12_16_235816_create_tyre_receipts_table, 2026_02_11_100000_create_tyre_stock_takes_table (+ items), 2026_02_11_110000_create_tyre_verifications_table, 2026_02_15_140308_create_tyre_movement_requests_table, 2026_02_15_160100_create_tyre_retread_requests_table (+ items), 2026_03_02_100000_create_tyre_purchase_orders_table (+ items 110000), 2026_03_02_100200_create_tyre_supplier_invoices_table (+ items, +invoice_photo ..100543, +cu_invoice_number ..104745), 2026_03_03_100000_create_tyre_retread_lpos_table (+ items, +retread_lpo_id ..100200), 2026_03_05_100100_create_driver_tyre_requests_table, ..100200_create_driver_tyre_request_items_table, ..100300_create_driver_tyre_mismatch_charges_table, ..100400_create_driver_tyre_incentives_table, 2026_03_09_151436_add_fitting_complete_to_driver_tyre_requests_status_enum.