Files
3d-pricing/HANDOFF.md
T

8.2 KiB

Handoff — 3D Pricing App

Ce document permet de reprendre le développement dans un nouveau contexte de conversation. Lire aussi GUIDE.md pour la documentation fonctionnelle complète.


État du projet (07/07/2026)

Application en production sur le réseau local. Tous les bugs critiques connus sont résolus.

Stack

  • Python 3.12 / Flask / SQLite (WAL mode)
  • Bootstrap 5 + vis.js Timeline
  • Docker (port 5010 → 5000 interne)
  • Données persistantes dans volume Docker /data/pricing.db

Dépôt

C:\Users\jbper\Claude\3d-pricing\
├── app.py                  # ~2557 lignes — backend complet
├── templates/              # 27 templates Jinja2
├── GUIDE.md                # Documentation fonctionnelle
├── HANDOFF.md              # Ce fichier
├── Dockerfile
├── docker-compose.yml
└── requirements.txt        # flask, authlib, requests

Règle absolue : ne jamais éditer app.py avec l'outil Edit

Le fichier app.py est sur un montage NTFS (Windows) — l'outil Edit tronque le fichier silencieusement.

Méthode obligatoire pour modifier app.py :

# Toujours passer par un script Python et écrire directement via le chemin bash
python3 - << 'EOF'
with open('/sessions/festive-sharp-bardeen/mnt/3d-pricing/app.py', 'r') as f:
    content = f.read()
content = content.replace(old, new, 1)
with open('/sessions/festive-sharp-bardeen/mnt/3d-pricing/app.py', 'w') as f:
    f.write(content)
EOF

La même règle s'applique aux templates si besoin (mais ils sont plus petits, moins risqués).


Migrations DB

Les migrations sont dans init_db() sous forme de liste migrations avec try/except par commande. Ne jamais supprimer une migration existante — l'ordre est cumulatif.

Dernières migrations ajoutées (à la fin de la liste) :

'ALTER TABLE printers ADD COLUMN ha_entity_prefix TEXT DEFAULT ""'
'ALTER TABLE print_slots ADD COLUMN pieces_ignored INTEGER DEFAULT 0'
'ALTER TABLE print_slots ADD COLUMN pieces_override INTEGER DEFAULT NULL'
'ALTER TABLE calendar_blocks ADD COLUMN date_end TEXT DEFAULT NULL'
'ALTER TABLE jobs ADD COLUMN marge_pct_on_ht REAL DEFAULT 0'
'ALTER TABLE jobs ADD COLUMN source_nc_path TEXT DEFAULT ""'

Fonctionnalités clés et leur implémentation

Moteur de pricing (calc(), ligne ~458)

Calcul par pièce. weight_g et print_time_s = valeurs du plateau complet Bambu, divisées par pieces_per_plate. Les charges fiscales (cotisations, VFL, autres taxes) sont incluses dans le prix via prix_ht_net = prix_ht / (1 - total_charges).

Thread Home Assistant (_ha_sync_once(), ligne ~2103)

  • Poll toutes les N secondes (setting ha_poll_interval, défaut 60s)
  • Connexions courtes par imprimante avec isolation_level=None (autocommit) pour éviter database is locked
  • Entités surveillées : sensor.{prefix}_etat_de_l_impression, binary_sensor.{prefix}_erreur_d_impression, sensor.{prefix}_objets_ignores, sensor.{prefix}_avancement_de_l_impression

Multi-plateau

  • pieces_per_plate sur le job = pièces par plateau physique
  • order_qty = quantité totale commandée
  • total_plates_needed = ceil(order_qty / pieces_per_plate) (division entière ceiling)
  • "À planifier" sur le planning : affiche les jobs tant que scheduled_plates < total_plates_needed
  • Compensation : si HA signale pieces_ignored > 0 sur un slot, les autres slots du job affichent un badge amber pour override le nb de pièces

Déjà imprimé (already_done)

Dans POST /api/slots/new : si already_done=true, les checks capacité/overlap sont sautés entièrement, slot inséré avec status='done'. L'ordre du check est important : already_done est testé avant _check_overlap et _check_capacity.

Gantt (planning.html, ligne ~366)

start: new Date(now.getTime() - 12 * 3600000),  // -12h pour voir les impressions en cours
end: new Date(now.getTime() + 14 * 86400000),

Indisponibilités par plage (calendar_blocks)

Colonne date_end optionnelle. Requêtes avec COALESCE(date_end, date) pour compatibilité ascendante.

