πŸ“š All guides ← Back to app

πŸ” Owner / Admin Manual

Platform management reference. Read end-to-end once, then use as a lookup.

Contents

  1. Creating a new company
  2. Understanding tenants
  3. Inviting staff
  4. Managing stores
  5. Managing drivers + token rotation
  6. Notifications settings
  7. Reading the audit log
  8. Reading reports
  9. Provisioning WhatsApp (Twilio)
  10. Disaster recovery
  11. Barcode scanning & tracking
  12. Predictive analytics
  13. Customer self-service portal
  14. Route reordering
  15. Role matrix reference

1Creating a new company

New customers sign up themselves at the signup page. Every signup gets a free 30-day trial (extendable by one more month upon request) β€” no manual approval needed.

The signup page is at /signup. The new owner fills in:

After signup, the owner is signed in automatically and lands on the onboarding page. The company gets status: trial with a 30-day countdown. If the client requests more time, the superadmin can extend the trial by another 30 days from the Admin dashboard. When the trial expires, API access is blocked until you activate them.

πŸ’‘ As superadmin, use the Admin dashboard (/superadmin) to monitor signups, trial expiry dates, and activate companies for paid plans.

2Understanding tenants

One company = one isolated world. A dispatcher in Company A cannot see orders, drivers, stores, or anything else from Company B. This is enforced at the database layer.

Every row in orders, drivers, stores, audit_events, customers has a company_id foreign key. A SQLAlchemy before_flush guard in database.py (_tenant_guard) refuses any write that doesn't match the session user's company_id. There is no escape hatch in the API layer.

What this means practically:

πŸ’‘ If you ever add a new table that should be tenant-scoped, just add company_id = Column(Integer, ForeignKey('companies.id'), index=True) β€” the guard picks it up automatically. No extra code needed.

3Inviting staff

Owners invite team members from the Team page. Each invite generates a one-time link.

On the Team page (/users-page):

  1. Click Invite user.
  2. Enter their email and pick a role: dispatcher, accountant, or driver.
  3. For driver invites, you can link to an existing driver record or auto-create a new one.
  4. Copy the invite link and send it to the new team member (e.g. via WhatsApp).
  5. They open the link, set their password, and they're in.

Valid roles: owner, dispatcher, accountant, driver. See section 11 for what each can do.

πŸ’‘ Invite links expire after 7 days. If someone misses it, just create a new invite from the Team page.

4Managing stores

A "store" in Tariq is a depot β€” the starting point for route optimization. Every company has at least one.

Add New Store / Depot

Baghdad Main
Karrada, Baghdad
πŸ“ 33.3152, 44.3912 βœ“
πŸ“
+ Add Store
Store form β€” the interactive map is where you set the depot pin.
  1. Name β€” a short identifier (e.g. "Baghdad Main" or "Basra Branch").
  2. Address β€” human-readable, just for your records.
  3. Coordinates β€” paste a Google Maps / WhatsApp location link into the GPS field, or click on the map. The green checkmark confirms extraction worked.
  4. Click Add Store. The store is immediately available as a depot in the Routes page.
πŸ’‘ Companies with multiple warehouses add one store per warehouse. Dispatchers then pick the starting depot on the Routes page when generating the day's routes.

5Managing drivers + token rotation

Each driver gets a unique URL that they open in their phone browser. No app install, no password. If that URL leaks, you rotate it.

Adding a driver on the Drivers page: name + phone + vehicle + commission per stop (IQD). On save, the server generates a URL like /driver?t=abc123.... Send it to the driver via WhatsApp.

Rotating the token when a phone is stolen, a driver leaves, or the URL was shared outside your team:

The rotation modal. Close is disabled until you check the acknowledgement.

  1. Open the Drivers page.
  2. Click Rotate token on the driver's row.
  3. The modal shows the plaintext URL once. Copy it, send it to the driver on WhatsApp.
  4. Tick the acknowledgement checkbox β†’ Close button enables.
  5. The old URL immediately returns 404. Only the new URL works.
⚠ If you lose the URL before the driver receives it, rotate again. The server stores only a SHA-256 hash β€” nobody, including you at the database level, can recover the plaintext after close.
πŸ’‘ The Drivers table shows a token_rotated: true indicator for drivers whose legacy URL has been revoked. Use that to spot stale driver records.

6Notifications settings

WhatsApp notifications are owner-only and opt-in. The Settings page has one toggle + three template textareas.

