Files
arrr-erp/app/modules/accounting/templates/accounting/tally.html
T
2026-08-22 22:12:23 +05:30

394 lines
27 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 "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 %}
{% if transactions_synced %}
<div class="rounded-xl border border-emerald-200 bg-emerald-50 px-4 py-3 text-sm text-emerald-800">Tally accounting transactions 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 Accounting</h1>
<p class="mt-1 text-sm text-slate-500">Company mapping, master sync and transaction sync remain available. Accounting tools are now grouped into Purchases & Expenses and Sales & Income without changing existing routes or controls.</p>
</div>
<div class="w-full space-y-3 xl:max-w-5xl">
<div class="rounded-2xl border border-slate-200 bg-white p-4">
<div class="mb-3 flex items-center justify-between gap-3">
<div>
<div class="text-xs font-semibold uppercase tracking-[0.14em] text-amber-700">Purchases & Expenses</div>
<div class="text-xs text-slate-500">Existing Phase 3–13 tools are grouped here; their URLs and behavior are unchanged.</div>
</div>
</div>
<div class="flex flex-wrap gap-2">
<a href="/tools/accounting/taxonomy" class="rounded-xl border border-slate-300 bg-white px-3 py-2 text-sm font-semibold text-slate-700 hover:bg-slate-50">Accounting Taxonomy</a>
{% if selected_client %}<a href="/tools/accounting/historical-learning?client_id={{ selected_client.id }}" class="rounded-xl border border-slate-300 bg-white px-3 py-2 text-sm font-semibold">Historical Learning</a>{% endif %}
{% if selected_client %}<a href="/tools/accounting/ledger-learning?client_id={{ selected_client.id }}" class="rounded-xl border border-slate-300 bg-white px-3 py-2 text-sm font-semibold">Ledger Learning</a>{% endif %}
{% if selected_client %}<a href="/tools/accounting/gstr2b?client_id={{ selected_client.id }}" class="rounded-xl border border-slate-300 bg-white px-3 py-2 text-sm font-semibold">GSTR-2B Intelligence</a>{% endif %}
{% if selected_client %}<a href="/tools/accounting/purchase-enrichment?client_id={{ selected_client.id }}" class="rounded-xl border border-slate-300 bg-white px-3 py-2 text-sm font-semibold">E-Invoice / E-Way Bill</a>{% endif %}
{% if selected_client %}<a href="/tools/accounting/purchase-review?client_id={{ selected_client.id }}" class="rounded-xl border border-slate-300 bg-white px-3 py-2 text-sm font-semibold">Purchase Review</a>{% endif %}
{% if selected_client %}<a href="/tools/accounting/purchase-posting?client_id={{ selected_client.id }}" class="rounded-xl border border-slate-300 bg-white px-3 py-2 text-sm font-semibold">Purchase → Tally</a>{% endif %}
{% if selected_client %}<a href="/tools/accounting/bank-posting?client_id={{ selected_client.id }}" class="rounded-xl border border-slate-300 bg-white px-3 py-2 text-sm font-semibold">Bank Expenses / Payments</a>{% endif %}
{% if selected_client %}<a href="/tools/tally/depreciation?client_id={{ selected_client.id }}" class="rounded-xl border border-slate-300 bg-white px-3 py-2 text-sm font-semibold">Depreciation (IT)</a>{% endif %}
<a href="/tools/accounting/ai{% if selected_client %}?client_id={{ selected_client.id }}{% endif %}" class="rounded-xl border border-indigo-300 bg-indigo-50 px-3 py-2 text-sm font-semibold text-indigo-800">Accounting AI</a>
<a href="/tools/accounting/internal-model" class="rounded-xl border border-emerald-300 bg-emerald-50 px-3 py-2 text-sm font-semibold text-emerald-800">Internal Model</a>
</div>
</div>
<div class="rounded-2xl border border-emerald-200 bg-emerald-50/40 p-4">
<div class="mb-3">
<div class="text-xs font-semibold uppercase tracking-[0.14em] text-emerald-700">Sales & Income</div>
<div class="text-xs text-slate-500">Phase 14 starts the outward-sales/customer/income workflow. Existing accounting infrastructure is reused.</div>
</div>
<div class="flex flex-wrap gap-2">
{% if selected_client %}<a href="/tools/accounting/sales-income?client_id={{ selected_client.id }}" class="rounded-xl bg-emerald-700 px-3 py-2 text-sm font-semibold text-white">Sales & Income Foundation</a>{% else %}<a href="/tools/accounting/sales-income" class="rounded-xl bg-emerald-700 px-3 py-2 text-sm font-semibold text-white">Sales & Income Foundation</a>{% endif %}
</div>
</div>
<div class="flex flex-wrap justify-end gap-2">
<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>
</div>
</div>
{% if initialized %}
<div class="rounded-xl border border-emerald-200 bg-emerald-50 px-4 py-3 text-sm font-medium text-emerald-800">Client accounting storage was initialized successfully.</div>
{% endif %}
{% if mapped %}
<div class="rounded-xl border border-emerald-200 bg-emerald-50 px-4 py-3 text-sm font-medium text-emerald-800">Tally company mapping was saved successfully.</div>
{% endif %}
{% if unmapped %}
<div class="rounded-xl border border-slate-200 bg-slate-50 px-4 py-3 text-sm font-medium text-slate-700">Tally company mapping was removed.</div>
{% endif %}
{% if command_error %}
<div class="rounded-xl border border-red-200 bg-red-50 px-4 py-3 text-sm text-red-800">{{ command_error }}</div>
{% endif %}
{% set agent = live_result.agent if live_result else None %}
{% set tally = live_result.tally if live_result else None %}
{% set accounting = live_result.accounting if live_result else None %}
<section class="grid gap-4 md:grid-cols-2 xl:grid-cols-4">
<div class="rounded-2xl bg-white p-5 shadow-soft">
<div class="text-xs font-semibold uppercase tracking-wide text-slate-500">ERP Local Agent</div>
<div class="mt-2 text-lg font-semibold {% if agent_online %}text-emerald-700{% else %}text-red-700{% endif %}">{{ 'Connected' if agent_online else 'Offline' }}</div>
<div class="mt-1 text-xs text-slate-500">{{ storage_node.node_name if storage_node else 'No active branch agent' }}</div>
</div>
<div class="rounded-2xl bg-white p-5 shadow-soft">
<div class="text-xs font-semibold uppercase tracking-wide text-slate-500">Mapping Module</div>
<div class="mt-2 text-lg font-semibold {% if agent and agent.tally_mapping_capability %}text-emerald-700{% else %}text-slate-700{% endif %}">
{{ 'Available' if agent and agent.tally_mapping_capability else ('Update Agent' if agent_online else 'Unavailable') }}
</div>
<div class="mt-1 text-xs text-slate-500">Agent {{ agent.version if agent and agent.version else '-' }}</div>
</div>
<div class="rounded-2xl bg-white p-5 shadow-soft">
<div class="text-xs font-semibold uppercase tracking-wide text-slate-500">TallyPrime</div>
<div class="mt-2 text-lg font-semibold {% if tally and tally.connected %}text-emerald-700{% elif tally %}text-red-700{% else %}text-slate-700{% endif %}">
{{ 'Connected' if tally and tally.connected else ('Not connected' if tally else 'Not checked') }}
</div>
<div class="mt-1 text-xs text-slate-500">{{ tally.url if tally and tally.url else '127.0.0.1:9000' }}</div>
</div>
<div class="rounded-2xl bg-white p-5 shadow-soft">
<div class="text-xs font-semibold uppercase tracking-wide text-slate-500">Loaded Companies</div>
<div class="mt-2 text-2xl font-semibold text-slate-900">{{ tally.company_count if tally else '-' }}</div>
<div class="mt-1 text-xs text-slate-500">GUID-based discovery</div>
</div>
</section>
<section class="rounded-2xl bg-white p-5 shadow-soft">
<form method="get" action="/tools/tally" class="flex flex-wrap items-end gap-3">
<label class="min-w-[280px] flex-1 text-sm font-medium text-slate-700">
ERP Client
<select name="client_id" class="mt-1 w-full rounded-xl border border-slate-300 bg-white px-3 py-2 text-sm focus:border-brand-500 focus:outline-none focus:ring-2 focus:ring-brand-100">
<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>
<button type="submit" class="rounded-xl border border-slate-300 bg-white px-4 py-2 text-sm font-semibold text-slate-700 hover:bg-slate-50">Open Client</button>
</form>
<p class="mt-3 text-xs text-slate-500">Only clients assigned to the logged-in Partner in the active branch are shown.</p>
</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">Accounting Storage</h2>
<p class="mt-1 text-sm text-slate-500">{{ selected_client.client_name }}</p>
</div>
<span class="rounded-full px-3 py-1 text-xs font-semibold {% if accounting and accounting.exists %}bg-emerald-50 text-emerald-700{% else %}bg-amber-50 text-amber-700{% endif %}">
{{ 'Initialized' if accounting and accounting.exists else 'Not initialized' }}
</span>
</div>
{% if accounting and accounting.exists %}
<dl class="mt-4 grid gap-4 md:grid-cols-2">
<div><dt class="text-xs font-semibold uppercase tracking-wide text-slate-500">.act Database</dt><dd class="mt-1 break-all text-sm font-medium text-slate-900">{{ accounting.db_path }}</dd></div>
<div><dt class="text-xs font-semibold uppercase tracking-wide text-slate-500">Schema Version</dt><dd class="mt-1 text-sm font-medium text-slate-900">{{ accounting.metadata.schema_version or '2' }}</dd></div>
</dl>
{% else %}
<form method="post" action="/tools/tally/initialize" class="mt-4">
<input type="hidden" name="csrf_token" value="{{ csrf_token }}" />
<input type="hidden" name="client_id" value="{{ selected_client.id }}" />
<button type="submit" class="rounded-xl bg-slate-900 px-4 py-2 text-sm font-semibold text-white hover:bg-slate-800">Create .act Accounting Storage</button>
</form>
{% endif %}
</section>
{% if accounting and accounting.exists and tally and tally.connected %}
<section class="rounded-2xl bg-white p-5 shadow-soft">
<div>
<h2 class="font-semibold text-slate-900">Map ERP Scope to Tally Company</h2>
<p class="mt-1 text-sm text-slate-500">The Tally GUID is stored as the permanent identifier. Company name and GSTIN are retained as descriptive snapshots.</p>
</div>
<form method="post" action="/tools/tally/map-company" class="mt-5 grid gap-4 lg:grid-cols-2">
<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">
ERP Scope
<select name="registration_id" class="mt-1 w-full rounded-xl border border-slate-300 bg-white px-3 py-2 text-sm">
<option value="">Client level · {{ selected_client.client_name }}</option>
{% for reg, reg_type in registrations %}
<option value="{{ reg.id }}">
{{ reg_type.name }} · {{ reg.registration_number }}
{% if reg.trade_name %} · {{ reg.trade_name }}{% elif reg.legal_name %} · {{ reg.legal_name }}{% endif %}
</option>
{% endfor %}
</select>
<span class="mt-1 block text-xs font-normal text-slate-500">Choose a GSTIN/other registration when the Tally company belongs to that registration; otherwise use client level.</span>
</label>
<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 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">Companies without a GUID are intentionally not offered for permanent mapping.</span>
</label>
<label class="flex items-start gap-2 lg:col-span-2">
<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 3 blocks a mapping when both ERP and Tally contain different GSTINs unless this override is explicitly selected.</span>
</span>
</label>
<div class="lg:col-span-2">
<button type="submit" class="rounded-xl bg-brand-600 px-4 py-2 text-sm font-semibold text-white hover:bg-brand-700">Save Tally Mapping</button>
</div>
</form>
</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>
{% 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 4 · Accounting Transaction Sync</h2>
<p class="mt-1 text-sm text-slate-500">Read vouchers and their accounting allocations from the selected company currently open in TallyPrime. The selected company must already be mapped to this ERP client.</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-transactions" class="mt-5 grid gap-4 lg:grid-cols-4 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">Live list from companies currently loaded/open in this computer's TallyPrime.</span>
</label>
<label class="text-sm font-medium text-slate-700">From Date
<input type="date" name="date_from" required value="{{ transaction_date_from }}" class="mt-1 w-full rounded-xl border border-slate-300 bg-white px-3 py-2 text-sm" />
</label>
<label class="text-sm font-medium text-slate-700">To Date
<input type="date" name="date_to" required value="{{ transaction_date_to }}" class="mt-1 w-full rounded-xl border border-slate-300 bg-white px-3 py-2 text-sm" />
</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 Transactions</button>
</form>
{% set tx = accounting.latest_transaction_sync if accounting else none %}
<div class="mt-5 grid gap-3 sm:grid-cols-2 lg:grid-cols-4">
<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">{{ tx.completed_at_utc if tx and tx.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">Period</div><div class="mt-1 text-sm font-medium text-slate-900">{% if tx and tx.details %}{{ tx.details.date_from or '-' }} → {{ tx.details.date_to or '-' }}{% else %}-{% endif %}</div></div>
<div class="rounded-xl border border-slate-200 p-3"><div class="text-xs font-semibold uppercase tracking-wide text-slate-500">Vouchers</div><div class="mt-1 text-sm font-medium text-slate-900">{% if tx and tx.details and tx.details.counts %}{{ tx.details.counts.vouchers or 0 }}{% else %}0{% endif %}</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">{{ tx.rows_processed if tx else 0 }}</div></div>
</div>
<div class="mt-4 rounded-xl bg-slate-50 px-4 py-3 text-xs text-slate-600">
Stored locally in the client's .act database: vouchers, ledger entries, inventory entries, bill allocations, cost-centre allocations and bank allocations. Raw voucher XML is preserved. Re-syncing a date range replaces only that company's vouchers in the selected range. No Tally data is modified.
</div>
</section>
{% endif %}
{% 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">
<h2 class="font-semibold text-slate-900">Active Tally Mappings</h2>
<p class="text-sm text-slate-500">Mappings remain valid across Tally company renames because the GUID is the persistent key.</p>
</div>
{% if accounting.mappings %}
<div class="overflow-x-auto">
<table class="min-w-full divide-y divide-slate-200 text-sm">
<thead class="bg-slate-50">
<tr>
<th class="px-4 py-3 text-left font-semibold text-slate-600">ERP Scope</th>
<th class="px-4 py-3 text-left font-semibold text-slate-600">Tally Company</th>
<th class="px-4 py-3 text-left font-semibold text-slate-600">GUID</th>
<th class="px-4 py-3 text-left font-semibold text-slate-600">ERP GSTIN</th>
<th class="px-4 py-3 text-left font-semibold text-slate-600">Tally GSTIN</th>
<th class="px-4 py-3 text-left font-semibold text-slate-600">GSTIN Match</th>
<th class="px-4 py-3 text-left font-semibold text-slate-600">Loaded</th>
<th class="px-4 py-3 text-right font-semibold text-slate-600">Action</th>
</tr>
</thead>
<tbody class="divide-y divide-slate-100">
{% for mapping in accounting.mappings %}
<tr>
<td class="px-4 py-3">
{% if mapping.registration_id %}
<div class="font-medium text-slate-900">{{ mapping.registration_type_code or 'Registration' }} · {{ mapping.registration_number or mapping.registration_id }}</div>
<div class="text-xs text-slate-500">{{ mapping.registration_trade_name or mapping.registration_legal_name or selected_client.client_name }}</div>
{% else %}
<div class="font-medium text-slate-900">Client level</div>
<div class="text-xs text-slate-500">{{ selected_client.client_name }}</div>
{% endif %}
</td>
<td class="px-4 py-3 font-medium text-slate-900">{{ mapping.company_name }}</td>
<td class="px-4 py-3 font-mono text-xs text-slate-600">{{ mapping.tally_guid }}</td>
<td class="px-4 py-3 text-slate-600">
{% if mapping.erp_gstins %}
{% for gstin in mapping.erp_gstins %}<div class="font-mono text-xs">{{ gstin }}</div>{% endfor %}
{% else %}-{% endif %}
</td>
<td class="px-4 py-3 font-mono text-xs text-slate-600">{{ mapping.tally_gstin_display or '-' }}</td>
<td class="px-4 py-3">
{% if mapping.gstin_match_status == 'matched' %}
<span class="rounded-full bg-emerald-50 px-2 py-1 text-xs font-semibold text-emerald-700">Matched</span>
{% elif mapping.gstin_match_status == 'mismatch' %}
<span class="rounded-full bg-red-50 px-2 py-1 text-xs font-semibold text-red-700">Mismatch</span>
{% elif mapping.gstin_match_status == 'tally_missing' %}
<span class="rounded-full bg-amber-50 px-2 py-1 text-xs font-semibold text-amber-700">Tally GSTIN unavailable</span>
{% elif mapping.gstin_match_status == 'erp_missing' %}
<span class="rounded-full bg-amber-50 px-2 py-1 text-xs font-semibold text-amber-700">ERP GSTIN unavailable</span>
{% else %}
<span class="rounded-full bg-slate-100 px-2 py-1 text-xs font-semibold text-slate-600">Unavailable</span>
{% endif %}
</td>
<td class="px-4 py-3">
<span class="rounded-full px-2 py-1 text-xs font-semibold {% if mapping.currently_loaded %}bg-emerald-50 text-emerald-700{% else %}bg-slate-100 text-slate-600{% endif %}">
{{ 'Yes' if mapping.currently_loaded else 'No' }}
</span>
</td>
<td class="px-4 py-3 text-right">
<form method="post" action="/tools/tally/unmap-company" onsubmit="return confirm('Remove this Tally company mapping?');">
<input type="hidden" name="csrf_token" value="{{ csrf_token }}" />
<input type="hidden" name="client_id" value="{{ selected_client.id }}" />
<input type="hidden" name="mapping_id" value="{{ mapping.id }}" />
<button type="submit" class="rounded-lg border border-red-200 bg-white px-3 py-1.5 text-xs font-semibold text-red-700 hover:bg-red-50">Unmap</button>
</form>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% else %}
<div class="p-6 text-sm text-slate-500">No Tally company has been mapped for this client yet.</div>
{% endif %}
</section>
{% endif %}
{% if tally %}
<section class="overflow-hidden rounded-2xl bg-white shadow-soft">
<div class="border-b border-slate-100 px-5 py-4">
<h2 class="font-semibold text-slate-900">Loaded Tally Companies</h2>
<p class="text-sm text-slate-500">Read-only company discovery from the local TallyPrime instance.</p>
</div>
{% if tally.companies %}
<div class="overflow-x-auto">
<table class="min-w-full divide-y divide-slate-200 text-sm">
<thead class="bg-slate-50"><tr><th class="px-4 py-3 text-left font-semibold text-slate-600">Company</th><th class="px-4 py-3 text-left font-semibold text-slate-600">GUID</th><th class="px-4 py-3 text-left font-semibold text-slate-600">Tally GSTIN</th></tr></thead>
<tbody class="divide-y divide-slate-100">
{% for company in tally.companies %}
<tr>
<td class="px-4 py-3 font-medium text-slate-900">{{ company.name }}</td>
<td class="px-4 py-3 font-mono text-xs text-slate-600">{{ company.guid or '-' }}</td>
<td class="px-4 py-3 text-slate-600">{{ company.gstin or '-' }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% else %}
<div class="p-6 text-sm text-slate-500">TallyPrime responded, but no loaded company was returned.</div>
{% endif %}
</section>
{% endif %}
{% endif %}
<div class="rounded-xl border border-blue-200 bg-blue-50 px-4 py-3 text-sm text-blue-800">
Phase 4 synchronizes accounting transactions read-only. Tally write-back remains disabled and is reserved for Phase 6.
</div>
</div>
{% endblock %}