311 lines
14 KiB
HTML
311 lines
14 KiB
HTML
{% extends 'base.html' %}
|
||
{% block title %}Parametres{% endblock %}
|
||
|
||
{% block content %}
|
||
<div class="page-header">
|
||
<h1><i class="bi bi-sliders me-2 text-secondary"></i>Parametres globaux</h1>
|
||
<p class="text-muted small mt-1">Valeurs par defaut — remplacees si un profil est selectionne lors du calcul.</p>
|
||
</div>
|
||
|
||
<form method="POST">
|
||
<div class="row g-4">
|
||
|
||
<div class="col-md-6">
|
||
<div class="card">
|
||
<div class="card-header py-3"><i class="bi bi-boxes me-2"></i>Filament</div>
|
||
<div class="card-body">
|
||
<div class="mb-3">
|
||
<label class="form-label">Prix du filament (€/kg)</label>
|
||
<input type="number" name="filament_price_per_kg" class="form-control"
|
||
step="0.01" value="{{ settings.filament_price_per_kg }}">
|
||
</div>
|
||
<div class="mb-3">
|
||
<label class="form-label">Marge matiere (%)</label>
|
||
<input type="number" name="material_margin_pct" class="form-control"
|
||
step="0.5" value="{{ settings.material_margin_pct }}">
|
||
<div class="form-text">Appliquee sur le cout matiere brut.</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="col-md-6">
|
||
<div class="card">
|
||
<div class="card-header py-3"><i class="bi bi-lightning-charge me-2"></i>Electricite</div>
|
||
<div class="card-body">
|
||
<div class="mb-3">
|
||
<label class="form-label">Prix kWh (€)</label>
|
||
<input type="number" name="electricity_price_kwh" class="form-control"
|
||
step="0.001" value="{{ settings.electricity_price_kwh }}">
|
||
</div>
|
||
<div class="mb-3">
|
||
<label class="form-label">Puissance imprimante (kW)</label>
|
||
<input type="number" name="printer_power_kw" class="form-control"
|
||
step="0.01" value="{{ settings.printer_power_kw }}">
|
||
<div class="form-text">Bambu X1C ≈ 0.35 kW en moyenne.</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="col-md-6">
|
||
<div class="card">
|
||
<div class="card-header py-3"><i class="bi bi-gear me-2"></i>Usure machine</div>
|
||
<div class="card-body">
|
||
<div class="row g-2 mb-2">
|
||
<div class="col-6">
|
||
<label class="form-label">Prix imprimante (€)</label>
|
||
<input type="number" name="printer_price" class="form-control"
|
||
step="1" value="{{ settings.printer_price }}">
|
||
</div>
|
||
<div class="col-6">
|
||
<label class="form-label">Duree de vie (h)</label>
|
||
<input type="number" name="printer_lifespan_hours" class="form-control"
|
||
step="100" value="{{ settings.printer_lifespan_hours }}">
|
||
</div>
|
||
</div>
|
||
<div class="row g-2 mb-2">
|
||
<div class="col-6">
|
||
<label class="form-label">Prix buse (€)</label>
|
||
<input type="number" name="nozzle_price" class="form-control"
|
||
step="0.5" value="{{ settings.nozzle_price }}">
|
||
</div>
|
||
<div class="col-6">
|
||
<label class="form-label">Duree de vie buse (h)</label>
|
||
<input type="number" name="nozzle_lifespan_hours" class="form-control"
|
||
step="10" value="{{ settings.nozzle_lifespan_hours }}">
|
||
</div>
|
||
</div>
|
||
<div class="row g-2">
|
||
<div class="col-6">
|
||
<label class="form-label">Prix plateau (€)</label>
|
||
<input type="number" name="plate_price" class="form-control"
|
||
step="0.5" value="{{ settings.plate_price }}">
|
||
</div>
|
||
<div class="col-6">
|
||
<label class="form-label">Duree de vie plateau (h)</label>
|
||
<input type="number" name="plate_lifespan_hours" class="form-control"
|
||
step="50" value="{{ settings.plate_lifespan_hours }}">
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="col-md-6">
|
||
<div class="card mb-4">
|
||
<div class="card-header py-3"><i class="bi bi-hand-index me-2"></i>Manutention</div>
|
||
<div class="card-body">
|
||
<div class="mb-3">
|
||
<label class="form-label">Taux horaire manutention (€/h)</label>
|
||
<input type="number" name="handling_rate_per_hour" class="form-control"
|
||
step="1" value="{{ settings.handling_rate_per_hour }}">
|
||
</div>
|
||
<div class="mb-3">
|
||
<label class="form-label">Temps par plateau (minutes)</label>
|
||
<input type="number" name="handling_minutes_per_plate" class="form-control"
|
||
step="1" value="{{ settings.handling_minutes_per_plate }}">
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="card mb-4">
|
||
<div class="card-header py-3"><i class="bi bi-calendar3 me-2"></i>Planning & capacite</div>
|
||
<div class="card-body">
|
||
<div class="mb-3">
|
||
<label class="form-label fw-semibold">Cooldown entre impressions (minutes)</label>
|
||
<input type="number" name="cooldown_minutes" class="form-control"
|
||
step="1" min="0" value="{{ settings.get('cooldown_minutes', 15) }}">
|
||
<div class="form-text">Temps de refroidissement + dechargement entre deux lancements sur la meme machine.</div>
|
||
</div>
|
||
<div class="mb-3">
|
||
<label class="form-label fw-semibold">Buffer echec filament (%)</label>
|
||
<input type="number" name="failure_buffer_pct" class="form-control"
|
||
step="1" min="0" max="50" value="{{ settings.get('failure_buffer_pct', 10) }}">
|
||
<div class="form-text">Surconsommation estimee due aux echecs d'impression (alerte restock).</div>
|
||
</div>
|
||
<div class="mb-3">
|
||
<label class="form-label fw-semibold">Nb max imprimantes simultanees</label>
|
||
<input type="number" name="max_simultaneous_printers" class="form-control"
|
||
step="1" min="1" max="10" value="{{ settings.get('max_simultaneous_printers', 5) }}">
|
||
<div class="form-text"><strong>5</strong> — contrainte electrique (6 × A1 = trop de W au-dela de 5).</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="card">
|
||
<div class="card-header py-3"><i class="bi bi-percent me-2"></i>Fiscal</div>
|
||
<div class="card-body">
|
||
<div class="mb-3">
|
||
<label class="form-label fw-semibold">Cotisations sociales (%)</label>
|
||
<input type="number" name="cotisations_rate_pct" class="form-control"
|
||
step="0.1" value="{{ settings.cotisations_rate_pct }}">
|
||
<div class="form-text">
|
||
<strong>12.3%</strong> — micro-entrepreneur BIC vente (fabrication/vente de marchandises, 2024).
|
||
Calculees sur le CA HT.
|
||
</div>
|
||
</div>
|
||
<div class="mb-3">
|
||
<label class="form-label fw-semibold">Versement Forfaitaire Liberatoire — impot (%)</label>
|
||
<input type="number" name="vfl_rate_pct" class="form-control"
|
||
step="0.1" value="{{ settings.vfl_rate_pct }}">
|
||
<div class="form-text">
|
||
<strong>1%</strong> — BIC vente (si option VFL activee).
|
||
Mettre <strong>0</strong> si vous n'avez pas opte pour le VFL (impot sur declaration classique).
|
||
</div>
|
||
</div>
|
||
<div class="mb-3">
|
||
<label class="form-label fw-semibold">Autres taxes a prevoir (%)</label>
|
||
<input type="number" name="other_taxes_rate_pct" class="form-control"
|
||
step="0.1" value="{{ settings.get('other_taxes_rate_pct', 1.0) }}">
|
||
<div class="form-text">
|
||
<strong>~1%</strong> — provision pour taxes diverses non incluses ci-dessus.
|
||
Exemples : <strong>CFE</strong> (Cotisation Fonciere des Entreprises, due des la 2e annee
|
||
d'activite, montant variable selon la commune),
|
||
frais bancaires, comptable, etc.
|
||
Mettre <strong>0</strong> si non concerne.
|
||
</div>
|
||
</div>
|
||
<hr>
|
||
<div class="mb-2">
|
||
<label class="form-label fw-semibold">TVA (%)</label>
|
||
<input type="number" name="tva_rate_pct" class="form-control"
|
||
step="0.1" value="{{ settings.tva_rate_pct }}">
|
||
<div class="form-text">
|
||
<strong>20%</strong> — taux normal applicable a la vente de biens imprimes 3D.
|
||
La TVA s'ajoute au prix HT et est collectee pour l'Etat (non conservee).
|
||
</div>
|
||
</div>
|
||
{% set other = settings.get('other_taxes_rate_pct', 1.0) %}
|
||
{% set total_charges = settings.cotisations_rate_pct + settings.vfl_rate_pct + other %}
|
||
<div class="alert alert-info py-2 mt-3 mb-0 small">
|
||
<i class="bi bi-info-circle me-1"></i>
|
||
Charges sur CA HT : <strong>{{ "%.1f"|format(total_charges) }} %</strong>
|
||
(cotisations {{ settings.cotisations_rate_pct }}%
|
||
+ VFL {{ settings.vfl_rate_pct }}%
|
||
+ autres {{ other }}%)
|
||
{% if settings.tva_rate_pct > 0 %}
|
||
— TVA {{ settings.tva_rate_pct }}% collectee et reversee a l'Etat
|
||
{% else %}
|
||
— franchise TVA active
|
||
{% endif %}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<div class="col-12">
|
||
<div class="card">
|
||
<div class="card-header py-3"><i class="bi bi-cloud me-2"></i>Nextcloud — Intégration fichiers 3D</div>
|
||
<div class="card-body">
|
||
<div class="row g-3">
|
||
<div class="col-md-6">
|
||
<label class="form-label fw-semibold">URL Nextcloud</label>
|
||
<input type="url" name="nc_url" class="form-control"
|
||
placeholder="https://nextcloud.tondomain.com"
|
||
value="{{ settings.get('nc_url','') }}">
|
||
<div class="form-text">Sans slash final.</div>
|
||
</div>
|
||
<div class="col-md-3">
|
||
<label class="form-label fw-semibold">Identifiant</label>
|
||
<input type="text" name="nc_username" class="form-control"
|
||
placeholder="ton_login"
|
||
value="{{ settings.get('nc_username','') }}">
|
||
</div>
|
||
<div class="col-md-3">
|
||
<label class="form-label fw-semibold">Mot de passe d'application</label>
|
||
<input type="password" name="nc_app_password" class="form-control"
|
||
placeholder="••••••••••••"
|
||
value="{{ settings.get('nc_app_password','') }}">
|
||
<div class="form-text">Générer dans Nextcloud → Paramètres → Sécurité.</div>
|
||
</div>
|
||
<div class="col-md-4">
|
||
<label class="form-label fw-semibold">Dossier racine 3D</label>
|
||
<input type="text" name="nc_root_path" class="form-control"
|
||
placeholder="/Impressions3D"
|
||
value="{{ settings.get('nc_root_path','/3D') }}">
|
||
<div class="form-text">Chemin de base pour l'explorateur de fichiers.</div>
|
||
</div>
|
||
<div class="col-md-8 d-flex align-items-end">
|
||
<button type="button" class="btn btn-sm btn-outline-secondary" id="testNcBtn">
|
||
<i class="bi bi-plug me-1"></i>Tester la connexion
|
||
</button>
|
||
<span id="testNcResult" class="ms-3 small"></span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- iCal -->
|
||
<div class="col-12">
|
||
<div class="card">
|
||
<div class="card-header py-3"><i class="bi bi-calendar-check me-2"></i>Calendrier iCal — Abonnement Android / iOS</div>
|
||
<div class="card-body">
|
||
<div class="row g-3 align-items-end">
|
||
<div class="col-md-4">
|
||
<label class="form-label fw-semibold">Token secret (optionnel)</label>
|
||
<input type="text" name="ical_token" class="form-control font-monospace"
|
||
placeholder="Laisser vide = pas de protection"
|
||
value="{{ settings.get('ical_token','') }}">
|
||
<div class="form-text">Si renseigné, l'URL contiendra <code>?token=xxx</code>.</div>
|
||
</div>
|
||
<div class="col-md-8">
|
||
<label class="form-label fw-semibold">URL d'abonnement</label>
|
||
<div class="input-group">
|
||
<input type="text" id="icalUrl" class="form-control font-monospace small" readonly
|
||
value="{{ request.host_url }}calendar.ics{% if settings.get('ical_token','') %}?token={{ settings.get('ical_token','') }}{% endif %}">
|
||
<button class="btn btn-outline-secondary" type="button" id="copyIcal" title="Copier">
|
||
<i class="bi bi-clipboard"></i>
|
||
</button>
|
||
</div>
|
||
<div class="form-text">
|
||
Dans Google Calendar → Autres agendas → Depuis une URL.
|
||
Samsung / iOS : Paramètres → Comptes → Ajouter → Calendrier.
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<div class="mt-4">
|
||
<button type="submit" class="btn px-4 py-2 fw-bold" style="background:#ff6b35;color:#fff">
|
||
<i class="bi bi-save me-2"></i>Enregistrer les parametres
|
||
</button>
|
||
</div>
|
||
</form>
|
||
{% endblock %}
|
||
|
||
{% block scripts %}
|
||
<script>
|
||
document.getElementById('copyIcal').addEventListener('click', function () {
|
||
const url = document.getElementById('icalUrl').value;
|
||
navigator.clipboard.writeText(url).then(() => {
|
||
this.innerHTML = '<i class="bi bi-check text-success"></i>';
|
||
setTimeout(() => { this.innerHTML = '<i class="bi bi-clipboard"></i>'; }, 2000);
|
||
});
|
||
});
|
||
|
||
document.getElementById('testNcBtn').addEventListener('click', async function () {
|
||
const res = document.getElementById('testNcResult');
|
||
res.textContent = 'Test en cours…';
|
||
try {
|
||
const r = await fetch('/api/nextcloud/browse');
|
||
const d = await r.json();
|
||
if (d.error) {
|
||
res.innerHTML = `<span class="text-danger"><i class="bi bi-x-circle me-1"></i>${d.error}</span>`;
|
||
} else {
|
||
res.innerHTML = `<span class="text-success"><i class="bi bi-check-circle me-1"></i>Connexion OK — ${d.items.length} élément(s) dans ${d.path}</span>`;
|
||
}
|
||
} catch (e) {
|
||
res.innerHTML = `<span class="text-danger"><i class="bi bi-x-circle me-1"></i>${e.message}</span>`;
|
||
}
|
||
});
|
||
</script>
|
||
{% endblock %}
|