ixg_platform/templates/ops/intake_form.html
ixgadmin 7d7248a23b Add an intake screen to the ops portal
Harvest intakes could only be recorded in the administration area or through the
API, so the first record the business actually creates was entered outside the
portal it works in. Add list, create and correct screens under Harvest Intakes.

The form is deliberately stricter than the model. HarvestIntake allows almost
everything to be blank because records also arrive by API, and the platform
copes by flagging the gaps later — the EUDR completeness check has an intake
data test whose whole job is to catch a missing date or weight. The form
therefore requires every field those checks read: reference, collector group,
origin zone, date, weight and grade.

Three things it does beyond capturing the fields: it suggests the next intake
reference for the year (editable, since it may have to match a weighbridge
ticket), fills the origin zone in from the collector group while leaving it
editable, and refuses a duplicate reference — including one differing only in
case. It queries, rather than blocks, when the chosen zone is not the one the
group is registered to, and it marks unverified zones in the dropdown, because
geolocation passes only for a verified zone.

Correcting amends the record in place: the reference a buyer or auditor quotes
does not change.
2026-09-17 13:45:39 +00:00

71 lines
2.8 KiB
HTML

{% extends 'ops/base_ops.html' %}
{% load static %}
{% block title %}{{ page_title }} - IXG Operations{% endblock %}
{% block content %}
<div class=page-header d-flex justify-content-between align-items-center>
<h2>{{ page_title }}</h2>
<a href={% url 'ops:intake_list' %} class=btn btn-sm btn-outline-secondary><i class=bi bi-arrow-left></i> Back</a>
</div>
<div class=row g-3>
<div class=col-md-7>
<div class=stat-card>
<form method=post novalidate id=intake-form data-group-zones="{{ group_zones }}">
{% csrf_token %}
{% if form.non_field_errors %}
{% for e in form.non_field_errors %}<div class="alert alert-danger py-2">{{ e }}</div>{% endfor %}
{% endif %}
{% for field in form %}
<div class=mb-3>
<label class=form-label for="{{ field.id_for_label }}">
{{ field.label }}{% if field.field.required %} <span class=text-danger>*</span>{% endif %}
</label>
{{ field }}
{% if field.help_text %}<div class=form-text>{{ field.help_text }}</div>{% endif %}
{% for e in field.errors %}<div class="text-danger small">{{ e }}</div>{% endfor %}
</div>
{% endfor %}
<button type=submit class="btn btn-primary">{{ submit_label }}</button>
<a href={% url 'ops:intake_list' %} class="btn btn-outline-secondary">Cancel</a>
</form>
</div>
</div>
<div class=col-md-5>
<div class="stat-card mb-3">
<h6 class=mb-3><i class=bi bi-info-circle></i> Recording an intake</h6>
<p class="small mb-2">
An intake is the moment a collector group hands over shea at the processing
site. It is the record a finished batch is traced back to, so the fields
marked <span class=text-danger>*</span> are required: they are the ones the
platform's due-diligence checks read.
</p>
<p class="small mb-0">
If something is wrong on a saved intake, use <strong>Correct</strong> on the
list. Nothing is deleted — the record is amended, so the reference a buyer
or an auditor sees stays the same.
</p>
</div>
<div class=stat-card>
<h6 class=mb-3><i class=bi bi-shield-check></i> What these records feed</h6>
<ul class="small mb-0 ps-3">
<li><strong>Lineage</strong> — a batch needs at least one intake linked to it</li>
<li><strong>Intake data</strong> — a linked intake must have both a date and a weight</li>
<li><strong>Geolocation</strong> — every linked intake must point at a <em>verified</em> origin zone</li>
</ul>
<p class="small text-secondary mt-2 mb-0">
A missing date, weight or zone holds the batch's EUDR case back until it is
corrected here.
</p>
</div>
</div>
</div>
<script src="{% static 'js/ixg_intake_form.js' %}"></script>
{% endblock %}