feat: profils par défaut par client + fix marge 0% + fix: suggest-slot ignore les créneaux déjà passés
Deploy via Portainer / deploy (push) Successful in 0s
Deploy via Portainer / deploy (push) Successful in 0s
This commit is contained in:
@@ -10,10 +10,11 @@
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="col-md-7">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<form method="POST">
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label fw-semibold">Nom *</label>
|
||||
<input type="text" name="name" class="form-control" required
|
||||
@@ -26,9 +27,67 @@
|
||||
</div>
|
||||
<div class="mb-4">
|
||||
<label class="form-label fw-semibold">Notes</label>
|
||||
<textarea name="notes" class="form-control" rows="3">{{ client.notes if client else '' }}</textarea>
|
||||
<textarea name="notes" class="form-control" rows="2">{{ client.notes if client else '' }}</textarea>
|
||||
</div>
|
||||
<div class="d-flex gap-2">
|
||||
|
||||
<hr>
|
||||
<div class="mb-2">
|
||||
<span class="fw-semibold"><i class="bi bi-bookmark-star me-1 text-secondary"></i>Profils par défaut</span>
|
||||
<div class="form-text mb-3">Pré-sélectionnés automatiquement dans "Nouveau calcul" quand ce client est choisi.</div>
|
||||
</div>
|
||||
|
||||
<div class="row g-3 mb-3">
|
||||
<div class="col-md-6">
|
||||
<label class="form-label">Profil machine</label>
|
||||
<select name="default_machine_profile_id" class="form-select">
|
||||
<option value="">— aucun —</option>
|
||||
{% for p in machine_profiles %}
|
||||
<option value="{{ p.id }}"
|
||||
{% if client and client.default_machine_profile_id == p.id %}selected{% endif %}>
|
||||
{{ p.name }}
|
||||
</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label class="form-label">Profil manutention</label>
|
||||
<select name="default_handling_profile_id" class="form-select">
|
||||
<option value="">— aucun —</option>
|
||||
{% for p in handling_profiles %}
|
||||
<option value="{{ p.id }}"
|
||||
{% if client and client.default_handling_profile_id == p.id %}selected{% endif %}>
|
||||
{{ p.name }}
|
||||
</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label class="form-label">Profil matière</label>
|
||||
<select name="default_material_profile_id" class="form-select">
|
||||
<option value="">— aucun —</option>
|
||||
{% for p in material_profiles %}
|
||||
<option value="{{ p.id }}"
|
||||
{% if client and client.default_material_profile_id == p.id %}selected{% endif %}>
|
||||
{{ p.name }}
|
||||
</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label class="form-label">Profil tarif client</label>
|
||||
<select name="default_pricing_profile_id" class="form-select">
|
||||
<option value="">— aucun —</option>
|
||||
{% for p in pricing_profiles %}
|
||||
<option value="{{ p.id }}"
|
||||
{% if client and client.default_pricing_profile_id == p.id %}selected{% endif %}>
|
||||
{{ p.name }}
|
||||
</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="d-flex gap-2 mt-4">
|
||||
<button type="submit" class="btn" style="background:#ff6b35;color:#fff">
|
||||
<i class="bi bi-save me-1"></i>Enregistrer
|
||||
</button>
|
||||
@@ -40,5 +99,3 @@
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block scripts %}{% endblock %}
|
||||
|
||||
+24
-8
@@ -328,14 +328,30 @@ document.getElementById('pricingProfileSelect').addEventListener('change', funct
|
||||
}
|
||||
});
|
||||
|
||||
// ── Client : auto-remplir DM ──────────────────────────────────────────────────
|
||||
// ── Client : auto-remplir profils par défaut ─────────────────────────────────
|
||||
document.getElementById('clientSelect').addEventListener('change', function() {
|
||||
const dm = this.options[this.selectedIndex].dataset.dm;
|
||||
if (dm) {
|
||||
document.getElementById('design_multiplier').value = parseFloat(dm);
|
||||
updateDMLabel(parseFloat(dm));
|
||||
recalc();
|
||||
}
|
||||
const clientId = this.value;
|
||||
if (!clientId) return;
|
||||
fetch('/api/client/' + clientId + '/defaults')
|
||||
.then(r => r.json())
|
||||
.then(d => {
|
||||
if (d.machine_profile_id != null) document.getElementById('machineProfileSelect').value = d.machine_profile_id;
|
||||
if (d.handling_profile_id != null) document.getElementById('handlingProfileSelect').value = d.handling_profile_id;
|
||||
if (d.material_profile_id != null) document.getElementById('materialProfileSelect').value = d.material_profile_id;
|
||||
if (d.pricing_profile_id != null) {
|
||||
const sel = document.getElementById('pricingProfileSelect');
|
||||
sel.value = d.pricing_profile_id;
|
||||
// Appliquer DM + marge du profil tarif sélectionné
|
||||
const opt = sel.options[sel.selectedIndex];
|
||||
if (opt && 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();
|
||||
});
|
||||
});
|
||||
|
||||
// ── Matiere : afficher stock ──────────────────────────────────────────────────
|
||||
@@ -374,7 +390,7 @@ function recalc() {
|
||||
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,
|
||||
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,
|
||||
|
||||
Reference in New Issue
Block a user