Hajjez
Open projectHajjez is a multi-tenant SaaS platform for businesses whose operations revolve around products, customers, and time. A business signs up, chooses a plan, receives its own subdomain, and gets a store for products, bookings, inventory, payments, and documents.
I designed the application around a hard boundary: the platform team manages accounts and subscriptions, while each business runs its own store. A platform billing action should never become a tenant data operation, and one business should never see another business’s customers, bookings, or financial records.
At a glance
| Product surface | Responsibility |
|---|---|
| Central application | Registration, email verification, plans, trials, subscriptions, payments, and tenant management |
| Tenant store | Products, inventory, customers, bookings, payments, contracts, invoices, receipts, and settings |
| Tenant resolution | Subdomain-based access with tenancy middleware |
| Data isolation | A separate database for each tenant |
| Operations UI | Filament app panel for store operators and a central admin panel for platform management |
The product model
The store does not treat every item as a rental. Products have an explicit type, and that type determines how pricing, availability, stock, and completion work.
| Product type | How Hajjez treats it |
|---|---|
| Rental | Uses a date range, a finite number of rental units, overlap checks, pickup, and return tracking |
| Sale | Uses finite or unlimited stock, decrements finite stock, and moves items to a sold state |
| Service | Does not consume product inventory and moves to a performed state when completed |
The same booking record can contain different item types. Each item keeps its product type and its own lifecycle, so a store can manage a rental, a sale, and a service without forcing them through the same operational rules.
The tenant store also manages categories, customers, tenant users, pricing units, discounts, VAT, documents, activity history, and reporting. The dashboard can compare booking activity and revenue by product type instead of flattening the business into one sales number.
Two applications in one product
The code keeps the two audiences visible instead of treating every model as if it belongs to the same database.
| Central side | Tenant side |
|---|---|
| Users, plans, subscriptions, payments, domains, and tenant records | Products, customers, bookings, booking items, documents, and tenant payments |
| Central dashboard, billing, settings, and plan selection | Store dashboard, booking operations, inventory, customer records, and documents |
| Platform operator administration | Business administration |
Tenant creation runs a setup pipeline that creates the tenant database, runs its migrations, seeds the tenant, and synchronizes tenant information. The tenant app is initialized from the request host before store routes and the Filament app panel run.
The booking flow
- The operator creates or selects a customer and sets the booking period, event date, payment terms, and notes.
- The operator adds products to the booking. Hajjez calculates each item using its product type, price unit, quantity, discounts, and tax settings.
- The system checks availability before the booking is saved. Sale items use stock on hand, service items are unlimited, and rental items use the requested date range.
- Payments update the booking’s paid amount and payment status, including partial payments and refunds.
- The operator moves items through their own lifecycle: rental items can be picked up and returned, sale items can be sold, and services can be marked performed.
- Contracts, invoices, receipts, credit notes, packing slips, activity records, and payment records remain attached to the booking and customer.
That gives the operator one booking workflow without pretending that renting a camera, selling a consumable, and performing a service have the same inventory behavior.
Availability and conflict management
Conflict management is part of the booking path, not a report added after the fact.
- Rental availability is calculated for the requested start and end dates.
- The check can expand by a configured buffer between bookings.
- Returned and cancelled items no longer consume rental availability; reserved and picked-up items do.
- The availability result reports total quantity, remaining quantity, status, and the bookings causing the conflict.
- When overlap is disallowed, booking creation and edits validate all requested quantities inside a transaction and lock the relevant product rows before accepting the change.
Sales use stock quantity or an unlimited-stock flag. Services return unlimited availability. This keeps the conflict rule specific to the kind of resource being booked instead of applying one blunt rule to every product.
Why I chose separate databases
Each tenant gets its own database. That adds operational work, but it gives the most sensitive boundary a physical separation.
Isolation decision. Businesses using the platform may be competitors. Their customers, payment records, contracts, and invoices should not depend on every future query remembering a
tenant_idfilter. I preferred database setup and connection-switching complexity to making data isolation a convention that every feature could accidentally break.
Payments and operational documents
Hajjez keeps two kinds of money movement separate. Central payments cover plans and subscriptions. Tenant payments cover customer bookings, deposits, refunds, and the store’s operational records.
The booking flow can produce contracts, invoices, receipts, credit notes, packing slips, and other documents. Payment status, signed-contract requirements, overdue pickup or return, and overdue payment can surface as warnings on the booking. This gives the operator a clear next action instead of leaving the problem buried in a transaction history.
Hajjez is therefore less about one booking form than the relationship between product type, time-based availability, stock, payment state, and tenant isolation. The central application handles the account and subscription. The tenant application handles the business operation. The booking record ties the customer, items, conflicts, payments, and documents together.