Files
crm/schema.sql
T
MachielandClaude Opus 5.5 bffa053c05 v0.0.1: eerste versie van de Van Emous CRM
Flask + SQLite, in de Van Emous-huisstijl (tokens uit het design system, GSAP-effecten
volgens de motion-regels, uit bij prefers-reduced-motion).

- Leads en contactpersonen: status, toewijzen, verwachte waarde, omzetten naar klant,
  tijdlijn, bestanden, taken/opvolging
- Relaties (zoals accounts in EspoCRM): klantbundels (huishouden/familie) en partners met
  type (leverancier, aannemer, ...); leveranciers van producten worden automatisch partners
- API voor formulieren (POST /api/leads) met dubbele-leaddetectie, bestanden en
  configurator-ontwerpen (house-design JSON)
- Import van CSV/JSON (leads, relaties, producten) met kolomkoppeling; configurator-formaat
- Producten met SKU als vaste sleutel, mutatiehistorie, POST /api/products, CSV-export;
  offerteregels houden hun eigen prijs
- Offertes met btw per tarief en afdrukversie (PDF via de browser)
- Inbox via IMAP/SMTP, gekoppeld aan contacten
- Gebruikers (beheerder/medewerker) en klantportaal (offertes accepteren, bestanden, berichten)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 21:56:55 +02:00

161 lines
4.9 KiB
SQL

