Fix depreciation mirror selection and TDS capital account exclusion

This commit is contained in:
A R R R Associates
2026-09-06 21:28:23 +05:30
parent f211d9e6a8
commit 5c19f23c2d
6 changed files with 153 additions and 34 deletions
@@ -18,7 +18,7 @@
<section class="grid gap-4 md:grid-cols-3">
<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 {{ 'text-emerald-700' if agent_online else 'text-red-700' }}">{{ 'Connected' if agent_online else 'Offline' }}</div></div>
<div class="rounded-2xl bg-white p-5 shadow-soft"><div class="text-xs font-semibold uppercase tracking-wide text-slate-500">SQLite Mirror</div><div class="mt-2 text-lg font-semibold {{ 'text-emerald-700' if mirror.ready_for_financial_year else 'text-amber-700' }}">{{ 'Ready' if mirror.ready_for_financial_year else 'Mirror required' }}</div><div class="mt-1 text-xs text-slate-500">{{ mirror.company.company_name if mirror.ready_for_financial_year and mirror.company 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">SQLite Mirror</div><div class="mt-2 text-lg font-semibold {{ 'text-emerald-700' if mirror.ready_for_financial_year else 'text-amber-700' }}">{{ 'Ready' if mirror.ready_for_financial_year else 'Mirror required' }}</div>{% if mirror.ready_for_financial_year and mirror.company %}<div class="mt-1 text-xs text-slate-500">{{ mirror.company.company_name }} · {{ fy_start }} to {{ fy_end }}</div>{% elif mirror.financial_year_reason %}<div class="mt-1 text-xs text-amber-700">{{ mirror.financial_year_reason }}</div>{% endif %}</div>
<div class="rounded-2xl bg-white p-5 shadow-soft"><div class="text-xs font-semibold uppercase tracking-wide text-slate-500">Write-back</div><div class="mt-2 text-lg font-semibold text-slate-700">Controlled</div><div class="mt-1 text-xs text-slate-500">Tally is contacted only after approval/posting.</div></div>
</section>
@@ -44,7 +44,7 @@
<input type="hidden" name="csrf_token" value="{{ csrf_token }}"><input type="hidden" name="client_id" value="{{ selected_client.id }}"><input type="hidden" name="fy_start" value="{{ fy_start }}"><input type="hidden" name="fy_end" value="{{ fy_end }}">
<section class="rounded-2xl bg-white p-5 shadow-soft">
<div class="grid gap-4 md:grid-cols-2">
<div><label class="text-sm font-medium text-slate-700">Depreciation Expense Ledger</label><select name="depreciation_expense_ledger" class="mt-1 w-full rounded-xl border border-slate-300 px-3 py-2"><option value="">Depreciation</option>{% for name in preview.all_ledgers %}<option value="{{ name }}">{{ name }}</option>{% endfor %}</select></div>
<div><label class="text-sm font-medium text-slate-700">Depreciation Debit Ledger</label><select id="depreciation-debit-ledger" name="depreciation_expense_ledger" class="mt-1 w-full rounded-xl border border-slate-300 px-3 py-2"><option value="">Depreciation</option>{% for name in preview.all_ledgers %}<option value="{{ name }}">{{ name }}</option>{% endfor %}</select><p class="mt-1 text-xs text-slate-500">Any existing ledger may be selected, including the asset ledger itself.</p></div>
<div><label class="text-sm font-medium text-slate-700">Depreciation Reserve / Credit Ledger</label><select name="depreciation_reserve_ledger" class="mt-1 w-full rounded-xl border border-slate-300 px-3 py-2"><option value="">Depreciation Reserve</option>{% for name in preview.all_ledgers %}<option value="{{ name }}">{{ name }}</option>{% endfor %}</select></div>
</div>
<p class="mt-3 text-xs text-slate-500">These ledger selections are stored with the draft. Phase 6 can post the Journal only after the saved draft is explicitly approved.</p>
@@ -60,7 +60,7 @@
<button type="button" id="apply-bulk-rate" class="rounded-lg border border-brand-300 bg-white px-3 py-2 text-sm font-semibold text-brand-700 hover:bg-brand-50">Apply rate to selected</button>
</div>
</div>
<div class="overflow-x-auto"><table class="min-w-full text-sm"><thead class="bg-slate-50 text-left text-xs uppercase tracking-wide text-slate-500"><tr><th class="px-4 py-3 w-10">Select</th><th class="px-4 py-3">Ledger</th><th class="px-4 py-3 text-right">Opening WDV</th><th class="px-4 py-3 text-right">Additions ≥180 days</th><th class="px-4 py-3 text-right">Additions &lt;180 days</th><th class="px-4 py-3 text-right">Reductions</th><th class="px-4 py-3">Rate</th></tr></thead><tbody class="divide-y divide-slate-100">
<div class="overflow-x-auto"><table class="min-w-full text-sm"><thead class="bg-slate-50 text-left text-xs uppercase tracking-wide text-slate-500"><tr><th class="px-4 py-3 w-10">Select</th><th class="px-4 py-3">Ledger</th><th class="px-4 py-3 text-right">Opening WDV</th><th class="px-4 py-3 text-right">Additions ≥180 days</th><th class="px-4 py-3 text-right">Additions &lt;180 days</th><th class="px-4 py-3 text-right">Other Reductions</th><th class="px-4 py-3 text-right">Depreciation Already Recorded</th><th class="px-4 py-3">Rate</th><th class="px-4 py-3">Posting Ledger</th></tr></thead><tbody class="divide-y divide-slate-100">
{% for row in preview.fixed_asset_ledgers %}
<tr>
<td class="px-4 py-3"><input type="checkbox" class="asset-select rounded border-slate-300" data-ledger-key="{{ row.ledger_key }}"></td>
@@ -69,9 +69,11 @@
<td class="px-4 py-3 text-right">{{ '%.2f'|format(row.additions_full_rate) }}</td>
<td class="px-4 py-3 text-right">{{ '%.2f'|format(row.additions_half_rate) }}</td>
<td class="px-4 py-3 text-right">{{ '%.2f'|format(row.reductions) }}</td>
<td class="px-4 py-3 text-right">{% if row.depreciation_already_recorded %}<div class="font-semibold text-emerald-700">{{ '%.2f'|format(row.recorded_depreciation) }}</div><div class="text-[11px] text-slate-500">{{ row.recorded_depreciation_voucher_count }} voucher(s)</div>{% else %}<span class="text-slate-400">—</span>{% endif %}</td>
<td class="px-4 py-3"><select id="rate-{{ loop.index0 }}" data-ledger-key="{{ row.ledger_key }}" name="rate__{{ row.ledger_key }}" class="asset-rate rounded-lg border border-slate-300 px-2 py-1.5"><option value="0" {% if row.rate_percent == 0 %}selected{% endif %}>0%</option>{% for rate in [5,10,15,20,25,30,40,45,60,80,100] %}<option value="{{ rate }}" {% if row.rate_percent == rate %}selected{% endif %}>{{ rate }}%</option>{% endfor %}</select></td>
<td class="px-4 py-3"><button type="button" class="use-asset-as-debit text-xs font-semibold text-brand-700 underline underline-offset-2" data-ledger-name="{{ row.ledger_name }}">Use as debit ledger</button></td>
</tr>
{% else %}<tr><td colspan="7" class="px-4 py-8 text-center text-slate-500">No active Fixed Asset ledgers were found. Zero-opening ledgers without additions are intentionally omitted.</td></tr>{% endfor %}
{% else %}<tr><td colspan="9" class="px-4 py-8 text-center text-slate-500">No active Fixed Asset ledgers were found. Zero-opening ledgers without additions are intentionally omitted.</td></tr>{% endfor %}
</tbody></table></div>
<div class="flex justify-end border-t border-slate-200 px-5 py-4"><button type="submit" {% if not preview.fixed_asset_ledgers %}disabled{% endif %} class="rounded-xl bg-brand-600 px-4 py-2 text-sm font-semibold text-white disabled:cursor-not-allowed disabled:opacity-50">Calculate & Save Draft</button></div>
</section>
@@ -94,7 +96,7 @@
</div>
{% if depreciation_run.tally_posting_error %}<div class="mt-3 rounded-xl border border-red-200 bg-red-50 px-4 py-3 text-sm text-red-800">{{ depreciation_run.tally_posting_error }}</div>{% endif %}
<div class="mt-4 overflow-x-auto"><table class="min-w-full text-sm"><thead class="bg-slate-50 text-left text-xs uppercase text-slate-500"><tr><th class="px-3 py-2">Ledger</th><th class="px-3 py-2 text-right">Rate</th><th class="px-3 py-2 text-right">Depreciation</th><th class="px-3 py-2 text-right">Closing WDV</th></tr></thead><tbody class="divide-y divide-slate-100">{% for row in depreciation_run.lines %}<tr><td class="px-3 py-2">{{ row.ledger_name }}</td><td class="px-3 py-2 text-right">{{ '%.2f'|format(row.rate_percent) }}%</td><td class="px-3 py-2 text-right">{{ '%.2f'|format(row.depreciation_amount) }}</td><td class="px-3 py-2 text-right">{{ '%.2f'|format(row.closing_wdv) }}</td></tr>{% endfor %}</tbody></table></div>
<div class="mt-4 overflow-x-auto"><table class="min-w-full text-sm"><thead class="bg-slate-50 text-left text-xs uppercase text-slate-500"><tr><th class="px-3 py-2">Ledger</th><th class="px-3 py-2 text-right">Rate</th><th class="px-3 py-2 text-right">Calculated</th><th class="px-3 py-2 text-right">Already Recorded</th><th class="px-3 py-2 text-right">Unrecorded</th><th class="px-3 py-2 text-right">Closing WDV</th></tr></thead><tbody class="divide-y divide-slate-100">{% for row in depreciation_run.lines %}<tr><td class="px-3 py-2">{{ row.ledger_name }}</td><td class="px-3 py-2 text-right">{{ '%.2f'|format(row.rate_percent) }}%</td><td class="px-3 py-2 text-right">{{ '%.2f'|format(row.depreciation_amount) }}</td><td class="px-3 py-2 text-right">{{ '%.2f'|format(row.recorded_depreciation or 0) }}</td><td class="px-3 py-2 text-right font-semibold">{{ '%.2f'|format(row.remaining_unrecorded_depreciation if row.remaining_unrecorded_depreciation is defined else row.depreciation_amount) }}</td><td class="px-3 py-2 text-right">{{ '%.2f'|format(row.closing_wdv) }}</td></tr>{% endfor %}</tbody></table></div>
{% if depreciation_run.status == 'draft' %}
<form method="post" action="/tools/tally/depreciation/approve" class="mt-5 rounded-xl border border-amber-200 bg-amber-50 p-4">
<input type="hidden" name="csrf_token" value="{{ csrf_token }}"><input type="hidden" name="client_id" value="{{ selected_client.id }}"><input type="hidden" name="run_id" value="{{ depreciation_run.id }}"><input type="hidden" name="fy_start" value="{{ depreciation_run.fy_start }}">
@@ -155,4 +157,19 @@
<div class="mt-3 overflow-x-auto"><table class="min-w-full text-sm"><thead><tr class="text-left text-xs uppercase text-slate-500"><th class="p-2">Run</th><th class="p-2">FY</th><th class="p-2">Period</th><th class="p-2">Completed</th><th class="p-2"></th></tr></thead><tbody>{% for row in analysis_history %}<tr class="border-t"><td class="p-2">#{{ row.id }}</td><td class="p-2">{{ row.financial_year }}</td><td class="p-2">{{ row.period_from }} → {{ row.period_to }}</td><td class="p-2">{{ row.completed_at_utc }}</td><td class="p-2 text-right"><a class="font-semibold text-brand-700" href="/tools/tally/analysis/{{ row.id }}/download?client_id={{ selected_client.id }}&financial_year={{ row.financial_year }}&format=xlsx">Excel</a> · <a class="font-semibold text-brand-700" href="/tools/tally/analysis/{{ row.id }}/download?client_id={{ selected_client.id }}&financial_year={{ row.financial_year }}&format=pdf">PDF</a></td></tr>{% endfor %}</tbody></table></div>
</section>
{% endif %}
<script>
document.addEventListener("DOMContentLoaded",function(){
const debit=document.getElementById("depreciation-debit-ledger");
document.querySelectorAll(".use-asset-as-debit").forEach(function(button){
button.addEventListener("click",function(){
if(!debit)return;
debit.value=button.dataset.ledgerName||"";
debit.scrollIntoView({behavior:"smooth",block:"center"});
debit.focus();
});
});
});
</script>
{% endblock %}
+24 -10
View File
@@ -810,9 +810,10 @@ def depreciation_it_tool(
node = get_active_storage_node_for_branch(db, scope.tenant_id, scope.branch_id)
online = _node_online(node)
today = date.today()
start_year = today.year if today.month >= 4 else today.year - 1
start_text = fy_start or date(start_year, 4, 1).isoformat()
end_text = fy_end or date(start_year + 1, 3, 31).isoformat()
active_start_year = today.year if today.month >= 4 else today.year - 1
explicit_period = bool(str(fy_start or "").strip() and str(fy_end or "").strip())
start_text = fy_start or date(active_start_year, 4, 1).isoformat()
end_text = fy_end or date(active_start_year + 1, 3, 31).isoformat()
preview = None
depreciation_run = None
mirror_status = None
@@ -820,17 +821,30 @@ def depreciation_it_tool(
analysis_history = []
if selected_client and node and online:
storage_payload = _accounting_storage_payload(
selected_client, _financial_year_for_date(date.fromisoformat(start_text))
)
selected_fy=_financial_year_for_date(date.fromisoformat(start_text))
storage_payload=_accounting_storage_payload(selected_client,selected_fy)
try:
status_response = request_agent_command(
node.node_code, "accounting_mirror_status", storage_payload, timeout_seconds=20
status_response=request_agent_command(
node.node_code,"accounting_mirror_status",storage_payload,timeout_seconds=20
)
if status_response.get("ok"):
mirror_status = status_response.get("result") or {}
mirror_status=status_response.get("result") or {}
else:
command_error = str(status_response.get("error") or "Accounting Mirror status failed.")
command_error=str(status_response.get("error") or "Accounting Mirror status failed.")
if not explicit_period and not ((mirror_status or {}).get("mirror") or {}).get("ready_for_financial_year"):
prior_start_year=active_start_year-1
prior_start=date(prior_start_year,4,1)
prior_end=date(prior_start_year+1,3,31)
prior_fy=_financial_year_for_date(prior_start)
prior_payload=_accounting_storage_payload(selected_client,prior_fy)
prior_response=request_agent_command(
node.node_code,"accounting_mirror_status",prior_payload,timeout_seconds=20
)
prior_status=(prior_response.get("result") or {}) if prior_response.get("ok") else {}
if ((prior_status.get("mirror") or {}).get("ready_for_financial_year")):
start_text=prior_start.isoformat(); end_text=prior_end.isoformat()
storage_payload=prior_payload; mirror_status=prior_status
if mirror_status and (mirror_status.get("mirror") or {}).get("ready_for_financial_year"):
preview_response = request_agent_command(