Skip to main content

ADR-001: Fuel Adjustment Factor (FAF) Implementation

Status

Accepted

Context

The Access Waste Management System needed a mechanism to apply a variable fuel surcharge (Fuel Adjustment Factor) to skip bin services. This surcharge accounts for fluctuating diesel/fuel costs in vehicle operations. The feature needed to:

  • Be configurable per supplier (Partner)
  • Support both percentage-based and fixed-amount calculations
  • Apply only to non-rental orders (one-time/ad-hoc services)
  • Display transparently on invoices as a separate line item
  • Sync with Xero accounting system
  • Maintain historical accuracy (immutable snapshots on orders)

Commits: 2f4ca69b through d0e482bb


ADR-001-A: FAF as General Partner Setting

Decision

FAF is configured at the Partner level as a general setting that applies to all non-rental orders for a supplier, rather than being configured per waste type or pricing tier.

Context

Initially considered configuring FAF per waste type (via the pricings table). However, business requirements clarified that FAF should apply uniformly across all skip bin services for a supplier.

Consequences

Positive:

  • Simplified admin interface - single configuration point
  • Consistent FAF application across all waste types
  • Reduced configuration overhead for administrators

Negative:

  • Less granular control - cannot disable FAF for specific waste types
  • All non-rental orders for a supplier share the same FAF rate

Implementation

Database migration adds FAF columns to partners table:

  • has_faf (boolean) - enable/disable FAF
  • faf_type (string: 'percentage' or 'fixed')
  • faf_value (decimal) - the rate or fixed amount

Admin UI is a separate card on the Bin Hire page (/admin/skip-bin/{type_id}/{supplier_id}/{area_id}) with its own AJAX form that saves to the Partner model.


ADR-001-B: Immutable FAF Snapshot Pattern

Decision

FAF values are stored as static snapshots on order_details at order creation time, preserving the exact rate and amount applied for historical accuracy and audit purposes.

Context

FAF rates may change over time as fuel costs fluctuate. For invoice integrity and audit trails, the system must record what FAF was actually applied at the time of order creation, not reference the current configuration.

Consequences

Positive:

  • Historical invoices remain accurate even if FAF rates change
  • Complete audit trail for regulatory compliance
  • Immutable record prevents disputes about what was charged

Negative:

  • Additional storage overhead (3 columns per order detail)
  • Cannot retroactively apply new FAF rates to existing orders

Implementation

Database migration adds snapshot columns to order_details:

  • faf_applied_type (string) - 'percentage' or 'fixed' at time of order
  • faf_applied_rate (decimal) - the rate value used
  • faf_total_amount (decimal) - calculated FAF amount applied

The FuelAdjustmentRepository::applyFAFToOrderDetail() method captures the snapshot during order creation.


ADR-001-C: View-Only Admin Access

Decision

Administrators cannot modify FAF on individual orders. FAF is calculated automatically from Partner configuration and displayed as read-only.

Context

CEO requirement for transparency and consistency. FAF should be a business rule applied uniformly, not subject to manual adjustment per order which could lead to inconsistent application or disputes.

Consequences

Positive:

  • Ensures consistent FAF application across all orders
  • Reduces admin decision fatigue
  • Prevents accidental or intentional under/over-charging
  • Simplified UI - no override controls needed

Negative:

  • Edge cases require updating Partner config rather than individual orders
  • Less flexibility for exceptional circumstances

Implementation

  • Admin order creation: FAF is calculated and displayed but not editable
  • Admin order edit: FAF snapshot is displayed read-only
  • Change-over orders: FAF recalculates from current Partner config (not inherited from original order)
  • No UI controls for modifying FAF on existing orders

ADR-001-D: Dual Calculation Methods

Decision

FAF supports two calculation types:

  1. Percentage-based: FAF = Base Price × (FAF Rate / 100)
  2. Fixed amount: FAF = Fixed FAF Value

Context

Business needs flexibility in how fuel costs are recovered. Percentage scales with service price (appropriate when fuel costs correlate with service value), while fixed amount provides predictable per-service recovery.

Consequences

Positive:

  • Flexibility to match business pricing strategy
  • Can switch methods as business needs change
  • UI dynamically shows $ or % prefix based on selection

Negative:

  • Slightly more complex calculation logic
  • Admin must understand when to use each method

Implementation

FuelAdjustmentRepository::calculateFAF() handles both methods:

$fafAmount = $partner->faf_type === 'percentage'
? $basePrice * ($partner->faf_value / 100)
: $partner->faf_value;

ADR-001-E: Invoice Integration via OrderInvoiceExtra

Decision

FAF appears on invoices as a separate line item using the existing OrderInvoiceExtra model, rather than modifying order totals directly or using a dedicated FAF invoice model.

Context

The system already has infrastructure for extra/optional line items on invoices (OrderInvoiceExtra). Reusing this pattern maintains consistency and minimizes new code.

Consequences

Positive:

  • Reuses existing invoice line item infrastructure
  • Natural fit for "extra charge" concept
  • Invoice templates already render OrderInvoiceExtra items
  • Tax calculations handled consistently

Negative:

  • FAF line items mixed with other extra services in the same table
  • Querying FAF totals requires filtering by name pattern

Implementation

FuelAdjustmentRepository::addFAFToInvoice() creates an OrderInvoiceExtra record:

  • name: "Fuel Adjustment Factor (X%)" or "Fuel Adjustment Factor"
  • unit_cost: FAF amount from order detail snapshot
  • total_cost: same as unit_cost (qty = 1)
  • qty: 1

ADR-001-F: Idempotent FAF Creation

Decision

FAF line items on invoices use an idempotent creation pattern to prevent duplicate entries when invoices are regenerated or updated.

Context

Invoices can be regenerated multiple times (e.g., corrections, updates). Without idempotency, each regeneration would create duplicate FAF line items.

Consequences

Positive:

  • Safe to call multiple times without side effects
  • Simplifies invoice regeneration logic
  • Prevents data corruption from duplicate charges

Negative:

  • Requires name-based lookup (fragile if display text changes)
  • Slightly more complex than simple insert

Implementation

// Check for existing FAF line item
$extra = OrderInvoiceExtra::query()->where([
'order_invoice_id' => $invoice->id,
'order_detail_id' => $orderDetail->id,
])->where('name', 'like', 'Fuel Adjustment Factor%')->first();

// Create new or update existing
if (!$extra) {
$extra = new OrderInvoiceExtra();
}
$extra->fill([...])->save();

ADR-001-G: Xero Integration Using Shared AccountCode

Decision

FAF line items in Xero use the same AccountCode as other line items ($itemXero), not a separate dedicated account code.

Context

CEO rejected the idea of a separate xero_faf_account_code field. Simpler accounting approach treats FAF as part of service revenue rather than tracking separately.

Consequences

Positive:

  • Simpler configuration - no additional Xero account code to manage
  • Consistent with treating FAF as part of service pricing
  • Reduced accounting complexity

Negative:

  • Cannot isolate FAF revenue in Xero reports by account code
  • Fuel surcharge revenue mixed with base service revenue in accounting

Implementation

In InvoiceManagement::xeroItemNonRental(), FAF line item uses $itemXero:

$lineItems[] = [
'Description' => $fafDescription,
'Quantity' => 1,
'UnitAmount' => round($orderDetail->faf_total_amount, 2),
'AccountCode' => $itemXero, // Same as main service
];

Date

April 10, 2026

Author

Cascade AI Assistant (based on commits by Aryan Jaya)