Files
arrr-erp/app/modules/accounting/templates/accounting/purchase_enrichment.html
T
2026-08-22 15:30:46 +05:30

196 lines
14 KiB
HTML

{% extends "ui/templates/base/layout.html" %}
{% block content %}
<div class="space-y-6">
<div class="flex flex-wrap items-start justify-between gap-4">
<div>
<p class="text-xs font-semibold uppercase tracking-[0.16em] text-brand-600">Tools · Accounting Intelligence</p>
<h1 class="mt-1 text-2xl font-semibold text-slate-900">E-Invoice / E-Way Bill Enrichment</h1>
<p class="mt-1 max-w-4xl text-sm text-slate-500">Phase 8 links E-Invoice and E-Way Bill source documents to existing GSTR-2B purchases and enriches classification with item description, HSN, quantity and logistics context. Original source fields remain unchanged and nothing is posted to Tally.</p>
</div>
<div class="flex flex-wrap gap-2">
<a href="/tools/accounting/gstr2b{% if selected_client %}?client_id={{ selected_client.id }}{% endif %}" class="rounded-xl border border-slate-300 bg-white px-4 py-2 text-sm font-semibold text-slate-700 hover:bg-slate-50">GSTR-2B Intelligence</a>
<a href="/tools/accounting/ledger-learning{% if selected_client %}?client_id={{ selected_client.id }}{% endif %}" class="rounded-xl border border-slate-300 bg-white px-4 py-2 text-sm font-semibold text-slate-700 hover:bg-slate-50">Ledger Learning</a>
<a href="/tools/tally{% if selected_client %}?client_id={{ selected_client.id }}{% endif %}" class="rounded-xl bg-brand-600 px-4 py-2 text-sm font-semibold text-white hover:bg-brand-700">Back to Tally</a>
</div>
</div>
{% if message %}<div class="rounded-xl border border-emerald-200 bg-emerald-50 px-4 py-3 text-sm font-medium text-emerald-800">{{ message }}</div>{% endif %}
{% if error %}<div class="rounded-xl border border-red-200 bg-red-50 px-4 py-3 text-sm text-red-800">{{ error }}</div>{% endif %}
<section class="rounded-2xl bg-white p-5 shadow-soft">
<form method="get" action="/tools/accounting/purchase-enrichment" class="grid gap-4 lg:grid-cols-5">
<label class="text-sm font-medium text-slate-700 lg:col-span-2">
ERP Client
<select name="client_id" required class="mt-1 w-full rounded-xl border border-slate-300 bg-white px-3 py-2 text-sm">
<option value="">Select client</option>
{% for client in clients %}
<option value="{{ client.id }}" {% if selected_client and selected_client.id == client.id %}selected{% endif %}>{{ client.client_name }}</option>
{% endfor %}
</select>
</label>
{% if selected_client %}
<label class="text-sm font-medium text-slate-700 lg:col-span-2">
Enrichment Batch
<select name="batch_id" class="mt-1 w-full rounded-xl border border-slate-300 bg-white px-3 py-2 text-sm">
<option value="">Latest batch</option>
{% for batch in batches %}
<option value="{{ batch.id }}" {% if selected_batch and selected_batch.id == batch.id %}selected{% endif %}>#{{ batch.id }} · {{ batch.source_type|replace('_',' ')|title }} · {{ batch.original_filename }}</option>
{% endfor %}
</select>
</label>
{% endif %}
<div class="flex items-end"><button class="w-full rounded-xl bg-brand-600 px-4 py-2 text-sm font-semibold text-white hover:bg-brand-700">Load</button></div>
</form>
</section>
{% if selected_client %}
<section class="rounded-2xl bg-white p-5 shadow-soft">
<div class="flex flex-wrap items-start justify-between gap-4">
<div>
<h2 class="font-semibold text-slate-900">Import enrichment source</h2>
<p class="mt-1 text-sm text-slate-500">Supported: JSON, XLSX/XLSM and CSV. JSON supports common GST E-Invoice and E-Way Bill field structures, including item lists.</p>
</div>
<span class="rounded-full bg-amber-50 px-3 py-1 text-xs font-semibold text-amber-700">Enrichment only · No voucher posting</span>
</div>
<form method="post" action="/tools/accounting/purchase-enrichment/upload" enctype="multipart/form-data" class="mt-4 grid gap-4 lg:grid-cols-6">
<input type="hidden" name="csrf_token" value="{{ csrf_token }}" />
<input type="hidden" name="client_id" value="{{ selected_client.id }}" />
<label class="text-sm font-medium text-slate-700">
Source
<select name="source_type" required class="mt-1 w-full rounded-xl border border-slate-300 bg-white px-3 py-2 text-sm">
<option value="e_invoice">E-Invoice</option>
<option value="e_way_bill">E-Way Bill</option>
</select>
</label>
<label class="text-sm font-medium text-slate-700">
Period
<input name="source_period" maxlength="20" class="mt-1 w-full rounded-xl border border-slate-300 px-3 py-2 text-sm" placeholder="e.g. 072026" />
</label>
<label class="text-sm font-medium text-slate-700 lg:col-span-2">
Tally Company Context
<select name="tally_guid" class="mt-1 w-full rounded-xl border border-slate-300 bg-white px-3 py-2 text-sm">
<option value="">No specific Tally company</option>
{% for guid, company in tally_companies %}
<option value="{{ guid }}">{{ company or guid }} · {{ guid }}</option>
{% endfor %}
</select>
</label>
<label class="text-sm font-medium text-slate-700">
File
<input type="file" name="upload" accept=".json,.xlsx,.xlsm,.csv,.txt" required class="mt-1 block w-full text-sm text-slate-700" />
</label>
<div class="flex items-end"><button class="w-full rounded-xl bg-slate-900 px-4 py-2 text-sm font-semibold text-white hover:bg-slate-800">Import & Match</button></div>
</form>
</section>
{% if selected_batch %}
<section class="grid gap-4 md:grid-cols-3 xl:grid-cols-6">
<div class="rounded-2xl bg-white p-4 shadow-soft"><div class="text-xs font-semibold uppercase text-slate-500">Source</div><div class="mt-2 font-semibold">{{ selected_batch.source_type|replace('_',' ')|title }}</div></div>
<div class="rounded-2xl bg-white p-4 shadow-soft"><div class="text-xs font-semibold uppercase text-slate-500">Imported</div><div class="mt-2 text-xl font-semibold">{{ selected_batch.records_imported }}</div></div>
<div class="rounded-2xl bg-white p-4 shadow-soft"><div class="text-xs font-semibold uppercase text-slate-500">Linked</div><div class="mt-2 text-xl font-semibold text-emerald-700">{{ selected_batch.records_linked }}</div></div>
<div class="rounded-2xl bg-white p-4 shadow-soft"><div class="text-xs font-semibold uppercase text-slate-500">Unmatched</div><div class="mt-2 text-xl font-semibold text-amber-700">{{ selected_batch.records_unmatched }}</div></div>
<div class="rounded-2xl bg-white p-4 shadow-soft"><div class="text-xs font-semibold uppercase text-slate-500">Ambiguous</div><div class="mt-2 text-xl font-semibold text-red-700">{{ selected_batch.records_ambiguous }}</div></div>
<div class="rounded-2xl bg-white p-4 shadow-soft"><div class="text-xs font-semibold uppercase text-slate-500">Duplicates</div><div class="mt-2 text-xl font-semibold">{{ selected_batch.records_duplicate }}</div></div>
</section>
<section class="rounded-2xl bg-white p-5 shadow-soft">
<div class="flex flex-wrap items-center justify-between gap-3">
<div>
<h2 class="font-semibold text-slate-900">{{ selected_batch.original_filename }}</h2>
<p class="mt-1 text-sm text-slate-500">Automatic matching uses supplier GSTIN + invoice number/date first, with a conservative value/date fallback.</p>
</div>
<form method="post" action="/tools/accounting/purchase-enrichment/batch/{{ selected_batch.id }}/rematch">
<input type="hidden" name="csrf_token" value="{{ csrf_token }}" />
<input type="hidden" name="client_id" value="{{ selected_client.id }}" />
<button class="rounded-xl bg-brand-600 px-4 py-2 text-sm font-semibold text-white hover:bg-brand-700">Re-match Batch</button>
</form>
</div>
</section>
<section class="space-y-4">
{% for record in records %}
{% set linked = gstr2b_by_id.get(record.gstr2b_purchase_id) if record.gstr2b_purchase_id else None %}
<article class="rounded-2xl bg-white p-5 shadow-soft">
<div class="grid gap-5 xl:grid-cols-12">
<div class="xl:col-span-4">
<div class="flex flex-wrap items-center gap-2">
<h3 class="font-semibold text-slate-900">{{ record.supplier_name or record.supplier_gstin or 'Supplier' }}</h3>
<span class="rounded-full bg-slate-100 px-2 py-1 text-xs font-semibold text-slate-600">{{ record.source_type|replace('_',' ')|title }}</span>
<span class="rounded-full px-2 py-1 text-xs font-semibold {% if record.match_status == 'linked' %}bg-emerald-50 text-emerald-700{% elif record.match_status == 'ambiguous' %}bg-red-50 text-red-700{% else %}bg-amber-50 text-amber-700{% endif %}">{{ record.match_status|title }}</span>
</div>
<div class="mt-2 space-y-1 text-sm text-slate-600">
<div>GSTIN: <span class="font-medium text-slate-900">{{ record.supplier_gstin or '-' }}</span></div>
<div>Document: <span class="font-medium text-slate-900">{{ record.document_number }}</span> · {{ record.document_date }}</div>
<div>Reference: {{ record.external_reference or '-' }}</div>
{% if record.vehicle_number %}<div>Vehicle: {{ record.vehicle_number }}{% if record.transport_mode %} · {{ record.transport_mode }}{% endif %}</div>{% endif %}
</div>
</div>
<div class="xl:col-span-3">
<div class="grid grid-cols-2 gap-2 text-sm">
<div class="rounded-xl bg-slate-50 p-2"><div class="text-xs text-slate-500">Taxable</div><div class="font-semibold">₹{{ '%.2f'|format(record.taxable_value) }}</div></div>
<div class="rounded-xl bg-slate-50 p-2"><div class="text-xs text-slate-500">Invoice Value</div><div class="font-semibold">₹{{ '%.2f'|format(record.invoice_value) }}</div></div>
<div class="rounded-xl bg-slate-50 p-2"><div class="text-xs text-slate-500">IGST</div><div class="font-semibold">₹{{ '%.2f'|format(record.igst) }}</div></div>
<div class="rounded-xl bg-slate-50 p-2"><div class="text-xs text-slate-500">CGST + SGST</div><div class="font-semibold">₹{{ '%.2f'|format(record.cgst + record.sgst) }}</div></div>
</div>
</div>
<div class="xl:col-span-5">
{% if linked %}
<div class="rounded-xl border border-emerald-200 bg-emerald-50 p-3">
<div class="text-xs font-semibold uppercase text-emerald-700">Linked GSTR-2B purchase · {{ record.match_confidence }}%</div>
<div class="mt-1 font-semibold text-slate-900">{{ linked.invoice_number }} · {{ linked.invoice_date }}</div>
<div class="mt-1 text-sm text-slate-700">₹{{ '%.2f'|format(linked.taxable_value) }} taxable · {{ record.match_method|replace('_',' ') }}</div>
</div>
<form method="post" action="/tools/accounting/purchase-enrichment/record/{{ record.id }}/unlink" class="mt-2">
<input type="hidden" name="csrf_token" value="{{ csrf_token }}" />
<input type="hidden" name="client_id" value="{{ selected_client.id }}" />
<button class="text-xs font-semibold text-red-600 hover:text-red-700">Unlink source</button>
</form>
{% else %}
<form method="post" action="/tools/accounting/purchase-enrichment/record/{{ record.id }}/link" class="rounded-xl border border-slate-200 p-3">
<input type="hidden" name="csrf_token" value="{{ csrf_token }}" />
<input type="hidden" name="client_id" value="{{ selected_client.id }}" />
<label class="text-xs font-semibold text-slate-600">
Manual GSTR-2B link
<select name="purchase_id" required class="mt-1 w-full rounded-xl border border-slate-300 bg-white px-2 py-2 text-sm">
<option value="">Select invoice</option>
{% for row in gstr2b_rows %}
<option value="{{ row.id }}">{{ row.supplier_name or row.supplier_gstin }} · {{ row.invoice_number }} · {{ row.invoice_date }} · ₹{{ '%.2f'|format(row.taxable_value) }}</option>
{% endfor %}
</select>
</label>
<button class="mt-2 rounded-lg bg-slate-900 px-3 py-2 text-xs font-semibold text-white">Link & Re-analyze</button>
</form>
{% endif %}
</div>
</div>
{% if items_by_record.get(record.id) %}
<div class="mt-4 overflow-x-auto rounded-xl border border-slate-200">
<table class="min-w-full text-sm">
<thead><tr class="bg-slate-50 text-left text-xs uppercase tracking-wide text-slate-500"><th class="px-3 py-2">Item</th><th class="px-3 py-2">HSN</th><th class="px-3 py-2 text-right">Qty</th><th class="px-3 py-2">Unit</th><th class="px-3 py-2 text-right">Taxable</th></tr></thead>
<tbody>
{% for item in items_by_record.get(record.id) %}
<tr class="border-t border-slate-100">
<td class="px-3 py-2"><div class="font-medium text-slate-900">{{ item.product_name or item.description_text or '-' }}</div>{% if item.description_text and item.description_text != item.product_name %}<div class="text-xs text-slate-500">{{ item.description_text }}</div>{% endif %}</td>
<td class="px-3 py-2">{{ item.hsn_code or '-' }}</td>
<td class="px-3 py-2 text-right">{{ item.quantity }}</td>
<td class="px-3 py-2">{{ item.unit or '-' }}</td>
<td class="px-3 py-2 text-right">₹{{ '%.2f'|format(item.taxable_value) }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% endif %}
</article>
{% else %}
<div class="rounded-2xl bg-white p-8 text-center text-sm text-slate-500 shadow-soft">No enrichment records in this batch.</div>
{% endfor %}
</section>
{% endif %}
{% endif %}
</div>
{% endblock %}