Files
cantarire-etichetare/cantarire/templates/recipes/form.html
T
ske087 bff6d70b17 Initial commit: aplicatie de cantarire si etichetare
Aplicatie Flask + SQLite pentru productie alimentara: cantarirea
componentelor unui produs finit si tiparirea etichetei prin QZ Tray.

- produse finite (retete) formate din componente cantarite
- designer vizual de etichete (mm/px, zona sigura, campuri variabile,
  cod QR cu continut din mai multe bucati)
- legare produs finit -> sablon de eticheta
- navigare pe doua taburi: Cantarire / Setari
- administrare: componente, produse finite, sabloane, campuri, istoric
- backup, resetare baza de date, checklist de pregatire, /healthz
- CLI de administrare si serviciu systemd pentru productie
- vendor/syncpulse-scale-bridge (GPL-3.0-or-later) pentru citirea cantarului
2026-10-18 23:38:45 +03:00

142 lines
5.8 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{% extends "base.html" %}
{% block title %}{{ "Produs finit nou" if is_new else "Editare produs finit" }}{% endblock %}
{% block content %}
<section class="page-head">
<div>
<h1>{{ "Produs finit nou" if is_new else "Editare produs finit" }}</h1>
<p class="subtitle">Ordinea componentelor este ordinea în care se cântăresc.</p>
</div>
</section>
<div class="grid template-grid">
<section class="card">
<form method="post" class="form">
<div class="field-row">
<div class="field">
<label for="code">Cod <span class="req">*</span></label>
<input id="code" name="code" type="text" maxlength="40" required value="{{ recipe.code or '' }}">
</div>
<div class="field">
<label for="unit">Unitate</label>
<select id="unit" name="unit">
{% for u in ["g", "kg", "mg", "t", "lb", "oz"] %}
<option value="{{ u }}" {{ 'selected' if (recipe.unit or 'g') == u }}>{{ u }}</option>
{% endfor %}
</select>
</div>
</div>
<div class="field">
<label for="name">Denumire pe etichetă <span class="req">*</span></label>
<input id="name" name="name" type="text" maxlength="120" required
value="{{ recipe.name or '' }}" placeholder="ex. Fasole cu carne">
</div>
<div class="field">
<label for="target_weight">Greutate totală țintă (opțional)</label>
<input id="target_weight" name="target_weight" type="number" step="0.001" min="0"
value="{{ recipe.target_weight|weight if recipe.target_weight else '' }}" placeholder="ex. 450">
<p class="muted small">Doar orientativă; eticheta afișează greutatea reală cântărită.</p>
</div>
<h2>Etichetă</h2>
<div class="field">
<label for="template_id">Șablon de etichetă</label>
<select id="template_id" name="template_id">
<option value="">— șablonul implicit din Setări —</option>
{% for t in templates %}
<option value="{{ t.id }}" {{ 'selected' if recipe.template_id|string == t.id|string }}>
{{ t.name }} · {{ t.width_mm|weight }} × {{ t.height_mm|weight }} mm
</option>
{% endfor %}
</select>
<p class="muted small">
{% if templates %}
Eticheta acestui produs finit se randează cu șablonul ales aici.
<a href="{{ url_for('label_templates.index') }}">Vezi șabloanele</a> ·
<a href="{{ url_for('label_templates.index') }}">creează unul nou</a>
{% else %}
Nu există șabloane. <a href="{{ url_for('label_templates.index') }}">Creează întâi un șablon</a>,
altfel eticheta nu poate fi tipărită.
{% endif %}
</p>
</div>
<h2>Componente</h2>
<p class="muted small">Completează cel puțin primul rând. Ordinea rândurilor = ordinea cântăririi.</p>
{% for row in slot_rows %}
<div class="component-row">
<span class="component-index">{{ row.slot }}</span>
<div class="field grow">
<label for="component_{{ row.slot }}_id">
Component {{ row.slot }}{% if row.slot == 1 %} <span class="req">*</span>{% endif %}
</label>
<select id="component_{{ row.slot }}_id" name="component_{{ row.slot }}_id">
<option value="">— {{ "obligatoriu" if row.slot == 1 else "opțional" }} —</option>
{% for c in components %}
<option value="{{ c.id }}"
{{ 'selected' if row.component_id|string == c.id|string }}>
{{ c.code }} · {{ c.name }} ({{ c.unit }})
</option>
{% endfor %}
</select>
</div>
<div class="field component-target">
<label for="component_{{ row.slot }}_target">Țintă</label>
<input id="component_{{ row.slot }}_target" name="component_{{ row.slot }}_target"
type="number" step="0.001" min="0" value="{{ row.target|weight if row.target else '' }}">
</div>
</div>
{% endfor %}
<div class="field">
<label for="note">Observații interne</label>
<textarea id="note" name="note" rows="2" maxlength="500">{{ recipe.note or '' }}</textarea>
</div>
<label class="check">
<input type="checkbox" name="active" {{ 'checked' if recipe.active in [True, 1, '1', 'on', 'true'] or is_new }}>
Produs finit activ
</label>
<div class="actions">
<button class="btn primary" type="submit">Salvează</button>
<a class="btn ghost" href="{{ url_for('recipes.index') }}">Renunță</a>
</div>
</form>
</section>
<aside class="card help-card">
<h2>Cum funcționează</h2>
<p class="small">
La cântărire alegi produsul finit. Aplicația cere pe rând fiecare componentă, în ordinea de aici.
După ultima componentă eticheta se tipărește automat.
</p>
<h2>Eticheta produsului</h2>
<p class="small">
Șablonul ales aici este folosit automat la tipărirea etichetelor pentru acest produs finit;
dacă lași gol, se folosește șablonul implicit din Setări.
</p>
<h2>Exemplu</h2>
<p class="small">
<strong>Fasole cu carne</strong>, unitate <code>g</code>:
</p>
<ul class="var-list">
<li>1 · Fasole — țintă 250</li>
<li>2 · Carne — țintă 200</li>
</ul>
<p class="small muted">
Eticheta va arăta totalul real cântărit și defalcarea:
<code>250 g Fasole</code>, <code>200 g Carne</code>.
</p>
<h2>Fără componentă nouă?</h2>
<p class="small muted">
Dacă lipsește o componentă din listă, o adaugi întâi în
<a href="{{ url_for('components.create') }}">Componente</a>, apoi revii aici.
</p>
</aside>
</div>
{% endblock %}