Files
arrr-erp/app/modules/accounting/templates/accounting/historical_learning.html
T
2026-08-22 13:01:17 +05:30

60 lines
9.7 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 · Phase 5</p><h1 class="mt-1 text-2xl font-semibold text-slate-900">Historical Tally Learning</h1><p class="mt-1 text-sm text-slate-500">Uses synced Tally purchase history as evidence. No voucher is created or altered in this phase.</p></div>
<div class="flex gap-2"><a href="/tools/accounting/taxonomy" class="rounded-xl border border-slate-300 bg-white px-4 py-2 text-sm font-semibold text-slate-700">Accounting Taxonomy</a><a href="/tools/tally{% 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">Back to Tally</a></div>
</div>
{% if collected %}<div class="rounded-xl border border-blue-200 bg-blue-50 px-4 py-3 text-sm text-blue-800">Historical evidence job queued. This page will ingest the result after the selected workstation completes it.</div>{% endif %}
{% if mapped %}<div class="rounded-xl border border-emerald-200 bg-emerald-50 px-4 py-3 text-sm text-emerald-800">Ledger classification mapping saved.</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" class="flex flex-wrap items-end gap-3">
<label class="min-w-[300px] flex-1 text-sm font-medium text-slate-700">Client<select name="client_id" class="mt-1 w-full rounded-xl border border-slate-300 px-3 py-2"><option value="">Select client</option>{% for c in clients %}<option value="{{ c.id }}" {% if selected_client and c.id==selected_client.id %}selected{% endif %}>{{ c.client_name }}</option>{% endfor %}</select></label>
<button class="rounded-xl bg-brand-600 px-4 py-2 text-sm font-semibold text-white">Open</button>
</form>
</section>
{% if selected_client %}
<section class="rounded-2xl bg-white p-5 shadow-soft">
<h2 class="text-lg font-semibold text-slate-900">Collect Historical Purchase Evidence</h2>
<p class="mt-1 text-sm text-slate-500">Select the workstation that holds this client's synced .act data. The agent returns neutral ledger-history facts; classification stays on the ERP server.</p>
<form method="post" action="/tools/accounting/historical-learning/collect" class="mt-4 grid gap-3 md:grid-cols-2 xl:grid-cols-5">
<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 xl:col-span-2">Workstation / Tally Company<select id="wsCompany" class="mt-1 w-full rounded-xl border border-slate-300 px-3 py-2" required><option value="">Select</option>{% for w in workstations %}{% for company in workstation_companies.get(w.id,[]) %}<option value="{{ w.id }}|{{ company.get('guid','') }}|{{ company.get('name','') }}">{{ w.machine_name }} · {{ company.get('name','') }}{% if not w.tally_connected %} (Tally not connected){% endif %}</option>{% endfor %}{% endfor %}</select></label>
<input type="hidden" name="workstation_id" id="workstationId"><input type="hidden" name="tally_guid" id="tallyGuid"><input type="hidden" name="company_name" id="companyName">
<label class="text-sm font-medium text-slate-700">From<input type="date" name="date_from" value="{{ date_from }}" class="mt-1 w-full rounded-xl border border-slate-300 px-3 py-2"></label>
<label class="text-sm font-medium text-slate-700">To<input type="date" name="date_to" value="{{ date_to }}" class="mt-1 w-full rounded-xl border border-slate-300 px-3 py-2"></label>
<div class="flex items-end"><button class="w-full rounded-xl bg-brand-600 px-4 py-2 text-sm font-semibold text-white">Collect Evidence</button></div>
</form>
</section>
<section class="rounded-2xl bg-white p-5 shadow-soft">
<h2 class="text-lg font-semibold text-slate-900">Recent Learning Runs</h2>
<div class="mt-3 overflow-x-auto"><table class="min-w-full text-sm"><thead class="text-left text-xs uppercase text-slate-500"><tr><th class="px-2 py-2">Created</th><th class="px-2 py-2">Company</th><th class="px-2 py-2">Status</th><th class="px-2 py-2">Rows</th><th class="px-2 py-2">Error</th></tr></thead><tbody>{% for r in runs %}<tr class="border-t"><td class="px-2 py-2">{{ r.created_at_utc }}</td><td class="px-2 py-2">{{ r.company_name or r.tally_guid }}</td><td class="px-2 py-2 font-medium">{{ r.status }}</td><td class="px-2 py-2">{{ r.evidence_rows }}</td><td class="px-2 py-2 text-red-700">{{ r.error_message or '' }}</td></tr>{% else %}<tr><td colspan="5" class="px-2 py-4 text-slate-500">No historical learning run yet.</td></tr>{% endfor %}</tbody></table></div>
</section>
<section class="rounded-2xl bg-white p-5 shadow-soft">
<h2 class="text-lg font-semibold text-slate-900">Classify Historical Tally Ledgers</h2><p class="mt-1 text-sm text-slate-500">Map the client's actual Tally ledger names to the canonical Phase 4 accounting taxonomy. This mapping is client and Tally-company specific.</p>
<div class="mt-3 overflow-x-auto"><table class="min-w-full text-sm"><thead class="text-left text-xs uppercase text-slate-500"><tr><th class="px-2 py-2">Ledger</th><th class="px-2 py-2">Parent</th><th class="px-2 py-2">Historical vouchers</th><th class="px-2 py-2">Amount evidence</th><th class="px-2 py-2">Accounting nature</th></tr></thead><tbody>
{% for row in classifiable_ledgers %}<tr class="border-t align-top"><td class="px-2 py-3 font-medium text-slate-900">{{ row.ledger_name }}</td><td class="px-2 py-3 text-slate-600">{{ row.parent_group }}</td><td class="px-2 py-3">{{ row.voucher_count }}</td><td class="px-2 py-3">{{ '%.2f'|format(row.amount) }}</td><td class="px-2 py-3"><form method="post" action="/tools/accounting/historical-learning/map-ledger" class="flex min-w-[360px] gap-2"><input type="hidden" name="csrf_token" value="{{ csrf_token }}"><input type="hidden" name="client_id" value="{{ selected_client.id }}"><input type="hidden" name="tally_guid" value="{{ row.tally_guid }}"><input type="hidden" name="company_name" value="{{ row.company_name }}"><input type="hidden" name="ledger_name" value="{{ row.ledger_name }}"><input type="hidden" name="parent_group" value="{{ row.parent_group }}"><select name="nature_id" class="min-w-[240px] rounded-lg border border-slate-300 px-2 py-1" required><option value="">Select nature</option>{% for n in natures %}<option value="{{ n.id }}" {% if row.mapping and row.mapping.nature_id==n.id %}selected{% endif %}>{{ n.name }}</option>{% endfor %}</select><button class="rounded-lg bg-slate-900 px-3 py-1 text-xs font-semibold text-white">Save</button></form></td></tr>{% else %}<tr><td colspan="5" class="px-2 py-4 text-slate-500">Collect historical evidence first.</td></tr>{% endfor %}
</tbody></table></div>
</section>
<section class="rounded-2xl bg-white p-5 shadow-soft">
<h2 class="text-lg font-semibold text-slate-900">Supplier / Party Historical Suggestions</h2><p class="mt-1 text-sm text-slate-500">Confidence is the share of mapped historical purchase vouchers for that party that resolve to the leading accounting nature.</p>
<div class="mt-3 overflow-x-auto"><table class="min-w-full text-sm"><thead class="text-left text-xs uppercase text-slate-500"><tr><th class="px-2 py-2">Party</th><th class="px-2 py-2">Suggested nature</th><th class="px-2 py-2">Confidence</th><th class="px-2 py-2">Evidence</th><th class="px-2 py-2">Ledgers</th></tr></thead><tbody>{% for s in suggestions %}<tr class="border-t"><td class="px-2 py-3 font-medium">{{ s.party_ledger_name }}</td><td class="px-2 py-3">{{ s.nature.name }}</td><td class="px-2 py-3"><span class="rounded-full px-2 py-1 text-xs font-semibold {% if s.confidence>=90 %}bg-emerald-100 text-emerald-800{% elif s.confidence>=70 %}bg-amber-100 text-amber-800{% else %}bg-slate-100 text-slate-700{% endif %}">{{ s.confidence }}%</span></td><td class="px-2 py-3">{{ s.voucher_count }} / {{ s.mapped_voucher_count }} mapped vouchers</td><td class="px-2 py-3 text-slate-600">{{ s.ledgers|join(', ') }}</td></tr>{% else %}<tr><td colspan="5" class="px-2 py-4 text-slate-500">Map historical Tally ledgers to accounting natures to generate party suggestions.</td></tr>{% endfor %}</tbody></table></div>
</section>
<section class="rounded-2xl bg-white p-5 shadow-soft">
<h2 class="text-lg font-semibold text-slate-900">Raw Historical Evidence</h2>
<div class="mt-3 overflow-x-auto"><table class="min-w-full text-xs"><thead class="text-left uppercase text-slate-500"><tr><th class="px-2 py-2">Party</th><th class="px-2 py-2">Counter ledger</th><th class="px-2 py-2">Group</th><th class="px-2 py-2">Vouchers</th><th class="px-2 py-2">First</th><th class="px-2 py-2">Last</th></tr></thead><tbody>{% for e in evidence[:300] %}<tr class="border-t"><td class="px-2 py-2">{{ e.party_ledger_name }}</td><td class="px-2 py-2">{{ e.counter_ledger_name }}</td><td class="px-2 py-2">{{ e.counter_parent_group }}</td><td class="px-2 py-2">{{ e.voucher_count }}</td><td class="px-2 py-2">{{ e.first_voucher_date or '' }}</td><td class="px-2 py-2">{{ e.last_voucher_date or '' }}</td></tr>{% else %}<tr><td colspan="6" class="px-2 py-4 text-slate-500">No evidence collected.</td></tr>{% endfor %}</tbody></table></div>
</section>
{% endif %}
</div>
<script>
const s=document.getElementById('wsCompany'); if(s){s.addEventListener('change',()=>{const p=s.value.split('|');document.getElementById('workstationId').value=p[0]||'';document.getElementById('tallyGuid').value=p[1]||'';document.getElementById('companyName').value=p.slice(2).join('|')||'';});}
</script>
{% endblock %}