194 lines
8.2 KiB
Markdown
194 lines
8.2 KiB
Markdown
# 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 :**
|
|
```bash
|
|
# 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) :
|
|
```python
|
|
'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)
|
|
```javascript
|
|
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
|
|
|
|
```bash
|
|
# 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).
|
|
|
|
```powershell
|
|
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` | — |
|