Add cash payment compliance and accounting tools

This commit is contained in:
A R R R Associates
2026-09-03 21:32:43 +05:30
parent 657a56712b
commit a71a26e216
9 changed files with 662 additions and 2 deletions
@@ -0,0 +1,109 @@
{% 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 · Tally</p>
<h1 class="mt-1 text-2xl font-semibold text-slate-900">Cash Payment Compliance Review</h1>
<p class="mt-1 max-w-4xl text-sm text-slate-600">Read-only Tally review for cash payments above the configured limit, same-day party aggregation and possible split-payment patterns. The accounting planner below uses only actual source payment dates supplied by the user; it never invents or changes dates.</p>
</div>
<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>
{% if saved %}<div class="rounded-xl border border-emerald-200 bg-emerald-50 px-4 py-3 text-sm text-emerald-800">Cash payment rule settings saved.</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 %}
<section class="grid gap-4 lg:grid-cols-3">
<div class="rounded-2xl bg-white p-5 shadow-soft lg:col-span-2">
<h2 class="text-lg font-semibold text-slate-900">Tax Payment Compliance Review</h2>
<form method="get" action="/tools/accounting/cash-payments" class="mt-4 grid gap-4 md:grid-cols-2 xl:grid-cols-4">
<div>
<label class="text-xs font-semibold uppercase text-slate-500">Client</label>
<select name="client_id" class="mt-1 w-full rounded-xl border-slate-300 text-sm" required onchange="this.form.submit()">
<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>
</div>
<div>
<label class="text-xs font-semibold uppercase text-slate-500">Tally Company</label>
<select name="tally_guid" class="mt-1 w-full rounded-xl border-slate-300 text-sm" {% if selected_client %}required{% endif %}>
<option value="">Select open/mapped company</option>
{% if live_result and live_result.accounting %}
{% for mapping in live_result.accounting.mappings or [] %}
<option value="{{ mapping.tally_guid }}" {% if selected_tally_guid == mapping.tally_guid %}selected{% endif %}>{{ mapping.company_name or mapping.tally_company_name or mapping.tally_guid }}</option>
{% endfor %}
{% endif %}
</select>
</div>
<div><label class="text-xs font-semibold uppercase text-slate-500">From</label><input type="date" name="date_from" value="{{ date_from }}" class="mt-1 w-full rounded-xl border-slate-300 text-sm" required></div>
<div><label class="text-xs font-semibold uppercase text-slate-500">To</label><input type="date" name="date_to" value="{{ date_to }}" class="mt-1 w-full rounded-xl border-slate-300 text-sm" required></div>
<input type="hidden" name="run" value="1">
<div class="md:col-span-2 xl:col-span-4"><button class="rounded-xl bg-brand-600 px-4 py-2 text-sm font-semibold text-white" {% if not selected_client %}disabled{% endif %}>Run Tally Review</button></div>
</form>
</div>
<div class="rounded-2xl bg-white p-5 shadow-soft">
<h2 class="text-lg font-semibold text-slate-900">Rule Settings</h2>
<form method="post" action="/tools/accounting/cash-payments/settings" class="mt-4 space-y-3">
<input type="hidden" name="csrf_token" value="{{ csrf_token }}"><input type="hidden" name="client_id" value="{{ selected_client.id if selected_client else '' }}">
<div><label class="text-xs font-semibold uppercase text-slate-500">Cash payment limit</label><input type="number" min="1" step="0.01" name="cash_limit" value="{{ '%.2f'|format(setting.cash_limit) }}" class="mt-1 w-full rounded-xl border-slate-300 text-sm" required></div>
<div><label class="text-xs font-semibold uppercase text-slate-500">Effective from</label><input type="date" name="effective_from" value="{{ setting.effective_from.isoformat() }}" class="mt-1 w-full rounded-xl border-slate-300 text-sm" required></div>
<div><label class="text-xs font-semibold uppercase text-slate-500">Split review window (days)</label><input type="number" min="1" max="31" name="split_window_days" value="{{ setting.split_window_days }}" class="mt-1 w-full rounded-xl border-slate-300 text-sm" required></div>
<div><label class="text-xs font-semibold uppercase text-slate-500">Near-limit threshold (%)</label><input type="number" min="1" max="100" step="0.1" name="near_limit_percent" value="{{ setting.near_limit_percent }}" class="mt-1 w-full rounded-xl border-slate-300 text-sm" required></div>
<div><label class="text-xs font-semibold uppercase text-slate-500">Max source-date allocation window</label><input type="number" min="1" max="366" name="max_allocation_days" value="{{ setting.max_allocation_days }}" class="mt-1 w-full rounded-xl border-slate-300 text-sm" required></div>
<button class="w-full rounded-xl border border-brand-300 bg-brand-50 px-4 py-2 text-sm font-semibold text-brand-700">Save Settings</button>
</form>
</div>
</section>
{% if review %}
<section class="space-y-4">
<div class="grid gap-4 md:grid-cols-2 xl:grid-cols-5">
{% for label, value in [('Vouchers reviewed', review.summary.vouchers_reviewed), ('Cash payments', review.summary.cash_payment_vouchers), ('Single-voucher exceptions', review.summary.single_voucher_exceptions), ('Same-day exceptions', review.summary.same_day_exceptions), ('Possible split patterns', review.summary.possible_split_patterns)] %}
<div class="rounded-2xl bg-white p-4 shadow-soft"><div class="text-xs font-semibold uppercase text-slate-500">{{ label }}</div><div class="mt-2 text-2xl font-semibold text-slate-900">{{ value }}</div></div>
{% endfor %}
</div>
<div class="rounded-2xl bg-white p-5 shadow-soft overflow-x-auto">
<h3 class="font-semibold text-slate-900">Above-limit / same-day aggregate exceptions</h3>
<table class="mt-3 min-w-full text-sm"><thead class="text-left text-xs uppercase text-slate-500"><tr><th class="p-2">Date</th><th class="p-2">Party / ledger</th><th class="p-2">Amount</th><th class="p-2">Reason</th><th class="p-2">Voucher(s)</th></tr></thead><tbody>
{% for row in review.exceptions %}<tr class="border-t border-slate-100"><td class="p-2">{{ row.date }}</td><td class="p-2 font-medium">{{ row.party }}</td><td class="p-2">₹{{ '%.2f'|format(row.amount) }}</td><td class="p-2">{{ row.reason }}</td><td class="p-2">{{ row.voucher_numbers|join(', ') }}</td></tr>{% else %}<tr><td colspan="5" class="p-4 text-slate-500">No above-limit exception found for the selected period.</td></tr>{% endfor %}
</tbody></table>
</div>
<div class="rounded-2xl bg-white p-5 shadow-soft overflow-x-auto">
<h3 class="font-semibold text-slate-900">Possible split-payment patterns — review only</h3>
<p class="mt-1 text-xs text-slate-500">These rows are indicators for auditor review; they do not by themselves determine tax treatment.</p>
<table class="mt-3 min-w-full text-sm"><thead class="text-left text-xs uppercase text-slate-500"><tr><th class="p-2">Party / ledger</th><th class="p-2">Period</th><th class="p-2">Total</th><th class="p-2">Daily amounts</th></tr></thead><tbody>
{% for row in review.possible_split_payments %}<tr class="border-t border-slate-100"><td class="p-2 font-medium">{{ row.party }}</td><td class="p-2">{{ row.date_from }} → {{ row.date_to }}</td><td class="p-2">₹{{ '%.2f'|format(row.total_amount) }}</td><td class="p-2">{% for item in row.days %}{{ item.date }}: ₹{{ '%.2f'|format(item.amount) }}{% if not loop.last %}<br>{% endif %}{% endfor %}</td></tr>{% else %}<tr><td colspan="4" class="p-4 text-slate-500">No near-limit multi-day pattern found.</td></tr>{% endfor %}
</tbody></table>
</div>
</section>
{% endif %}
<section class="rounded-2xl bg-white p-5 shadow-soft">
<h2 class="text-lg font-semibold text-slate-900">Cash Payment Entry Allocation — Actual Source Dates</h2>
<p class="mt-1 text-sm text-slate-600">Use this when the source records establish that one accounting amount was actually paid on multiple dates. Enter the real dates and amounts. The tool validates totals, daily limits and the configured date window; it does not fabricate dates or automatically rewrite Tally vouchers.</p>
<form method="post" action="/tools/accounting/cash-payments/allocation-plan" class="mt-4 grid gap-4 lg:grid-cols-2">
<input type="hidden" name="csrf_token" value="{{ csrf_token }}">
<div><label class="text-xs font-semibold uppercase text-slate-500">Client</label><select name="client_id" class="mt-1 w-full rounded-xl border-slate-300 text-sm" required>{% 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></div>
<div><label class="text-xs font-semibold uppercase text-slate-500">Total accounting amount</label><input type="number" min="0.01" step="0.01" name="total_amount" value="{{ allocation_input.total_amount }}" class="mt-1 w-full rounded-xl border-slate-300 text-sm" required></div>
<div><label class="text-xs font-semibold uppercase text-slate-500">Party</label><input name="party_name" value="{{ allocation_input.party_name }}" class="mt-1 w-full rounded-xl border-slate-300 text-sm"></div>
<div><label class="text-xs font-semibold uppercase text-slate-500">Expense / purchase ledger</label><input name="expense_ledger" value="{{ allocation_input.expense_ledger }}" class="mt-1 w-full rounded-xl border-slate-300 text-sm"></div>
<div class="lg:col-span-2"><label class="text-xs font-semibold uppercase text-slate-500">Actual source payments — one per line: YYYY-MM-DD, amount</label><textarea name="source_payments" rows="7" class="mt-1 w-full rounded-xl border-slate-300 font-mono text-sm" placeholder="2026-09-01, 8500.00
2026-09-02, 9000.00">{{ allocation_input.source_payments }}</textarea></div>
<div class="lg:col-span-2"><button class="rounded-xl bg-slate-900 px-4 py-2 text-sm font-semibold text-white">Validate Source-Date Allocation</button></div>
</form>
{% if allocation_plan %}
<div class="mt-5 rounded-xl border {% if allocation_plan.status == 'ready' %}border-emerald-200 bg-emerald-50{% else %}border-amber-200 bg-amber-50{% endif %} p-4">
<div class="font-semibold {% if allocation_plan.status == 'ready' %}text-emerald-800{% else %}text-amber-900{% endif %}">{{ 'Ready for accounting review' if allocation_plan.status == 'ready' else 'Manual review required' }}</div>
{% if allocation_plan.errors %}<ul class="mt-2 list-disc pl-5 text-sm text-amber-900">{% for item in allocation_plan.errors %}<li>{{ item }}</li>{% endfor %}</ul>{% endif %}
<div class="mt-3 text-sm text-slate-700">Expected total: ₹{{ '%.2f'|format(allocation_plan.expected_total or 0) }} · Source total: ₹{{ '%.2f'|format(allocation_plan.supplied_total or 0) }} · Date span: {{ allocation_plan.span_days or 0 }} day(s) / {{ allocation_plan.max_allocation_days or setting.max_allocation_days }} allowed.</div>
{% if allocation_plan.entries %}<table class="mt-3 min-w-full text-sm"><thead><tr><th class="p-2 text-left">Actual Date</th><th class="p-2 text-left">Proposed Entry Amount</th></tr></thead><tbody>{% for row in allocation_plan.entries %}<tr class="border-t border-slate-200"><td class="p-2">{{ row.date }}</td><td class="p-2">₹{{ '%.2f'|format(row.amount) }}</td></tr>{% endfor %}</tbody></table>{% endif %}
<p class="mt-3 text-xs text-slate-600">{{ allocation_plan.note or 'No accounting dates are generated automatically.' }}</p>
</div>
{% endif %}
</section>
</div>
{% endblock %}
@@ -46,6 +46,7 @@
<a href="/tools/accounting/bank-posting{% if selected_client %}?client_id={{ selected_client.id }}{% endif %}" class="rounded-xl border border-slate-300 bg-white px-3 py-2 text-sm font-semibold">Bank Expenses / Payments</a>
<a href="/tools/accounting/bank-reconciliation{% if selected_client %}?client_id={{ selected_client.id }}{% endif %}" class="rounded-xl border border-emerald-300 bg-white px-3 py-2 text-sm font-semibold text-emerald-800">Bank Reconciliation</a>
<a href="/tools/tally/depreciation{% if selected_client %}?client_id={{ selected_client.id }}{% endif %}" class="rounded-xl border border-slate-300 bg-white px-3 py-2 text-sm font-semibold">Depreciation (IT)</a>
<a href="/tools/accounting/cash-payments{% if selected_client %}?client_id={{ selected_client.id }}{% endif %}" class="rounded-xl border border-orange-300 bg-white px-3 py-2 text-sm font-semibold text-orange-800">Cash Payment Compliance</a>
<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>