Planning complet : multi-pièces, capacité, Gantt, iCal, horaires, imprimantes

This commit is contained in:
Jo
2026-07-06 16:15:14 +02:00
parent dbbbc92d91
commit bd6f244260
8 changed files with 1308 additions and 76 deletions
+61 -4
View File
@@ -71,7 +71,7 @@
<div id="stockInfo" class="form-text"></div>
</div>
</div>
<div class="row g-3">
<div class="row g-3 mb-3">
<div class="col-md-4">
<label class="form-label fw-semibold">Poids filament (g)</label>
<input type="number" name="weight_g" id="weight_g" class="form-control"
@@ -88,6 +88,20 @@
<div class="form-text">Temps total Bambu</div>
</div>
</div>
<div class="row g-3">
<div class="col-md-6">
<label class="form-label fw-semibold">Pièces par plateau</label>
<input type="number" name="pieces_per_plate" id="pieces_per_plate" class="form-control"
min="1" step="1" value="1">
<div class="form-text">Combien de pièces tiennent sur un plateau.</div>
</div>
<div class="col-md-6">
<label class="form-label fw-semibold">Quantité commandée</label>
<input type="number" name="order_qty" id="order_qty" class="form-control"
min="1" step="1" value="1">
<div class="form-text">Nombre total de pièces à produire.</div>
</div>
</div>
</div>
</div>
@@ -338,7 +352,7 @@ document.getElementById('materialSelect').addEventListener('change', function()
});
// ── Autres champs → recalc ────────────────────────────────────────────────────
['weight_g','hours','minutes','discount_pct'].forEach(id => {
['weight_g','hours','minutes','discount_pct','pieces_per_plate','order_qty'].forEach(id => {
const el = document.getElementById(id);
if (el) el.addEventListener('input', recalc);
});
@@ -366,6 +380,8 @@ function recalc() {
machine_profile_id: document.getElementById('machineProfileSelect').value || null,
handling_profile_id: document.getElementById('handlingProfileSelect').value || null,
material_profile_id: document.getElementById('materialProfileSelect').value || null,
pieces_per_plate: parseInt(document.getElementById('pieces_per_plate').value) || 1,
order_qty: parseInt(document.getElementById('order_qty').value) || 1,
};
fetch('/api/calculate', {
@@ -407,7 +423,47 @@ function renderBreakdown(d, body) {
const disRow = body.discount_pct > 0
? `<div class="breakdown-row text-danger"><span class="ps-2">Remise (${body.discount_pct}%)</span><span>-${fmt2(d.prix_ttc * body.discount_pct / 100)}</span></div>` : '';
document.getElementById('breakdown').innerHTML = `
// Multi-piece header
const qty = body.order_qty || 1;
const ppp = body.pieces_per_plate || 1;
const nbPlateaux = Math.ceil(qty / ppp);
const lastPlate = qty % ppp;
const priceOrder = d.price_order || (d.final_price * qty);
let plateauAlert = '';
if (lastPlate !== 0) {
plateauAlert = `<div class="alert alert-warning py-2 mb-2 small">
<i class="bi bi-exclamation-triangle me-1"></i>
Dernier plateau incomplet : <strong>${lastPlate}/${ppp} pièces</strong>.
Vous pourriez imprimer jusqu'à <strong>${nbPlateaux * ppp}</strong> pièces pour le même coût fixe.
</div>`;
}
// Prix header (1 ou 2 colonnes)
const multiPriceHeader = qty > 1
? `<div class="d-flex justify-content-between align-items-center mb-3">
<div class="text-center flex-fill border-end">
<div class="small text-muted">Prix / pièce</div>
<div class="fw-bold fs-5" style="color:#ff6b35">${fmt2(d.final_price)}</div>
</div>
<div class="text-center flex-fill">
<div class="small text-muted">Prix commande (×${qty})</div>
<div class="fw-bold fs-5" style="color:#ff6b35">${fmt2(priceOrder)}</div>
</div>
</div>`
: '';
// Capacité
const cap = d.capacity || {};
const totalMin = d.total_print_min || 0;
const capBlock = `
<div class="breakdown-row section-header mt-1"><span>Capacite de production</span></div>
<div class="breakdown-row"><span class="breakdown-muted ps-2">Nb plateaux necessaires</span><span><strong>${nbPlateaux}</strong> plateau${nbPlateaux>1?'x':''}</span></div>
<div class="breakdown-row"><span class="breakdown-muted ps-2">Duree totale (impression + cooldown)</span><span>${Math.floor(totalMin/60)}h${String(Math.round(totalMin%60)).padStart(2,'0')}min</span></div>
<div class="breakdown-row"><span class="breakdown-muted ps-2">Pieces / jour (${cap.effective_printers||5} machines)</span><span>${cap.pieces_per_day||'—'}</span></div>
<div class="breakdown-row"><span class="breakdown-muted ps-2">Pieces / semaine</span><span>${cap.pieces_per_week||'—'}</span></div>
<div class="breakdown-row"><span class="breakdown-muted ps-2">Pieces / mois (30j)</span><span>${cap.pieces_per_month||'—'}</span></div>`;
document.getElementById('breakdown').innerHTML = plateauAlert + multiPriceHeader + `
<div class="breakdown-row section-header"><span>Cout fixe</span></div>
<div class="breakdown-row"><span class="breakdown-muted ps-2">Matiere <small class="text-secondary">${matName}</small></span><span>${fmt(d.material_cost)}</span></div>
<div class="breakdown-row"><span class="breakdown-muted ps-2">Marge matiere (${d._material_margin_pct}%)</span><span>${fmt(d.material_margin)}</span></div>
@@ -430,7 +486,8 @@ function renderBreakdown(d, body) {
<div class="breakdown-row total" style="border-top:none"><span>Prix HT</span><span class="final-price">${fmt2(d.prix_ht_net)}</span></div>
<div class="breakdown-row ${tvaClass}"><span class="ps-2">${tvaLabel}</span><span>${d._tva_pct > 0 ? fmt2(d.tva_amount) : ''}</span></div>
${disRow}
<div class="breakdown-row total"><span>PRIX FINAL TTC</span><span class="final-price">${fmt2(d.final_price)}</span></div>`;
<div class="breakdown-row total"><span>PRIX FINAL TTC</span><span class="final-price">${fmt2(d.final_price)}</span></div>
` + capBlock;
}
// Init