- Versioned product specification table (Unrefined Shea Butter v1.0 seed) with spec-based QC evaluation replacing hard-coded thresholds - Structured QC results with test methods and four-eyes verification (submitter cannot self-approve) - reportlab IXG Quality Summary PDF auto-generated on quality approval, SHA-256 hashed; original independent lab PDF kept authoritative - CertificateOfAnalysis version/status/hash/approval/supersede lifecycle; commercial release publishes to buyers and triggers notification - EUDR evidence workflow: Facility, OriginZone, CollectorGroup, HarvestIntake, IntakeBatchLineage, DueDiligenceCase, EUDREvidence, RiskAssessment, MitigationAction, DueDiligenceStatementReference with rule-based completeness check and risk assessment (no AI legal conclusions) - Ops portal: QC Approvals, CoA Reviews, EUDR Compliance screens - Buyer portal: /portal/compliance page - 22 automated tests covering four-eyes, CAPA, PDF+hash, publish/notify, EUDR blocks and buyer isolation
52 lines
3.4 KiB
HTML
52 lines
3.4 KiB
HTML
{% extends 'ops/base_ops.html' %}
|
|
{% block title %}QC Verification - IXG Operations{% endblock %}
|
|
{% block content %}
|
|
|
|
<div class=page-header d-flex justify-content-between align-items-center>
|
|
<h2>QC Verification: {{ result.batch.batch_reference }}</h2>
|
|
<a href={% url 'ops:qc_verify_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-7>
|
|
<div class=stat-card>
|
|
<h6 class=mb-3>Structured QC Result</h6>
|
|
<table class=table table-sm table-borderless mb-0>
|
|
<tr><td class=text-secondary>Batch</td><td><a href={% url 'ops:batch_detail' result.batch.pk %}>{{ result.batch.batch_reference }}</a></td></tr>
|
|
<tr><td class=text-secondary>Overall Result</td><td>{% if result.overall_pass %}<span class=badge bg-success>Pass</span>{% else %}<span class=badge bg-danger>Fail</span>{% endif %}</td></tr>
|
|
<tr><td class=text-secondary>Specification</td><td>{{ result.spec_version|default:'legacy limits' }}</td></tr>
|
|
<tr><td class=text-secondary>FFA %</td><td>{{ result.ffa_percentage }} <span class=text-muted>({{ result.method_ffa|default:'no method' }})</span></td></tr>
|
|
<tr><td class=text-secondary>Moisture %</td><td>{{ result.moisture_percentage }} <span class=text-muted>({{ result.method_moisture|default:'no method' }})</span></td></tr>
|
|
<tr><td class=text-secondary>Peroxide Value</td><td>{{ result.peroxide_value }} <span class=text-muted>({{ result.method_peroxide|default:'no method' }})</span></td></tr>
|
|
<tr><td class=text-secondary>Visual Grade</td><td>{{ result.visual_grade }}</td></tr>
|
|
<tr><td class=text-secondary>Result Status</td><td>{{ result.get_result_status_display }}</td></tr>
|
|
<tr><td class=text-secondary>Lab Reference</td><td>{{ result.lab_reference|default:'-' }}</td></tr>
|
|
<tr><td class=text-secondary>Tested By</td><td>{{ result.tested_by|default:'-' }}</td></tr>
|
|
<tr><td class=text-secondary>Entered By</td><td>{{ result.entered_by.full_name|default:result.entered_by.email|default:'-' }}</td></tr>
|
|
<tr><td class=text-secondary>Tested At</td><td>{{ result.tested_at|date:'d/m/Y H:i' }}</td></tr>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
<div class=col-md-5>
|
|
<div class=stat-card>
|
|
<h6 class=mb-3>Approval Decision</h6>
|
|
{% if result.verification_status == 'draft' %}
|
|
<form method=post>
|
|
{% csrf_token %}
|
|
<p class=text-muted>This action is recorded as the quality approval. The submitter cannot approve their own result.</p>
|
|
<button type=submit name=approved value=1 class="btn btn-success w-100 mb-2"><i class=bi bi-check-circle></i> Approve & Generate CoA</button>
|
|
<button type=submit name=approved value=0 class="btn btn-outline-danger w-100"><i class=bi bi-x-circle></i> Reject</button>
|
|
</form>
|
|
{% else %}
|
|
<p><span class=badge {% if result.verification_status == 'approved' %}bg-success{% else %}bg-danger{% endif %}>{{ result.get_verification_status_display }}</span></p>
|
|
{% if result.verified_by %}<p class=text-muted mb-0>Verified by {{ result.verified_by.full_name|default:result.verified_by.email }} on {{ result.approved_at|date:'d/m/Y H:i' }}</p>{% endif %}
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{% endblock %}
|