Files
inven/app/accounting/page.tsx
2026-03-23 16:16:45 -05:00

93 lines
4.8 KiB
TypeScript

import { createAccount, createManualJournalEntry } from "@/lib/actions";
import { formatCurrency, formatDate } from "@/lib/format";
import { getAccountBalances, getAccounts, getJournalEntries } from "@/lib/repository";
export default function AccountingPage() {
const entries = getJournalEntries();
const balances = getAccountBalances();
const accounts = getAccounts();
return (
<div className="grid">
<section className="two-up">
<article className="panel">
<h2 className="section-title">Chart of Accounts</h2>
<p className="section-copy">Seeded operational accounts are ready, and you can add your own accounts here.</p>
<form action={createAccount} className="form-grid">
<div className="form-row"><label htmlFor="account-code">Code</label><input className="input" id="account-code" name="code" required /></div>
<div className="form-row"><label htmlFor="account-name">Name</label><input className="input" id="account-name" name="name" required /></div>
<div className="form-row">
<label htmlFor="account-category">Category</label>
<select className="select" id="account-category" name="category" defaultValue="expense">
<option value="asset">Asset</option>
<option value="liability">Liability</option>
<option value="equity">Equity</option>
<option value="revenue">Revenue</option>
<option value="expense">Expense</option>
</select>
</div>
<button className="button secondary" type="submit">Add Account</button>
</form>
</article>
<article className="panel">
<h2 className="section-title">Manual Journal Entry</h2>
<p className="section-copy">Enter one line per row as `account code,debit,credit`. Debits and credits must balance.</p>
<form action={createManualJournalEntry} className="form-grid">
<div className="form-row"><label htmlFor="description">Description</label><input className="input" id="description" name="description" /></div>
<div className="form-row"><label htmlFor="lines">Lines</label><textarea className="textarea" id="lines" name="lines" placeholder={"1000,250,0\n3000,0,250"} required /></div>
<button className="button" type="submit">Post Journal Entry</button>
</form>
<div className="muted">
Available accounts: {accounts.map((account) => `${account.code} ${account.name}`).join(" | ")}
</div>
</article>
</section>
<section className="panel">
<h2 className="section-title">Account Balances</h2>
<p className="section-copy">Balances are rolled up from every journal line currently posted.</p>
<div className="table-wrap">
<table className="table">
<thead><tr><th>Code</th><th>Account</th><th>Category</th><th>Debits</th><th>Credits</th><th>Balance</th></tr></thead>
<tbody>
{balances.map((balance) => (
<tr key={balance.code}>
<td>{balance.code}</td>
<td>{balance.name}</td>
<td>{balance.category}</td>
<td>{formatCurrency(balance.debitTotal)}</td>
<td>{formatCurrency(balance.creditTotal)}</td>
<td>{formatCurrency(balance.balance)}</td>
</tr>
))}
</tbody>
</table>
</div>
</section>
<section className="panel">
<h2 className="section-title">Accounting Journal</h2>
<p className="section-copy">Purchase receipts and sales shipments automatically create operational journal entries.</p>
<div className="table-wrap">
<table className="table">
<thead><tr><th>When</th><th>Type</th><th>Reference</th><th>Description</th><th>Lines</th></tr></thead>
<tbody>
{entries.length === 0 ? (
<tr><td colSpan={5} className="muted">Journal entries appear after PO receipts and SO shipments.</td></tr>
) : (
entries.map((entry) => (
<tr key={entry.id}>
<td>{formatDate(entry.createdAt)}</td>
<td>{entry.entryType}</td>
<td>{entry.referenceType} #{entry.referenceId ?? "n/a"}</td>
<td>{entry.description}</td>
<td>{entry.lines.map((line, index) => <div key={`${entry.id}-${index}`} className="muted">{line.accountCode} {line.accountName}: {formatCurrency(line.debit)} / {formatCurrency(line.credit)}</div>)}</td>
</tr>
))
)}
</tbody>
</table>
</div>
</section>
</div>
);
}