190 lines
9.6 KiB
HTML
190 lines
9.6 KiB
HTML
{% extends 'base.html' %}
|
||
{% block title %}{{ job.name }}{% endblock %}
|
||
|
||
{% block content %}
|
||
<div class="page-header d-flex justify-content-between align-items-center">
|
||
<div>
|
||
<a href="{{ url_for('jobs') }}" class="text-muted text-decoration-none small">
|
||
<i class="bi bi-arrow-left"></i> Commandes
|
||
</a>
|
||
<h1 class="mt-1">{{ job.name }}</h1>
|
||
</div>
|
||
<form method="POST" action="{{ url_for('delete_job', id=job.id) }}"
|
||
onsubmit="return confirm('Supprimer cette commande ?')">
|
||
<button class="btn btn-sm btn-outline-danger"><i class="bi bi-trash"></i> Supprimer</button>
|
||
</form>
|
||
</div>
|
||
|
||
{% set qty = job.order_qty or 1 %}
|
||
{% set multi = qty > 1 %}
|
||
|
||
<div class="row g-4">
|
||
<div class="col-md-5">
|
||
<div class="card mb-3">
|
||
<div class="card-header py-3">Informations</div>
|
||
<div class="card-body">
|
||
<table class="table table-sm mb-0">
|
||
<tr><th class="text-muted fw-normal" style="width:45%">Client</th><td>{{ job.client_name or '—' }}</td></tr>
|
||
<tr><th class="text-muted fw-normal">Matiere</th>
|
||
<td>
|
||
{% if job.material_name %}
|
||
<span class="badge bg-secondary">{{ job.material_type }}</span> {{ job.material_name }}
|
||
{% else %}—{% endif %}
|
||
</td>
|
||
</tr>
|
||
<tr><th class="text-muted fw-normal">Fichier source</th>
|
||
<td class="small font-monospace">{{ job.source_file or '—' }}</td></tr>
|
||
<tr><th class="text-muted fw-normal">Date</th><td>{{ job.created_at[:10] }}</td></tr>
|
||
<tr><th class="text-muted fw-normal">Poids plateau</th><td>{{ job.weight_g }} g</td></tr>
|
||
<tr><th class="text-muted fw-normal">Pieces / plateau</th><td>{{ job.pieces_per_plate or 1 }}</td></tr>
|
||
<tr><th class="text-muted fw-normal">Quantite commandee</th><td>{{ qty }} pièce{{ 's' if qty > 1 else '' }}</td></tr>
|
||
<tr><th class="text-muted fw-normal">Duree plateau</th><td>{{ job.print_time_s | fmt_time }}</td></tr>
|
||
<tr><th class="text-muted fw-normal">Coeff design</th><td>×{{ job.design_multiplier }}</td></tr>
|
||
<tr><th class="text-muted fw-normal">Marge brute</th><td>{{ job.gross_margin_pct }} %</td></tr>
|
||
<tr><th class="text-muted fw-normal">Remise</th><td>{{ job.discount_pct }} %</td></tr>
|
||
{% if job.machine_profile_name %}
|
||
<tr><th class="text-muted fw-normal">Profil machine</th><td>{{ job.machine_profile_name }}</td></tr>
|
||
{% endif %}
|
||
{% if job.handling_profile_name %}
|
||
<tr><th class="text-muted fw-normal">Profil manutention</th><td>{{ job.handling_profile_name }}</td></tr>
|
||
{% endif %}
|
||
{% if job.pricing_profile_name %}
|
||
<tr><th class="text-muted fw-normal">Profil tarif</th><td>{{ job.pricing_profile_name }}</td></tr>
|
||
{% endif %}
|
||
</table>
|
||
{% if job.notes %}
|
||
<hr>
|
||
<small class="text-muted">{{ job.notes }}</small>
|
||
{% endif %}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="col-md-7">
|
||
<div class="card">
|
||
<div class="card-header py-3 d-flex justify-content-between align-items-center">
|
||
<span><i class="bi bi-receipt me-2"></i>Decomposition du prix</span>
|
||
{% if multi %}
|
||
<span class="badge bg-secondary">{{ qty }} pièces commandées</span>
|
||
{% endif %}
|
||
</div>
|
||
<div class="card-body">
|
||
|
||
{# Macro pour une ligne à 1 ou 2 colonnes #}
|
||
{% macro brow(label, val_piece, cls='', precision=2) %}
|
||
<div class="breakdown-row {{ cls }}">
|
||
<span class="flex-fill">{{ label | safe }}</span>
|
||
<span class="text-end" style="min-width:85px">
|
||
{% if precision == 4 %}{{ "%.4f"|format(val_piece) }} €
|
||
{% else %}{{ "%.2f"|format(val_piece) }} €{% endif %}
|
||
</span>
|
||
{% if multi %}
|
||
<span class="text-end text-muted" style="min-width:85px">
|
||
{% if precision == 4 %}{{ "%.2f"|format(val_piece * qty) }} €
|
||
{% else %}{{ "%.2f"|format(val_piece * qty) }} €{% endif %}
|
||
</span>
|
||
{% endif %}
|
||
</div>
|
||
{% endmacro %}
|
||
|
||
{# En-tête colonnes #}
|
||
{% if multi %}
|
||
<div class="d-flex justify-content-end small text-muted mb-2 border-bottom pb-1 gap-0">
|
||
<span style="min-width:85px;text-align:right">/ pièce</span>
|
||
<span style="min-width:85px;text-align:right">/ commande ×{{ qty }}</span>
|
||
</div>
|
||
{% endif %}
|
||
|
||
{% 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 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 %}
|
||
|
||
<!-- Cout fixe -->
|
||
<div class="breakdown-row section-header"><span>Cout fixe</span></div>
|
||
{{ brow('Matiere' + (' <small class="text-secondary">' + job.material_name + '</small>' if job.material_name else ''), job.material_cost, '', 4) }}
|
||
{{ brow('Marge matiere', job.material_margin, '', 4) }}
|
||
{{ brow('Usure machine', job.wear_cost, '', 4) }}
|
||
{{ brow('Electricite', job.electricity_cost, '', 4) }}
|
||
{{ brow('<span class="fw-semibold">Total cout fixe</span>', cout_f, 'subtotal') }}
|
||
|
||
<!-- Partie variable -->
|
||
<div class="breakdown-row section-header"><span>Partie variable</span></div>
|
||
{{ brow('Manutention', job.handling_cost, '', 4) }}
|
||
{{ brow('Design (×' + job.design_multiplier|string + ')', job.design_cost, '', 4) }}
|
||
{{ brow('Marge brute (' + job.gross_margin_pct|string + '%)', job.margin_amount) }}
|
||
{{ brow('<span class="fw-semibold">Total marge <small class="text-muted fw-normal">(' + marge_pct_ht|string + '% du HT)</small></span>', tot_m, 'subtotal') }}
|
||
|
||
<!-- Fiscal -->
|
||
<div class="breakdown-row section-header"><span>Fiscal</span></div>
|
||
{{ brow('Cotisations BIC vente', job.cotisations_amount or job.tax_amount or 0) }}
|
||
{% if job.vfl_amount and job.vfl_amount > 0 %}
|
||
{{ brow('VFL impot', job.vfl_amount) }}
|
||
{% endif %}
|
||
{% if job.other_taxes_amount and job.other_taxes_amount > 0 %}
|
||
{{ brow('Autres taxes / CFE', job.other_taxes_amount) }}
|
||
{% endif %}
|
||
|
||
<!-- Prix HT -->
|
||
<div class="breakdown-row" style="border-top:none">
|
||
<span class="fw-bold flex-fill">Prix HT</span>
|
||
<span class="final-price text-end" style="font-size:1.3rem;min-width:85px">{{ "%.2f"|format(prix_ht_net) }} €</span>
|
||
{% if multi %}<span class="final-price text-end" style="font-size:1.3rem;min-width:85px;opacity:.75">{{ "%.2f"|format(prix_ht_net * qty) }} €</span>{% endif %}
|
||
</div>
|
||
|
||
{% if job.tva_amount and job.tva_amount > 0 %}
|
||
<div class="breakdown-row">
|
||
<span class="ps-2 flex-fill">TVA</span>
|
||
<span class="text-end" style="min-width:85px">{{ "%.2f"|format(job.tva_amount) }} €</span>
|
||
{% if multi %}<span class="text-end text-muted" style="min-width:85px">{{ "%.2f"|format(job.tva_amount * qty) }} €</span>{% endif %}
|
||
</div>
|
||
{% endif %}
|
||
|
||
{% if job.discount_pct > 0 %}
|
||
<div class="breakdown-row text-danger">
|
||
<span class="ps-2 flex-fill">Remise ({{ job.discount_pct }}%)</span>
|
||
<span class="text-end" style="min-width:85px">−{{ "%.2f"|format(prix_ttc * job.discount_pct / 100) }} €</span>
|
||
{% if multi %}<span class="text-end" style="min-width:85px">−{{ "%.2f"|format(prix_ttc * qty * job.discount_pct / 100) }} €</span>{% endif %}
|
||
</div>
|
||
{% endif %}
|
||
|
||
<!-- Prix final -->
|
||
{% if multi %}
|
||
<div class="d-flex justify-content-between align-items-center mt-2 pt-2" style="border-top:2px solid #dee2e6">
|
||
<span class="fw-bold">PRIX FINAL TTC</span>
|
||
<div class="d-flex gap-3">
|
||
<span class="final-price" style="font-size:1.4rem">{{ "%.2f"|format(job.final_price) }} €</span>
|
||
<span class="final-price" style="font-size:1.4rem;opacity:.7">× {{ qty }} = {{ "%.2f"|format(job.final_price * qty) }} €</span>
|
||
</div>
|
||
</div>
|
||
{% else %}
|
||
<div class="breakdown-row total">
|
||
<span>PRIX FINAL TTC</span>
|
||
<span class="final-price">{{ "%.2f"|format(job.final_price) }} €</span>
|
||
</div>
|
||
{% endif %}
|
||
|
||
<!-- Barre de composition -->
|
||
{% 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) %}
|
||
{% if job.final_price > 0 %}
|
||
<div class="mt-4">
|
||
<div class="d-flex justify-content-between small text-muted mb-1">
|
||
<span>Cout fixe ({{ (cout_f/job.final_price*100)|round(1) }}%)</span>
|
||
<span>Marge ({{ (tot_m/job.final_price*100)|round(1) }}%)</span>
|
||
<span>Fiscal ({{ (fiscal/job.final_price*100)|round(1) }}%)</span>
|
||
</div>
|
||
<div class="progress" style="height:18px;border-radius:6px">
|
||
<div class="progress-bar bg-secondary" style="width:{{ (cout_f/job.final_price*100)|round(1) }}%"></div>
|
||
<div class="progress-bar" style="width:{{ (tot_m/job.final_price*100)|round(1) }}%;background:#ff6b35"></div>
|
||
<div class="progress-bar bg-warning text-dark" style="width:{{ (fiscal/job.final_price*100)|round(1) }}%"></div>
|
||
</div>
|
||
</div>
|
||
{% endif %}
|
||
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
{% endblock %}
|