Adding all functionalities

This commit is contained in:
Muluhabt
2026-08-26 23:57:26 +03:00
parent 70171fa9d8
commit 1587620e14
62 changed files with 14331 additions and 357 deletions

View File

@@ -85,7 +85,11 @@ export function AssetsPage() {
</>
)}
{can(FINANCE_PERMS.asset.depreciate) && (
<Button variant="light" onClick={() => setRunOpen(true)}>
<Button
data-testid="run-depreciation-btn"
variant="light"
onClick={() => setRunOpen(true)}
>
Run depreciation
</Button>
)}
@@ -135,7 +139,7 @@ export function AssetsPage() {
</Table.Thead>
<Table.Tbody>
{(assets.data ?? []).map((a) => (
<Table.Tr key={a.id}>
<Table.Tr key={a.id} data-testid={`asset-row-${a.id}`}>
<Table.Td><Text ff="monospace" size="sm">{a.assetCode}</Text></Table.Td>
<Table.Td>{a.name}</Table.Td>
<Table.Td>
@@ -160,6 +164,7 @@ export function AssetsPage() {
{a.status !== "DISPOSED" && a.status !== "WRITTEN_OFF" &&
can(FINANCE_PERMS.asset.dispose) && (
<Button
data-testid={`dispose-btn-${a.id}`}
size="xs"
variant="light"
color="orange"
@@ -394,7 +399,12 @@ function RunModal({
/>
<Group justify="flex-end">
<Button variant="default" onClick={onClose}>Cancel</Button>
<Button loading={run.isPending} disabled={!periodId} onClick={() => run.mutate()}>
<Button
data-testid="run-depreciation-confirm-btn"
loading={run.isPending}
disabled={!periodId}
onClick={() => run.mutate()}
>
Run
</Button>
</Group>

View File

@@ -106,12 +106,18 @@ export function BudgetsPage() {
)}
{isDraft &&
(can(FINANCE_PERMS.budget.approve) ? (
<Button loading={approve.isPending} onClick={() => approve.mutate()}>
<Button
data-testid="budget-approve-btn"
loading={approve.isPending}
onClick={() => approve.mutate()}
>
Approve
</Button>
) : (
<Tooltip label="Approving a budget is a separate authorisation from preparing it.">
<Button disabled>Approve</Button>
<Button data-testid="budget-approve-btn" disabled>
Approve
</Button>
</Tooltip>
))}
{can(FINANCE_PERMS.budget.manage) && (
@@ -138,6 +144,7 @@ export function BudgetsPage() {
<Group mb="md" gap="sm" align="flex-end">
<Select
label="Budget"
data-testid="budget-select"
data={(budgets.data ?? []).map((b) => ({
value: b.id,
label: `${b.name} (${b.status})`,
@@ -228,7 +235,10 @@ export function BudgetsPage() {
</Table.Thead>
<Table.Tbody>
{rows.map((r) => (
<Table.Tr key={`${r.accountId}-${r.costCenterId ?? "none"}`}>
<Table.Tr
key={`${r.accountId}-${r.costCenterId ?? "none"}`}
data-testid={`budget-variance-row-${r.accountId}-${r.costCenterId ?? "none"}`}
>
<Table.Td>
<Text ff="monospace" size="sm">{r.accountCode}</Text>
</Table.Td>
@@ -303,7 +313,7 @@ export function BudgetsPage() {
</Table.Thead>
<Table.Tbody>
{(budget.data?.lines ?? []).map((l) => (
<Table.Tr key={l.id}>
<Table.Tr key={l.id} data-testid={`budget-line-row-${l.id}`}>
<Table.Td><Text ff="monospace" size="sm">{l.accountCode}</Text></Table.Td>
<Table.Td>{l.accountName?.en}</Table.Td>
<Table.Td>

View File

@@ -121,7 +121,7 @@ export function CutoverPage() {
description="Going live: the opening balances, and whether the books are ready to carry them."
actions={
canManage ? (
<Button onClick={() => setDateOpen(true)}>
<Button data-testid="cutover-set-date-btn" onClick={() => setDateOpen(true)}>
{cutoverDate ? "Change cutover date" : "Set cutover date"}
</Button>
) : undefined
@@ -159,7 +159,12 @@ export function CutoverPage() {
{(readiness.data?.checks ?? []).map((check) => {
const Icon = CHECK_ICON[check.status];
return (
<Group key={check.key} align="flex-start" wrap="nowrap">
<Group
key={check.key}
data-testid={`cutover-readiness-check-${check.key}`}
align="flex-start"
wrap="nowrap"
>
<ThemeIcon
size="sm"
radius="xl"
@@ -255,6 +260,7 @@ export function CutoverPage() {
<Textarea
label="Rows"
data-testid="cutover-import-paste"
placeholder={SAMPLE}
autosize
minRows={6}
@@ -264,7 +270,7 @@ export function CutoverPage() {
/>
{parsed.length > 0 ? (
<>
<div data-testid="cutover-import-preview">
{bad.length > 0 ? (
<Alert color="red" mt="md" title={`${bad.length} row(s) could not be read`}>
<Stack gap={4}>
@@ -380,7 +386,7 @@ export function CutoverPage() {
put a wrong plug in the ledger.
</Text>
) : null}
</>
</div>
) : null}
</Card>
) : null}

View File

@@ -130,12 +130,18 @@ export function JournalDetailPage() {
rather than left as an absence. */}
{isDraft &&
(can(FINANCE_PERMS.journal.post) ? (
<Button loading={post.isPending} onClick={() => post.mutate()}>
<Button
data-testid="journal-post-btn"
loading={post.isPending}
onClick={() => post.mutate()}
>
Post to ledger
</Button>
) : (
<Tooltip label="Posting is a separate approval from preparing. Ask someone who holds it.">
<Button disabled>Post to ledger</Button>
<Button data-testid="journal-post-btn" disabled>
Post to ledger
</Button>
</Tooltip>
))}

View File

@@ -186,7 +186,7 @@ export function NewJournalPage() {
</Table.Thead>
<Table.Tbody>
{lines.map((line) => (
<Table.Tr key={line.key}>
<Table.Tr key={line.key} data-testid={`journal-line-row-${line.key}`}>
<Table.Td>
<Select
placeholder="Select an account"
@@ -293,11 +293,21 @@ export function NewJournalPage() {
Enter the debits and credits.
</Text>
) : balanced ? (
<Alert color="green" variant="light" py={6}>
<Alert
color="green"
variant="light"
py={6}
data-testid="journal-balance-indicator"
>
Balanced {formatMoney(totals.debit)} on each side
</Alert>
) : (
<Alert color="red" variant="light" py={6}>
<Alert
color="red"
variant="light"
py={6}
data-testid="journal-balance-indicator"
>
Out of balance by {formatMoney(Math.abs(totals.difference))} {" "}
{totals.difference > 0 ? "credits" : "debits"} are short
</Alert>

View File

@@ -125,6 +125,7 @@ export function PayablesPage() {
return (
<Table.Tr
key={b.id}
data-testid={`bill-row-${b.id}`}
style={{ cursor: "pointer" }}
onClick={() => setOpenBillId(b.id)}
>
@@ -390,18 +391,26 @@ function BillDrawer({
<Group justify="flex-end">
{data.status === "DRAFT" &&
(can(FINANCE_PERMS.payable.approveBill) ? (
<Button loading={approve.isPending} onClick={() => approve.mutate()}>
<Button
data-testid="bill-approve-btn"
loading={approve.isPending}
onClick={() => approve.mutate()}
>
Approve &amp; post
</Button>
) : (
<Tooltip label="Approving is a separate authorisation from entering the bill. Ask someone who holds it.">
<Button disabled>Approve &amp; post</Button>
<Button data-testid="bill-approve-btn" disabled>
Approve &amp; post
</Button>
</Tooltip>
))}
{outstanding > 0 &&
data.status !== "DRAFT" &&
can(FINANCE_PERMS.payable.recordPayment) && (
<Button onClick={() => setPayOpen(true)}>Record payment</Button>
<Button data-testid="bill-record-payment-btn" onClick={() => setPayOpen(true)}>
Record payment
</Button>
)}
</Group>
@@ -508,6 +517,7 @@ function PaymentModal({
<Group justify="flex-end">
<Button variant="default" onClick={onClose}>Cancel</Button>
<Button
data-testid="bill-record-payment-confirm-btn"
loading={pay.isPending}
disabled={!accountId || !amount}
onClick={() => pay.mutate()}

View File

@@ -166,7 +166,7 @@ export function ReportsPage() {
// unused side of a one-sided row stays blank.
const netZero = !Number(r.debit) && !Number(r.credit);
return (
<Table.Tr key={r.accountCode}>
<Table.Tr key={r.accountCode} data-testid={`report-trial-balance-row-${r.accountCode}`}>
<Table.Td><Text ff="monospace" size="sm">{r.accountCode}</Text></Table.Td>
<Table.Td>{r.accountName?.en}</Table.Td>
<Table.Td><Text size="sm" c="dimmed">{r.accountType}</Text></Table.Td>
@@ -240,9 +240,19 @@ export function ReportsPage() {
{bs.data?.balanced ? (
<Alert color="green" variant="light" icon={<IconCheck size={18} />}>
<Text size="sm">
Balanced as at {bs.data.asOf} assets {formatMoney(bs.data.totalAssets)} =
liabilities {formatMoney(bs.data.totalLiabilities)} + equity{" "}
{formatMoney(bs.data.equityWithResult)}.
Balanced as at {bs.data.asOf} assets{" "}
<span data-testid="report-bs-total-assets">
{formatMoney(bs.data.totalAssets)}
</span>{" "}
= liabilities{" "}
<span data-testid="report-bs-total-liabilities">
{formatMoney(bs.data.totalLiabilities)}
</span>{" "}
+ equity{" "}
<span data-testid="report-bs-total-equity">
{formatMoney(bs.data.equityWithResult)}
</span>
.
</Text>
</Alert>
) : (