From e560d824cc9a90cf6986fac24562b9039cdaef6c Mon Sep 17 00:00:00 2001 From: root Date: Mon, 24 Aug 2026 16:07:28 +0000 Subject: [PATCH] feat(c4): complete quota projection and copier views --- app_new/c4/routes.py | 15 +++++++++++++-- app_new/c4/templates/c4/contract_detail.html | 12 +++++++++++- app_new/c4/templates/c4/equipment_detail.html | 3 +++ 3 files changed, 27 insertions(+), 3 deletions(-) create mode 100644 app_new/c4/templates/c4/equipment_detail.html diff --git a/app_new/c4/routes.py b/app_new/c4/routes.py index e3fbb99..207aa8d 100644 --- a/app_new/c4/routes.py +++ b/app_new/c4/routes.py @@ -11,7 +11,7 @@ from ..core.models import ( ) from ..core.services.c4_service import ( C4DomainError, assign_equipment_to_contract, calculate_contract_usage, - create_consumable_order, create_contract, evaluate_quota_alert, + create_consumable_order, create_contract, evaluate_quota_alert, project_contract_usage, estimate_consumable_lifetime, receive_consumable_order, record_consumable_issue, ) from ..extensions import db @@ -31,6 +31,16 @@ def contracts(): return render_template("c4/contracts.html", contracts=rows) +@c4_bp.get("/equipment/") +@login_required +@permission_required("patrimoine.view") +def equipment_detail(equipment_id): + equipment = Equipment.query.get_or_404(equipment_id) + assignments = ContractEquipmentAssignment.query.filter_by(equipment_id=equipment.id).order_by(ContractEquipmentAssignment.entry_date.desc()).all() + links = EquipmentConsumable.query.filter_by(equipment_id=equipment.id).all() + return render_template("c4/equipment_detail.html", equipment=equipment, assignments=assignments, links=links) + + @c4_bp.route("/contracts/new", methods=["GET", "POST"]) @login_required @permission_required("contract.manage") @@ -60,7 +70,8 @@ def contract_detail(contract_id): contract = PhotocopierContract.query.get_or_404(contract_id) periods = contract.periods data = {period.id: calculate_contract_usage(period) for period in periods} - return render_template("c4/contract_detail.html", contract=contract, periods=periods, usage=data) + projections = {period.id: project_contract_usage(period) for period in periods} + return render_template("c4/contract_detail.html", contract=contract, periods=periods, usage=data, projections=projections) @c4_bp.post("/contracts//equipment") diff --git a/app_new/c4/templates/c4/contract_detail.html b/app_new/c4/templates/c4/contract_detail.html index b1989ab..d2afeab 100644 --- a/app_new/c4/templates/c4/contract_detail.html +++ b/app_new/c4/templates/c4/contract_detail.html @@ -1,3 +1,13 @@ {% extends "base.html" %} {% block title %}{{ contract.name }} — GMAO{% endblock %} -{% block content %}

{{ contract.name }}

{{ contract.status }}

{{ contract.supplier.name }} · {{ contract.start_date }} → {{ contract.end_date }} · anniversaire {{ '%02d/%02d'|format(contract.anniversary_day, contract.anniversary_month) }}

{% for period in periods %}{% set u=usage[period.id] %}
Période {{ period.start_date }} → {{ period.end_date }}
N&B
{{ '%.0f'|format(u.bw) }}{% if period.quota_bw is not none %} / {{ period.quota_bw }}{% else %} — sans quota{% endif %}
Couleur
{{ '%.0f'|format(u.color) }}{% if period.quota_color is not none %} / {{ period.quota_color }}{% else %} — sans quota{% endif %}
Machines prises en compte : {{ u.machines }} · qualité : {{ u.quality }}
{% endfor %}

Machines rattachées

    {% for a in contract.assignments %}
  • {{ a.equipment.name }} — {{ a.entry_date }}{% if a.exit_date %} → {{ a.exit_date }}{% endif %}
  • {% else %}
  • Aucune machine.
  • {% endfor %}
{% endblock %} +{% block content %} +
+

{{ contract.name }}

{{ contract.status }}
+

{{ contract.supplier.name }} · {{ contract.start_date }} → {{ contract.end_date }} · période quota à partir du {{ contract.quota_start_date or contract.start_date }} · anniversaire {{ '%02d/%02d'|format(contract.anniversary_day, contract.anniversary_month) }}

+
+ {% for period in periods %}{% set u=usage[period.id] %}{% set p=projections[period.id] %} +
Période {{ period.start_date }} → {{ period.end_date }}
N&B
{{ '%.0f'|format(u.bw) }}{% if period.quota_bw is not none %} / {{ period.quota_bw }}{% else %} — sans quota{% endif %}Projection : {{ '%.0f'|format(p.projection.bw) if p.projection.bw is not none else 'indisponible' }}
Couleur
{{ '%.0f'|format(u.color) }}{% if period.quota_color is not none %} / {{ period.quota_color }}{% else %} — sans quota{% endif %}Projection : {{ '%.0f'|format(p.projection.color) if p.projection.color is not none else 'indisponible' }}
Machines prises en compte : {{ u.machines }} · qualité : {{ u.quality }}{% if p.confidence == 'LOW' %} · projection à faible confiance{% endif %}
+ {% endfor %} +

Machines rattachées

    {% for a in contract.assignments %}
  • {{ a.equipment.name }} — {{ a.entry_date }}{% if a.exit_date %} → {{ a.exit_date }}{% endif %}
  • {% else %}
  • Aucune machine.
  • {% endfor %}
+
+{% endblock %} diff --git a/app_new/c4/templates/c4/equipment_detail.html b/app_new/c4/templates/c4/equipment_detail.html new file mode 100644 index 0000000..9d7fd9a --- /dev/null +++ b/app_new/c4/templates/c4/equipment_detail.html @@ -0,0 +1,3 @@ +{% extends "base.html" %} +{% block title %}{{ equipment.name }} — C4{% endblock %} +{% block content %}

{{ equipment.name }}

Type
{{ equipment.device_type or '—' }}
Fabricant / modèle
{{ equipment.manufacturer or '—' }} / {{ equipment.model_reference or '—' }}
N° de série
{{ equipment.serial_number or '—' }}
Référence fournisseur
{{ equipment.supplier_reference or '—' }}
IP / hostname
{{ equipment.ip_address or '—' }} / {{ equipment.hostname or '—' }}
MAC
{{ equipment.mac_address or '—' }}
Emplacement
{{ equipment.full_path }}

Contrats

    {% for assignment in assignments %}
  • {{ assignment.contract.name }} — {{ assignment.entry_date }}{% if assignment.exit_date %} → {{ assignment.exit_date }}{% endif %}
  • {% else %}
  • Aucun contrat.
  • {% endfor %}

Consommables compatibles

    {% for link in links %}
  • {{ link.consumable.name }} — stock {{ link.consumable.quantity }}
  • {% else %}
  • Aucun consommable.
  • {% endfor %}

Compteurs

    {% for meter in equipment.meters %}
  • {{ meter.name }} — {{ meter.current_value }} {{ meter.unit }}
  • {% else %}
  • Aucun compteur.
  • {% endfor %}
{% endblock %}