Templates, decomposition prix restructuree, TVA + VFL, fix DM buttons
This commit is contained in:
+236
-66
@@ -8,6 +8,7 @@
|
||||
|
||||
<div class="row g-4">
|
||||
<div class="col-lg-7">
|
||||
|
||||
<!-- Import 3mf -->
|
||||
<div class="card mb-3 border border-warning border-opacity-50">
|
||||
<div class="card-header py-3">
|
||||
@@ -22,22 +23,26 @@
|
||||
</button>
|
||||
<span id="parseStatus" class="text-muted small"></span>
|
||||
</div>
|
||||
<div class="form-text mt-1">Glissez votre fichier .3mf depuis Bambu Studio pour remplir poids et duree automatiquement.</div>
|
||||
<div class="form-text mt-1">Votre fichier .3mf depuis Bambu Studio pour remplir poids et duree automatiquement.</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<form method="POST" id="jobForm">
|
||||
|
||||
<!-- Donnees impression -->
|
||||
<div class="card mb-3">
|
||||
<div class="card-header py-3">Donnees impression</div>
|
||||
<div class="card-body">
|
||||
<div class="row g-3 mb-3">
|
||||
<div class="col-md-8">
|
||||
<label class="form-label fw-semibold">Nom de la commande</label>
|
||||
<input type="text" name="name" id="jobName" class="form-control" required placeholder="ex: Marqueurs competition salle X">
|
||||
<input type="text" name="name" id="jobName" class="form-control" required
|
||||
placeholder="ex: Marqueurs competition salle X">
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<label class="form-label fw-semibold">Fichier source</label>
|
||||
<input type="text" name="source_file" id="sourceFile" class="form-control" placeholder="nom_fichier.3mf">
|
||||
<input type="text" name="source_file" id="sourceFile" class="form-control"
|
||||
placeholder="nom_fichier.3mf">
|
||||
</div>
|
||||
</div>
|
||||
<div class="row g-3 mb-3">
|
||||
@@ -51,12 +56,13 @@
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label class="form-label fw-semibold">Matiere</label>
|
||||
<label class="form-label fw-semibold">Matiere (filament)</label>
|
||||
<select name="material_id" id="materialSelect" class="form-select">
|
||||
<option value="">Prix global (parametres)</option>
|
||||
{% for m in materials %}
|
||||
<option value="{{ m.id }}" data-price="{{ m.price_per_kg }}" data-stock="{{ m.stock_g }}" data-threshold="{{ m.low_stock_threshold_g }}">
|
||||
{{ m.name }} ({{ m.type }}) - {{ m.price_per_kg }}EUR/kg
|
||||
<option value="{{ m.id }}" data-price="{{ m.price_per_kg }}"
|
||||
data-stock="{{ m.stock_g }}" data-threshold="{{ m.low_stock_threshold_g }}">
|
||||
{{ m.name }} ({{ m.type }}) — {{ m.price_per_kg }} €/kg
|
||||
{% if m.stock_g <= 0 %}[RUPTURE]{% elif m.stock_g <= m.low_stock_threshold_g %}[stock bas]{% endif %}
|
||||
</option>
|
||||
{% endfor %}
|
||||
@@ -67,7 +73,8 @@
|
||||
<div class="row g-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" step="0.01" min="0.1" required placeholder="423.97">
|
||||
<input type="number" name="weight_g" id="weight_g" class="form-control"
|
||||
step="0.01" min="0.1" required placeholder="423.97">
|
||||
<div class="form-text">Champ Modele dans Bambu</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
@@ -83,6 +90,71 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Profils -->
|
||||
<div class="card mb-3">
|
||||
<div class="card-header py-3">
|
||||
<i class="bi bi-bookmark-star me-2"></i>Profils
|
||||
<a href="{{ url_for('profiles') }}" class="btn btn-sm btn-outline-secondary ms-2 py-0" target="_blank">
|
||||
<i class="bi bi-pencil-square"></i> Gerer
|
||||
</a>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="row g-3">
|
||||
<div class="col-md-6">
|
||||
<label class="form-label fw-semibold small">
|
||||
<i class="bi bi-cpu me-1 text-muted"></i>Profil machine
|
||||
</label>
|
||||
<select name="machine_profile_id" id="machineProfileSelect" class="form-select form-select-sm">
|
||||
<option value="">— Parametres globaux —</option>
|
||||
{% for p in machine_profiles %}
|
||||
<option value="{{ p.id }}">{{ p.name }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label class="form-label fw-semibold small">
|
||||
<i class="bi bi-hand-index me-1 text-muted"></i>Profil manutention
|
||||
</label>
|
||||
<select name="handling_profile_id" id="handlingProfileSelect" class="form-select form-select-sm">
|
||||
<option value="">— Parametres globaux —</option>
|
||||
{% for p in handling_profiles %}
|
||||
<option value="{{ p.id }}">{{ p.name }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label class="form-label fw-semibold small">
|
||||
<i class="bi bi-box me-1 text-muted"></i>Profil matiere
|
||||
</label>
|
||||
<select name="material_profile_id" id="materialProfileSelect" class="form-select form-select-sm">
|
||||
<option value="">— Parametres globaux —</option>
|
||||
{% for p in material_profiles %}
|
||||
<option value="{{ p.id }}" data-margin="{{ p.material_margin_pct }}">
|
||||
{{ p.name }} ({{ p.material_margin_pct }}%)
|
||||
</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label class="form-label fw-semibold small">
|
||||
<i class="bi bi-tags me-1 text-muted"></i>Profil tarif client
|
||||
</label>
|
||||
<select name="pricing_profile_id" id="pricingProfileSelect" class="form-select form-select-sm">
|
||||
<option value="">— Manuel —</option>
|
||||
{% for p in pricing_profiles %}
|
||||
<option value="{{ p.id }}"
|
||||
data-dm="{{ p.design_multiplier }}"
|
||||
data-margin="{{ p.gross_margin_pct }}">
|
||||
{{ p.name }}
|
||||
</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Parametres tarifaires -->
|
||||
<div class="card mb-3">
|
||||
<div class="card-header py-3">Parametres tarifaires</div>
|
||||
<div class="card-body">
|
||||
@@ -90,22 +162,31 @@
|
||||
<label class="form-label fw-semibold">
|
||||
Coefficient design <span class="badge bg-secondary ms-1" id="dmLabel">x1.80</span>
|
||||
</label>
|
||||
<div class="d-flex gap-2 mb-2">
|
||||
<button type="button" class="btn btn-sm btn-outline-success" onclick="setDM(0.80)">STL fourni (x0.80)</button>
|
||||
<button type="button" class="btn btn-sm btn-outline-primary" onclick="setDM(1.80)">Creation complete (x1.80)</button>
|
||||
<div class="d-flex gap-2 mb-2" id="dmButtons">
|
||||
<button type="button" id="btnDM080" class="btn btn-sm btn-outline-success" onclick="setDM(0.80)">
|
||||
<i class="bi bi-check2 me-1 d-none" id="checkDM080"></i>STL fourni (x0.80)
|
||||
</button>
|
||||
<button type="button" id="btnDM180" class="btn btn-sm btn-outline-primary" onclick="setDM(1.80)">
|
||||
<i class="bi bi-check2 me-1 d-none" id="checkDM180"></i>Creation complete (x1.80)
|
||||
</button>
|
||||
</div>
|
||||
<input type="number" name="design_multiplier" id="design_multiplier" class="form-control form-control-sm" step="0.05" min="0" value="1.80">
|
||||
<input type="number" name="design_multiplier" id="design_multiplier"
|
||||
class="form-control form-control-sm" step="0.05" min="0" value="1.80">
|
||||
</div>
|
||||
<div class="mb-4">
|
||||
<label class="form-label fw-semibold d-flex justify-content-between">
|
||||
Marge brute <span id="marginDisplay" class="text-muted">27.5 %</span>
|
||||
Marge brute <span id="marginDisplay" class="fw-bold" style="color:#ff6b35">27.5 %</span>
|
||||
</label>
|
||||
<input type="range" name="gross_margin_pct" id="gross_margin_pct" class="form-range" min="20" max="35" step="0.5" value="27.5">
|
||||
<div class="d-flex justify-content-between" style="font-size:.75rem;color:#6b7280"><span>20%</span><span>35%</span></div>
|
||||
<input type="range" name="gross_margin_pct" id="gross_margin_pct" class="form-range"
|
||||
min="20" max="35" step="0.5" value="27.5">
|
||||
<div class="d-flex justify-content-between" style="font-size:.75rem;color:#6b7280">
|
||||
<span>20%</span><span>35%</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label fw-semibold">Remise client (%)</label>
|
||||
<input type="number" name="discount_pct" id="discount_pct" class="form-control" min="0" max="100" step="0.5" value="0">
|
||||
<input type="number" name="discount_pct" id="discount_pct" class="form-control"
|
||||
min="0" max="100" step="0.5" value="0">
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label fw-semibold">Notes internes</label>
|
||||
@@ -120,9 +201,12 @@
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<!-- Recap temps reel -->
|
||||
<div class="col-lg-5">
|
||||
<div class="card sticky-top" style="top:1.5rem">
|
||||
<div class="card-header py-3"><i class="bi bi-receipt me-2"></i>Recapitulatif temps reel</div>
|
||||
<div class="card-header py-3">
|
||||
<i class="bi bi-receipt me-2"></i>Recapitulatif temps reel
|
||||
</div>
|
||||
<div class="card-body" id="breakdown">
|
||||
<div class="text-center text-muted py-4">
|
||||
<i class="bi bi-calculator fs-2"></i>
|
||||
@@ -136,6 +220,7 @@
|
||||
|
||||
{% block scripts %}
|
||||
<script>
|
||||
// ── 3MF parser ────────────────────────────────────────────────────────────────
|
||||
function parse3mf() {
|
||||
const file = document.getElementById('file3mf').files[0];
|
||||
if (!file) { alert('Selectionnez un fichier .3mf'); return; }
|
||||
@@ -147,87 +232,172 @@ function parse3mf() {
|
||||
.then(r => r.json())
|
||||
.then(d => {
|
||||
let filled = [];
|
||||
if (d.weight_g) { document.getElementById('weight_g').value = d.weight_g; filled.push('poids'); }
|
||||
if (d.hours != null){ document.getElementById('hours').value = d.hours; filled.push('heures'); }
|
||||
if (d.weight_g) { document.getElementById('weight_g').value = d.weight_g; filled.push('poids'); }
|
||||
if (d.hours != null) { document.getElementById('hours').value = d.hours; filled.push('heures'); }
|
||||
if (d.minutes != null){ document.getElementById('minutes').value = d.minutes; }
|
||||
if (d.filename) {
|
||||
document.getElementById('sourceFile').value = d.filename;
|
||||
if (!document.querySelector('[name=name]').value)
|
||||
document.querySelector('[name=name]').value = d.filename;
|
||||
if (!document.getElementById('jobName').value)
|
||||
document.getElementById('jobName').value = d.filename;
|
||||
}
|
||||
status.textContent = filled.length ? 'Extrait: '+filled.join(', ') : 'Donnees non trouvees';
|
||||
status.style.color = filled.length ? '#22c55e' : '#f59e0b';
|
||||
status.textContent = filled.length ? 'Extrait: ' + filled.join(', ') : 'Donnees non trouvees';
|
||||
status.style.color = filled.length ? '#22c55e' : '#f59e0b';
|
||||
recalc();
|
||||
})
|
||||
.catch(() => { status.textContent = 'Erreur de lecture'; status.style.color='#ef4444'; });
|
||||
.catch(() => { status.textContent = 'Erreur de lecture'; status.style.color = '#ef4444'; });
|
||||
}
|
||||
|
||||
document.getElementById('materialSelect').addEventListener('change', function() {
|
||||
const opt = this.options[this.selectedIndex];
|
||||
const stock = parseFloat(opt.dataset.stock)||0;
|
||||
const threshold = parseFloat(opt.dataset.threshold)||200;
|
||||
const info = document.getElementById('stockInfo');
|
||||
if (!opt.value) { info.textContent=''; return; }
|
||||
if (stock<=0) info.innerHTML='<span class="text-danger">RUPTURE DE STOCK</span>';
|
||||
else if (stock<=threshold) info.innerHTML='<span class="text-warning">Stock bas: '+stock.toFixed(0)+' g</span>';
|
||||
else info.innerHTML='<span class="text-success">Stock: '+stock.toFixed(0)+' g</span>';
|
||||
recalc();
|
||||
});
|
||||
|
||||
// ── Coefficient design ────────────────────────────────────────────────────────
|
||||
function setDM(val) {
|
||||
document.getElementById('design_multiplier').value = val;
|
||||
document.getElementById('dmLabel').textContent = 'x'+val.toFixed(2);
|
||||
updateDMLabel(val);
|
||||
recalc();
|
||||
}
|
||||
document.getElementById('gross_margin_pct').addEventListener('input', function(){
|
||||
document.getElementById('marginDisplay').textContent = this.value+' %'; recalc();
|
||||
|
||||
function updateDMLabel(val) {
|
||||
val = parseFloat(val);
|
||||
document.getElementById('dmLabel').textContent = 'x' + val.toFixed(2);
|
||||
// Etat actif des boutons
|
||||
const is080 = Math.abs(val - 0.80) < 0.001;
|
||||
const is180 = Math.abs(val - 1.80) < 0.001;
|
||||
document.getElementById('btnDM080').className = 'btn btn-sm ' + (is080 ? 'btn-success' : 'btn-outline-success');
|
||||
document.getElementById('btnDM180').className = 'btn btn-sm ' + (is180 ? 'btn-primary' : 'btn-outline-primary');
|
||||
}
|
||||
|
||||
document.getElementById('design_multiplier').addEventListener('input', function() {
|
||||
updateDMLabel(this.value);
|
||||
recalc();
|
||||
});
|
||||
document.getElementById('design_multiplier').addEventListener('input', function(){
|
||||
document.getElementById('dmLabel').textContent = 'x'+parseFloat(this.value).toFixed(2); recalc();
|
||||
|
||||
// ── Marge ─────────────────────────────────────────────────────────────────────
|
||||
function updateMarginDisplay(val) {
|
||||
document.getElementById('marginDisplay').textContent = parseFloat(val).toFixed(1) + ' %';
|
||||
}
|
||||
|
||||
const marginSlider = document.getElementById('gross_margin_pct');
|
||||
marginSlider.addEventListener('input', function() { updateMarginDisplay(this.value); recalc(); });
|
||||
marginSlider.addEventListener('change', function() { updateMarginDisplay(this.value); recalc(); });
|
||||
|
||||
// ── Profil tarif client ───────────────────────────────────────────────────────
|
||||
document.getElementById('pricingProfileSelect').addEventListener('change', function() {
|
||||
const opt = this.options[this.selectedIndex];
|
||||
if (opt.value) {
|
||||
const dm = parseFloat(opt.dataset.dm);
|
||||
const margin = parseFloat(opt.dataset.margin);
|
||||
if (!isNaN(dm)) {
|
||||
document.getElementById('design_multiplier').value = dm;
|
||||
updateDMLabel(dm);
|
||||
}
|
||||
if (!isNaN(margin)) {
|
||||
marginSlider.value = margin;
|
||||
updateMarginDisplay(margin);
|
||||
}
|
||||
recalc();
|
||||
}
|
||||
});
|
||||
document.getElementById('clientSelect').addEventListener('change', function(){
|
||||
|
||||
// ── Client : auto-remplir DM ──────────────────────────────────────────────────
|
||||
document.getElementById('clientSelect').addEventListener('change', function() {
|
||||
const dm = this.options[this.selectedIndex].dataset.dm;
|
||||
if (dm) setDM(parseFloat(dm));
|
||||
if (dm) {
|
||||
document.getElementById('design_multiplier').value = parseFloat(dm);
|
||||
updateDMLabel(parseFloat(dm));
|
||||
recalc();
|
||||
}
|
||||
});
|
||||
|
||||
// ── Matiere : afficher stock ──────────────────────────────────────────────────
|
||||
document.getElementById('materialSelect').addEventListener('change', function() {
|
||||
const opt = this.options[this.selectedIndex];
|
||||
const stock = parseFloat(opt.dataset.stock) || 0;
|
||||
const threshold = parseFloat(opt.dataset.threshold) || 200;
|
||||
const info = document.getElementById('stockInfo');
|
||||
if (!opt.value) { info.textContent = ''; }
|
||||
else if (stock <= 0) info.innerHTML = '<span class="text-danger">RUPTURE DE STOCK</span>';
|
||||
else if (stock <= threshold) info.innerHTML = '<span class="text-warning">Stock bas : ' + stock.toFixed(0) + ' g</span>';
|
||||
else info.innerHTML = '<span class="text-success">Stock : ' + stock.toFixed(0) + ' g</span>';
|
||||
recalc();
|
||||
});
|
||||
|
||||
// ── Autres champs → recalc ────────────────────────────────────────────────────
|
||||
['weight_g','hours','minutes','discount_pct'].forEach(id => {
|
||||
const el = document.getElementById(id);
|
||||
if (el) el.addEventListener('input', recalc);
|
||||
});
|
||||
['machineProfileSelect','handlingProfileSelect','materialProfileSelect'].forEach(id => {
|
||||
const el = document.getElementById(id);
|
||||
if (el) el.addEventListener('change', recalc);
|
||||
});
|
||||
|
||||
// ── Calcul API ────────────────────────────────────────────────────────────────
|
||||
function recalc() {
|
||||
const w = parseFloat(document.getElementById('weight_g').value)||0;
|
||||
const h = parseInt(document.getElementById('hours').value)||0;
|
||||
const m = parseInt(document.getElementById('minutes').value)||0;
|
||||
if (w<=0 || (h===0 && m===0)) return;
|
||||
const w = parseFloat(document.getElementById('weight_g').value) || 0;
|
||||
const h = parseInt(document.getElementById('hours').value) || 0;
|
||||
const m = parseInt(document.getElementById('minutes').value) || 0;
|
||||
if (w <= 0 && h === 0 && m === 0) return;
|
||||
if (w <= 0) return;
|
||||
|
||||
const body = {
|
||||
weight_g: w, hours: h, minutes: m,
|
||||
design_multiplier: parseFloat(document.getElementById('design_multiplier').value)||1.80,
|
||||
gross_margin_pct: parseFloat(document.getElementById('gross_margin_pct').value)||27.5,
|
||||
discount_pct: parseFloat(document.getElementById('discount_pct').value)||0,
|
||||
material_id: document.getElementById('materialSelect').value||null,
|
||||
weight_g: w,
|
||||
hours: h,
|
||||
minutes: m,
|
||||
design_multiplier: parseFloat(document.getElementById('design_multiplier').value) || 1.80,
|
||||
gross_margin_pct: parseFloat(document.getElementById('gross_margin_pct').value) || 27.5,
|
||||
discount_pct: parseFloat(document.getElementById('discount_pct').value) || 0,
|
||||
material_id: document.getElementById('materialSelect').value || null,
|
||||
machine_profile_id: document.getElementById('machineProfileSelect').value || null,
|
||||
handling_profile_id: document.getElementById('handlingProfileSelect').value || null,
|
||||
material_profile_id: document.getElementById('materialProfileSelect').value || null,
|
||||
};
|
||||
fetch('/api/calculate',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify(body)})
|
||||
.then(r=>r.json()).then(d=>renderBreakdown(d,body));
|
||||
|
||||
fetch('/api/calculate', {
|
||||
method: 'POST',
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
body: JSON.stringify(body)
|
||||
})
|
||||
.then(r => r.json())
|
||||
.then(d => renderBreakdown(d, body))
|
||||
.catch(() => {});
|
||||
}
|
||||
|
||||
function fmt(n) { return parseFloat(n).toFixed(4)+' EUR'; }
|
||||
function fmt2(n) { return parseFloat(n).toFixed(2)+' EUR'; }
|
||||
// ── Rendu du recap ────────────────────────────────────────────────────────────
|
||||
function fmt(n) { return parseFloat(n).toFixed(4) + ' €'; }
|
||||
function fmt2(n) { return parseFloat(n).toFixed(2) + ' €'; }
|
||||
|
||||
function renderBreakdown(d, body) {
|
||||
const dis = body.discount_pct>0
|
||||
? '<div class="breakdown-row text-danger"><span>Remise ('+body.discount_pct+'%)</span></div>' : '';
|
||||
const dm = parseFloat(body.design_multiplier).toFixed(2);
|
||||
const mgn = parseFloat(body.gross_margin_pct).toFixed(1);
|
||||
const dis = body.discount_pct > 0
|
||||
? `<div class="breakdown-row text-danger"><span>Remise (${body.discount_pct}%)</span><span>-${fmt2(d.final_price / (1 - body.discount_pct/100) * (body.discount_pct/100))}</span></div>` : '';
|
||||
const tvaRow = d._tva_pct > 0
|
||||
? `<div class="breakdown-row"><span class="breakdown-muted">TVA (${d._tva_pct}%)</span><span>${fmt2(d.tva_amount)}</span></div>` : '';
|
||||
const vflRow = d._vfl_pct > 0
|
||||
? `<div class="breakdown-row"><span class="breakdown-muted">VFL impot (${d._vfl_pct}%)</span><span>${fmt2(d.vfl_amount)}</span></div>` : '';
|
||||
|
||||
document.getElementById('breakdown').innerHTML = `
|
||||
<div class="breakdown-row"><span class="breakdown-muted">Matiere</span><span>${fmt(d.material_cost)}</span></div>
|
||||
<div class="breakdown-row"><span class="breakdown-muted">Marge matiere (20%)</span><span>${fmt(d.material_margin)}</span></div>
|
||||
<div class="breakdown-row"><span class="breakdown-muted">Design (x${body.design_multiplier.toFixed(2)})</span><span>${fmt(d.design_cost)}</span></div>
|
||||
<div class="breakdown-row"><span class="breakdown-muted">Manutention</span><span>${fmt(d.handling_cost)}</span></div>
|
||||
<div class="breakdown-row"><span class="breakdown-muted">Usure machine</span><span>${fmt(d.wear_cost)}</span></div>
|
||||
<div class="breakdown-row"><span class="breakdown-muted">Electricite</span><span>${fmt(d.electricity_cost)}</span></div>
|
||||
<div class="breakdown-row subtotal"><span class="fw-semibold">Sous-total couts</span><span class="fw-semibold">${fmt2(d.subtotal)}</span></div>
|
||||
<div class="breakdown-row"><span class="breakdown-muted">Marge brute (${body.gross_margin_pct}%)</span><span>${fmt2(d.margin_amount)}</span></div>
|
||||
<div class="breakdown-row"><span class="breakdown-muted">Cotisations (12.3%)</span><span>${fmt2(d.tax_amount)}</span></div>
|
||||
<div class="breakdown-row section-header"><span>Cout fixe</span></div>
|
||||
<div class="breakdown-row"><span class="breakdown-muted ps-2">Matiere</span><span>${fmt(d.material_cost)}</span></div>
|
||||
<div class="breakdown-row"><span class="breakdown-muted ps-2">Marge matiere</span><span>${fmt(d.material_margin)}</span></div>
|
||||
<div class="breakdown-row"><span class="breakdown-muted ps-2">Usure machine</span><span>${fmt(d.wear_cost)}</span></div>
|
||||
<div class="breakdown-row"><span class="breakdown-muted ps-2">Electricite</span><span>${fmt(d.electricity_cost)}</span></div>
|
||||
<div class="breakdown-row subtotal"><span class="fw-semibold">Total cout fixe</span><span class="fw-semibold">${fmt2(d.cout_fixe)}</span></div>
|
||||
|
||||
<div class="breakdown-row section-header"><span>Partie variable</span></div>
|
||||
<div class="breakdown-row"><span class="breakdown-muted ps-2">Manutention</span><span>${fmt(d.handling_cost)}</span></div>
|
||||
<div class="breakdown-row"><span class="breakdown-muted ps-2">Design (x${dm})</span><span>${fmt(d.design_cost)}</span></div>
|
||||
<div class="breakdown-row"><span class="breakdown-muted ps-2">Marge brute (${mgn}%)</span><span>${fmt2(d.margin_amount)}</span></div>
|
||||
<div class="breakdown-row subtotal"><span class="fw-semibold">Total marge</span><span class="fw-semibold">${fmt2(d.total_marge)}</span></div>
|
||||
|
||||
<div class="breakdown-row section-header"><span>Fiscal</span></div>
|
||||
<div class="breakdown-row"><span class="breakdown-muted ps-2">Cotisations (${d._cot_pct}%)</span><span>${fmt2(d.cotisations_amount)}</span></div>
|
||||
${vflRow}
|
||||
${tvaRow}
|
||||
${dis}
|
||||
<div class="breakdown-row total"><span>PRIX FINAL</span><span class="final-price">${fmt2(d.final_price)}</span></div>`;
|
||||
}
|
||||
|
||||
// Init
|
||||
updateDMLabel(1.80);
|
||||
updateMarginDisplay(27.5);
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
Reference in New Issue
Block a user