Major expansion: HR module, CRM, integrations, packages, validation pipeline
Validate / validate (push) Successful in 34s

HR module:
- Multi-role per company (admin/manager/user/viewer/hr orthogonal)
- Employees with salary history, terminate/reactivate
- Per-company public holidays (seeded from ppraserts/thailand-open-data
  with manual fallback for unsupported years)
- Leave types (editable defaults), leave requests with approve/reject
- Per-employee leave balances (auto-seeded), remaining-days hint on
  request form, HR balance summary on requests page
- Thai-compliant payroll: SSO 5% capped, PND1 brackets, monthly WHT
- Payslip generation with editable line items, finalize/mark-paid,
  pdf-lib PDF download
- CSV export of leave per employee or company-wide

CRM & invoicing:
- Customer/supplier party database with archive
- Invoice line items, VAT 7%, status transitions, PDF generation
- Outgoing/incoming direction; incoming auto-creates linked expense

Package tracking:
- packages + package_events + shipping_accounts tables
- 8 carrier stubs (UPS/FedEx/DHL/USPS/Flash Express/Kerry/J&T/TH Post)
  with API doc references for future implementation
- Manual status updates with timeline
- Customs duty invoice flow on delivery
- Per-company carrier credentials (admin only)

Integrations scaffold:
- external_accounts + external_transactions (Kasikorn K-Biz, Ether.fi)
- Manual transaction matching to expenses

Infrastructure:
- APP_NAME env var for branding
- Soft-delete for companies and parties
- Light/dark mode toggle, dark-mode classes throughout
- pre-push hook (husky) + Gitea/GitHub Actions running svelte-check
  with --threshold warning + vite build
- npm run validate combines both checks

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-14 16:35:13 +07:00
parent 765bf0d402
commit b6f07fe4df
98 changed files with 12012 additions and 145 deletions
+31
View File
@@ -0,0 +1,31 @@
/**
* Tiny CSV serializer. RFC 4180 — quotes fields that contain commas, quotes, or newlines.
*/
export function csvEscape(value: unknown): string {
if (value === null || value === undefined) return '';
const s = String(value);
if (/[",\n\r]/.test(s)) {
return `"${s.replace(/"/g, '""')}"`;
}
return s;
}
export function csvRow(values: unknown[]): string {
return values.map(csvEscape).join(',');
}
export function csvBuild(rows: unknown[][]): string {
return rows.map(csvRow).join('\r\n');
}
/** Build a Response with a CSV download. */
export function csvResponse(csv: string, filename: string): Response {
// Prepend BOM so Excel detects UTF-8 (important for Thai characters)
const body = '\uFEFF' + csv;
return new Response(body, {
headers: {
'Content-Type': 'text/csv; charset=utf-8',
'Content-Disposition': `attachment; filename="${filename}"`
}
});
}