Add Phase 14 sales and income foundation
This commit is contained in:
@@ -0,0 +1,156 @@
|
||||
{% extends "ui/templates/base/layout.html" %}
|
||||
{% block content %}
|
||||
<div class="space-y-6">
|
||||
<div class="flex flex-wrap items-start justify-between gap-3">
|
||||
<div>
|
||||
<p class="text-xs font-semibold uppercase tracking-[0.16em] text-brand-600">Accounting · Sales & Income · Phase 14</p>
|
||||
<h1 class="mt-1 text-2xl font-semibold text-slate-900">Sales & Income Foundation</h1>
|
||||
<p class="mt-1 max-w-4xl text-sm text-slate-500">Normalized outward transaction register for GSTR-1/exported sales data, customer/GSTIN identity, income-nature classification and reviewer-confirmed sales ledger mapping. Phase 14 is review-only; Tally sales posting is not enabled yet.</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">Back to Accounting</a>
|
||||
</div>
|
||||
|
||||
{% if message %}<div class="rounded-xl border border-emerald-200 bg-emerald-50 p-3 text-sm text-emerald-800">{{ message }}</div>{% endif %}
|
||||
{% if error %}<div class="rounded-xl border border-red-200 bg-red-50 p-3 text-sm text-red-800">{{ error }}</div>{% endif %}
|
||||
|
||||
<section class="rounded-2xl bg-white p-5 shadow-soft">
|
||||
<form method="get" class="grid gap-4 md:grid-cols-[1fr_auto]">
|
||||
<label class="text-sm font-semibold">ERP Client
|
||||
<select name="client_id" required class="mt-1 w-full rounded-xl border border-slate-300 bg-white px-3 py-2">
|
||||
<option value="">Select client</option>
|
||||
{% for c in clients %}
|
||||
<option value="{{ c.id }}" {% if selected_client and c.id == selected_client.id %}selected{% endif %}>{{ c.client_name }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</label>
|
||||
<div class="flex items-end"><button class="rounded-xl bg-brand-600 px-4 py-2 text-sm font-semibold text-white">Open Sales & Income</button></div>
|
||||
</form>
|
||||
</section>
|
||||
|
||||
{% if selected_client %}
|
||||
<section class="rounded-2xl bg-white p-5 shadow-soft">
|
||||
<h2 class="font-semibold text-slate-900">Import Outward Sales / Income</h2>
|
||||
<p class="mt-1 text-sm text-slate-500">Upload CSV/XLS/XLSX exported from GSTR-1 working papers, billing software or another outward-sales source. Flexible common column names are detected automatically. Duplicate invoice fingerprints are skipped.</p>
|
||||
<form method="post" action="/tools/accounting/sales-income/import" enctype="multipart/form-data" class="mt-4 grid gap-4 md:grid-cols-[220px_1fr_auto] md: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-semibold">Source
|
||||
<select name="source_type" class="mt-1 w-full rounded-xl border border-slate-300 bg-white px-3 py-2">
|
||||
<option value="outward_import">Outward Sales Import</option>
|
||||
<option value="gstr1">GSTR-1</option>
|
||||
<option value="einvoice_outward">E-Invoice Outward</option>
|
||||
<option value="ewaybill_outward">E-Way Bill Outward</option>
|
||||
</select>
|
||||
</label>
|
||||
<label class="text-sm font-semibold">CSV / Excel file
|
||||
<input type="file" name="file" accept=".csv,.xlsx,.xls,.xlsm" required class="mt-1 block w-full rounded-xl border border-slate-300 bg-white px-3 py-2 text-sm">
|
||||
</label>
|
||||
<button class="rounded-xl bg-slate-900 px-4 py-2 text-sm font-semibold text-white">Import</button>
|
||||
</form>
|
||||
</section>
|
||||
|
||||
<section class="rounded-2xl bg-white p-5 shadow-soft">
|
||||
<form method="get" class="grid gap-3 md:grid-cols-5">
|
||||
<input type="hidden" name="client_id" value="{{ selected_client.id }}">
|
||||
<label class="text-xs font-semibold">Status
|
||||
<select name="status" class="mt-1 w-full rounded-lg border border-slate-300 px-2 py-2">
|
||||
{% for value,label in [('all','All'),('suggested','Suggested'),('review_required','Review Required'),('reviewed','Reviewed')] %}
|
||||
<option value="{{ value }}" {% if status_filter==value %}selected{% endif %}>{{ label }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</label>
|
||||
<label class="text-xs font-semibold md:col-span-2">Customer / GSTIN
|
||||
<input name="customer" value="{{ customer_filter }}" class="mt-1 w-full rounded-lg border border-slate-300 px-2 py-2">
|
||||
</label>
|
||||
<label class="text-xs font-semibold">Return Period
|
||||
<input name="period" value="{{ period_filter }}" placeholder="2026-07" class="mt-1 w-full rounded-lg border border-slate-300 px-2 py-2">
|
||||
</label>
|
||||
<div class="flex items-end"><button class="w-full rounded-lg border border-slate-300 px-3 py-2 text-sm font-semibold">Filter</button></div>
|
||||
</form>
|
||||
</section>
|
||||
|
||||
<section class="rounded-2xl bg-white shadow-soft">
|
||||
<div class="border-b border-slate-200 px-5 py-4">
|
||||
<h2 class="font-semibold">{{ selected_client.client_name }} · Sales & Income Register</h2>
|
||||
<p class="text-sm text-slate-500">{{ total }} normalized outward transaction(s).</p>
|
||||
</div>
|
||||
<div class="divide-y divide-slate-100">
|
||||
{% for row in rows %}
|
||||
<article class="p-5">
|
||||
<div class="grid gap-4 xl:grid-cols-12">
|
||||
<div class="xl:col-span-3">
|
||||
<div class="font-semibold">{{ row.invoice_date or 'No date' }} · {{ row.invoice_number or 'No invoice no.' }}</div>
|
||||
<div class="mt-1 text-sm">{{ row.customer_name or 'Unnamed customer' }}</div>
|
||||
<div class="text-xs text-slate-500">{{ row.customer_gstin or 'No GSTIN' }}</div>
|
||||
<div class="mt-2 text-sm">{{ row.description_text or '—' }}</div>
|
||||
<div class="mt-2 font-semibold">₹{{ '%.2f'|format(row.invoice_value) }}</div>
|
||||
</div>
|
||||
|
||||
<div class="xl:col-span-3">
|
||||
<div class="text-xs uppercase text-slate-500">First-pass suggestion</div>
|
||||
<div class="mt-1 font-semibold">
|
||||
{% for n in natures %}{% if n.id == row.suggested_nature_id %}{{ n.name }}{% endif %}{% endfor %}
|
||||
</div>
|
||||
<div class="text-sm">{{ row.suggested_confidence }}% confidence</div>
|
||||
<div class="mt-2 text-xs text-slate-500">{{ row.suggestion_reason or '—' }}</div>
|
||||
<div class="mt-2 text-xs text-slate-500">{{ row.source_type }} · {{ row.hsn_code or 'No HSN/SAC' }}</div>
|
||||
</div>
|
||||
|
||||
<div class="xl:col-span-6">
|
||||
{% if row.review_status != 'reviewed' %}
|
||||
<form method="post" action="/tools/accounting/sales-income/{{ row.id }}/review" class="grid gap-2 md:grid-cols-2">
|
||||
<input type="hidden" name="csrf_token" value="{{ csrf_token }}">
|
||||
<input type="hidden" name="client_id" value="{{ selected_client.id }}">
|
||||
<input type="hidden" name="status" value="{{ status_filter }}">
|
||||
<input type="hidden" name="customer" value="{{ customer_filter }}">
|
||||
<input type="hidden" name="period" value="{{ period_filter }}">
|
||||
<input type="hidden" name="page" value="{{ page }}">
|
||||
<input type="hidden" name="per_page" value="{{ per_page }}">
|
||||
|
||||
<label class="text-xs font-semibold">Sales / Income Nature
|
||||
<select name="final_nature_id" required class="mt-1 w-full rounded-lg border border-slate-300 px-2 py-2">
|
||||
{% for n in natures %}
|
||||
<option value="{{ n.id }}" {% if n.id == row.suggested_nature_id %}selected{% endif %}>{{ n.name }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</label>
|
||||
<label class="text-xs font-semibold">Client Tally Sales / Income Ledger
|
||||
<input name="final_ledger_name" value="{{ row.suggested_ledger_name }}" placeholder="Reviewer maps existing Tally ledger" class="mt-1 w-full rounded-lg border border-slate-300 px-2 py-2">
|
||||
</label>
|
||||
<label class="text-xs font-semibold md:col-span-2">Review note
|
||||
<input name="review_note" class="mt-1 w-full rounded-lg border border-slate-300 px-2 py-2">
|
||||
</label>
|
||||
<button class="rounded-lg bg-brand-600 px-3 py-2 text-sm font-semibold text-white md:col-span-2">Confirm Sales / Income Treatment</button>
|
||||
</form>
|
||||
{% else %}
|
||||
<div class="rounded-xl border border-emerald-200 bg-emerald-50 p-3 text-sm text-emerald-800">
|
||||
<strong>Reviewed.</strong>
|
||||
{% for n in natures %}{% if n.id == row.final_nature_id %}{{ n.name }}{% endif %}{% endfor %}
|
||||
{% if row.final_ledger_name %} · {{ row.final_ledger_name }}{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
{% else %}
|
||||
<div class="p-10 text-center text-sm text-slate-500">No Sales & Income records yet. Import an outward-sales CSV/Excel file above.</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
{% if pages > 1 %}
|
||||
<div class="flex items-center justify-between border-t border-slate-200 p-4 text-sm">
|
||||
<span>Page {{ page }} of {{ pages }}</span>
|
||||
<div class="flex gap-2">
|
||||
{% if page > 1 %}
|
||||
<a class="rounded-lg border border-slate-300 px-3 py-1" href="?client_id={{ selected_client.id }}&status={{ status_filter }}&customer={{ customer_filter }}&period={{ period_filter }}&page={{ page-1 }}&per_page={{ per_page }}">Previous</a>
|
||||
{% endif %}
|
||||
{% if page < pages %}
|
||||
<a class="rounded-lg border border-slate-300 px-3 py-1" href="?client_id={{ selected_client.id }}&status={{ status_filter }}&customer={{ customer_filter }}&period={{ period_filter }}&page={{ page+1 }}&per_page={{ per_page }}">Next</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</section>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -10,21 +10,44 @@
|
||||
<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. Phase 5 adds review-only accounting tools without Tally write-back.</p>
|
||||
<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="flex flex-wrap gap-2">
|
||||
<a href="/tools/accounting/taxonomy" class="rounded-xl border border-slate-300 bg-white px-4 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-4 py-2 text-sm font-semibold text-slate-700 hover:bg-slate-50">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-4 py-2 text-sm font-semibold text-slate-700 hover:bg-slate-50">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-4 py-2 text-sm font-semibold text-slate-700 hover:bg-slate-50">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-4 py-2 text-sm font-semibold text-slate-700 hover:bg-slate-50">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-4 py-2 text-sm font-semibold text-slate-700 hover:bg-slate-50">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-4 py-2 text-sm font-semibold text-slate-700 hover:bg-slate-50">Post to 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-4 py-2 text-sm font-semibold text-slate-700 hover:bg-slate-50">Bank → Tally</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-4 py-2 text-sm font-semibold text-indigo-800 hover:bg-indigo-100">Accounting AI</a>
|
||||
<a href="/tools/accounting/internal-model" class="rounded-xl border border-emerald-300 bg-emerald-50 px-4 py-2 text-sm font-semibold text-emerald-800 hover:bg-emerald-100">Internal Model</a>
|
||||
{% if selected_client %}<a href="/tools/tally/depreciation?client_id={{ selected_client.id }}" class="rounded-xl border border-slate-300 bg-white px-4 py-2 text-sm font-semibold text-slate-700 hover:bg-slate-50">Depreciation (IT)</a>{% endif %}
|
||||
<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 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>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user