Full-Stack Web

FF Inventory (FFSynca) — Multi-Tenant SaaS

Multi-tenant SaaS inventory management platform covering the full order-to-warehouse lifecycle with Stripe billing, Shopify sync, and a custom multi-method costing engine — deployed on Azure.

Timeline: ~24 months — Apr 2024 to Apr 2026

Key Results

80+

Tenant DB Tables

Full supply chain schema: products, orders, stock, locations, bins, costing layers, movements, shipments, and more

6 methods

Costing Methods

FIFO, FIFO Batch, FIFO Serial, FEFO, Special Serial, Special Batch — with immutable cost layer audit trail

20+

Filament Resources

Full CRUD admin resources covering every domain entity with modular Forms/Tables/Tabs architecture

Bidirectional

Shopify Sync

Products, inventory levels, orders, and customers synced via queued jobs with OAuth integration

The Problem

Small to midsize product-based businesses were managing inventory, purchase orders, sales orders, warehousing, and fulfilment across disconnected spreadsheets and generic tools not built for their workflows.

  • No single affordable SaaS tool covered the full cycle: purchasing → receiving → stock → sales → fulfilment → shipment.
  • No support for multi-method inventory costing (FIFO, FEFO, serial/batch tracking) in accessible tools at this price point.
  • Businesses couldn't connect their Shopify storefront to their back-office stock — orders, inventory levels, and customers were out of sync.
  • Each business's data needed to be fully isolated from other tenants — shared infrastructure but zero data bleed-through.
  • Subscription and billing management had to be self-serve, including trial periods, plan enforcement, and usage limits.

The Solution

Built a fully featured multi-tenant SaaS platform from the ground up on Laravel 12 with Filament 4 as the admin UI layer. Each business gets a fully isolated tenant context (shared PostgreSQL database scoped by team_id via global query scopes) with their own settings, stock, orders, and users — covering the entire supply chain from supplier POs through warehouse receiving, bin-level stock tracking, sales orders, picking, packing, shipment, and Shopify integration.

  1. 1Implemented stancl/tenancy with a shared PostgreSQL database approach — every tenant model uses a TenantScope global query scope and a HasTenant trait that auto-injects team_id on creation. Central DB holds teams, users, and subscriptions; tenant DB holds all business data.
  2. 2Built a multi-step Wizard registration (business details → account details) that creates a Team, User, and Stripe subscription in a single guarded database transaction, then dispatches a PostRegistrationSetupJob to seed default data asynchronously.
  3. 3Integrated Laravel Cashier with regional pricing (GBP/USD), 14-day trials, subscription feature gating, and a real-time usage dashboard showing monthly order consumption vs. plan limits with colour-coded warnings at 75% and 90% thresholds.
  4. 4Built a CostingEngine service supporting FIFO, FEFO, Special Serial, and Special Batch costing methods, with a separate AllocationEngine that reserves stock for sales orders (committed vs. available) without consuming cost layers until fulfilment. All stock movements recorded as an immutable audit trail.
  5. 5Implemented warehouse management — locations → zones → bins hierarchy with bin-level stock tracking, location locking for stock takes, and zone-based picking logic. Inbound receiving tracks expected vs. actual quantities with split shipment (A/B/C grouping) support.
  6. 6Built Shopify integration — OAuth flow, bidirectional product/inventory sync, order import, customer mapping, and location mapping via a ShopifyService with repository pattern, queued sync jobs, and an integration status state machine.
  7. 7Architected modular Filament 4 resources — each resource split into dedicated Forms/, Tables/, Tabs/, and RelationManagers/ classes for maintainability. Enums implement Filament's HasLabel/HasColor contracts via a shared HasEnumHelpers trait.
  8. 8Set up Azure Pipelines CI/CD deploying to Azure App Service (PHP 8.2, Linux) on push to main, with separate staging and production instances backed by separate PostgreSQL Flexible Server databases.

Architecture Decisions

Key technical decisions made during the project and the reasoning behind them.

Shared Database Multi-Tenancy over Separate Databases

Reasoning

Evaluated stancl/tenancy in both modes. Separate databases would mean provisioning and migrating hundreds of databases as the platform scales. Shared database with team_id scoping via global query scopes gives tenant isolation at the application layer with operationally simpler infrastructure.

Outcome

Single PostgreSQL database handles all tenants — migrations run once, no per-tenant provisioning overhead. Global scopes and the HasTenant trait make it impossible to accidentally query cross-tenant data.

Filament 4 over Custom React SPA

Reasoning

The product requires dense, data-heavy admin interfaces (tables, filters, relation managers, forms with conditional logic). Building these in a custom SPA would have taken 3–4× the time. Filament 4 with Livewire provides reactive UI without a JavaScript build step for every feature.

Outcome

Full CRUD, filtering, relation management, and custom pages delivered rapidly. Custom pages (stock dashboard, product setup, billing) added as Filament Page extensions where the standard resource pattern wasn't sufficient.

Async Post-Registration Setup via Queued Job

Reasoning

The original implementation called Artisan::call synchronously inside the HTTP registration request, adding 1–3 seconds to sign-up time. Moved all post-commit work (seeding, settings, email verification) into a PostRegistrationSetupJob dispatched after the DB transaction commits.

Outcome

Registration response returns immediately after account + Stripe subscription creation. Background job handles seeding and email independently — ~2–3s faster sign-up.

Immutable Stock Movement Audit Trail

Reasoning

Every stock change (inbound receipt, fulfilment, adjustment, transfer) creates an immutable StockMovement record. Current stock levels are the sum of movements, not a mutable counter — mirroring double-entry bookkeeping principles.

Outcome

Full traceability of every stock change, ability to reconstruct stock levels at any point in time, and a clear audit log for discrepancy investigation.

The Tech Stack

Laravel 12

PHP 8.2 — Eloquent ORM with custom query scopes, event/observer system, Redis queue workers, scheduled commands, service container DI

Filament 4

20+ resource panels, custom pages (Dashboard, ProductSetup, Billing, Stock Dashboard), relation managers, reusable form/table component classes

PostgreSQL

Azure Flexible Server — two logical databases (main for central data, tenant for business data), separate connections configured in Laravel

Redis

Azure Cache for Redis — queue backend for Shopify sync, import processing, analytics tracking, post-registration setup; also session/cache store

Stripe

Laravel Cashier 15 — trial periods, regional pricing (GBP/USD), usage enforcement, webhook handling, self-serve billing portal

Shopify API

OAuth integration, bidirectional product/inventory/order sync via ShopifyService with repository pattern, queued import jobs for bulk data

Azure

App Service (Linux PHP 8.2) for hosting, PostgreSQL Flexible Server for databases, Blob Storage for file uploads, Pipelines for CI/CD

Stancl Tenancy

v3.9 shared database mode — TenantScope global query scopes, HasTenant trait, custom DatabaseTenancyBootstrapper for connection switching

The Impact

A production multi-tenant SaaS platform with 80+ tenant database tables, 6 costing methods, bidirectional Shopify sync, and automated Azure CI/CD — developed over 24 months of active iteration.

Deep dives and comparisons related to the technologies used in this project.

Explore similar case studies with overlapping technologies and challenges.