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)