Files
2026-07-07 12:46:37 +02:00

376 lines
18 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{% 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>
<!-- Nextcloud -->
<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>
<!-- Home Assistant -->
<div class="col-12">
<div class="card">
<div class="card-header py-3"><i class="bi bi-house-heart me-2"></i>Home Assistant — Sync imprimantes</div>
<div class="card-body">
<div class="row g-3">
<div class="col-md-6">
<label class="form-label fw-semibold">URL Home Assistant</label>
<input type="url" name="ha_url" class="form-control"
placeholder="https://homeassistant.tondomain.com"
value="{{ settings.get('ha_url','') }}">
<div class="form-text">Sans slash final. Ex : <code>https://homeassistant.lespatas.ovh</code></div>
</div>
<div class="col-md-6">
<label class="form-label fw-semibold">Token longue durée</label>
<input type="password" name="ha_token" class="form-control font-monospace"
placeholder="••••••••••••••••••••"
value="{{ settings.get('ha_token','') }}">
<div class="form-text">Profil HA → Sécurité → Jetons d'accès longue durée.</div>
</div>
<div class="col-md-4">
<label class="form-label fw-semibold">Intervalle de synchro</label>
<div class="input-group">
<input type="number" name="ha_poll_interval" class="form-control"
min="10" max="3600" step="5"
value="{{ settings.get('ha_poll_interval','60') }}">
<span class="input-group-text">secondes</span>
</div>
<div class="form-text">Min 10 s. Recommandé : 60 s pour ne pas surcharger HA.</div>
</div>
<div class="col-md-8 d-flex align-items-end">
<div>
<button type="button" class="btn btn-sm btn-outline-secondary" id="testHaBtn">
<i class="bi bi-plug me-1"></i>Tester la connexion HA
</button>
<span id="testHaResult" class="ms-3 small"></span>
<div class="form-text mt-1">
<i class="bi bi-info-circle me-1"></i>
Configurez ensuite le <strong>Préfixe entité HA</strong> pour chaque imprimante dans la page <a href="{{ url_for('printers_page') }}">Imprimantes</a>.
</div>
</div>
</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>
<!-- Bouton sauvegarde -->
<div class="col-12 pb-2">
<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 paramètres
</button>
</div>
</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>`;
}
});
document.getElementById('testHaBtn').addEventListener('click', async function () {
const res = document.getElementById('testHaResult');
res.textContent = 'Test en cours…';
try {
const r = await fetch('/api/ha/status');
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 {
const count = Object.keys(d.printers).length;
res.innerHTML = `<span class="text-success"><i class="bi bi-check-circle me-1"></i>Connexion OK — ${count} imprimante(s) avec préfixe HA</span>`;
}
} catch (e) {
res.innerHTML = `<span class="text-danger"><i class="bi bi-x-circle me-1"></i>${e.message}</span>`;
}
});
</script>
{% endblock %}