Notifications

Enable WhatsApp notifications
βœ“ Provider: Twilio WhatsApp (live)
Templates (leave blank for default)
Hello {customer} πŸ‘‹
Your Tariq order #{order_id} is out for delivery with {driver_name}.
Track: {track_url}
Settings page β€” notifications toggle and template editor.

Provider chip tells you the current state:

Three templates, each supports variables:

Variables available: {customer}, {order_id}, {driver_name}, {track_url}, {rate_url}. Leave a template blank to use the built-in default.

7Reading the audit log

Every mutation in Tariq β€” every order created, every status change, every token rotation, every login attempt β€” is written to the audit log. This is your forensic trail.

2026-04-17 09:12:04owner@...driver.token.rotatedAli Hassan (id=7)
2026-04-17 09:10:01dispatcher@...order.bulk_update5 orders β†’ driver_id=7
2026-04-17 08:55:12dispatcher@...order.createorder_id=142
2026-04-17 08:44:33β€”auth.login.failedemail=unknown@foo.com
Audit page β€” tenant-scoped, newest first. Red badges are security-interesting events.

Common actions to look for:

Red banner at top of the audit page = rate alarm. Shows when auth.login.failed > 10 per hour. Click into the filter to investigate.

Export CSV button: date range β†’ downloads tenant-scoped audit rows. Open in Excel, share with compliance, archive off-server.

πŸ’‘ Audit events older than the retention window (default 180 days) are pruned automatically each night at 03:00 UTC. Change the window via companies.plan.audit_retention_days if a tenant has longer compliance requirements.

8Reading reports

Same /report page the dispatcher uses, but viewed through a platform-owner lens.

What to scan for, in order:

  1. Success rate. Below 80% for multiple days running β†’ something is wrong. Usually bad addresses (check failure reasons) or a specific driver underperforming.
  2. Failure reason chips. "Wrong address" concentrated in one area β†’ consider adding a customer-facing track link so they can fix it themselves. "Customer not home" high β†’ reconsider dispatch time window.
  3. Cash collected vs cash pending. "Pending" is delivered-but-not-yet-reconciled. If this number is large at end of day, reconciliations aren't happening.
  4. Date range. Compare last Monday vs this Monday. Growth or drop should match your intuition; big divergences mean data quality issues (orders missed, drivers marking wrong).

9Provisioning WhatsApp (Twilio)

First-time setup. One-time cost in your time, then it just runs.

  1. Sign up at twilio.com β†’ free trial gives you enough credit to test.
  2. Find WhatsApp Sandbox under Messaging β†’ Try it out β†’ Send a WhatsApp message. Each recipient (every customer) joins the sandbox by sending a join phrase to a Twilio number. This is a sandbox limitation β€” once you upgrade to a real WhatsApp Business sender, customers don't need to join.
  3. Copy your credentials from the Twilio console:
    • Account SID
    • Auth Token
    • WhatsApp sandbox number (e.g. +14155238886)
  4. Add them as GitHub secrets at github.com/Ahmedabdulla80/tariq β†’ Settings β†’ Secrets and variables β†’ Actions:
    • TARIQ_TWILIO_ACCOUNT_SID
    • TARIQ_TWILIO_AUTH_TOKEN
    • TARIQ_TWILIO_WHATSAPP_FROM (format: +14155238886, no whatsapp: prefix)
  5. Add repo variables (same page, Variables tab): TARIQ_NOTIFY_PROVIDER=twilio, TARIQ_NOTIFY_BASE_URL=https://tariq.codeator.ai, TARIQ_NOTIFY_DEFAULT_CC=+964.
  6. Redeploy β€” push any commit to main, or re-run the latest workflow manually. On redeploy, the provider chip on /settings-page turns green.
  7. Flip the toggle on Settings. Test by creating one order with your own WhatsApp number in the phone field. You should receive a message within seconds.
⚠ Cost note: Twilio sandbox is free for testing but rate-limited. For production with >50 customers/day, upgrade to a WhatsApp Business API sender through Twilio β€” pricing is per-message (~$0.005 per session-initiated message in MENA as of 2026). Budget ~$15/month per 100 active-order customers.

10Disaster recovery

What to do when something bad happens.

Server hardware failure / image rollback:

  1. On GitHub Actions, find the last known-good run. Re-run it from there β€” the self-hosted runner will pull the previous image and restart.
  2. Database is in the shared familycapital-sql container; it persists across Tariq container restarts. No data loss from an app restart.

