first commit

This commit is contained in:
mrtoine 2025-09-20 13:18:04 +02:00
commit e6c52820cd
227 changed files with 16156 additions and 0 deletions

View 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 %}

View 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 %}