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 @@
| Nom | Coeff design par défaut | -Commandes | CA total | + | Nom | Commandes | CA 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 %}
|