147 lines
5.8 KiB
TypeScript
147 lines
5.8 KiB
TypeScript
|
|
// lib/sars-runners.ts
|
||
|
|
// Registry of SARS return types the workbench knows how to run.
|
||
|
|
//
|
||
|
|
// One entry per return type. Adding a new return type = one entry here.
|
||
|
|
// The form, the API, the results table, and the .py lookup all read
|
||
|
|
// from this — the page and components stay generic.
|
||
|
|
//
|
||
|
|
// Per return type we record:
|
||
|
|
// - The Python script that knows how to capture it (in scripts/).
|
||
|
|
// - The period format the user types (e.g. "YYYYMM" for monthly).
|
||
|
|
// - The period granularity, which drives the period-UX component.
|
||
|
|
// - The columns to render in the results table.
|
||
|
|
// - The PB collection that future per-row writes will target.
|
||
|
|
//
|
||
|
|
// EMP201 + VAT201 are both enabled today. The other 3 are registered
|
||
|
|
// disabled so the dropdown can show them as "coming soon". The VAT201
|
||
|
|
// capture and push scripts are in scripts/ (mirrors of the EMP201
|
||
|
|
// pair) and target PB collection ysterbaard_SARS_VAT_VAT201 (note the
|
||
|
|
// double-VAT in the collection name — that's the actual schema name).
|
||
|
|
|
||
|
|
export type PeriodGranularity = "monthly" | "annual" | "bi-monthly";
|
||
|
|
|
||
|
|
export interface ResultColumn {
|
||
|
|
/** JSON key in the .py's per-row payload. */
|
||
|
|
key: string;
|
||
|
|
/** Column header. */
|
||
|
|
label: string;
|
||
|
|
/** Optional Tailwind class for the cell (e.g. text-right for numbers). */
|
||
|
|
cellClassName?: string;
|
||
|
|
}
|
||
|
|
|
||
|
|
export interface SarsRunner {
|
||
|
|
/** Machine code, matches the SARS form code (also the .py CLI value). */
|
||
|
|
code: string;
|
||
|
|
/** Human-readable label shown in the dropdown. */
|
||
|
|
label: string;
|
||
|
|
/** Script filename inside the scripts/ directory. */
|
||
|
|
script: string;
|
||
|
|
/** Free-text period format the user types. Shown as a hint. */
|
||
|
|
periodFormat: string;
|
||
|
|
/** Drives the period-UX component. */
|
||
|
|
periodGranularity: PeriodGranularity;
|
||
|
|
/** Columns to render in the results table. */
|
||
|
|
resultSchema: ResultColumn[];
|
||
|
|
/** PB collection for future per-row writes (null until wired up). */
|
||
|
|
pbCollection: string | null;
|
||
|
|
/** Push-to-PB script filename in scripts/. null until a push tool exists. */
|
||
|
|
pushScript: string | null;
|
||
|
|
/** Whether the runner is currently wired up and runnable. */
|
||
|
|
enabled: boolean;
|
||
|
|
}
|
||
|
|
|
||
|
|
export const SARS_RUNNERS: SarsRunner[] = [
|
||
|
|
{
|
||
|
|
code: "EMP201",
|
||
|
|
label: "EMP201 (PAYE)",
|
||
|
|
script: "SARS Retun EMP201 single or all pty.py",
|
||
|
|
periodFormat: "YYYYMM",
|
||
|
|
periodGranularity: "monthly",
|
||
|
|
resultSchema: [
|
||
|
|
{ key: "period", label: "Period" },
|
||
|
|
{ key: "date_submitted", label: "Date Submitted", cellClassName: "font-mono text-muted" },
|
||
|
|
{ key: "paye_payable", label: "PAYE", cellClassName: "text-right tabular-nums" },
|
||
|
|
{ key: "sdl_payable", label: "SDL", cellClassName: "text-right tabular-nums" },
|
||
|
|
{ key: "uif_payable", label: "UIF", cellClassName: "text-right tabular-nums" },
|
||
|
|
{ key: "total_payable", label: "Total", cellClassName: "text-right tabular-nums font-semibold" },
|
||
|
|
{ key: "payment_ref_no", label: "Payment Ref" },
|
||
|
|
],
|
||
|
|
pbCollection: "ysterbaard_SARS_EMP201_PAYE",
|
||
|
|
pushScript: "SARS retun EMP201 push to PB.py",
|
||
|
|
enabled: true,
|
||
|
|
},
|
||
|
|
// ── Coming soon ───────────────────────────────────────────────
|
||
|
|
{
|
||
|
|
code: "VAT201",
|
||
|
|
label: "VAT201",
|
||
|
|
script: "SARS Retun VAT201 single or all pty.py",
|
||
|
|
periodFormat: "YYYYMM",
|
||
|
|
// VAT201 is NOT monthly for every company. Each client's SARS
|
||
|
|
// "VAT Return Every" setting (`sars_vat_returns_period`) decides which
|
||
|
|
// months exist — e.g. an uneven-months client has no Feb/Apr/Jun
|
||
|
|
// returns at all, they are non-existent rather than unfiled.
|
||
|
|
//
|
||
|
|
// `monthly` here describes the period *entry format* (the user still
|
||
|
|
// types YYYYMM), not the filing frequency. The per-company cadence is
|
||
|
|
// resolved from PocketBase in the run route and passed to the .py as
|
||
|
|
// `--cadence`; the UI narrows the previewed months with the same table
|
||
|
|
// in lib/vat-cadence.ts.
|
||
|
|
periodGranularity: "monthly",
|
||
|
|
resultSchema: [
|
||
|
|
{ key: "period", label: "Period" },
|
||
|
|
{ key: "date_filed", label: "Date Filed", cellClassName: "font-mono text-muted" },
|
||
|
|
{ key: "total_payable", label: "Total (38)", cellClassName: "text-right tabular-nums font-semibold" },
|
||
|
|
{ key: "standard_rate_1", label: "Std rate (1)", cellClassName: "text-right tabular-nums" },
|
||
|
|
{ key: "zero_rate_2", label: "Zero rate (2)", cellClassName: "text-right tabular-nums" },
|
||
|
|
{ key: "exempt_and_non_supplies_3", label: "Exempt & non (3)", cellClassName: "text-right tabular-nums" },
|
||
|
|
{ key: "capital_goods_and_services_supplied_to_you_14", label: "Capital goods (14)", cellClassName: "text-right tabular-nums" },
|
||
|
|
{ key: "other_goods_and_services_supplied_to_you_15", label: "Other inputs (15)", cellClassName: "text-right tabular-nums" },
|
||
|
|
{ key: "payment_reference", label: "Pay Ref" },
|
||
|
|
],
|
||
|
|
pbCollection: "ysterbaard_SARS_VAT_VAT201",
|
||
|
|
pushScript: "SARS retun VAT201 push to PB.py",
|
||
|
|
enabled: true,
|
||
|
|
},
|
||
|
|
{
|
||
|
|
code: "IRP6",
|
||
|
|
label: "Provisional Tax (IRP6)",
|
||
|
|
script: "",
|
||
|
|
periodFormat: "YYYYMM",
|
||
|
|
periodGranularity: "bi-monthly",
|
||
|
|
resultSchema: [],
|
||
|
|
pbCollection: null,
|
||
|
|
pushScript: null,
|
||
|
|
enabled: false,
|
||
|
|
},
|
||
|
|
{
|
||
|
|
code: "ITR14",
|
||
|
|
label: "Income Tax (ITR14/ITR12T/IT12EI)",
|
||
|
|
script: "",
|
||
|
|
periodFormat: "YYYY",
|
||
|
|
periodGranularity: "annual",
|
||
|
|
resultSchema: [],
|
||
|
|
pbCollection: null,
|
||
|
|
pushScript: null,
|
||
|
|
enabled: false,
|
||
|
|
},
|
||
|
|
{
|
||
|
|
code: "STC",
|
||
|
|
label: "STC",
|
||
|
|
script: "",
|
||
|
|
periodFormat: "YYYY-MM-DD",
|
||
|
|
periodGranularity: "annual",
|
||
|
|
resultSchema: [],
|
||
|
|
pbCollection: null,
|
||
|
|
pushScript: null,
|
||
|
|
enabled: false,
|
||
|
|
},
|
||
|
|
];
|
||
|
|
|
||
|
|
export function getRunner(code: string): SarsRunner | undefined {
|
||
|
|
return SARS_RUNNERS.find((r) => r.code === code);
|
||
|
|
}
|
||
|
|
|
||
|
|
export function listEnabledRunners(): SarsRunner[] {
|
||
|
|
return SARS_RUNNERS.filter((r) => r.enabled);
|
||
|
|
}
|