WORKFLOW-APP/lib/types.ts

356 lines
11 KiB
TypeScript

// lib/types.ts
// Mirrors the two PocketBase collections used by the app. Field names
// match the live PB schema exactly (case-sensitive). Optional fields
// are nullable on the wire; we narrow them to `T | null` so callers
// have to handle empty / missing cases.
export interface YsterbaardDirector {
[key: string]: unknown;
id: string;
collectionId: string;
collectionName: string;
/** 13-digit SA ID number, e.g. "8001015009087". */
id_no: string;
surname: string;
first_names: string;
/** True if Ysterbaard / SJ is the appointed accountant for this director. */
is_client: boolean;
/** Free-text from CIPC: "Active", "Disqualified", etc. */
disqualification_status: string | null;
/** SA-format phone or empty. Pattern ^[0-9 +()\-]{0,30}$ enforced in PB. */
telephone: string | null;
email: string | null;
total_companies: number;
active_companies: number;
inactive_companies: number;
first_seen: string | null; // ISO date
last_checked: string | null; // ISO date
notes: string | null;
created: string;
updated: string;
}
export interface YsterbaardGroup {
[key: string]: unknown;
id: string;
collectionId: string;
collectionName: string;
name: string;
description: string | null;
created: string;
updated: string;
}
export interface YsterbaardCompany {
[key: string]: unknown;
id: string;
collectionId: string;
collectionName: string;
/** Unique CIPC enterprise number, e.g. "K2020123456". */
enterprise_no: string;
enterprise_name: string;
/** e.g. "In Business", "Deregistered", "In Process". */
enterprise_status: string | null;
/** Per-director status: "ACTIVE", "INACTIVE", "DISQUALIFIED", etc. */
director_status: string | null;
is_active: boolean;
/** True if Ysterbaard / SJ is the appointed accountant for this company. */
is_client: boolean;
/** Relation -> ysterbaard_directors. Expanded manually below. */
director: string;
/** Relation -> groups (multiple). Expanded manually below. */
groups: string[];
/** Expanded groups payload (when `?expand=groups` is used). */
expand?: {
director?: Pick<
YsterbaardDirector,
"id" | "id_no" | "surname" | "first_names" | "is_client"
>;
groups?: YsterbaardGroup[];
};
source: string | null;
notes: string | null;
enterprise_type: string | null;
registration_date: string | null;
physical_address: string | null;
postal_address: string | null;
physical_address_normalized: string | null;
postal_address_normalized: string | null;
company_email: string | null;
sars_tax_number: string | null;
uif_registration_number: string | null;
compensation_fund_registration_number: string | null;
/** SARS eFiling username */
sars_login_username: string | null;
/** SARS eFiling password */
sars_login_password: string | null;
/** SARS PAYE reference number */
sars_paye_no: string | null;
/** SARS VAT registration number */
sars_vat_no: string | null;
/** SARS VAT returns frequency: 0=None, 1=Every Month, 2=Even Month, 3=Uneven Months, 6=Bi Annually, 12=Annually */
sars_vat_returns_period: number | null;
/** SARS PAYE returns frequency: 0=None, 1=Every month */
sars_paye_return_period: number | null;
compliance_notice: string | null;
bbbee_status: string | null;
bbbee_certificate_applied: boolean;
info_regulator_registered: boolean;
ar_outstanding: boolean;
ar_outstanding_details: string | null;
last_5_ars: unknown[] | Record<string, unknown> | null;
enterprise_history: unknown[] | Record<string, unknown> | null;
first_seen: string | null;
last_checked: string | null;
created: string;
updated: string;
}
/** PocketBase paginated response envelope. */
export interface PBList<T> {
page: number;
perPage: number;
totalItems: number;
totalPages: number;
items: T[];
}
/** Standard query params we forward to the proxy on every list call. */
export interface ListParams {
page?: number;
perPage?: number;
sort?: string;
filter?: string;
expand?: string;
fields?: string;
}
export type CompanyStatusFilter = "all" | "active" | "inactive" | "in_process" | "deregistered";
// ─────────────────────────────────────────────────────────────────────────
// Onboarding intake — paperless replacement for the Annexure 1/2/3 pack.
// A single submission holds company + directors + shareholders as JSON
// blobs, with uploaded documents attached. Once processed into the live
// `ysterbaard_directors` records, the row stays around for audit until
// archived (status='rejected') and deleted by hand.
// ─────────────────────────────────────────────────────────────────────────
export interface OnboardingDirector {
/** Generated client-side, never persisted as-is — just a UI key. */
ui_key: string;
first_names: string;
surname: string;
id_no: string;
date_of_birth: string; // YYYY-MM-DD
residential_address: string;
residential_address_line_1: string;
residential_address_line_2: string;
residential_town_city: string;
residential_province: string;
residential_postal_code: string;
postal_address: string;
postal_address_line_1: string;
postal_address_line_2: string;
postal_town_city: string;
postal_province: string;
postal_postal_code: string;
cell_phone: string;
email: string;
}
export interface OnboardingShareholder {
ui_key: string;
/** "person" or "entity" — toggles whether the next field validates as ID or company number. */
kind: "person" | "entity";
name: string;
id_or_company_no: string;
share_percent: number;
}
export interface OnboardingCompany {
proposed_names: [string, string, string]; // up to 3 in priority order
financial_year_end_month: number; // 1-12
authorised_shares: "100" | "1000";
physical_address: string;
physical_address_line_1: string;
physical_address_line_2: string;
physical_town_city: string;
physical_province: string;
physical_postal_code: string;
postal_address: string;
postal_address_line_1: string;
postal_address_line_2: string;
postal_town_city: string;
postal_province: string;
postal_postal_code: string;
company_cell_phone: string;
company_email: string;
public_officer_name: string;
company_secretary_name: string;
}
export type OnboardingDirectorPayload = Omit<OnboardingDirector, "ui_key">;
export type OnboardingShareholderPayload = Omit<OnboardingShareholder, "ui_key">;
export interface OnboardingSubmissionPayload {
/** Client may omit this — server mints a fresh reference_no on submit. */
reference_no?: string;
client_name: string;
client_email: string;
client_telephone: string;
/** Free-form notes from the client (may have been captured via voice). */
client_notes?: string;
company: OnboardingCompany;
directors: OnboardingDirectorPayload[];
shareholders: OnboardingShareholderPayload[];
}
export type OnboardingStatus =
| "draft"
| "submitted"
| "processing"
| "processed"
| "rejected";
export interface OnboardingSubmission {
[key: string]: unknown;
id: string;
collectionId: string;
collectionName: string;
created: string;
updated: string;
reference_no: string;
access_token_hash: string;
client_name: string;
client_email: string;
client_telephone: string | null;
company_data: OnboardingCompany;
directors_data: OnboardingDirector[];
shareholders_data: OnboardingShareholder[] | null;
processed_director_ids: string[] | null;
documents: string[];
submitted_at: string | null;
processed_at: string | null;
internal_notes: string | null;
status: OnboardingStatus;
}
export type ResolutionType =
| "company_registration"
| "director_resignation"
| "director_appointment_acceptance"
| "resolution_appointment_resignation_directors"
| "general_resolution";
export type ResolutionStatus = "draft" | "pending" | "completed" | "archived";
// ─────────────────────────────────────────────────────────────────────────
// Document management — types and document types taxonomy.
// ─────────────────────────────────────────────────────────────────────────
export type DocumentContext = "director" | "company";
export interface YsterbaardDocumentType {
[key: string]: unknown;
id: string;
collectionId: string;
collectionName: string;
name: string;
/** "director" | "company" */
context: DocumentContext;
description: string | null;
created: string;
updated: string;
}
export interface YsterbaardDirectorDocument {
[key: string]: unknown;
id: string;
collectionId: string;
collectionName: string;
director: string;
document_type: string;
file: string;
original_name: string;
notes: string | null;
uploaded_at: string;
created: string;
updated: string;
expand?: {
document_type?: Pick<YsterbaardDocumentType, "id" | "name" | "description">;
};
}
export interface YsterbaardCompanyDocument {
[key: string]: unknown;
id: string;
collectionId: string;
collectionName: string;
company: string;
document_type: string;
file: string;
original_name: string;
notes: string | null;
uploaded_at: string;
created: string;
updated: string;
expand?: {
document_type?: Pick<YsterbaardDocumentType, "id" | "name" | "description">;
};
}
export interface YsterbaardResolution {
[key: string]: unknown;
id: string;
collectionId: string;
collectionName: string;
/** Resolution type drives which tab it appears under. */
type: ResolutionType;
/** Workflow state of this resolution. */
status: ResolutionStatus;
/** Short human-readable label, e.g. "Onboarding — M Smith". */
title: string | null;
/** Related company (optional — not all resolutions are company-specific). */
company: string | null;
/** Related director (optional). */
director: string | null;
/** For onboarding: the client's full name. */
client_name: string | null;
/** For onboarding: SA ID number. */
client_id_no: string | null;
/** For onboarding: client's email address. */
client_email: string | null;
/** Free-form notes, e.g. "CIPC returned — resubmit". */
notes: string | null;
/** ISO date string of the resolution itself. */
resolution_date: string | null;
created: string;
updated: string;
}
export interface YsterbaardDirectorAppointment {
[key: string]: unknown;
id: string;
collectionId: string;
collectionName: string;
director: string;
company: string;
status: "pending" | "mail_sent" | "signature_received" | "mail_sent_to_all";
letter: string | null;
signature: string | null;
sent_to: string | null;
sent_at: string | null;
sign_token: string | null;
expand?: {
director?: Pick<
YsterbaardDirector,
"id" | "first_names" | "surname" | "email" | "cell" | "id_no"
>;
company?: Pick<
YsterbaardCompany,
"id" | "enterprise_name" | "enterprise_no"
>;
};
created: string;
updated: string;
}