diff --git a/app.py b/app.py index 692a781..a76d6da 100644 --- a/app.py +++ b/app.py @@ -100,6 +100,7 @@ def init_db(): ('cotisations_rate_pct','12.3'), ('vfl_rate_pct','1.0'), ('tva_rate_pct','20.0'), + ('other_taxes_rate_pct','1.0'), ('printer_power_kw','0.35'), ] conn.executemany('INSERT OR IGNORE INTO settings VALUES (?,?)', defaults) @@ -115,6 +116,7 @@ def init_db(): 'ALTER TABLE jobs ADD COLUMN cotisations_amount REAL', 'ALTER TABLE jobs ADD COLUMN vfl_amount REAL', 'ALTER TABLE jobs ADD COLUMN tva_amount REAL', + 'ALTER TABLE jobs ADD COLUMN other_taxes_amount REAL', ] for sql in migrations: try: @@ -214,10 +216,11 @@ def calc(weight_g, print_time_s, design_mult, gross_margin_pct, discount_pct, total_marge= handling + design + margin prix_ht = cout_fixe + total_marge # = sous_total + margin - cot_rate = s.get('cotisations_rate_pct', 12.3) / 100 - vfl_rate = s.get('vfl_rate_pct', 0.0) / 100 - tva_rate = s.get('tva_rate_pct', 0.0) / 100 - total_charges = cot_rate + vfl_rate + cot_rate = s.get('cotisations_rate_pct', 12.3) / 100 + vfl_rate = s.get('vfl_rate_pct', 0.0) / 100 + tva_rate = s.get('tva_rate_pct', 0.0) / 100 + other_taxes_rate = s.get('other_taxes_rate_pct', 1.0) / 100 + total_charges = cot_rate + vfl_rate + other_taxes_rate # Charges calculées sur le CA (prix de vente HT net) # prix_ht_net = prix_ht / (1 - total_charges) so that charges = prix_ht_net * total_charges @@ -226,11 +229,15 @@ def calc(weight_g, print_time_s, design_mult, gross_margin_pct, discount_pct, else: prix_ht_net = prix_ht - cot_amount = prix_ht_net * cot_rate - vfl_amount = prix_ht_net * vfl_rate - tva_amount = prix_ht_net * tva_rate - prix_ttc = prix_ht_net + tva_amount - final = prix_ttc * (1 - discount_pct / 100) + cot_amount = prix_ht_net * cot_rate + vfl_amount = prix_ht_net * vfl_rate + other_taxes_amount = prix_ht_net * other_taxes_rate + tva_amount = prix_ht_net * tva_rate + prix_ttc = prix_ht_net + tva_amount + final = prix_ttc * (1 - discount_pct / 100) + + # % de marge effective sur le prix HT final + marge_pct_on_ht = (total_marge / prix_ht_net * 100) if prix_ht_net > 0 else 0 return { # Lignes détaillées @@ -248,20 +255,23 @@ def calc(weight_g, print_time_s, design_mult, gross_margin_pct, discount_pct, 'subtotal': round(sous_total, 4), 'margin_amount': round(margin, 4), 'total_marge': round(total_marge, 4), + 'marge_pct_on_ht': round(marge_pct_on_ht, 1), 'prix_ht': round(prix_ht, 4), # Fiscal - 'cotisations_amount': round(cot_amount, 4), - 'vfl_amount': round(vfl_amount, 4), - 'tva_amount': round(tva_amount, 4), - 'prix_ht_net': round(prix_ht_net, 4), - 'prix_ttc': round(prix_ttc, 4), + 'cotisations_amount': round(cot_amount, 4), + 'vfl_amount': round(vfl_amount, 4), + 'other_taxes_amount': round(other_taxes_amount, 4), + 'tva_amount': round(tva_amount, 4), + 'prix_ht_net': round(prix_ht_net, 4), + 'prix_ttc': round(prix_ttc, 4), # Compat - 'tax_amount': round(cot_amount + vfl_amount, 4), + 'tax_amount': round(cot_amount + vfl_amount + other_taxes_amount, 4), 'final_price': round(final, 2), # Taux et paramètres appliqués (pour affichage) '_cot_pct': round(cot_rate * 100, 2), '_vfl_pct': round(vfl_rate * 100, 2), '_tva_pct': round(tva_rate * 100, 2), + '_other_taxes_pct': round(other_taxes_rate * 100, 2), '_material_margin_pct': s['material_margin_pct'], '_handling_rate': s['handling_rate_per_hour'], '_handling_minutes': s['handling_minutes_per_plate'], diff --git a/templates/clients.html b/templates/clients.html index a403564..093ae95 100644 --- a/templates/clients.html +++ b/templates/clients.html @@ -15,8 +15,7 @@ - - + @@ -24,13 +23,6 @@ -
NomEmailCoeff design par défautCommandesCA totalNomEmailCommandesCA total
{{ c.name }} {{ c.email or '—' }} - {% if c.design_multiplier <= 0.80 %} - ×{{ c.design_multiplier }} (STL fourni) - {% else %} - ×{{ c.design_multiplier }} - {% endif %} - {{ c.job_count }} {{ c.total_revenue or 0 }} € diff --git a/templates/job_detail.html b/templates/job_detail.html index 0d73e91..60e211e 100644 --- a/templates/job_detail.html +++ b/templates/job_detail.html @@ -62,15 +62,14 @@
Cout fixe
-
Matiere{{ "%.4f"|format(job.material_cost) }} €
+
Matiere{% if job.material_name %} {{ job.material_name }}{% endif %}{{ "%.4f"|format(job.material_cost) }} €
Marge matiere{{ "%.4f"|format(job.material_margin) }} €
Usure machine{{ "%.4f"|format(job.wear_cost) }} €
Electricite{{ "%.4f"|format(job.electricity_cost) }} €
+ {% set cout_f = job.cout_fixe or (job.material_cost + job.material_margin + job.wear_cost + job.electricity_cost) %}
Total cout fixe - - {% if job.cout_fixe %}{{ "%.2f"|format(job.cout_fixe) }}{% else %}{{ "%.2f"|format(job.material_cost + job.material_margin + job.wear_cost + job.electricity_cost) }}{% endif %} € - + {{ "%.2f"|format(cout_f) }} €
@@ -78,18 +77,20 @@
Manutention{{ "%.4f"|format(job.handling_cost) }} €
Design (×{{ job.design_multiplier }}){{ "%.4f"|format(job.design_cost) }} €
Marge brute ({{ job.gross_margin_pct }}%){{ "%.2f"|format(job.margin_amount) }} €
+ {% set tot_m = job.total_marge or (job.handling_cost + job.design_cost + job.margin_amount) %} + {% set prix_ttc = job.final_price / (1 - job.discount_pct / 100) if job.discount_pct < 100 else job.final_price %} + {% set prix_ht_net = prix_ttc - (job.tva_amount or 0) %} + {% set marge_pct_ht = (tot_m / prix_ht_net * 100)|round(1) if prix_ht_net > 0 else 0 %}
- Total marge - - {% if job.total_marge %}{{ "%.2f"|format(job.total_marge) }}{% else %}{{ "%.2f"|format(job.handling_cost + job.design_cost + job.margin_amount) }}{% endif %} € - + Total marge ({{ marge_pct_ht }}% du HT) + {{ "%.2f"|format(tot_m) }} €
Fiscal
- Cotisations sociales - {% if job.cotisations_amount %}{{ "%.2f"|format(job.cotisations_amount) }}{% else %}{{ "%.2f"|format(job.tax_amount) }}{% endif %} € + Cotisations BIC vente + {% if job.cotisations_amount %}{{ "%.2f"|format(job.cotisations_amount) }}{% else %}{{ "%.2f"|format(job.tax_amount or 0) }}{% endif %} €
{% if job.vfl_amount and job.vfl_amount > 0 %}
@@ -97,27 +98,35 @@ {{ "%.2f"|format(job.vfl_amount) }} €
{% endif %} + {% if job.other_taxes_amount and job.other_taxes_amount > 0 %} +
+ Autres taxes / CFE + {{ "%.2f"|format(job.other_taxes_amount) }} € +
+ {% endif %} +
+ Prix HT + {{ "%.2f"|format(prix_ht_net) }} € +
{% if job.tva_amount and job.tva_amount > 0 %}
- TVA + TVA {{ "%.2f"|format(job.tva_amount) }} €
{% endif %} {% if job.discount_pct > 0 %}
Remise ({{ job.discount_pct }}%) - −{{ "%.2f"|format(job.final_price / (1 - job.discount_pct/100) * (job.discount_pct/100)) }} € + −{{ "%.2f"|format(prix_ttc * job.discount_pct / 100) }} €
{% endif %}
- PRIX FINAL + PRIX FINAL TTC {{ "%.2f"|format(job.final_price) }} €
- {% set cout_f = job.cout_fixe or (job.material_cost + job.material_margin + job.wear_cost + job.electricity_cost) %} - {% set tot_m = job.total_marge or (job.handling_cost + job.design_cost + job.margin_amount) %} - {% set fiscal = (job.cotisations_amount or job.tax_amount or 0) + (job.vfl_amount or 0) + (job.tva_amount or 0) %} + {% set fiscal = (job.cotisations_amount or job.tax_amount or 0) + (job.vfl_amount or 0) + (job.other_taxes_amount or 0) + (job.tva_amount or 0) %} {% set total = job.final_price %} {% if total > 0 %}
diff --git a/templates/new_job.html b/templates/new_job.html index 7c905a1..962647f 100644 --- a/templates/new_job.html +++ b/templates/new_job.html @@ -178,10 +178,16 @@ +
+ + + + +
+ min="0" max="100" step="0.5" value="27.5">
- 20%35% + 0%100%
@@ -271,8 +277,19 @@ document.getElementById('design_multiplier').addEventListener('input', function( }); // ── Marge ───────────────────────────────────────────────────────────────────── +function setMargin(val) { + document.getElementById('gross_margin_pct').value = val; + updateMarginDisplay(val); + recalc(); +} + function updateMarginDisplay(val) { - document.getElementById('marginDisplay').textContent = parseFloat(val).toFixed(1) + ' %'; + val = parseFloat(val); + document.getElementById('marginDisplay').textContent = val.toFixed(1) + ' %'; + [20, 25, 30, 35].forEach(function(v) { + const btn = document.querySelector('#marginButtons button[onclick="setMargin(' + v + ')"]'); + if (btn) btn.className = 'btn btn-sm ' + (Math.abs(val - v) < 0.01 ? 'btn-secondary' : 'btn-outline-secondary'); + }); } const marginSlider = document.getElementById('gross_margin_pct'); @@ -374,20 +391,22 @@ function renderBreakdown(d, body) { const matOpt = matSel.options[matSel.selectedIndex]; const matName = matOpt.value ? (matOpt.dataset.name || matOpt.text.split('—')[0].trim()) : 'global'; - // Remise - const dis = body.discount_pct > 0 - ? `
Remise (${body.discount_pct}%)-${fmt2(d.prix_ttc * body.discount_pct / 100)}
` : ''; - - // TVA — toujours affichee - const tvaLabel = d._tva_pct > 0 - ? `TVA ${d._tva_pct}% — ${fmt2(d.tva_amount)}` - : `TVA — franchise en base`; + // TVA + const tvaLabel = d._tva_pct > 0 ? `TVA (${d._tva_pct}%)` : `TVA — franchise en base`; const tvaClass = d._tva_pct > 0 ? '' : 'text-muted fst-italic'; // VFL const vflRow = d._vfl_pct > 0 ? `
VFL impot (${d._vfl_pct}%)${fmt2(d.vfl_amount)}
` : ''; + // Autres taxes + const otherRow = d._other_taxes_pct > 0 + ? `
Autres taxes / CFE (${d._other_taxes_pct}%)${fmt2(d.other_taxes_amount)}
` : ''; + + // Remise + const disRow = body.discount_pct > 0 + ? `
Remise (${body.discount_pct}%)-${fmt2(d.prix_ttc * body.discount_pct / 100)}
` : ''; + document.getElementById('breakdown').innerHTML = `
Cout fixe
Matiere ${matName}${fmt(d.material_cost)}
@@ -402,13 +421,15 @@ function renderBreakdown(d, body) {
Manutention ${d._handling_minutes} min × ${d._handling_rate} €/h${fmt(d.handling_cost)}
Design (x${dm})${fmt(d.design_cost)}
Marge brute (${mgn}%)${fmt2(d.margin_amount)}
-
Total marge${fmt2(d.total_marge)}
+
Total marge (${d.marge_pct_on_ht}% du HT)${fmt2(d.total_marge)}
Fiscal
Cotisations BIC vente (${d._cot_pct}%)${fmt2(d.cotisations_amount)}
${vflRow} -
${tvaLabel}
- ${dis} + ${otherRow} +
Prix HT${fmt2(d.prix_ht_net)}
+
${tvaLabel}${d._tva_pct > 0 ? fmt2(d.tva_amount) : ''}
+ ${disRow}
PRIX FINAL TTC${fmt2(d.final_price)}
`; } diff --git a/templates/settings.html b/templates/settings.html index e828fc6..80a83de 100644 --- a/templates/settings.html +++ b/templates/settings.html @@ -130,6 +130,18 @@ Mettre 0 si vous n'avez pas opte pour le VFL (impot sur declaration classique).
+
+ + +
+ ~1% — provision pour taxes diverses non incluses ci-dessus. + Exemples : CFE (Cotisation Fonciere des Entreprises, due des la 2e annee + d'activite, montant variable selon la commune), + frais bancaires, comptable, etc. + Mettre 0 si non concerne. +
+

@@ -140,11 +152,14 @@ La TVA s'ajoute au prix HT et est collectee pour l'Etat (non conservee).
- {% set total_charges = settings.cotisations_rate_pct + settings.vfl_rate_pct %} + {% set other = settings.get('other_taxes_rate_pct', 1.0) %} + {% set total_charges = settings.cotisations_rate_pct + settings.vfl_rate_pct + other %}
- Charges sur CA HT : {{ total_charges }} % - (cotisations {{ settings.cotisations_rate_pct }}% + VFL {{ settings.vfl_rate_pct }}%) + Charges sur CA HT : {{ "%.1f"|format(total_charges) }} % + (cotisations {{ settings.cotisations_rate_pct }}% + + VFL {{ settings.vfl_rate_pct }}% + + autres {{ other }}%) {% if settings.tva_rate_pct > 0 %} — TVA {{ settings.tva_rate_pct }}% collectee et reversee a l'Etat {% else %}