# Guide — 3D Pricing App Application web Flask/SQLite de gestion du pricing et de la planification pour un atelier d'impression 3D. --- ## Architecture technique ``` Flask (Python) ──► SQLite (WAL mode) │ ├── Templates Jinja2 (Bootstrap 5 + vis.js) ├── Thread arrière-plan : sync Home Assistant └── Docker : /data/pricing.db (persistant) ``` **Stack :** - Python 3.12 + Flask - SQLite avec WAL (pas de blocage lecture/écriture simultanée) - Bootstrap 5 + Bootstrap Icons - vis.js Timeline (Gantt planning) - Authelia OIDC (optionnel, activé via variables d'env) - Home Assistant REST API (optionnel) - Nextcloud WebDAV (optionnel) --- ## Schéma de base de données | Table | Rôle | |---|---| | `settings` | Clés/valeurs de configuration globale | | `clients` | Clients avec profils par défaut | | `projects` | Projets regroupant plusieurs commandes | | `jobs` | Commandes / calculs de prix (1 plateau = 1 job) | | `materials` | Filaments avec stock et historique de prix | | `material_price_history` | Historique des changements de prix filament | | `machine_profiles` | Profils machine (usure, électricité) | | `handling_profiles` | Profils manutention (taux horaire, minutes/plateau) | | `material_profiles` | Profils marge matière | | `pricing_profiles` | Profils multiplicateur design + marge brute | | `printers` | Imprimantes physiques avec préfixe HA | | `print_slots` | Créneaux d'impression planifiés | | `working_schedule` | Planning hebdomadaire (TT / sur site / off) | | `calendar_blocks` | Indisponibilités ponctuelles (plage de dates) | --- ## Moteur de calcul (`calc()`) Tout se calcule **par pièce** à partir des données du plateau Bambu. ``` Coût fixe = matière + marge_matière + usure_machine + électricité (divisé par pièces/plateau) Sous-total = coût_fixe + manutention + design Marge brute = sous-total × gross_margin_pct / 100 Total marge = manutention + design + marge_brute Prix HT = coût_fixe + total_marge Prix HT net = prix_ht / (1 - cotisations - VFL - autres_taxes) └─ les charges fiscales sont "self-contained" dans le prix Prix TTC = prix_ht_net × (1 + TVA) Prix final = prix_ttc × (1 - remise) ``` **Multiplicateur design** : coefficient appliqué au coût matière pour valoriser le temps de modélisation. `×0.8` = design simple, `×1.8` = design complexe. --- ## Pages et fonctionnalités ### Dashboard (`/`) Vue d'ensemble : 10 dernières commandes, CA total, CA du mois, alertes stock bas. ### Commandes (`/jobs`) Liste de toutes les commandes avec réf. plateau, poids, durée, marge, remise, prix final. - **Nouveau job** : saisie manuelle ou import `.gcode.3mf` depuis Nextcloud - **Import Bambu** : parse `slice_info.config` → détecte les plateaux, poids, durée, filaments - **Duplication** (`?clone_from=ID`) : copie les paramètres + re-parse automatiquement le fichier Nextcloud si `source_nc_path` est renseigné - **Export CSV** : toutes les commandes en CSV ### Détail commande (`/jobs/`) - Décomposition du prix (coût fixe, variable, fiscal) - Section **Plateaux d'impression** : liste des slots avec statut, pièces, avancement HA - Portail client : génération d'un lien de partage tokenisé ### Projets (`/projects`) Regroupe plusieurs commandes sous un même projet client. Affiche total pièces et CA. ### Clients (`/clients`) Clients avec profils par défaut (machine, manutention, matière, tarif). Ces profils se chargent automatiquement à la création d'un job pour ce client. ### Matières (`/materials`) - Stock en grammes avec alerte seuil bas - Historique des prix - Déduction automatique du stock à la création d'un job - Suivi du gaspillage (impressions ratées) ### Profils (`/profiles`) 4 types de profils réutilisables : - **Machine** : prix imprimante, durée de vie, nozzle, plateau, puissance, électricité - **Manutention** : taux horaire, minutes par plateau - **Matière** : marge matière en % - **Tarif** : multiplicateur design + marge brute ### Planning (`/planning`) Gantt vis.js sur 14 jours (démarre 12h avant maintenant pour voir les impressions en cours). - **Gantt** : slots par imprimante, codes couleur par statut, bandes rouges si capacité saturée - **À planifier** : jobs dont tous les plateaux ne sont pas encore planifiés - **Overlay HA live** : rafraîchissement automatique (intervalle configurable) des statuts depuis Home Assistant - **Badges compensation** : si HA signale des pièces ignorées sur un plateau, les autres plateaux du même job proposent d'augmenter leur quantité **Planifier un slot :** 1. Cliquer sur un job "À planifier" 2. Choisir imprimante + date/heure de lancement 3. Option "Déjà imprimé" pour enregistrer rétroactivement sans vérification de capacité **Statuts des slots :** | Statut | Couleur | Signification | |---|---|---| | `planned` | bleu | Planifié, pas encore lancé | | `running` | orange | En cours (mis à jour par HA sync) | | `done` | vert | Terminé | | `failed` | rouge | Raté (gaspillage logué) | | `cancelled` | gris | Annulé | ### Imprimantes (`/printers`) - Activer/désactiver des imprimantes - Configurer le préfixe HA (`a1_1` → surveille `sensor.a1_1_etat_de_l_impression`) - Modifier nom, préfixe HA, notes ### Planning horaire (`/working-schedule`) Configuré par jour de la semaine : - **TT** (télétravail) : fenêtre horaire avec nb de lancements calculé automatiquement - **Onsite** : 2 créneaux fixes (ex: 07h00 + 18h30) - **Off** : aucun lancement possible ### Indisponibilités (`/calendar-blocks`) Bloquer une période (date début → date fin optionnelle) pour une ou toutes les imprimantes. Types : Off / Télétravail / Sur site / Maintenance machine. ### Page mobile (`/mobile`) Interface simplifiée pour saisie rapide sur smartphone : - Ajouter une indisponibilité - Décaler un créneau - Marquer une impression comme ratée ou terminée ### Statistiques (`/stats`) CA par période, matières les plus utilisées, gaspillage, top clients. ### Paramètres (`/settings`) - Taux fiscaux (cotisations, VFL, TVA, autres taxes) - Paramètres machine par défaut - Configuration Nextcloud (URL, user, mot de passe d'app, dossier racine) - Configuration Home Assistant (URL, token, intervalle de sync) - Nombre max d'imprimantes simultanées - Cooldown entre impressions (minutes) --- ## Home Assistant Sync Le thread HA tourne en arrière-plan et poll toutes les N secondes (configurable, défaut 60s). **Entités surveillées** (par imprimante avec préfixe `{prefix}`) : - `sensor.{prefix}_etat_de_l_impression` → `running` / `finish` / `unknown` - `binary_sensor.{prefix}_erreur_d_impression` → `on` / `off` - `sensor.{prefix}_objets_ignores` → nombre de pièces ignorées (compensation multi-plateau) - `sensor.{prefix}_avancement_de_l_impression` → % d'avancement **Logique de sync :** 1. Cherche un slot `planned` ou `running` qui chevauche la fenêtre actuelle (±30 min) 2. `running` → passe le slot à `running` 3. `finish` → passe le slot à `done` 4. `binary_sensor erreur = on` → passe le slot à `failed` 5. `objets_ignores > 0` → met à jour `pieces_ignored` sur le slot **Important** : le sync utilise des connexions courtes par imprimante (autocommit) pour ne pas bloquer les requêtes Flask. --- ## Nextcloud Utilisé comme bibliothèque de fichiers `.gcode.3mf`. - Navigation WebDAV via le picker modal dans "Nouveau job" - Téléchargement + parsing du `.3mf` côté serveur (`/api/nextcloud/parse`) - Chemin stocké dans `source_nc_path` pour permettre la duplication sans re-sélection --- ## Portail client Chaque commande peut générer un lien tokenisé (`/portal/`) donnant accès à : - Décomposition du prix en lecture seule - Sans authentification requise --- ## Variables d'environnement (Docker) | Variable | Défaut | Rôle | |---|---|---| | `SECRET_KEY` | généré aléatoirement | Clé session Flask | | `DATABASE_PATH` | `/data/pricing.db` | Chemin SQLite | | `OIDC_CLIENT_ID` | — | Active l'auth OIDC si défini | | `OIDC_CLIENT_SECRET` | — | Secret OIDC | | `OIDC_DISCOVERY_URL` | — | URL metadata OIDC (Authelia) | | `OIDC_END_SESSION_URL` | — | URL déconnexion OIDC | --- ## Calendrier iCal (`/calendar.ics`) Export iCal des créneaux planifiés. Protégé par token (`ical_token` dans settings). URL : `http://host/calendar.ics?token=` --- ## API JSON principales | Endpoint | Méthode | Rôle | |---|---|---| | `/api/calculate` | POST | Calcul de prix live (formulaire new_job) | | `/api/slots` | GET | Liste des slots pour le Gantt (7 derniers jours → +) | | `/api/slots/new` | POST | Créer un slot planifié | | `/api/slots//move` | POST | Déplacer un slot | | `/api/slots//fail` | POST | Marquer comme raté | | `/api/slots//done` | POST | Marquer comme terminé | | `/api/slots//status` | POST | Changer le statut | | `/api/slots//pieces_override` | POST | Override nb pièces (compensation) | | `/api/suggest-slot` | POST | Suggérer le prochain créneau libre | | `/api/capacity` | POST | Calculer la capacité de production | | `/api/ha/status` | GET | Statuts HA de toutes les imprimantes | | `/api/parse-3mf` | POST | Parser un fichier .3mf uploadé | | `/api/nextcloud/browse` | GET | Lister un dossier Nextcloud | | `/api/nextcloud/parse` | GET | Fetch + parser un .3mf Nextcloud | | `/api/nextcloud/file` | GET | Proxy téléchargement fichier Nextcloud | | `/api/client//defaults` | GET | Profils par défaut d'un client |