v0.0.2: pre-productie (Bootstrap 5.3.3, LightPdf klant-PDF, server-setup en release-script)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
bffa053c05
commit
7d302dc405
@@ -11,6 +11,20 @@ Eenvoudige CRM in Python (Flask) met SQLite, in de Van Emous-huisstijl. Versie 0
|
||||
familie of bedrijf, met meerdere contactpersonen en hun rol) en **partners** met een type (leverancier,
|
||||
aannemer, installateur, ...; de typen pas je aan bij Instellingen). Een lead omzetten naar klant zet de
|
||||
persoon in een nieuwe of bestaande klantrelatie. Leveranciers van producten worden automatisch partners.
|
||||
- **Gekozen model** per lead/contact: een woningontwerp uit de configurator (`house-design`-JSON, via het
|
||||
formulier, de API of geüpload bij Bestanden) wordt het gekozen model; het nieuwste telt, een ander ontwerp
|
||||
kies je op de contactpagina. Daar staat het in een **3D-viewer** op de engine van de configurator
|
||||
(`/files/<id>/viewer`): 3D, doorsnede en plattegrond, met maten, m², inhoud, gewicht en een indicatieve
|
||||
prijs (uit de kosten-CSV's van de configurator). In de leadlijst staat het model als kolom.
|
||||
Met **Lagen** in de viewer kies je een bouwfase (t/m regelwerk, elektra, beplating, …) of losse lagen
|
||||
(regelwerk, elektra, kozijnen, dak, …), zoals Lagen in de configurator, zodat de ploeg de opbouw kan bekijken.
|
||||
- **PDF's per ontwerp** (`HousePdf` van de configurator, gemaakt in de viewer en bewaard bij het contact): een
|
||||
**werktekening** (alle tekenbladen, elektra, zaaglijst en kosten; intern, niet in het klantportaal) en een
|
||||
**klant-PDF** zoals de light-configurator die maakt (`LightPdf`: voorblad met beeld, kerncijfers, keuzes, indicatieve
|
||||
prijs en QR-code, dan aanzichten en plattegrond en de werkwijze; wel in het portaal). Ontbreken ze, dan maakt de viewer ze vanzelf bij
|
||||
het eerste openen; opnieuw maken via PDF in de weergave. Op de contactpagina bij het gekozen model en op de
|
||||
relatiepagina (Ontwerpen en tekeningen, voor alle contactpersonen).
|
||||
- Op mobiel staan bij contact en relatie de gegevens bovenaan, de 3D-weergave verderop.
|
||||
- **Taken/opvolging** per contact met datum en eigenaar; "Mijn taken" op het dashboard.
|
||||
- **API voor formulieren** (`POST /api/leads`): JSON, gewone formulieren en multipart met bestanden.
|
||||
Nederlandse en Engelse veldnamen worden herkend, onbekende velden bewaard. Dubbele leads (zelfde
|
||||
@@ -29,6 +43,22 @@ Eenvoudige CRM in Python (Flask) met SQLite, in de Van Emous-huisstijl. Versie 0
|
||||
formaat als de import.
|
||||
- **Offertes** met regels (product of vrije regel), btw per tarief en een afdrukversie die je als PDF
|
||||
opslaat via de browser.
|
||||
- **Bestek uit ontwerp** bij een offerte (of "Offerte met bestek" bij het gekozen model): de engine van de
|
||||
configurator rekent het ontwerp door zoals Kosten & tijden (`static/bestek.js`, `CostModel.compute` met de
|
||||
kosten-CSV's) en zet alle posten met hoeveelheden in hoofdstukken (casco, isolatie, gevel, dak, vloer,
|
||||
kozijnen, afwerking, inrichting, verlichting, elektra met wcd's, schakelaars en meters leiding per type,
|
||||
transport en plaatsing). Elke post krijgt zijn kostprijs plus algemene kosten en marge; samen precies het
|
||||
totaal van de configurator. Schroeven, zaag- en boorwerk staan per hoofdstuk samen als "Bevestiging en
|
||||
bewerking". In de offerte per post (standaard: aantal × prijs per eenheid) of per hoofdstuk; het
|
||||
afrondingsverschil van de prijzen per eenheid gaat naar de laatste regel van 1 post zonder product, zodat
|
||||
het totaal precies klopt. Het bestek zelf komt als bijlage (zonder prijzen) in de afdrukversie en het portaal.
|
||||
- **Producten uit het bestek:** posten met een vaste prijs per eenheid (wcd, schakelaar, aansluitpunten,
|
||||
leidingen per type, spots en lampen, toilet, douche, keuken per m, deuren, ramen per maat, gevel- en
|
||||
dakbedekking per m², isolatie, plaatmateriaal, ...) worden gekoppeld aan een product met de kostencode van de
|
||||
configurator als SKU (`elektra.wcdDubbel`, `inrichting.toilet`, `kozijnen.raam.1200x500`; dezelfde codes als
|
||||
in `kosten/leveranciers.csv`). Ontbreekt het product, dan wordt het aangemaakt met de prijs per eenheid van de
|
||||
berekening; bestaande productprijzen worden alleen bijgewerkt als je dat aanvinkt (met producthistorie).
|
||||
De regels staan in `PRODUCT_RULES` in `static/bestek.js`.
|
||||
- **Gebruikers** met de rollen beheerder (ook gebruikers en instellingen) en medewerker.
|
||||
- **Klantportaal** (`/portal`): de klant krijgt een uitnodigingslink (kopiëren of mailen vanaf het
|
||||
contact), stelt een wachtwoord in en kan dan offertes bekijken, accepteren of afwijzen, bestanden
|
||||
@@ -44,23 +74,57 @@ python3 -m venv .venv
|
||||
.venv/bin/python app.py # http://127.0.0.1:5000
|
||||
```
|
||||
|
||||
De viewer heeft de engine van de configurator nodig (niet in git, ca. 160 MB met texturen):
|
||||
|
||||
```sh
|
||||
tools/configurator-sync.sh [../configurator] # kopieert js/, assets/, kosten/ naar static/configurator/
|
||||
```
|
||||
|
||||
Opnieuw draaien als er in de configurator iets aan de weergave verandert; `deploy.sh` doet dit zelf bij elke deploy.
|
||||
|
||||
Demoleads (lorem ipsum, elk met een standaardmodel als gekozen model):
|
||||
|
||||
```sh
|
||||
node tools/export-models.js # standaardmodellen (HouseModel.MODELS) → demo/modellen/*.json
|
||||
.venv/bin/python app.py demo # leads aanmaken; bestaande demoleads worden overgeslagen
|
||||
```
|
||||
|
||||
Bij de eerste keer maak je het beheerdersaccount aan. Daarna vul je onder Instellingen de
|
||||
bedrijfsgegevens, de e-mail en de toegestane websites voor de API in.
|
||||
|
||||
## Online zetten
|
||||
## Online zetten (server)
|
||||
|
||||
Draai de app achter nginx/Apache met HTTPS, bijvoorbeeld met gunicorn:
|
||||
Op een nieuwe Ubuntu-server (26.04 LTS) eenmalig, nadat de DNS van het domein naar de server wijst:
|
||||
|
||||
```sh
|
||||
.venv/bin/pip install gunicorn
|
||||
.venv/bin/gunicorn -w 2 -b 127.0.0.1:8000 app:app
|
||||
scp -r server ubuntu@SERVER:
|
||||
ssh ubuntu@SERVER sudo bash server/setup.sh crm.voorbeeld.nl jij@voorbeeld.nl
|
||||
```
|
||||
|
||||
E-mail automatisch ophalen (cron, elke 10 minuten):
|
||||
`server/setup.sh` installeert Python, nginx, certbot, rsync en sqlite3, zet de tijdzone op Europe/Amsterdam,
|
||||
maakt de gebruiker `crm` met `/srv/vanemous-crm` (`releases/`, `current`, `data/`), de systemd-dienst
|
||||
`vanemous-crm` (gunicorn op 127.0.0.1:5050, `CRM_PROXY=1`), de nginx-site met HTTPS, de firewall (SSH, 80, 443),
|
||||
automatische beveiligingsupdates en cron (e-mail ophalen elke 10 minuten, elke nacht een databaseback-up, 7 dagen rond).
|
||||
|
||||
Een versie uitrollen (of terugrollen) vanaf je eigen machine:
|
||||
|
||||
```sh
|
||||
tools/release.sh crm@crm.voorbeeld.nl # nieuwste tag
|
||||
tools/release.sh crm@crm.voorbeeld.nl v0.0.1 # bepaalde versie
|
||||
```
|
||||
*/10 * * * * cd /pad/naar/crm && .venv/bin/python app.py sync
|
||||
```
|
||||
|
||||
Dat stuurt `git archive <tag>` en de 3D-engine uit `../configurator` naar de server; `server/activate.sh` maakt daar
|
||||
een venv, back-upt de database, zet `current` om en herstart de dienst. Start de nieuwe versie niet, dan gaat hij
|
||||
terug naar de vorige. Logboek: `sudo journalctl -u vanemous-crm`.
|
||||
|
||||
`CRM_PROXY=1` laat de app de `X-Forwarded-Proto`/`-Host`-headers van de proxy volgen (links naar buiten, zoals
|
||||
portaaluitnodigingen, worden dan `https://domein/...`) en zet sessiecookies op alleen-HTTPS. De proxy moet
|
||||
uploads tot 25 MB doorlaten (nginx: `client_max_body_size 25m`).
|
||||
|
||||
Lokaal zoals op de server, maar zonder nginx: `./deploy.sh` (zie het script).
|
||||
|
||||
Back-ups buiten de server (bijv. de back-upoptie van OVH of een rsync van `/srv/vanemous-crm/data`) regel je apart:
|
||||
de nachtelijke kopie staat op dezelfde schijf en dekt `uploads/` niet.
|
||||
|
||||
## Opslag
|
||||
|
||||
@@ -79,5 +143,11 @@ importer.py CSV/JSON inlezen en kolommen herkennen
|
||||
mail.py IMAP/SMTP
|
||||
schema.sql database
|
||||
templates/ pagina's (Jinja)
|
||||
static/ style.css (huisstijltokens), fx.js (GSAP-effecten) en logo.svg
|
||||
static/ style.css (huisstijltokens), fx.js (GSAP-effecten), logo.svg, viewer.js (3D-viewer), bestek.js
|
||||
static/configurator/ engine van de configurator (tools/configurator-sync.sh, niet in git)
|
||||
tools/ configurator-sync.sh, export-models.js (standaardmodellen als JSON)
|
||||
demo/modellen standaardmodellen als house-design-JSON voor de demoleads
|
||||
```
|
||||
|
||||
De plattegrond in `static/viewer.js` is overgenomen uit `js/light/light.js` (`drawPlan`) van de
|
||||
configurator; verandert die, dan hier meenemen.
|
||||
|
||||
Reference in New Issue
Block a user