{# templates/pos_api/android_sales_approve_edit_single.html #} {% extends "pos/base.html" %} {% load humanize %} {% block title %}Approve Invoice #{{ invoice_no }}{% endblock %} {% block content %}
{# ── Page header ── #}

Approve Invoice #{{ invoice_no }}

Customer: {{ pay.party_descp|default:"—" }} • Date: {{ pay.transaction_date }} • Time: {{ pay.bill_time|default:"—" }}
{# ── Stock mode indicator ── #}
{% if zero_stock_allow %} ⚡ Zero-Stock Mode (simple deduction) {% elif check_batch %} 📦 Batch-Layer Mode {% if expiry_warn_days > 0 %}+ Expiry Check ({{ expiry_warn_days }}d){% endif %} {% else %} 📊 Total-Stock-Only Mode {% endif %} Pick: {{ stock_roles.pick_mode }}
← Back
{# ── Block banner ── #} {% if blocked %}
❌ Blocked: Fix the highlighted lines or remove them before approving.
{% endif %} {# ── Mode help text ── #} {% if zero_stock_allow %}
Simple Mode: No batch/expiry checks. Stock may go negative. All lines will always pass.
{% elif check_batch and expiry_warn_days > 0 %}
Batch + Expiry Mode: Lines expiring within {{ expiry_warn_days }} days are flagged. Remove those lines or reduce qty to available valid stock.
{% elif check_batch %}
Batch Mode: Stock is consumed layer-by-layer ({{ stock_roles.pick_mode }}). Lines with no available batch are blocked — remove them or reduce qty.
{% else %}
Stock-Only Mode: Checks total stock (all batches summed). No batch detail. Reduce qty to available or remove the line.
{% endif %} {# ════════════════════════════════════════════════════════════════ #} {# FORM #} {# ════════════════════════════════════════════════════════════════ #}
{% csrf_token %}
Products
Total: {{ pay.total_amount|floatformat:2|intcomma }} • Received: {{ pay.amount_transaction|floatformat:2|intcomma }}
{% if not zero_stock_allow %} {% endif %} {% for item in items %} {# ── Product name ── #} {# ── Availability column ── #} {# ── Qty input ── #} {# ── Bonus input ── #} {# ── Remove checkbox ── #} {# ── Status / detail column ── #} {% endfor %}
# Product Avail Qty Qty BonusRemove Status / Detail
{{ forloop.counter }}
{{ item.product_name }}
Code: {{ item.product_code }} • Stock: {{ item.legacy_stock|floatformat:2 }}
{% if item.removed %} Removed {% endif %}
{% if zero_stock_allow %} — {% else %} {{ item.plan.qty_usable_total|floatformat:2 }}
{% with pct=item.plan.qty_usable_total|default:0 %}
{% endwith %}
{% endif %}
{% if item.qty_short > 0 %}
Short: {{ item.qty_short|floatformat:3 }}
{% endif %}
pcs (1 pack = {{ item.p_per_pack }})
{% if item.bonus_short_pcs > 0 %}
Short: {{ item.bonus_short_pcs|floatformat:3 }} pcs
{% endif %}
{% if item.removed %} {% elif zero_stock_allow %} {# In simple mode, user can still remove a line if they want #} {% elif item.can_remove or item.has_error %} {% if item.has_error %}
Remove to approve
{% endif %} {% else %} — {% endif %}
{# Status chip #} {% if item.removed %} Removed {% elif zero_stock_allow %} Simple ✓ {% elif item.has_error %} Blocked {% elif item.plan.skipped_soon %} Expiry Warn {% else %} OK {% endif %} {# Errors list #} {% if item.plan.errors and not item.removed %}
{% for e in item.plan.errors %}
• {{ e }}
{% endfor %}
{% endif %} {# Qty allocation layers (batch mode) #} {% if check_batch and not zero_stock_allow and item.plan.qty_allocation and not item.removed %}
Qty Layers
{% for layer in item.plan.qty_allocation %}
{{ layer.sku_code }} → {{ layer.take|floatformat:2 }}/{{ layer.available|floatformat:2 }} {% if layer.batch %}[{{ layer.batch }}]{% endif %} {% if layer.expiry %}exp:{{ layer.expiry }}{% endif %}
{% endfor %}
{% endif %} {# Bonus allocation layers (batch mode) #} {% if check_batch and not zero_stock_allow and item.plan.bonus_allocation and not item.removed %}
Bonus Layers
{% for layer in item.plan.bonus_allocation %}
{{ layer.sku_code }} → {{ layer.take|floatformat:2 }}/{{ layer.available|floatformat:2 }} {% if layer.expiry %}exp:{{ layer.expiry }}{% endif %}
{% endfor %}
{% endif %} {# Skipped soon-expiry layers (only when expiry_warn_days active) #} {% if expiry_warn_days > 0 and item.plan.skipped_soon and not item.removed %}
Skipped (Expiring ≤ {{ expiry_warn_days }}d)
{% for layer in item.plan.skipped_soon %}
{{ layer.sku_code }} {% if layer.expiry %}(exp {{ layer.expiry }}){% endif %} — {{ layer.reason }}
{% endfor %}
{% endif %} {# Expired layers #} {% if item.plan.skipped_expired and not item.removed %}
Skipped (Expired)
{% for layer in item.plan.skipped_expired %}
{{ layer.sku_code }} {% if layer.expiry %}(exp {{ layer.expiry }}){% endif %}
{% endfor %}
{% endif %} {# stock_only mode: just show total #} {% if not check_batch and not zero_stock_allow and not item.removed %}
Total available qty: {{ item.plan.qty_usable_total|floatformat:2 }} {% if item.bonus > 0 %} | bonus: {{ item.plan.bonus_usable_total_pcs|floatformat:2 }} {% endif %}
{% endif %}
{# ── Bank Payments ── #} {% if bank_payments %}
Bank Payments
{% for bp in bank_payments %} {% endfor %}
BankAmount
{{ bp.bank_name }} {{ bp.amount_transaction|floatformat:2 }}
{% endif %} {# ── Action buttons ── #}
Cancel
{% endblock %}