ixg_platform/templates/ops/eudr_detail.html
ixgadmin f6aedc0002 Add the compliance registers to the ops portal
Facility, origin zone and collector group could only be registered in the
Django admin, which left the portal unable to onboard a new supply site,
zone or group. Add list/create/edit screens for all three, and create/edit
for mitigation actions, EUDR evidence and external-submission references
on a due-diligence case.

The origin-zone form keeps the map a view of the stored geometry, not a
check of it: the map wording is unchanged and geometry is only required to
be valid JSON. Verification is stamped from the acting user when the status
moves to verified/rejected, and cleared when it returns to unverified.

Nothing is removed from the admin; it stays as the superuser fallback.
Adds an nginx /tiles/ route on the ops host so the map imagery loads there
too (the tile view is root-mounted but ops remaps / to /ops/).
2026-09-25 16:26:59 +00:00

162 lines
8 KiB
HTML

{% extends 'ops/base_ops.html' %}
{% block title %}EUDR Case - IXG Operations{% endblock %}
{% block content %}
<div class="page-header d-flex justify-content-between align-items-center">
<h2>EUDR Case: {{ case.batch.batch_reference }}</h2>
<a href="{% url 'ops:eudr_list' %}" class="btn btn-sm btn-outline-secondary"><i class="bi bi-arrow-left"></i> Back</a>
</div>
{% if messages %}
{% for m in messages %}<div class="alert alert-{{ m.tags }} py-2">{{ m }}</div>{% endfor %}
{% endif %}
<div class="row g-3">
<div class="col-md-8">
<div class="stat-card mb-3">
<h6 class="mb-3">Completeness Check</h6>
{% if case.completeness_results %}
<table class="table table-sm mb-0">
<thead><tr><th>Check</th><th>Status</th><th>Detail</th></tr></thead>
<tbody>
{% for key, item in case.completeness_results.items %}
<tr>
<td>{{ key }}</td>
<td>{% if item.pass %}<span class="badge bg-success">Pass</span>{% else %}<span class="badge bg-danger">Fail</span>{% endif %}</td>
<td>{{ item.detail }}</td>
</tr>
{% endfor %}
</tbody>
</table>
{% else %}
<p class="text-muted mb-0">Not yet run.</p>
{% endif %}
</div>
<div class="stat-card mb-3">
<h6 class="mb-3">Intake Lineage</h6>
{% if lineage %}
<table class="table table-sm mb-0">
<thead><tr><th>Intake</th><th>Date</th><th>Weight (kg)</th><th>Zone</th><th>Zone Status</th><th>Collector</th></tr></thead>
<tbody>
{% for link in lineage %}
<tr>
<td>{{ link.harvest_intake.intake_reference }}</td>
<td>{{ link.harvest_intake.intake_date|date:'d/m/Y'|default:'-' }}</td>
<td>{% if link.weight_allocated_kg %}{{ link.weight_allocated_kg }}{% elif link.harvest_intake %}{{ link.harvest_intake.weight_kg }}{% else %}-{% endif %}</td>
<td>{{ link.harvest_intake.origin_zone.zone_code|default:'-' }}</td>
<td>{% if link.harvest_intake.origin_zone %}<span class="badge {% if link.harvest_intake.origin_zone.verification_status == 'verified' %}bg-success{% elif link.harvest_intake.origin_zone.verification_status == 'rejected' %}bg-danger{% else %}bg-warning text-dark{% endif %}">{{ link.harvest_intake.origin_zone.get_verification_status_display }}</span>{% else %}-{% endif %}</td>
<td>{{ link.harvest_intake.collector_group.name|default:'-' }}</td>
</tr>
{% empty %}
<tr><td colspan="6" class="text-muted">No intakes linked. Record intake lineage via API/admin.</td></tr>
{% endfor %}
</tbody>
</table>
{% else %}
<p class="text-muted mb-0">No intake lineage recorded for this batch.</p>
{% endif %}
</div>
<div class="stat-card mb-3">
<div class="d-flex justify-content-between align-items-center mb-3">
<h6 class="mb-0">EUDR Evidence</h6>
<a href="{% url 'ops:eudr_evidence_create' case.pk %}" class="btn btn-sm btn-outline-primary"><i class="bi bi-paperclip"></i> Attach evidence</a>
</div>
{% for e in evidence %}
<div class="border-bottom pb-2 mb-2">
<span class="badge bg-secondary">{{ e.get_evidence_type_display }}</span>
<span class="small">{{ e.file }} <span class="text-muted">{% if e.source %}&middot; {{ e.source }}{% endif %}{% if e.collected_at %} &middot; {{ e.collected_at|date:'d/m/Y' }}{% endif %}</span></span>
<a href="{% url 'ops:eudr_evidence_update' case.pk e.pk %}" class="btn btn-sm btn-link p-0 ms-2">Edit</a>
</div>
{% empty %}
<p class="text-muted mb-0">No evidence attached. The completeness check needs geolocation, harvest document and supplier identity.</p>
{% endfor %}
</div>
<div class="stat-card mb-3">
<h6 class="mb-3">Risk Assessments</h6>
{% for r in risk_assessments %}
<div class="border-bottom pb-2 mb-2">
<span class="badge {% if r.risk_level == 'high' %}bg-danger{% elif r.risk_level == 'low' %}bg-success{% else %}bg-info text-dark{% endif %}">{{ r.get_risk_level_display }}</span>
<span class="text-muted small">by {{ r.assessed_by.full_name|default:'-' }} · {{ r.assessed_at|date:'d/m/Y H:i' }}</span>
<div class="small mt-1">{{ r.basis }}</div>
</div>
{% empty %}
<p class="text-muted mb-0">No risk assessment recorded.</p>
{% endfor %}
</div>
<div class="stat-card mb-3">
<div class="d-flex justify-content-between align-items-center mb-3">
<h6 class="mb-0">Mitigation Actions</h6>
<a href="{% url 'ops:eudr_mitigation_create' case.pk %}" class="btn btn-sm btn-outline-primary"><i class="bi bi-plus-lg"></i> Add action</a>
</div>
{% for m in mitigation_actions %}
<div class="border-bottom pb-2 mb-2">
<span class="badge {% if m.status == 'verified' %}bg-success{% elif m.status == 'completed' %}bg-primary{% else %}bg-warning text-dark{% endif %}">{{ m.get_status_display }}</span>
<span class="small">{{ m.action }} <span class="text-muted">(owner: {{ m.owner|default:'-' }}{% if m.due_date %}, due {{ m.due_date|date:'d/m/Y' }}{% endif %})</span></span>
<a href="{% url 'ops:eudr_mitigation_update' case.pk m.pk %}" class="btn btn-sm btn-link p-0 ms-2">Edit</a>
</div>
{% empty %}
<p class="text-muted mb-0">No mitigation actions.</p>
{% endfor %}
</div>
<div class="stat-card">
<h6 class="mb-3">External Submission (DDS) References</h6>
{% for s in statement_references %}
<div class="border-bottom pb-2 mb-2">
<span class="badge bg-secondary">{{ s.reconciliation_status }}</span>
<span class="small">Ref: <code>{{ s.dds_reference }}</code> &middot; {{ s.submission_date|date:'d/m/Y'|default:'-' }} &middot; by {{ s.submitted_by.full_name|default:'-' }}</span>
<a href="{% url 'ops:eudr_statement_update' case.pk s.pk %}" class="btn btn-sm btn-link p-0 ms-2">Edit</a>
</div>
{% empty %}
<p class="text-muted mb-0">No external submission recorded. Submissions are manual.</p>
{% endfor %}
</div>
</div>
<div class="col-md-4">
<div class="stat-card mb-3">
<h6 class="mb-3">Case Controls</h6>
<form method="post" class="mb-2">
{% csrf_token %}
<input type="hidden" name="action" value="check">
<button type="submit" class="btn btn-outline-primary w-100"><i class="bi bi-check2-square"></i> Run Completeness Check</button>
</form>
<form method="post" class="mb-2">
{% csrf_token %}
<input type="hidden" name="action" value="assess_risk">
<button type="submit" class="btn btn-outline-warning w-100"><i class="bi bi-activity"></i> Run Risk Assessment</button>
</form>
<form method="post" class="mb-2">
{% csrf_token %}
<input type="hidden" name="action" value="approve">
<button type="submit" class="btn btn-success w-100"><i class="bi bi-check-circle"></i> Approve Case</button>
</form>
<form method="post">
{% csrf_token %}
<input type="hidden" name="action" value="reject">
<label class="small">Reject reason</label>
<input type="text" name="reason" class="form-control form-control-sm mb-2" required>
<button type="submit" class="btn btn-outline-danger w-100"><i class="bi bi-x-circle"></i> Reject Case</button>
</form>
</div>
<div class="stat-card">
<h6 class="mb-3">Record External Submission</h6>
<form method="post">
{% csrf_token %}
<input type="hidden" name="action" value="record_submission">
<label class="small">DDS reference</label>
<input type="text" name="dds_reference" class="form-control form-control-sm mb-2" placeholder="e.g. DD-2026-00001" required>
<label class="small">Submission date</label>
<input type="date" name="submission_date" class="form-control form-control-sm mb-2">
<button type="submit" class="btn btn-primary w-100"><i class="bi bi-send"></i> Record Submission</button>
</form>
</div>
</div>
</div>
{% endblock %}