Duplication avec re-parse Nextcloud (source_nc_path)

  • Nouveau champ DB source_nc_path : chemin WebDAV complet du fichier .3mf
  • À la création d'un job depuis Nextcloud, le path est sauvegardé dans un hidden input
  • Au chargement de new_job avec clone_from, autoParseOnClone() appelle /api/nextcloud/parse?path=... et reconstitue le sélecteur de plateaux

Bugs résolus dans cette session

Bug Fix
database is locked lors de la création de job WAL mode + connexions autocommit par imprimante dans le thread HA
Slot HA-synced invisible dans le Gantt Gantt démarre 12h avant maintenant
Vérification capacité bloquait "Déjà imprimé" Check already_done déplacé avant _check_capacity
Vérification capacité bloquait sur impressions en cours Filtre AND planned_end > datetime('now')
"À planifier" ne montrait qu'un seul plateau Requête compare scheduled_plates < total_plates_needed
datetime.utcnow() deprecated Remplacé par datetime.now(timezone.utc).replace(tzinfo=None)
INSERT jobs : 36 ? pour 37 colonnes Ajout du ? manquant pour marge_pct_on_ht
jobs.html tronqué après écriture Python Toujours partir d'une version git saine avant modification

Points d'attention / dette technique

Ce qui est propre

  • WAL mode SQLite : pas de blocage lecture/écriture
  • Thread HA : connexions courtes, autocommit, pas de transaction longue
  • Migrations cumulatives dans init_db() avec try/except par ligne
  • login_required décorateur + require_login_global pour protection globale si OIDC activé

À surveiller

  • jobs.html : toujours utiliser le script Python pour modifier, vérifier la syntaxe Jinja2 après chaque modif avec python3 -c "import jinja2; ..."
  • app.py : après ajout de docstrings avec des accents, vérifier python3 -m py_compile app.py — les chaînes avec apostrophes dans les docstrings """ sont valides mais certaines substitutions de texte peuvent créer des conflits
  • INSERT jobs : 38 colonnes = 38 ?. Si on ajoute une colonne, ajouter aussi le ? ET la valeur dans le tuple

Fonctionnalité pas encore implémentée

  • Recalcul de marge_pct_on_ht pour les jobs existants (valeur = 0 pour les anciens jobs)
  • Stats : le gaspillage filament (total_wasted_g) n'est pas encore affiché dans les stats globales
  • Page mobile : n'enregistre pas source_nc_path (pas critique)

Commandes utiles

# Vérifier la syntaxe Python
python3 -m py_compile /sessions/festive-sharp-bardeen/mnt/3d-pricing/app.py

# Vérifier la syntaxe Jinja2 d'un template
python3 -c "
import jinja2
env = jinja2.Environment(loader=jinja2.FileSystemLoader('templates'))
env.parse(open('templates/jobs.html').read())
print('OK')
"

# Voir les routes Flask
grep -n "^@app.route" /sessions/festive-sharp-bardeen/mnt/3d-pricing/app.py

# Chercher dans app.py sans tronquer
grep -n "mot_clé" /sessions/festive-sharp-bardeen/mnt/3d-pricing/app.py

Git

Le dépôt est sur Windows — les commandes git doivent être exécutées depuis un terminal Windows, pas depuis le shell Linux (le fichier git/index.lock bloque les commits depuis Linux).

cd C:\Users\jbper\Claude\3d-pricing
git add -A
git commit -m "description"

Derniers commits :

3da19f5  feat: auto-reparse Nextcloud 3mf on job clone
0069ab2  fix: INSERT jobs missing ? for marge_pct_on_ht
80307ac  feat: plate_name and total_marge in jobs list
f51a789  fix: HA sync per-printer autocommit connections
522a0c5  fix: WAL mode SQLite
b55edce  fix: skip capacity/overlap when already_done=true
7824124  fix: replace deprecated utcnow()
1899165  fix: gantt starts 12h before now

Chemins importants

Quoi Chemin Windows Chemin Linux (bash)
Dossier projet C:\Users\jbper\Claude\3d-pricing\ /sessions/festive-sharp-bardeen/mnt/3d-pricing/
app.py C:\Users\jbper\Claude\3d-pricing\app.py /sessions/festive-sharp-bardeen/mnt/3d-pricing/app.py
Templates C:\Users\jbper\Claude\3d-pricing\templates\ /sessions/festive-sharp-bardeen/mnt/3d-pricing/templates/
Base de données Dans le container Docker : /data/pricing.db