6.3 KiB
6.3 KiB
BellSystems CRM — Build Plan & Step Prompts
Overview
A bespoke CRM module built directly into the existing BellSystems web console. Stack: FastAPI backend (Firestore), React + Vite frontend. No new auth — uses the existing JWT + permission system. No file storage on VPS — all media lives on Nextcloud via WebDAV.
Architecture Summary
Backend
- New module:
backend/crm/withmodels.py,service.py,router.py - Firestore collections:
crm_customers,crm_orders,crm_products - SQLite (existing
mqtt_data.db) for comms_log (high-write, queryable) - Router registered in
backend/main.pyas/api/crm
Frontend
- New section:
frontend/src/crm/ - Routes added to
frontend/src/App.jsx - Nav entries added to
frontend/src/layout/Sidebar.jsx
Integrations (later steps)
- Nextcloud: WebDAV via
httpxin backend - Email: IMAP (read) + SMTP (send) via
imaplib/smtplib - WhatsApp: Meta Cloud API webhook
- FreePBX: Asterisk AMI socket listener
Data Model Reference
crm_customers (Firestore)
{
"id": "auto",
"name": "Στέλιος Μπιμπης",
"organization": "Ενορία Αγ. Παρασκευής",
"contacts": [
{ "type": "email", "label": "personal", "value": "...", "primary": true },
{ "type": "phone", "label": "mobile", "value": "...", "primary": true }
],
"notes": [
{ "text": "...", "by": "user_name", "at": "ISO datetime" }
],
"location": { "city": "", "country": "", "region": "" },
"language": "el",
"tags": [],
"owned_items": [
{ "type": "console_device", "device_id": "UID", "label": "..." },
{ "type": "product", "product_id": "pid", "product_name": "...", "quantity": 1, "serial_numbers": [] },
{ "type": "freetext", "description": "...", "serial_number": "", "notes": "" }
],
"linked_user_ids": [],
"nextcloud_folder": "05_Customers/FOLDER_NAME",
"created_at": "ISO",
"updated_at": "ISO"
}
crm_orders (Firestore)
{
"id": "auto",
"customer_id": "ref",
"order_number": "ORD-2026-001",
"status": "draft",
"items": [
{
"type": "console_device|product|freetext",
"product_id": "",
"product_name": "",
"description": "",
"quantity": 1,
"unit_price": 0.0,
"serial_numbers": []
}
],
"subtotal": 0.0,
"discount": { "type": "percentage|fixed", "value": 0, "reason": "" },
"total_price": 0.0,
"currency": "EUR",
"shipping": {
"method": "",
"tracking_number": "",
"carrier": "",
"shipped_at": null,
"delivered_at": null,
"destination": ""
},
"payment_status": "pending",
"invoice_path": "",
"notes": "",
"created_at": "ISO",
"updated_at": "ISO"
}
crm_products (Firestore)
{
"id": "auto",
"name": "Vesper Plus",
"sku": "VSP-001",
"category": "controller|striker|clock|part|repair_service",
"description": "",
"price": 0.0,
"currency": "EUR",
"costs": {
"pcb": 0.0, "components": 0.0, "enclosure": 0.0,
"labor_hours": 0, "labor_rate": 0.0, "shipping_in": 0.0,
"total": 0.0
},
"stock": { "on_hand": 0, "reserved": 0, "available": 0 },
"nextcloud_folder": "02_Products/FOLDER",
"linked_device_type": "",
"active": true,
"created_at": "ISO",
"updated_at": "ISO"
}
crm_comms_log (SQLite table — existing mqtt_data.db)
CREATE TABLE crm_comms_log (
id TEXT PRIMARY KEY,
customer_id TEXT NOT NULL,
type TEXT NOT NULL, -- email|whatsapp|call|sms|note|in_person
direction TEXT NOT NULL, -- inbound|outbound|internal
subject TEXT,
body TEXT,
attachments TEXT, -- JSON array of {filename, nextcloud_path}
ext_message_id TEXT, -- IMAP uid, WhatsApp msg id, AMI call id
logged_by TEXT,
occurred_at TEXT NOT NULL,
created_at TEXT NOT NULL
);
crm_media (SQLite table — existing mqtt_data.db)
CREATE TABLE crm_media (
id TEXT PRIMARY KEY,
customer_id TEXT,
order_id TEXT,
filename TEXT NOT NULL,
nextcloud_path TEXT NOT NULL,
mime_type TEXT,
direction TEXT, -- received|sent|internal
tags TEXT, -- JSON array
uploaded_by TEXT,
created_at TEXT NOT NULL
);
IMPORTANT NOTES FOR ALL STEPS
- Backend location:
c:\development\bellsystems-cp\backend\ - Frontend location:
c:\development\bellsystems-cp\frontend\ - Auth pattern: All routes use
Depends(require_permission("crm", "view"))or"edit". Import fromauth.dependencies. - Firestore pattern: Use
from shared.firebase import get_db. Seebackend/devices/service.pyfor reference patterns. - SQLite pattern: Use
from mqtt import database as mqtt_db—mqtt_db.dbis the aiosqlite connection. Seebackend/mqtt/database.py. - Frontend auth:
getAuthHeaders()from../api/authgives Bearer token headers. See any existing page for pattern. - Frontend routing: Routes live in
frontend/src/App.jsx. Sidebar nav infrontend/src/layout/Sidebar.jsx. - Token: localStorage key is
"access_token". - UI pattern: Use existing component style —
SectionCard,FieldRow, inline styles for grids. Seefrontend/src/devices/for reference. - No new dependencies unless absolutely necessary.
Step 1 — Backend: CRM Module Scaffold + Products CRUD
File: .claude/crm-step-01.md
Step 2 — Backend: Customers CRUD
File: .claude/crm-step-02.md
Step 3 — Backend: Orders CRUD
File: .claude/crm-step-03.md
Step 4 — Backend: Comms Log + Media (SQLite)
File: .claude/crm-step-04.md
Step 5 — Frontend: Products Module
File: .claude/crm-step-05.md
Step 6 — Frontend: Customers List + Detail Page
File: .claude/crm-step-06.md
Step 7 — Frontend: Orders Module
File: .claude/crm-step-07.md
Step 8 — Frontend: Comms Log + Media Tab (manual entry)
File: .claude/crm-step-08.md
Step 9 — Integration: Nextcloud WebDAV
File: .claude/crm-step-09.md
Step 10 — Integration: IMAP/SMTP Email
File: .claude/crm-step-10.md
Step 11 — Integration: WhatsApp Business API
File: .claude/crm-step-11.md
Step 12 — Integration: FreePBX AMI Call Logging
File: .claude/crm-step-12.md