Tagline: "A Service On Your Time"
Watch Quick Marble & Granite Management System in action.
https://drive.google.com/file/d/1UWRXCh6NO8ETE6hdahJJpANSbhej2V7Z/view?usp=sharing
https://drive.google.com/file/d/1n8iCKSy_CrQNE9mq3OUYCfKYRTcMZfPo/view?usp=sharing
These demonstration videos showcase the application's key features, including authentication, client management, quotations, contracts, reports, PDF generation, and overall workflow.
A mobile-first (Android + iOS), multi-office business management system for Quick Marble & Granite, covering Clients, Quotations, and Contracts, with real-time sync, offline viewing, and per-office data ownership. Desktop/Web are future phases — architecture must not block them.
Offices (branches): Nansana (Main), Kajjansi, Buloba, Bulenga — admin can add more anytime.
Currency: UGX. Tax: VAT 18% (configurable in Settings).
Numbering format: QM0001 sequential, company-wide, zero-padded, auto-incrementing.
The application now uses office-based access control. Every Manager and Sales Officer belongs to one office through assignedOfficeId. They can only access records belonging to that office.
| Capability | Administrator | Manager | Sales Officer |
|---|---|---|---|
| View all offices | ✅ | ❌ | ❌ |
| View own office | ✅ | ✅ | ✅ |
| Create/Edit records in own office | ✅ | ✅ | ✅ |
| Create/Edit records in other offices | ✅ | ❌ | ❌ |
| Approve quotations | ✅ | ✅ (own office) | ❌ |
| Create contracts | ✅ | ✅ (own office) | Workflow only |
| Reports | All offices | Own office | Own office |
| Company Settings | ✅ | ❌ | ❌ |
| User & Office Management | ✅ | ❌ | ❌ |
| Delete Records | ✅ | ❌ | ❌ |
Firestore Security Rules enforce these permissions in addition to the application UI.
┌───────────────────────────────────────────────────────────┐
│ Presentation Layer │
│ Flutter (Android/iOS) — Material 3 — Riverpod — GoRouter │
│ Screens / Widgets (per-office tabs, dashboard, modules) │
└───────────────────────────┬─────────────────────────────────┘
│ (Controllers/Providers)
┌───────────────────────────▼─────────────────────────────────┐
│ Domain Layer │
│ Entities, UseCases, Repository Interfaces │
└───────────────────────────┬─────────────────────────────────┘
│
┌───────────────────────────▼─────────────────────────────────┐
│ Data Layer │
│ Repository Impl → Firestore Service / Local Cache (Hive) │
│ Offline queue for pending writes (sync when online) │
└───────────────────────────┬─────────────────────────────────┘
│
┌───────────────────────────▼─────────────────────────────────┐
│ Firebase Backend │
│ Auth │ Firestore (streams) │ Storage │ Cloud Functions │
│ (counters, PDF triggers, notifications, activity logging) │
└───────────────────────────────────────────────────────────────┘
Offline strategy: Firestore's native offline persistence (enabled) handles cached reads/writes automatically; Hive is used only for user settings, last-selected office, and auth session — not as a second source of truth for business data. This avoids a fragile custom sync engine while still giving true offline viewing.
offices/{officeId}
name: string
location: string
createdAt: timestamp
isActive: bool
users/{uid}
name, email, phone: string
role: "admin" | "manager" | "sales_officer"
assignedOfficeId: string | null // null only for admin
isActive: bool
createdAt: timestamp
clients/{clientId}
officeId: string // ownership
companyName, clientName, phone, altPhone, email: string
address, district, country: string
tin: string | null
notes: string
registrationDate: timestamp
createdBy: uid
quotations/{quotationId}
quotationNumber: string // "QM0001"
officeId: string
clientId: string
date: timestamp
preparedBy: uid
items: [ { description, material, dimensions, quantity, unitPrice, subtotal } ]
labourCost, transportCost, discount, tax, grandTotal: number
status: "draft" | "pending" | "approved" | "rejected" | "converted"
createdAt, updatedAt: timestamp
contracts/{contractId}
contractNumber: string
officeId: string
quotationId: string
clientId: string
contractValue: number
projectLocation: string
startDate, completionDate: timestamp
status: "active" | "completed" | "cancelled"
documentUrl: string | null // Storage
signedCopyUrl: string | null
notes: string
createdAt: timestamp
activity_logs/{logId}
officeId: string
userId: uid
userName: string
action: string // "created quotation QM0001"
targetType: "client"|"quotation"|"contract"
targetId: string
timestamp: timestamp
notifications/{notificationId}
recipientUid: string | "all"
officeId: string | null
type: string
message: string
read: bool
createdAt: timestamp
settings/company
name, logoUrl, phone, email, tin, address: string
currency: "UGX"
taxPercent: number // 18
quotationPrefix: "QM"
lastQuotationSeq: number // maintained via Cloud Function transaction
Indexes needed: quotations on (officeId, status, date), clients on (officeId, companyName), contracts on (officeId, status).
Splash → Auth check
├─ Not logged in → Login → (Forgot Password)
└─ Logged in → Home Shell (Bottom Nav / Drawer)
├─ Dashboard (company-wide summary + per-office tabs)
│ └─ Office Tab (Nansana | Kajjansi | Buloba | Bulenga | +Add)
├─ Clients (list → profile → edit)
├─ Quotations (list → detail → PDF preview → convert to contract)
├─ Contracts (list → detail → upload signed copy)
├─ Reports
├─ Notifications
└─ Settings (company profile, users, offices — admin only)
Dashboard's office tabs are horizontally swipeable; each shows that office's own cards/charts, while a "Company" tab aggregates all offices (visible to Admin/Manager; Sales Officer sees only their own office tab plus a read-only Company summary).
lib/
core/
config/ (firebase_options, env)
themes/ (brand colors, text styles)
utils/ (validators, formatters, currency)
errors/
models/ (Client, Quotation, Contract, Office, AppUser, ActivityLog)
services/ (firestore_service, storage_service, pdf_service, auth_service)
repositories/ (interfaces + impl per module)
controllers/ (Riverpod StateNotifiers per module)
providers/ (riverpod providers, DI wiring)
routes/ (go_router config, route guards by role)
screens/
auth/
dashboard/
clients/
quotations/
contracts/
reports/
settings/
widgets/ (shared: office_tab_bar, status_chip, empty_state, etc.)
- Firebase Authentication
- Role-based authentication
- Office management
- User management
- Client management
- Quotation management
- Contract management
- Automatic quotation numbering
- Dashboard foundation
- Invoice, Receipt and Delivery Note PDFs
- Print, Save and Share for all business documents
- Reports (Snapshot, Daily, Weekly, Monthly and Yearly)
- CSV export for Excel
- Firebase integration
- Android release configuration
- Office-level access restrictions
- Firestore Security Rules
- Production testing
- Google Play (.aab) release
- Finalize office-based permissions.
- Notifications & Activity Log.
- Inventory module.
- Payment improvements.
- Internal testing.
- Google Play production release.
