218 lines
9.8 KiB
HTML
218 lines
9.8 KiB
HTML
{% extends 'layouts/base.html' %}
|
|
|
|
{% block title %}Suite Consultance - Créer un devis{% endblock %}
|
|
{% block module_name %}devis{% endblock %}
|
|
|
|
{% block content %}
|
|
<div class="d-flex justify-content-between align-items-center mb-4">
|
|
<h1 class="h2 text-devis">Créer un devis</h1>
|
|
<a href="{{ url_for('devis') }}" class="btn btn-outline-devis">
|
|
<i class="fas fa-arrow-left"></i> Retour aux devis
|
|
</a>
|
|
</div>
|
|
|
|
<div class="card">
|
|
<div class="card-body">
|
|
<form method="POST" action="{{ url_for('create_devis') }}">
|
|
<div class="row g-3">
|
|
<!-- Sélection du client -->
|
|
<div class="col-12">
|
|
<h5 class="border-bottom pb-2 mb-3">Sélection du client</h5>
|
|
</div>
|
|
|
|
<div class="col-md-6">
|
|
<div class="mb-3">
|
|
<label for="client_select" class="form-label">Sélectionner un client *</label>
|
|
<select class="form-select" id="client_select" name="client_name" required>
|
|
<option value="" selected disabled>-- Choisir un client --</option>
|
|
{% for client in clients %}
|
|
<option value="{{ client.id }}">{{ client.name }}</option>
|
|
{% endfor %}
|
|
</select>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="col-md-6">
|
|
<div class="mb-3">
|
|
<label for="numero" class="form-label">Numéro du devis *</label>
|
|
<input type="text" class="form-control" id="numero" name="numero" required>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Détails du devis -->
|
|
<div class="col-12">
|
|
<h5 class="border-bottom pb-2 mb-3 mt-3">Détails du devis</h5>
|
|
</div>
|
|
|
|
<div class="col-md-6">
|
|
<div class="mb-3">
|
|
<label for="date_emission" class="form-label">Date d'émission</label>
|
|
<input type="date" class="form-control" id="date_emission" name="date_emission" value="{{ today }}">
|
|
</div>
|
|
</div>
|
|
|
|
<div class="col-md-6">
|
|
<div class="mb-3">
|
|
<label for="date_validite" class="form-label">Date de validité</label>
|
|
<input type="date" class="form-control" id="date_validite" name="date_validite">
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Prestations -->
|
|
<div id="prestations-container">
|
|
<div class="col-12">
|
|
<h5 class="border-bottom pb-2 mb-3 mt-3">Prestations</h5>
|
|
<button type="button" id="add-prestation" class="btn btn-outline-primary mb-3">
|
|
<i class="fas fa-plus"></i> Ajouter une prestation
|
|
</button>
|
|
</div>
|
|
|
|
<div class="prestation-row mb-3">
|
|
<div class="row">
|
|
<div class="col-md-6">
|
|
<div class="mb-3">
|
|
<label class="form-label">Description</label>
|
|
<input type="text" class="form-control prestation-description" name="prestation_description[]">
|
|
</div>
|
|
</div>
|
|
<div class="col-md-2">
|
|
<div class="mb-3">
|
|
<label class="form-label">Quantité</label>
|
|
<input type="number" class="form-control prestation-quantity" name="prestation_quantity[]" value="1" min="1">
|
|
</div>
|
|
</div>
|
|
<div class="col-md-2">
|
|
<div class="mb-3">
|
|
<label class="form-label">Prix unitaire (€)</label>
|
|
<input type="number" class="form-control prestation-price" name="prestation_price[]" step="0.01" min="0">
|
|
</div>
|
|
</div>
|
|
<div class="col-md-2">
|
|
<div class="mb-3">
|
|
<label class="form-label">Total</label>
|
|
<input type="text" class="form-control prestation-total" readonly>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Totaux -->
|
|
<div class="col-12">
|
|
<div class="row justify-content-end mt-3">
|
|
<div class="col-md-4">
|
|
<div class="card">
|
|
<div class="card-body">
|
|
<div class="d-flex justify-content-between mb-2">
|
|
<span>Total HT:</span>
|
|
<span id="total-ht">0.00 €</span>
|
|
</div>
|
|
<div class="d-flex justify-content-between mb-2">
|
|
<span>TVA (20%):</span>
|
|
<span id="total-tva">0.00 €</span>
|
|
</div>
|
|
<div class="d-flex justify-content-between fw-bold">
|
|
<span>Total TTC:</span>
|
|
<span id="total-ttc">0.00 €</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="col-12 mt-3">
|
|
<button type="submit" class="btn btn-primary">
|
|
<i class="fas fa-file-download"></i> Générer le devis
|
|
</button>
|
|
<a href="{{ url_for('devis') }}" class="btn btn-outline-secondary">
|
|
Annuler
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
{% endblock %}
|
|
|
|
{% block extra_js %}
|
|
<script>
|
|
// Gérer l'ajout de prestations
|
|
document.getElementById('add-prestation').addEventListener('click', function() {
|
|
const container = document.getElementById('prestations-container');
|
|
const newRow = document.querySelector('.prestation-row').cloneNode(true);
|
|
|
|
// Réinitialiser les valeurs
|
|
newRow.querySelectorAll('input').forEach(input => {
|
|
if (input.className.includes('prestation-quantity')) {
|
|
input.value = 1;
|
|
} else if (!input.className.includes('prestation-total')) {
|
|
input.value = '';
|
|
}
|
|
});
|
|
|
|
// Ajouter un bouton de suppression
|
|
const deleteBtn = document.createElement('button');
|
|
deleteBtn.type = 'button';
|
|
deleteBtn.className = 'btn btn-outline-danger btn-sm mt-1';
|
|
deleteBtn.innerHTML = '<i class="fas fa-trash"></i>';
|
|
deleteBtn.onclick = function() {
|
|
this.closest('.prestation-row').remove();
|
|
updateTotals();
|
|
};
|
|
|
|
newRow.querySelector('.row').appendChild(document.createElement('div')).appendChild(deleteBtn);
|
|
container.appendChild(newRow);
|
|
|
|
// Mettre à jour les événements pour les calculs
|
|
addCalculationEvents();
|
|
});
|
|
|
|
// Calcul des totaux
|
|
function addCalculationEvents() {
|
|
document.querySelectorAll('.prestation-quantity, .prestation-price').forEach(input => {
|
|
input.addEventListener('input', function() {
|
|
const row = this.closest('.prestation-row');
|
|
const quantity = parseFloat(row.querySelector('.prestation-quantity').value) || 0;
|
|
const price = parseFloat(row.querySelector('.prestation-price').value) || 0;
|
|
const total = quantity * price;
|
|
|
|
row.querySelector('.prestation-total').value = total.toFixed(2) + ' €';
|
|
updateTotals();
|
|
});
|
|
});
|
|
}
|
|
|
|
// Calculer les totaux généraux
|
|
function updateTotals() {
|
|
let totalHT = 0;
|
|
|
|
document.querySelectorAll('.prestation-row').forEach(row => {
|
|
const quantity = parseFloat(row.querySelector('.prestation-quantity').value) || 0;
|
|
const price = parseFloat(row.querySelector('.prestation-price').value) || 0;
|
|
totalHT += quantity * price;
|
|
});
|
|
|
|
const totalTVA = totalHT * 0.2;
|
|
const totalTTC = totalHT + totalTVA;
|
|
|
|
document.getElementById('total-ht').textContent = totalHT.toFixed(2) + ' €';
|
|
document.getElementById('total-tva').textContent = totalTVA.toFixed(2) + ' €';
|
|
document.getElementById('total-ttc').textContent = totalTTC.toFixed(2) + ' €';
|
|
}
|
|
|
|
// Initialiser les événements au chargement
|
|
addCalculationEvents();
|
|
|
|
// Définir la date du jour par défaut
|
|
if (document.getElementById('date_emission')) {
|
|
const today = new Date().toISOString().split('T')[0];
|
|
document.getElementById('date_emission').value = today;
|
|
|
|
// Date de validité par défaut (aujourd'hui + 30 jours)
|
|
const validUntil = new Date();
|
|
validUntil.setDate(validUntil.getDate() + 30);
|
|
document.getElementById('date_validite').value = validUntil.toISOString().split('T')[0];
|
|
}
|
|
</script>
|
|
{% endblock %}
|