Workflow-App/Workflow/app/api/compliance/route.ts
sjcoetzeesj-coder 11d0178ab1 feat: commit Workflow app source; move CIPC credentials to env
Adds the full Next.js app (app/, components/, lib/, scripts/, assets/,
docs/, Dockerfile, compose, configs) to version control.

Security:
- CIPC BizPortal customer-code logins (Login 1 + Login 3) now read
  CIPC_LOGIN1_CODE / CIPC_LOGIN1_PASSWORD / CIPC_LOGIN3_CODE /
  CIPC_LOGIN3_PASSWORD from .env.local instead of source.
- BizPortal personal login in apply-bizportal route reads
  BIZPORTAL_ID_NO / BIZPORTAL_PASSWORD.
- Added .env.example as the committed template.

.gitignore excludes .env*, node_modules, .next, logs, sqlite, Playwright
storage_state.json (live CIPC cookies), scratch dirs, client PDFs and
debug screenshots.
2026-10-18 15:21:45 +02:00

121 lines
3.4 KiB
TypeScript

// app/api/compliance/route.ts
// GET /api/compliance?groupId=<group_id>
// Fetches client companies + annual returns + IRP6/IncomeTax + PAYE EMP201 data
import { NextRequest, NextResponse } from "next/server";
import { fetchCollection, type PBRecord } from "@/lib/pb";
export const dynamic = "force-dynamic";
type ComplianceCompany = PBRecord & {
enterprise_name: string;
enterprise_no: string;
registration_date: string;
financial_year_end_month: number;
groups: string[];
sars_paye_no: string | null;
};
type AnnualReturn = PBRecord & {
company: string;
ar_year: number;
ar_month: string;
is_filed: boolean;
date_filed: string;
amount_paid: number;
};
type IRP6Record = PBRecord & {
company: string;
return_type: string;
period: string;
date_filed: string;
amount_paid: number;
};
type PayeRecord = PBRecord & {
company: string;
period: string;
Return_Filed: boolean;
total_payable: number;
paye_amount: number;
sdl_amount: number;
uif_amount: number;
date_submitted: string;
date_paid: string;
due_date: string;
payment_reference: string;
status: string;
last_updated: string;
};
type VatRecord = PBRecord & {
company: string;
period: string;
Return_Filed: boolean;
total_payable: number;
date_submitted: string;
date_paid: string;
due_date: string;
payment_reference: string;
status: string;
last_updated: string;
};
export async function GET(req: NextRequest) {
const groupId = new URL(req.url).searchParams.get("groupId") ?? "";
try {
const filterParts: string[] = ["is_client = true"];
if (groupId) filterParts.push(`groups ?~ '${groupId}'`);
const companies = await fetchCollection<ComplianceCompany>("ysterbaard_companies", {
filter: filterParts.join(" && "),
sort: "enterprise_name",
perPage: 500,
fields:
"id,enterprise_name,enterprise_no,registration_date,financial_year_end_month,groups,sars_paye_no,sars_vat_no,sars_vat_returns_period",
});
const companyIds = companies.items.map((c) => c.id);
if (companyIds.length === 0) {
return NextResponse.json({ companies: [], annualReturns: [], irp6Records: [], payeRecords: [], vatRecords: [] });
}
const companyIdFilter = companyIds.map((id) => `company = '${id}'`).join(" || ");
const [annualReturns, irp6Records, payeRecords, vatRecords] = await Promise.all([
fetchCollection<AnnualReturn>("ysterbaard_annual_returns", {
filter: `(${companyIdFilter})`,
perPage: 1000,
sort: "-ar_year,-ar_month",
}),
fetchCollection<IRP6Record>("ysterbaard_sars_irp6_income_tax", {
filter: `(${companyIdFilter})`,
perPage: 2000,
sort: "-period",
}),
fetchCollection<PayeRecord>("ysterbaard_SARS_EMP201_PAYE", {
filter: `(${companyIdFilter})`,
perPage: 2000,
sort: "-period",
}),
fetchCollection<VatRecord>("ysterbaard_SARS_VAT_VAT201", {
filter: `(${companyIdFilter})`,
perPage: 2000,
sort: "-period",
}),
]);
return NextResponse.json({
companies: companies.items,
annualReturns: annualReturns.items,
irp6Records: irp6Records.items,
payeRecords: payeRecords.items,
vatRecords: vatRecords.items,
});
} catch (err) {
console.error("[/api/compliance]", err);
return NextResponse.json({ error: String(err) }, { status: 500 });
}
}