Postgres backup & restore:

# Backup (run on server)
docker exec familycapital-sql pg_dump -U tariq tariq > tariq-$(date +%F).sql

# Restore
docker exec -i familycapital-sql psql -U tariq tariq < tariq-2026-04-01.sql

POD images live in a named volume pod-data, mounted at /data/pod/. To back up:

docker run --rm -v tariq_pod-data:/data -v $(pwd):/backup alpine tar czf /backup/pod-$(date +%F).tar.gz -C /data .

Stolen driver phone: rotate that driver's token (section 5). Old URL dies the instant you hit rotate. No other action needed.

Rogue dispatcher / compromised account: demote the user to accountant or delete via SQL, then review their audit log (actor_email filter) for the last 30 days to assess damage.

11Barcode scanning & tracking

Orders can carry barcode identifiers. Drivers scan them with their phone camera; customers can look up orders by barcode on the public portal.

How it works end-to-end:

  1. Assign barcode: dispatchers or owners set a barcode on any order via the orders table or POST /orders/{id}/barcode.
  2. Driver scans: the driver taps the scan button in their PWA, points the camera at the physical package barcode. The app uses the browser BarcodeDetector API.
  3. Match & navigate: the scanned code hits GET /orders/scan/{barcode}, which returns the order. The driver's app scrolls to that stop.
  4. Customer lookup: customers visit /customer-portal and enter the barcode string. GET /customer/status-by-barcode/{barcode} returns status without authentication.
πŸ’‘ Barcodes are unique per company. Two different companies can use the same barcode string without conflict β€” they're scoped by company_id.

12Predictive analytics

The /report page has a Predictions tab that analyzes historical data to surface patterns.

Data returned by GET /report/predictions:

πŸ’‘ Use failure-by-area data to decide where to require mandatory GPS coordinates before accepting orders. High failure + no GPS = preventable failures.

13Customer self-service portal

The customer portal at /customer-portal lets customers check order status without contacting your team.

Two lookup methods:

Full portal with OTP: customers can also enter their phone number, receive an OTP via WhatsApp, and see all their orders β€” not just one. This uses POST /customer/request-otp and POST /customer/verify-otp.

⚠ The quick lookup endpoints are public by design β€” they return minimal info (status, area, timestamp) but not phone numbers or full addresses. This is intentional for customer convenience.

14Route reordering

Dispatchers can manually reorder stops after the optimizer generates routes, using drag-and-drop on the Routes page.

The POST /routes/reorder endpoint accepts a driver_id and a new ordered list of order_ids. The route view page calls this when a dispatcher drags stops into a new sequence and clicks "Save Order".

Who can reorder: owners and dispatchers only. The change is reflected immediately on the driver's phone β€” no page refresh needed (the driver's app auto-polls).

πŸ’‘ Reordering is audited. Every reorder creates an route.reorder entry in the audit log with the driver ID and new sequence.

15Role matrix reference

What each role can do. βœ“ = allowed, βœ— = blocked, πŸ‘ = read-only.

Endpoint / action Owner Dispatcher Accountant Driver
View orders / report / auditβœ“βœ“βœ“βœ—
Create / update ordersβœ“βœ“πŸ‘βœ—
Delete ordersβœ“βœ“βœ—βœ—
Bulk reassignβœ“βœ“βœ—βœ—
Add stores / driversβœ“βœ“βœ—βœ—
Delete stores / driversβœ“βœ—βœ—βœ—
Rotate driver tokenβœ“βœ“βœ—βœ—
Reconcile cashβœ“βœ“βœ“βœ—
Notifications settingsβœ“βœ—βœ—βœ—
Export audit CSVβœ“βœ“βœ“βœ—
Team management / invitesβœ“βœ—βœ—βœ—
Reorder route stopsβœ“βœ“βœ—βœ—
Set order barcodeβœ“βœ“βœ—βœ“
Scan barcode lookupβœ“βœ“βœ—βœ“
View predictionsβœ“βœ“βœ“βœ—
View own route + deliver/failβœ—βœ—βœ—βœ“
Submit GPS locationβœ—βœ—βœ—βœ“
πŸ’‘ The UI hides buttons your role can't use, but the server enforces the matrix via require_role(*roles) in main.py. Hidden β‰  secure β€” the server check is the real one.
↑