first commit
This commit is contained in:
commit
e6c52820cd
227 changed files with 16156 additions and 0 deletions
218
Templates/devis/create_devis.html
Normal file
218
Templates/devis/create_devis.html
Normal file
|
|
@ -0,0 +1,218 @@
|
|||
{% 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 %}
|
||||
61
Templates/devis/devis.html
Normal file
61
Templates/devis/devis.html
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
{% extends 'layouts/base.html' %}
|
||||
|
||||
{% block title %}Suite Consultance - 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">Devis clients</h1>
|
||||
<a href="{{ url_for('create_devis') }}" class="btn btn-devis">
|
||||
<i class="fas fa-plus"></i> Nouveau devis
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Client</th>
|
||||
<th>Date de création</th>
|
||||
<th>Fichier</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% if devis %}
|
||||
{% for d in devis %}
|
||||
<tr>
|
||||
<td>{{ d.client_name }}</td>
|
||||
<td>{{ d.date }}</td>
|
||||
<td>{{ d.filename }}</td>
|
||||
<td>
|
||||
<div class="btn-group" role="group">
|
||||
<a href="{{ url_for('download_file', filename='devis/' + d.filename) }}" class="btn btn-sm btn-primary" title="Télécharger">
|
||||
<i class="fas fa-download"></i>
|
||||
</a>
|
||||
<button class="btn btn-sm btn-info" title="Envoyer par email">
|
||||
<i class="fas fa-envelope"></i>
|
||||
</button>
|
||||
<button class="btn btn-sm btn-success" title="Convertir en facture">
|
||||
<i class="fas fa-file-invoice"></i>
|
||||
</button>
|
||||
<button class="btn btn-sm btn-danger" title="Supprimer">
|
||||
<i class="fas fa-trash-alt"></i>
|
||||
</button>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
<tr>
|
||||
<td colspan="4" class="text-center">Aucun devis trouvé</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
Loading…
Add table
Add a link
Reference in a new issue