796 lines
44 KiB
HTML
796 lines
44 KiB
HTML
{% extends "base.html" %}
|
|
{% block title %}{{ equipment.name }} — Modifier — GMAO{% endblock %}
|
|
|
|
{% block content %}
|
|
<div class="container-fluid">
|
|
<div class="d-flex justify-content-between align-items-center mb-4">
|
|
<div>
|
|
<nav aria-label="breadcrumb">
|
|
<ol class="breadcrumb mb-1">
|
|
<li class="breadcrumb-item"><a href="{{ url_for('equipments.index') }}">Équipements</a></li>
|
|
<li class="breadcrumb-item"><a href="{{ url_for('equipments.detail', id=equipment.id) }}">{{ equipment.name }}</a></li>
|
|
<li class="breadcrumb-item active">Modifier</li>
|
|
</ol>
|
|
</nav>
|
|
<h1 class="h3 mb-0">Modifier l'équipement</h1>
|
|
</div>
|
|
<a href="{{ url_for('equipments.detail', id=equipment.id) }}" class="btn btn-outline-secondary">
|
|
<i class="bi bi-arrow-left me-1"></i>Annuler
|
|
</a>
|
|
</div>
|
|
|
|
<ul class="nav nav-tabs mb-3" id="equipmentTabs" role="tablist">
|
|
<li class="nav-item" role="presentation">
|
|
<button class="nav-link active" id="info-tab" data-bs-toggle="tab" data-bs-target="#info" type="button">
|
|
<i class="bi bi-info-circle me-1"></i>Informations
|
|
</button>
|
|
</li>
|
|
<li class="nav-item" role="presentation">
|
|
<button class="nav-link" id="meters-tab" data-bs-toggle="tab" data-bs-target="#meters" type="button">
|
|
<i class="bi bi-speedometer2 me-1"></i>Compteurs
|
|
</button>
|
|
</li>
|
|
<li class="nav-item" role="presentation">
|
|
<button class="nav-link" id="consumables-tab" data-bs-toggle="tab" data-bs-target="#consumables" type="button">
|
|
<i class="bi bi-box-seam me-1"></i>Consommables
|
|
</button>
|
|
</li>
|
|
<li class="nav-item" role="presentation">
|
|
<button class="nav-link" id="documents-tab" data-bs-toggle="tab" data-bs-target="#documents" type="button">
|
|
<i class="bi bi-file-earmark me-1"></i>Documents
|
|
</button>
|
|
</li>
|
|
<li class="nav-item" role="presentation">
|
|
<button class="nav-link" id="history-tab" data-bs-toggle="tab" data-bs-target="#history" type="button">
|
|
<i class="bi bi-clock-history me-1"></i>Historique
|
|
</button>
|
|
</li>
|
|
</ul>
|
|
|
|
<div class="tab-content">
|
|
<!-- Onglet Informations -->
|
|
<div class="tab-pane fade show active" id="info" role="tabpanel">
|
|
<form method="POST" action="{{ url_for('equipments.edit', id=equipment.id) }}">
|
|
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
|
|
|
|
<div class="row">
|
|
<div class="col-lg-8">
|
|
<div class="card mb-3">
|
|
<div class="card-header">
|
|
<i class="bi bi-pencil me-1"></i>Informations générales
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="row mb-3">
|
|
<div class="col-md-6">
|
|
<label for="name" class="form-label">Nom *</label>
|
|
<input type="text" class="form-control" id="name" name="name"
|
|
value="{{ equipment.name }}" required>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<label for="code" class="form-label">Code / N° série</label>
|
|
<input type="text" class="form-control" id="code" name="code"
|
|
value="{{ equipment.code or '' }}">
|
|
</div>
|
|
</div>
|
|
|
|
<div class="row mb-3">
|
|
<div class="col-md-6">
|
|
<label for="category_id" class="form-label">Catégorie</label>
|
|
<select class="form-select" id="category_id" name="category_id">
|
|
<option value="">-- Sans catégorie --</option>
|
|
{% for cat in categories %}
|
|
<option value="{{ cat.id }}" {{ 'selected' if equipment.category_id == cat.id else '' }}>
|
|
{{ cat.name }}
|
|
</option>
|
|
{% endfor %}
|
|
</select>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<label for="lot_id" class="form-label">Lot</label>
|
|
<select class="form-select" id="lot_id" name="lot_id">
|
|
<option value="">-- Sans lot --</option>
|
|
{% for lot in lots %}
|
|
<option value="{{ lot.id }}" data-category-id="{{ lot.category_id or '' }}" {{ 'selected' if equipment.lot_id == lot.id else '' }}>
|
|
{{ lot.name }}
|
|
</option>
|
|
{% endfor %}
|
|
</select>
|
|
<button type="button" class="btn btn-sm btn-outline-primary mt-2" id="quick-add-lot"><i class="bi bi-plus-lg"></i> Créer un lot dans cette catégorie</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="row mb-3">
|
|
<div class="col-md-6">
|
|
<label for="room_id" class="form-label">Salle</label>
|
|
<select class="form-select" id="room_id" name="room_id">
|
|
<option value="">-- Non localisé --</option>
|
|
{% for building in buildings %}
|
|
<optgroup label="{{ building.name }}">
|
|
{% for room in building.rooms %}
|
|
<option value="{{ room.id }}" {{ 'selected' if equipment.room_id == room.id else '' }}>
|
|
{{ room.name }}
|
|
</option>
|
|
{% endfor %}
|
|
</optgroup>
|
|
{% endfor %}
|
|
</select>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<label for="status" class="form-label">Statut</label>
|
|
<select class="form-select" id="status" name="status">
|
|
<option value="en_service" {{ 'selected' if equipment.status == 'en_service' else '' }}>En service</option>
|
|
<option value="en_panne" {{ 'selected' if equipment.status == 'en_panne' else '' }}>En panne</option>
|
|
<option value="en_maintenance" {{ 'selected' if equipment.status == 'en_maintenance' else '' }}>En maintenance</option>
|
|
<option value="hs" {{ 'selected' if equipment.status == 'hs' else '' }}>Hors service</option>
|
|
<option value="a_jeter" {{ 'selected' if equipment.status == 'a_jeter' else '' }}>À jeter</option>
|
|
<option value="jete" {{ 'selected' if equipment.status == 'jete' else '' }}>Jeté</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="row mb-3">
|
|
<div class="col-md-4">
|
|
<label for="quantity" class="form-label">Quantité</label>
|
|
<input type="number" class="form-control" id="quantity" name="quantity"
|
|
value="{{ equipment.quantity }}" min="1">
|
|
</div>
|
|
<div class="col-md-4">
|
|
<div class="form-check mt-4">
|
|
<input type="checkbox" class="form-check-input" id="is_group" name="is_group"
|
|
{{ 'checked' if equipment.is_group else '' }}>
|
|
<label for="is_group" class="form-check-label">Est un groupe</label>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-4">
|
|
<div class="form-check mt-4">
|
|
<input type="checkbox" class="form-check-input" id="tracked_individually" name="tracked_individually"
|
|
{{ 'checked' if equipment.tracked_individually else '' }}>
|
|
<label for="tracked_individually" class="form-check-label">Suivi individuel</label>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="mb-3">
|
|
<label for="description" class="form-label">Description</label>
|
|
<textarea class="form-control" id="description" name="description" rows="3">{{ equipment.description or '' }}</textarea>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="card mb-3">
|
|
<div class="card-header">
|
|
<i class="bi bi-calendar me-1"></i>Dates
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="row">
|
|
<div class="col-md-6">
|
|
<label for="install_date" class="form-label">Date d'installation</label>
|
|
<input type="date" class="form-control" id="install_date" name="install_date"
|
|
value="{{ equipment.install_date.strftime('%Y-%m-%d') if equipment.install_date else '' }}">
|
|
</div>
|
|
<div class="col-md-6">
|
|
<label for="warranty_end" class="form-label">Fin de garantie</label>
|
|
<input type="date" class="form-control" id="warranty_end" name="warranty_end"
|
|
value="{{ equipment.warranty_end.strftime('%Y-%m-%d') if equipment.warranty_end else '' }}">
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="card mb-3"><div class="card-header"><i class="bi bi-wrench-adjustable"></i> Données techniques</div><div class="card-body row g-3">
|
|
<div class="col-md-4"><label class="form-label" for="mobility">Mobilité</label><select class="form-select" id="mobility" name="mobility">{% for value, label in [('non_precise','À préciser'),('mobile','Mobile'),('fixe','Fixe')] %}<option value="{{ value }}" {% if equipment.mobility == value %}selected{% endif %}>{{ label }}</option>{% endfor %}</select></div>
|
|
<div class="col-md-4"><label class="form-label" for="serial_number">N° de série</label><input class="form-control" id="serial_number" name="serial_number" value="{{ equipment.serial_number or '' }}"></div>
|
|
<div class="col-md-4"><label class="form-label" for="manufacturer">Fabricant</label><input class="form-control" id="manufacturer" name="manufacturer" value="{{ equipment.manufacturer or '' }}"></div>
|
|
<div class="col-md-4"><label class="form-label" for="model_reference">Modèle</label><input class="form-control" id="model_reference" name="model_reference" value="{{ equipment.model_reference or '' }}"></div>
|
|
<div class="col-md-4"><label class="form-label" for="supplier_id">Fournisseur</label><select class="form-select" id="supplier_id" name="supplier_id"><option value="">—</option>{% for company in companies %}<option value="{{ company.id }}" {% if equipment.supplier_id == company.id %}selected{% endif %}>{{ company.name }}</option>{% endfor %}</select></div>
|
|
<div class="col-md-4"><label class="form-label" for="supplier_reference">Référence fournisseur</label><input class="form-control" id="supplier_reference" name="supplier_reference" value="{{ equipment.supplier_reference or '' }}"></div>
|
|
<div class="col-md-4"><label class="form-label" for="purchase_price">Prix d'achat</label><input class="form-control" id="purchase_price" name="purchase_price" type="number" min="0" step="0.01" value="{{ equipment.purchase_price or '' }}"></div>
|
|
<div class="col-md-4"><label class="form-label" for="purchase_date">Date d'achat</label><input class="form-control" id="purchase_date" name="purchase_date" type="date" value="{{ equipment.purchase_date.strftime('%Y-%m-%d') if equipment.purchase_date else '' }}"></div>
|
|
<div class="col-md-4 d-flex align-items-end"><div class="form-check mb-2"><input class="form-check-input" id="recurrence_monitoring" name="recurrence_monitoring" type="checkbox" {% if equipment.recurrence_monitoring %}checked{% endif %}><label class="form-check-label" for="recurrence_monitoring">Surveiller les récurrences</label></div></div>
|
|
</div></div>
|
|
|
|
<div class="card mb-3">
|
|
<div class="card-header">
|
|
<i class="bi bi-geo me-1"></i>Position
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="row">
|
|
<div class="col-md-4">
|
|
<label for="position" class="form-label">Position (texte)</label>
|
|
<input type="text" class="form-control" id="position" name="position"
|
|
value="{{ equipment.position or '' }}" placeholder="Ex: Angle nord-ouest">
|
|
</div>
|
|
<div class="col-md-4">
|
|
<label for="position_x" class="form-label">Position X</label>
|
|
<input type="number" step="0.01" class="form-control" id="position_x" name="position_x"
|
|
value="{{ equipment.position_x or '' }}">
|
|
</div>
|
|
<div class="col-md-4">
|
|
<label for="position_y" class="form-label">Position Y</label>
|
|
<input type="number" step="0.01" class="form-control" id="position_y" name="position_y"
|
|
value="{{ equipment.position_y or '' }}">
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="col-lg-4">
|
|
<div class="card mb-3">
|
|
<div class="card-header">
|
|
<i class="bi bi-diagram-3 me-1"></i>Hiérarchie
|
|
</div>
|
|
<div class="card-body">
|
|
{% if equipment.parent %}
|
|
<div class="mb-3">
|
|
<label class="form-label">Parent</label>
|
|
<p class="form-control-plaintext">
|
|
<a href="{{ url_for('equipments.detail', id=equipment.parent.id) }}">
|
|
{{ equipment.parent.name }}
|
|
</a>
|
|
</p>
|
|
</div>
|
|
{% endif %}
|
|
|
|
<div class="mb-3">
|
|
<label for="parent_id" class="form-label">Changer de parent</label>
|
|
<select class="form-select" id="parent_id" name="parent_id">
|
|
<option value="">-- Aucun parent --</option>
|
|
{% for eq in parent_equipments %}
|
|
{% if eq.id != equipment.id %}
|
|
<option value="{{ eq.id }}" {{ 'selected' if equipment.parent_id == eq.id else '' }}>
|
|
{{ eq.name }}
|
|
</option>
|
|
{% endif %}
|
|
{% endfor %}
|
|
</select>
|
|
</div>
|
|
|
|
{% if equipment.is_group and equipment.children_count > 0 %}
|
|
<div class="alert alert-info">
|
|
<i class="bi bi-info-circle me-1"></i>
|
|
Cet équipement a {{ equipment.children_count }} enfant(s).
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
|
|
</div>
|
|
</div>
|
|
|
|
<div class="d-flex justify-content-end gap-2 mt-3">
|
|
<a href="{{ url_for('equipments.detail', id=equipment.id) }}" class="btn btn-outline-secondary">
|
|
<i class="bi bi-x-lg me-1"></i>Annuler
|
|
</a>
|
|
<button type="submit" class="btn btn-primary">
|
|
<i class="bi bi-check-lg me-1"></i>Enregistrer
|
|
</button>
|
|
</div>
|
|
</form>
|
|
|
|
<!-- Formulaire de suppression séparé (hors du formulaire principal) -->
|
|
<div class="card border-danger mt-3">
|
|
<div class="card-header text-danger">
|
|
<i class="bi bi-exclamation-triangle me-1"></i>Zone dangereuse
|
|
</div>
|
|
<div class="card-body">
|
|
<form action="{{ url_for('equipments.soft_delete', id=equipment.id) }}" method="POST"
|
|
onsubmit="return confirm('Supprimer cet équipement ?')">
|
|
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
|
|
<button type="submit" class="btn btn-outline-danger btn-sm w-100">
|
|
<i class="bi bi-trash me-1"></i>Supprimer
|
|
</button>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Onglet Compteurs -->
|
|
<div class="tab-pane fade" id="meters" role="tabpanel">
|
|
<div class="card">
|
|
<div class="card-header d-flex justify-content-between align-items-center">
|
|
<span><i class="bi bi-speedometer2 me-1"></i>Compteurs</span>
|
|
<button class="btn btn-sm btn-primary" data-bs-toggle="modal" data-bs-target="#addMeterModal">
|
|
<i class="bi bi-plus-lg me-1"></i>Ajouter
|
|
</button>
|
|
</div>
|
|
<div class="table-responsive">
|
|
<table class="table table-hover mb-0">
|
|
<thead>
|
|
<tr>
|
|
<th>Nom</th>
|
|
<th>Type</th>
|
|
<th>Valeur actuelle</th>
|
|
<th>Unité</th>
|
|
<th>Seuil alerte</th>
|
|
<th>Seuil critique</th>
|
|
<th>Prochaine maintenance</th>
|
|
<th>Actions</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{% for meter in meters %}
|
|
<tr class="{{ 'table-warning' if meter.warning_level == 'warning' else '' }}{{ 'table-danger' if meter.warning_level == 'critical' else '' }}">
|
|
<td>{{ meter.name }}</td>
|
|
<td>{{ meter.meter_type }}</td>
|
|
<td><strong>{{ meter.current_value }}</strong></td>
|
|
<td>{{ meter.unit }}</td>
|
|
<td>{{ meter.warning_threshold or '—' }}</td>
|
|
<td>{{ meter.critical_threshold or '—' }}</td>
|
|
<td>
|
|
{% if meter.next_maintenance_at %}
|
|
{{ meter.next_maintenance_at }} {{ meter.unit }}
|
|
{% else %}
|
|
—
|
|
{% endif %}
|
|
</td>
|
|
<td>
|
|
<button class="btn btn-sm btn-outline-primary" onclick="addReading({{ meter.id }})">
|
|
<i class="bi bi-pencil"></i> Relevé
|
|
</button>
|
|
</td>
|
|
</tr>
|
|
{% else %}
|
|
<tr>
|
|
<td colspan="8" class="text-center text-muted py-4">
|
|
<i class="bi bi-speedometer2 fs-1"></i>
|
|
<p>Aucun compteur</p>
|
|
</td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Onglet Consommables -->
|
|
<div class="tab-pane fade" id="consumables" role="tabpanel">
|
|
<div class="card">
|
|
<div class="card-header d-flex justify-content-between align-items-center">
|
|
<span><i class="bi bi-box-seam me-1"></i>Consommables compatibles</span>
|
|
<button class="btn btn-sm btn-primary" data-bs-toggle="modal" data-bs-target="#addConsumableModal">
|
|
<i class="bi bi-plus-lg me-1"></i>Ajouter
|
|
</button>
|
|
</div>
|
|
<div class="table-responsive">
|
|
<table class="table table-hover mb-0">
|
|
<thead>
|
|
<tr>
|
|
<th>Consommable</th>
|
|
<th>Réf.</th>
|
|
<th>Intervalle (jours)</th>
|
|
<th>Intervalle (heures)</th>
|
|
<th>Stock</th>
|
|
<th>Actions</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{% for ec in equipment_consumables %}
|
|
<tr>
|
|
<td>{{ ec.consumable.name }}</td>
|
|
<td><code>{{ ec.consumable.reference or '—' }}</code></td>
|
|
<td>{{ ec.replacement_interval_days or '—' }}</td>
|
|
<td>{{ ec.replacement_interval_hours or '—' }}</td>
|
|
<td>
|
|
<span class="badge {{ 'bg-danger' if ec.consumable.is_low_stock else 'bg-success' }}">
|
|
{{ ec.consumable.quantity }} {{ ec.consumable.unit }}
|
|
</span>
|
|
</td>
|
|
<td>
|
|
<button class="btn btn-sm btn-outline-danger" onclick="removeConsumable({{ ec.id }})">
|
|
<i class="bi bi-trash"></i>
|
|
</button>
|
|
</td>
|
|
</tr>
|
|
{% else %}
|
|
<tr>
|
|
<td colspan="6" class="text-center text-muted py-4">
|
|
<i class="bi bi-box-seam fs-1"></i>
|
|
<p>Aucun consommable associé</p>
|
|
</td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Onglet Documents -->
|
|
<div class="tab-pane fade" id="documents" role="tabpanel">
|
|
<div class="card">
|
|
<div class="card-header d-flex justify-content-between align-items-center">
|
|
<span><i class="bi bi-file-earmark me-1"></i>Documents</span>
|
|
<button class="btn btn-sm btn-primary" data-bs-toggle="modal" data-bs-target="#addDocumentModal">
|
|
<i class="bi bi-upload me-1"></i>Téléverser
|
|
</button>
|
|
</div>
|
|
<div class="table-responsive">
|
|
<table class="table table-hover mb-0">
|
|
<thead>
|
|
<tr>
|
|
<th>Fichier</th>
|
|
<th>Description</th>
|
|
<th>Ajouté le</th>
|
|
<th>Par</th>
|
|
<th>Actions</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{% for doc in equipment.documents %}
|
|
<tr>
|
|
<td>
|
|
<i class="bi bi-file-earmark-text me-1"></i>
|
|
{{ doc.filename }}
|
|
</td>
|
|
<td>{{ doc.description or '—' }}</td>
|
|
<td>{{ doc.uploaded_at.strftime('%d/%m/%Y') }}</td>
|
|
<td>{{ doc.uploaded_by.username if doc.uploaded_by else '—' }}</td>
|
|
<td>
|
|
<a href="{{ url_for('equipments_documents.download_document', id=doc.id) }}"
|
|
class="btn btn-sm btn-outline-primary">
|
|
<i class="bi bi-download"></i>
|
|
</a>
|
|
<button class="btn btn-sm btn-outline-danger" onclick="deleteDocument({{ doc.id }})">
|
|
<i class="bi bi-trash"></i>
|
|
</button>
|
|
</td>
|
|
</tr>
|
|
{% else %}
|
|
<tr>
|
|
<td colspan="5" class="text-center text-muted py-4">
|
|
<i class="bi bi-file-earmark fs-1"></i>
|
|
<p>Aucun document</p>
|
|
</td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Onglet Historique -->
|
|
<div class="tab-pane fade" id="history" role="tabpanel">
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<i class="bi bi-clock-history me-1"></i>Historique des déplacements
|
|
</div>
|
|
<div class="table-responsive">
|
|
<table class="table table-hover mb-0">
|
|
<thead>
|
|
<tr>
|
|
<th>Date</th>
|
|
<th>Ancienne salle</th>
|
|
<th>Nouvelle salle</th>
|
|
<th>Raison</th>
|
|
<th>Par</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{% for h in room_history %}
|
|
<tr>
|
|
<td>{{ h.changed_at|datetime_fmt }}</td>
|
|
<td>{{ h.old_room.name if h.old_room else '—' }}</td>
|
|
<td>{{ h.new_room.name if h.new_room else '—' }}</td>
|
|
<td>{{ h.reason or '—' }}</td>
|
|
<td>{{ h.changed_by.username if h.changed_by else '—' }}</td>
|
|
</tr>
|
|
{% else %}
|
|
<tr>
|
|
<td colspan="5" class="text-center text-muted py-4">
|
|
<i class="bi bi-clock-history fs-1"></i>
|
|
<p>Aucun déplacement enregistré</p>
|
|
</td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Modal Ajouter Compteur -->
|
|
<div class="modal fade" id="addMeterModal" tabindex="-1">
|
|
<div class="modal-dialog">
|
|
<div class="modal-content">
|
|
<form id="meterForm">
|
|
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
|
|
<div class="modal-header">
|
|
<h5 class="modal-title">Ajouter un compteur</h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<div class="mb-3">
|
|
<label for="meter_name" class="form-label">Nom *</label>
|
|
<input type="text" class="form-control" id="meter_name" name="name" required>
|
|
</div>
|
|
<div class="row mb-3">
|
|
<div class="col-md-6">
|
|
<label for="meter_type" class="form-label">Type</label>
|
|
<select class="form-select" id="meter_type" name="meter_type">
|
|
<option value="hours">Heures</option>
|
|
<option value="cycles">Cycles</option>
|
|
<option value="km">Kilomètres</option>
|
|
<option value="custom">Personnalisé</option>
|
|
</select>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<label for="meter_unit" class="form-label">Unité</label>
|
|
<input type="text" class="form-control" id="meter_unit" name="unit" value="h">
|
|
</div>
|
|
</div>
|
|
<div class="row mb-3">
|
|
<div class="col-md-6">
|
|
<label for="meter_initial" class="form-label">Valeur initiale</label>
|
|
<input type="number" step="0.01" class="form-control" id="meter_initial" name="initial_value" value="0">
|
|
</div>
|
|
<div class="col-md-6">
|
|
<label for="meter_current" class="form-label">Valeur actuelle</label>
|
|
<input type="number" step="0.01" class="form-control" id="meter_current" name="current_value" value="0">
|
|
</div>
|
|
</div>
|
|
<div class="row mb-3">
|
|
<div class="col-md-6">
|
|
<label for="meter_warning" class="form-label">Seuil alerte</label>
|
|
<input type="number" step="0.01" class="form-control" id="meter_warning" name="warning_threshold">
|
|
</div>
|
|
<div class="col-md-6">
|
|
<label for="meter_critical" class="form-label">Seuil critique</label>
|
|
<input type="number" step="0.01" class="form-control" id="meter_critical" name="critical_threshold">
|
|
</div>
|
|
</div>
|
|
<div class="row mb-3">
|
|
<div class="col-md-6">
|
|
<label for="meter_interval" class="form-label">Intervalle maintenance</label>
|
|
<input type="number" step="0.01" class="form-control" id="meter_interval" name="maintenance_interval">
|
|
</div>
|
|
<div class="col-md-6">
|
|
<label for="meter_last_maint" class="form-label">Dernière maintenance (valeur)</label>
|
|
<input type="number" step="0.01" class="form-control" id="meter_last_maint" name="last_maintenance_value">
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Annuler</button>
|
|
<button type="submit" class="btn btn-primary">Ajouter</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Modal Ajouter Consommable -->
|
|
<div class="modal fade" id="addConsumableModal" tabindex="-1">
|
|
<div class="modal-dialog">
|
|
<div class="modal-content">
|
|
<form id="consumableForm">
|
|
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
|
|
<input type="hidden" name="equipment_id" value="{{ equipment.id }}">
|
|
<div class="modal-header">
|
|
<h5 class="modal-title">Ajouter un consommable compatible</h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<div class="mb-3">
|
|
<label for="consumable_id" class="form-label">Consommable *</label>
|
|
<select class="form-select" id="consumable_id" name="consumable_id" required>
|
|
<option value="">-- Sélectionner --</option>
|
|
{% for c in all_consumables %}
|
|
<option value="{{ c.id }}">{{ c.name }} ({{ c.reference or '—' }}) - Stock: {{ c.quantity }}</option>
|
|
{% endfor %}
|
|
</select>
|
|
</div>
|
|
<div class="row mb-3">
|
|
<div class="col-md-4">
|
|
<label for="interval_days" class="form-label">Intervalle (jours)</label>
|
|
<input type="number" class="form-control" id="interval_days" name="replacement_interval_days">
|
|
</div>
|
|
<div class="col-md-4">
|
|
<label for="interval_hours" class="form-label">Intervalle (heures)</label>
|
|
<input type="number" class="form-control" id="interval_hours" name="replacement_interval_hours">
|
|
</div>
|
|
<div class="col-md-4">
|
|
<label for="interval_cycles" class="form-label">Intervalle (cycles)</label>
|
|
<input type="number" class="form-control" id="interval_cycles" name="replacement_interval_cycles">
|
|
</div>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label for="consumable_notes" class="form-label">Notes</label>
|
|
<textarea class="form-control" id="consumable_notes" name="notes" rows="2"></textarea>
|
|
</div>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Annuler</button>
|
|
<button type="submit" class="btn btn-primary">Ajouter</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Modal Téléverser Document -->
|
|
<div class="modal fade" id="addDocumentModal" tabindex="-1">
|
|
<div class="modal-dialog">
|
|
<div class="modal-content">
|
|
<form id="documentForm" enctype="multipart/form-data">
|
|
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
|
|
<div class="modal-header">
|
|
<h5 class="modal-title">Téléverser un document</h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<div class="mb-3">
|
|
<label for="document_file" class="form-label">Fichier *</label>
|
|
<input type="file" class="form-control" id="document_file" name="file" required>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label for="document_description" class="form-label">Description</label>
|
|
<textarea class="form-control" id="document_description" name="description" rows="2"></textarea>
|
|
</div>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Annuler</button>
|
|
<button type="submit" class="btn btn-primary">Téléverser</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
const equipmentCategory = document.getElementById('category_id');
|
|
const equipmentLot = document.getElementById('lot_id');
|
|
const quickAddEquipmentLot = document.getElementById('quick-add-lot');
|
|
function filterEquipmentLots() {
|
|
const categoryId = equipmentCategory.value;
|
|
let selectedIsValid = !equipmentLot.value;
|
|
[...equipmentLot.options].forEach((option, index) => {
|
|
if (!index) return;
|
|
const visible = !!categoryId && option.dataset.categoryId === categoryId;
|
|
option.hidden = !visible;
|
|
option.disabled = !visible;
|
|
if (visible && option.selected) selectedIsValid = true;
|
|
});
|
|
if (!selectedIsValid) equipmentLot.value = '';
|
|
quickAddEquipmentLot.disabled = !categoryId;
|
|
}
|
|
equipmentCategory.addEventListener('change', filterEquipmentLots);
|
|
quickAddEquipmentLot.addEventListener('click', async () => {
|
|
const categoryName = equipmentCategory.selectedOptions[0]?.textContent.trim();
|
|
const name = window.prompt(`Nom du nouveau lot pour « ${categoryName} » :`);
|
|
if (!name || !name.trim()) return;
|
|
const response = await fetch('{{ url_for("wizard.quick_create_lot") }}', {
|
|
method: 'POST',
|
|
headers: {'Content-Type': 'application/json', 'X-CSRFToken': '{{ csrf_token() }}'},
|
|
body: JSON.stringify({name: name.trim(), category_id: equipmentCategory.value})
|
|
});
|
|
const data = await response.json();
|
|
if (!response.ok && !data.lot) {
|
|
window.alert(data.error || 'Création du lot impossible.');
|
|
return;
|
|
}
|
|
const created = data.lot;
|
|
let option = [...equipmentLot.options].find(item => item.value === String(created.id));
|
|
if (!option) {
|
|
option = new Option(created.name, created.id);
|
|
option.dataset.categoryId = String(created.category_id);
|
|
equipmentLot.add(option);
|
|
}
|
|
filterEquipmentLots();
|
|
equipmentLot.value = String(created.id);
|
|
if (!response.ok) window.alert(data.error);
|
|
});
|
|
filterEquipmentLots();
|
|
|
|
// Gestion des formulaires AJAX
|
|
document.getElementById('meterForm').addEventListener('submit', function(e) {
|
|
e.preventDefault();
|
|
const formData = new FormData(this);
|
|
fetch('{{ url_for("equipments_meters.add_meter", id=equipment.id) }}', {
|
|
method: 'POST',
|
|
body: formData
|
|
})
|
|
.then(r => r.json())
|
|
.then(data => {
|
|
if (data.success) {
|
|
location.reload();
|
|
} else {
|
|
alert('Erreur: ' + (data.error || 'Impossible d\'ajouter le compteur'));
|
|
}
|
|
});
|
|
});
|
|
|
|
document.getElementById('consumableForm').addEventListener('submit', function(e) {
|
|
e.preventDefault();
|
|
const formData = new FormData(this);
|
|
fetch('{{ url_for("equipments_meters.add_consumable", id=equipment.id) }}', {
|
|
method: 'POST',
|
|
body: formData
|
|
})
|
|
.then(r => r.json())
|
|
.then(data => {
|
|
if (data.success) {
|
|
location.reload();
|
|
} else {
|
|
alert('Erreur: ' + (data.error || 'Impossible d\'ajouter le consommable'));
|
|
}
|
|
});
|
|
});
|
|
|
|
document.getElementById('documentForm').addEventListener('submit', function(e) {
|
|
e.preventDefault();
|
|
const formData = new FormData(this);
|
|
fetch('{{ url_for("equipments_documents.upload_document", id=equipment.id) }}', {
|
|
method: 'POST',
|
|
body: formData
|
|
})
|
|
.then(r => r.json())
|
|
.then(data => {
|
|
if (data.success) {
|
|
location.reload();
|
|
} else {
|
|
alert('Erreur: ' + (data.error || 'Impossible de téléverser le document'));
|
|
}
|
|
});
|
|
});
|
|
|
|
function addReading(meterId) {
|
|
const value = prompt('Entrez la nouvelle valeur du compteur:');
|
|
if (value !== null) {
|
|
fetch('{{ url_for("equipments_meters.add_meter_reading", id=equipment.id) }}', {
|
|
method: 'POST',
|
|
headers: {'Content-Type': 'application/json'},
|
|
body: JSON.stringify({meter_id: meterId, value: parseFloat(value)})
|
|
})
|
|
.then(r => r.json())
|
|
.then(data => {
|
|
if (data.success) {
|
|
location.reload();
|
|
} else {
|
|
alert('Erreur: ' + (data.error || 'Impossible d\'ajouter le relevé'));
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
function removeConsumable(ecId) {
|
|
if (confirm('Retirer ce consommable ?')) {
|
|
fetch('{{ url_for("equipments_meters.remove_consumable", id=equipment.id) }}', {
|
|
method: 'POST',
|
|
headers: {'Content-Type': 'application/json'},
|
|
body: JSON.stringify({ec_id: ecId})
|
|
})
|
|
.then(r => r.json())
|
|
.then(data => {
|
|
if (data.success) {
|
|
location.reload();
|
|
} else {
|
|
alert('Erreur: ' + (data.error || 'Impossible de retirer le consommable'));
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
function deleteDocument(docId) {
|
|
if (confirm('Supprimer ce document ?')) {
|
|
fetch('{{ url_for("equipments_documents.delete_document", doc_id=0) }}'.replace('0', docId), {
|
|
method: 'POST',
|
|
headers: {'Content-Type': 'application/json'},
|
|
credentials: 'same-origin'
|
|
})
|
|
.then(r => r.json())
|
|
.then(data => {
|
|
if (data.success) {
|
|
location.reload();
|
|
} else {
|
|
alert('Erreur: ' + (data.error || 'Impossible de supprimer le document'));
|
|
}
|
|
});
|
|
}
|
|
}
|
|
</script>
|
|
{% endblock %}
|