Fix Tally mirror reload loop and simplify company mapping
This commit is contained in:
@@ -166,10 +166,27 @@
|
|||||||
</section>
|
</section>
|
||||||
|
|
||||||
{% if accounting and accounting.exists and tally and tally.connected %}
|
{% if accounting and accounting.exists and tally and tally.connected %}
|
||||||
<section class="rounded-2xl bg-white p-5 shadow-soft">
|
{% set active_mappings = (accounting.mappings or []) | selectattr('is_active') | list %}
|
||||||
|
{% if active_mappings %}
|
||||||
|
{% set current_mapping = active_mappings[0] %}
|
||||||
|
<details class="rounded-2xl border border-slate-200 bg-white shadow-soft">
|
||||||
|
<summary class="cursor-pointer list-none px-5 py-4">
|
||||||
|
<div class="flex flex-wrap items-center justify-between gap-3">
|
||||||
<div>
|
<div>
|
||||||
<h2 class="font-semibold text-slate-900">Map ERP Scope to Tally Company</h2>
|
<h2 class="font-semibold text-slate-900">Tally Company Linked</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>
|
<p class="mt-1 text-sm text-slate-500">
|
||||||
|
{{ selected_client.client_name }} → {{ current_mapping.company_name }}
|
||||||
|
{% if current_mapping.gstin %} · {{ current_mapping.gstin }}{% endif %}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<span class="rounded-full bg-emerald-50 px-3 py-1 text-xs font-semibold text-emerald-700">Mapped by GUID</span>
|
||||||
|
</div>
|
||||||
|
<p class="mt-2 text-xs text-slate-500">Mapping is a one-time setup. Open this panel only when you need to change the linked Tally company.</p>
|
||||||
|
</summary>
|
||||||
|
<div class="border-t border-slate-100 p-5">
|
||||||
|
<div>
|
||||||
|
<h3 class="font-semibold text-slate-900">Change ERP Scope to Tally Company Mapping</h3>
|
||||||
|
<p class="mt-1 text-sm text-slate-500">The Tally GUID remains the permanent identifier. Company name and GSTIN are descriptive snapshots.</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<form method="post" action="/tools/tally/map-company" class="mt-5 grid gap-4 lg:grid-cols-2">
|
<form method="post" action="/tools/tally/map-company" class="mt-5 grid gap-4 lg:grid-cols-2">
|
||||||
@@ -211,12 +228,64 @@
|
|||||||
</span>
|
</span>
|
||||||
</label>
|
</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>
|
||||||
|
</div>
|
||||||
|
</details>
|
||||||
|
{% else %}
|
||||||
|
<section class="rounded-2xl bg-white p-5 shadow-soft">
|
||||||
|
<div>
|
||||||
|
<h2 class="font-semibold text-slate-900">Link Tally Company</h2>
|
||||||
|
<p class="mt-1 text-sm text-slate-500">One-time setup. The Tally GUID is stored as the permanent identifier; company name and GSTIN are 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 registration only when the Tally company belongs specifically 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>
|
||||||
|
</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">Use only when the ERP registration and the selected Tally company have intentionally different GSTIN snapshots.</span>
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
|
||||||
<div class="lg:col-span-2">
|
<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>
|
<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>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</section>
|
</section>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
{% if accounting and accounting.exists %}
|
{% if accounting and accounting.exists %}
|
||||||
<section class="rounded-2xl bg-white p-5 shadow-soft" id="full-accounting-export">
|
<section class="rounded-2xl bg-white p-5 shadow-soft" id="full-accounting-export">
|
||||||
@@ -474,7 +543,7 @@
|
|||||||
const clientId = {{ selected_client.id if selected_client else 'null' }};
|
const clientId = {{ selected_client.id if selected_client else 'null' }};
|
||||||
if (!clientId) return;
|
if (!clientId) return;
|
||||||
const box=document.getElementById('fullExportProgress'); if(!box) return;
|
const box=document.getElementById('fullExportProgress'); if(!box) return;
|
||||||
let completedReloaded=false;
|
let pollTimer=null;
|
||||||
function setText(id,v){const e=document.getElementById(id);if(e)e.textContent=String(v??'');}
|
function setText(id,v){const e=document.getElementById(id);if(e)e.textContent=String(v??'');}
|
||||||
async function pollFullExport(){
|
async function pollFullExport(){
|
||||||
try{
|
try{
|
||||||
@@ -486,11 +555,15 @@
|
|||||||
const rows=j.rows||((j.mirror||{}).counts)||{}; setText('fullExportLedgers',rows.ledger_master||0); setText('fullExportVouchers',rows.voucher||rows.voucher_header||0); setText('fullExportLines',rows.voucher_ledger_entry||rows.voucher_ledger||0); setText('fullExportInventory',rows.inventory_movement||rows.inventory||0);
|
const rows=j.rows||((j.mirror||{}).counts)||{}; setText('fullExportLedgers',rows.ledger_master||0); setText('fullExportVouchers',rows.voucher||rows.voucher_header||0); setText('fullExportLines',rows.voucher_ledger_entry||rows.voucher_ledger||0); setText('fullExportInventory',rows.inventory_movement||rows.inventory||0);
|
||||||
const paths=[]; if(j.mirror_db_path)paths.push('Mirror: '+j.mirror_db_path); if(j.accounting_db_path)paths.push('Client DB: '+j.accounting_db_path); setText('fullExportPaths',paths.join(' • '));
|
const paths=[]; if(j.mirror_db_path)paths.push('Mirror: '+j.mirror_db_path); if(j.accounting_db_path)paths.push('Client DB: '+j.accounting_db_path); setText('fullExportPaths',paths.join(' • '));
|
||||||
const er=document.getElementById('fullExportError'); if(j.error){er.textContent=j.error;er.classList.remove('hidden')}else{er.classList.add('hidden')}
|
const er=document.getElementById('fullExportError'); if(j.error){er.textContent=j.error;er.classList.remove('hidden')}else{er.classList.add('hidden')}
|
||||||
const btn=document.getElementById('fullExportButton'); if(btn)btn.disabled=['queued','running'].includes(String(j.status||'').toLowerCase());
|
const status=String(j.status||'').toLowerCase();
|
||||||
if(String(j.status||'').toLowerCase()==='completed'&&!completedReloaded){completedReloaded=true;setTimeout(()=>location.href='/tools/tally?client_id='+clientId+'&refresh=1',1800)}
|
const btn=document.getElementById('fullExportButton'); if(btn)btn.disabled=['queued','running'].includes(status);
|
||||||
|
if(['completed','failed','cancelled','paused'].includes(status)){
|
||||||
|
if(pollTimer){clearInterval(pollTimer);pollTimer=null;}
|
||||||
|
}
|
||||||
}catch(e){}
|
}catch(e){}
|
||||||
}
|
}
|
||||||
pollFullExport(); setInterval(pollFullExport,1500);
|
pollTimer=setInterval(pollFullExport,1500);
|
||||||
|
pollFullExport();
|
||||||
})();
|
})();
|
||||||
</script>
|
</script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
Reference in New Issue
Block a user