Add Phase 3 Tally accounting master synchronization

This commit is contained in:
A R R R Associates
2026-08-19 16:11:03 +05:30
parent 2f061d29b4
commit 93ae6caa54
11 changed files with 586 additions and 337 deletions
@@ -1,11 +1,12 @@
{% extends "ui/templates/base/layout.html" %}
{% block content %}
{% if synced %}<div class="mb-4 rounded-xl border border-emerald-200 bg-emerald-50 px-4 py-3 text-sm text-emerald-800">Tally accounting masters synchronized successfully.</div>{% endif %}
<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</p>
<h1 class="mt-1 text-2xl font-semibold text-slate-900">Tally Company Mapping</h1>
<p class="mt-1 text-sm text-slate-500">Phase 2 permanently maps an ERP client or registration to a loaded TallyPrime company using the Tally GUID.</p>
<h1 class="mt-1 text-2xl font-semibold text-slate-900">Tally Accounting Masters</h1>
<p class="mt-1 text-sm text-slate-500">Phase 3 permanently maps an ERP client or registration to a loaded TallyPrime company using the Tally GUID.</p>
</div>
<a href="/tools/tally{% if selected_client %}?client_id={{ selected_client.id }}&refresh=1{% else %}?refresh=1{% endif %}" class="rounded-xl bg-brand-600 px-4 py-2 text-sm font-semibold text-white shadow-soft hover:bg-brand-700">Refresh Tally Companies</a>
</div>
@@ -122,7 +123,7 @@
</label>
<label class="text-sm font-medium text-slate-700">
Loaded Tally Company
Currently Open in TallyPrime
<select name="tally_guid" required class="mt-1 w-full rounded-xl border border-slate-300 bg-white px-3 py-2 text-sm">
<option value="">Select Tally company</option>
{% for company in tally.companies %}
@@ -138,7 +139,7 @@
<input type="checkbox" name="allow_gstin_mismatch" value="true" class="mt-1 rounded border-slate-300" />
<span class="text-sm text-slate-700">
Allow GSTIN mismatch after manual verification.
<span class="block text-xs text-slate-500">For a GSTIN registration, Phase 2 blocks a mapping when both ERP and Tally contain different GSTINs unless this override is explicitly selected.</span>
<span class="block text-xs text-slate-500">For a GSTIN registration, Phase 3 blocks a mapping when both ERP and Tally contain different GSTINs unless this override is explicitly selected.</span>
</span>
</label>
@@ -149,6 +150,47 @@
</section>
{% endif %}
{% if accounting and accounting.exists and tally and tally.connected %}
<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">Phase 3 · Accounting Master Sync</h2>
<p class="mt-1 text-sm text-slate-500">Select the company that is currently open/loaded in TallyPrime. The Local Agent verifies that the selected company is already mapped to this ERP client before reading masters.</p>
</div>
<span class="rounded-full bg-blue-50 px-3 py-1 text-xs font-semibold text-blue-700">Read-only</span>
</div>
<form method="post" action="/tools/tally/sync-masters" class="mt-5 grid gap-4 lg:grid-cols-[1fr_auto] lg:items-end">
<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">
Currently Open in TallyPrime
<select name="tally_guid" required class="mt-1 w-full rounded-xl border border-slate-300 bg-white px-3 py-2 text-sm">
<option value="">Select open Tally company</option>
{% for company in tally.companies %}
{% if company.guid %}
<option value="{{ company.guid }}">{{ company.name }}{% if company.gstin %} · {{ company.gstin }}{% endif %}</option>
{% endif %}
{% endfor %}
</select>
<span class="mt-1 block text-xs font-normal text-slate-500">This dropdown is populated live from the companies currently loaded/open in the local TallyPrime instance.</span>
</label>
<button type="submit" class="rounded-xl bg-brand-600 px-4 py-2 text-sm font-semibold text-white hover:bg-brand-700">Sync Accounting Masters</button>
</form>
<div class="mt-5 grid gap-3 sm:grid-cols-2 lg:grid-cols-4">
{% set last_sync = accounting.latest_master_sync if accounting else none %}
<div class="rounded-xl border border-slate-200 p-3"><div class="text-xs font-semibold uppercase tracking-wide text-slate-500">Latest Sync</div><div class="mt-1 text-sm font-medium text-slate-900">{{ last_sync.completed_at_utc if last_sync and last_sync.completed_at_utc else 'Not synced yet' }}</div></div>
<div class="rounded-xl border border-slate-200 p-3"><div class="text-xs font-semibold uppercase tracking-wide text-slate-500">Company</div><div class="mt-1 text-sm font-medium text-slate-900">{{ last_sync.company_name if last_sync else '-' }}</div></div>
<div class="rounded-xl border border-slate-200 p-3"><div class="text-xs font-semibold uppercase tracking-wide text-slate-500">Rows Stored</div><div class="mt-1 text-sm font-medium text-slate-900">{{ last_sync.rows_processed if last_sync else 0 }}</div></div>
<div class="rounded-xl border border-slate-200 p-3"><div class="text-xs font-semibold uppercase tracking-wide text-slate-500">Status</div><div class="mt-1 text-sm font-medium text-slate-900">{{ last_sync.status|title if last_sync else '-' }}</div></div>
</div>
<div class="mt-4 rounded-xl bg-slate-50 px-4 py-3 text-xs text-slate-600">
Synced into the selected client's .act database: Groups, Ledgers, Voucher Types, Stock Groups, Stock Categories, Stock Items, Units, Cost Categories and Cost Centres. No voucher or transaction data is imported in Phase 3.
</div>
</section>
{% if accounting and accounting.exists %}
<section class="overflow-hidden rounded-2xl bg-white shadow-soft">
<div class="border-b border-slate-100 px-5 py-4">
@@ -236,7 +278,7 @@
{% endif %}
<div class="rounded-xl border border-blue-200 bg-blue-50 px-4 py-3 text-sm text-blue-800">
Phase 2 only stores company mappings. Ledger masters, vouchers, inventory and Tally write-back remain disabled.
Phase 3 synchronizes accounting masters only. Voucher/transaction sync and Tally write-back remain disabled and are reserved for later phases.
</div>
</div>
{% endblock %}