CREATE TABLE IF NOT EXISTS settings (
key TEXT PRIMARY KEY,
value TEXT
);
CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
email TEXT NOT NULL UNIQUE COLLATE NOCASE,
password_hash TEXT NOT NULL,
role TEXT NOT NULL DEFAULT 'user',
active INTEGER NOT NULL DEFAULT 1,
created_at TEXT NOT NULL
);
-- Relaties (zoals accounts in EspoCRM): klantbundels (huishouden, familie, bedrijf) en partners
-- (leverancier, aannemer, ...). Contactpersonen hangen eronder met een rol.
CREATE TABLE IF NOT EXISTS relations (
id INTEGER PRIMARY KEY AUTOINCREMENT,
kind TEXT NOT NULL DEFAULT 'klant',
type TEXT DEFAULT '',
name TEXT NOT NULL,
email TEXT DEFAULT '',
phone TEXT DEFAULT '',
website TEXT DEFAULT '',
address TEXT DEFAULT '',
postcode TEXT DEFAULT '',
city TEXT DEFAULT '',
kvk TEXT DEFAULT '',
notes TEXT DEFAULT '',
created_at TEXT NOT NULL,
updated_at TEXT NOT NULL
);
CREATE INDEX IF NOT EXISTS idx_relations_name ON relations(kind, name COLLATE NOCASE);
CREATE TABLE IF NOT EXISTS contacts (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL DEFAULT '',
email TEXT DEFAULT '',
phone TEXT DEFAULT '',
company TEXT DEFAULT '',
address TEXT DEFAULT '',
postcode TEXT DEFAULT '',
city TEXT DEFAULT '',
type TEXT NOT NULL DEFAULT 'lead',
status TEXT NOT NULL DEFAULT 'nieuw',
source TEXT DEFAULT '',
value REAL NOT NULL DEFAULT 0,
assigned_user_id INTEGER REFERENCES users(id) ON DELETE SET NULL,
relation_id INTEGER REFERENCES relations(id) ON DELETE SET NULL,
relation_role TEXT DEFAULT '',
portal_token TEXT,
portal_password_hash TEXT,
portal_last_login TEXT,
notes TEXT DEFAULT '',
extra TEXT DEFAULT '{}',
created_at TEXT NOT NULL,
updated_at TEXT NOT NULL
);
CREATE INDEX IF NOT EXISTS idx_contacts_email ON contacts(email COLLATE NOCASE);
CREATE TABLE IF NOT EXISTS activities (
id INTEGER PRIMARY KEY AUTOINCREMENT,
contact_id INTEGER NOT NULL REFERENCES contacts(id) ON DELETE CASCADE,
type TEXT NOT NULL,
body TEXT DEFAULT '',
user_id INTEGER REFERENCES users(id) ON DELETE SET NULL,
created_at TEXT NOT NULL
);
CREATE TABLE IF NOT EXISTS files (
id INTEGER PRIMARY KEY AUTOINCREMENT,
contact_id INTEGER REFERENCES contacts(id) ON DELETE CASCADE,
stored TEXT NOT NULL,
original TEXT NOT NULL,
mime TEXT DEFAULT '',
size INTEGER DEFAULT 0,
created_at TEXT NOT NULL
);
CREATE TABLE IF NOT EXISTS products (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
sku TEXT DEFAULT '',
description TEXT DEFAULT '',
unit TEXT DEFAULT 'stuk',
supplier TEXT DEFAULT '',
supplier_id INTEGER REFERENCES relations(id) ON DELETE SET NULL,
price REAL NOT NULL DEFAULT 0,
vat REAL NOT NULL DEFAULT 21,
image TEXT DEFAULT '',
active INTEGER NOT NULL DEFAULT 1,
created_at TEXT NOT NULL,
updated_at TEXT
);
-- Elke wijziging aan een product (handmatig, import of API), zodat prijsmutaties terug te zien zijn.
CREATE TABLE IF NOT EXISTS product_history (
id INTEGER PRIMARY KEY AUTOINCREMENT,
product_id INTEGER NOT NULL REFERENCES products(id) ON DELETE CASCADE,
field TEXT NOT NULL,
old_value TEXT,
new_value TEXT,
source TEXT DEFAULT '',
user_id INTEGER REFERENCES users(id) ON DELETE SET NULL,
created_at TEXT NOT NULL
);
CREATE INDEX IF NOT EXISTS idx_product_history ON product_history(product_id, created_at);
CREATE TABLE IF NOT EXISTS quotes (
id INTEGER PRIMARY KEY AUTOINCREMENT,
number TEXT UNIQUE NOT NULL,
contact_id INTEGER REFERENCES contacts(id) ON DELETE SET NULL,
status TEXT NOT NULL DEFAULT 'concept',
title TEXT DEFAULT '',
date TEXT NOT NULL,
valid_until TEXT,
intro TEXT DEFAULT '',
notes TEXT DEFAULT '',
created_at TEXT NOT NULL,
updated_at TEXT NOT NULL
);
CREATE TABLE IF NOT EXISTS quote_lines (
id INTEGER PRIMARY KEY AUTOINCREMENT,
quote_id INTEGER NOT NULL REFERENCES quotes(id) ON DELETE CASCADE,
position INTEGER NOT NULL DEFAULT 0,
product_id INTEGER,
description TEXT NOT NULL DEFAULT '',
qty REAL NOT NULL DEFAULT 1,
unit TEXT DEFAULT '',
price REAL NOT NULL DEFAULT 0,
vat REAL NOT NULL DEFAULT 21
);
CREATE TABLE IF NOT EXISTS emails (
id INTEGER PRIMARY KEY AUTOINCREMENT,
uid TEXT UNIQUE,
message_id TEXT DEFAULT '',
direction TEXT NOT NULL DEFAULT 'in',
contact_id INTEGER REFERENCES contacts(id) ON DELETE SET NULL,
from_name TEXT DEFAULT '',
from_addr TEXT DEFAULT '',
to_addr TEXT DEFAULT '',
subject TEXT DEFAULT '',
body TEXT DEFAULT '',
date TEXT NOT NULL,
is_read INTEGER NOT NULL DEFAULT 0
);
CREATE INDEX IF NOT EXISTS idx_emails_from ON emails(from_addr);
CREATE TABLE IF NOT EXISTS tasks (
id INTEGER PRIMARY KEY AUTOINCREMENT,
contact_id INTEGER REFERENCES contacts(id) ON DELETE CASCADE,
user_id INTEGER REFERENCES users(id) ON DELETE SET NULL,
title TEXT NOT NULL,
due_date TEXT,
done INTEGER NOT NULL DEFAULT 0,
created_at TEXT NOT NULL
);