Commit Graph

50 Commits

Author SHA1 Message Date
grabowski 65cee9855c Add procedures templates, step management, and nav tab
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-17 14:51:29 +07:00
grabowski f1dd6877f6 Add procedures schema: templates, steps, instances, 7 audit events
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-17 14:47:36 +07:00
grabowski 8a23a849da Fix CSP: allow unsafe-inline scripts for SvelteKit hydration
Deploy to LXC / deploy (push) Successful in 1m56s
Validate / validate (push) Successful in 38s
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-17 14:32:12 +07:00
grabowski b4eda2d553 Fix security audit findings: auth scoping, OIDC hardening, CSP, file download
Deploy to LXC / deploy (push) Successful in 1m56s
Validate / validate (push) Successful in 33s
C3: Budget allocation now verifies project belongs to company
M4: Expense approve/reject scoped by company via project join
H2: OIDC cookies get secure flag on HTTPS
H3: OIDC auto-link only when email_verified by provider
H4: Content-Security-Policy + X-Content-Type-Options in hooks
M7: SSRF favicon redirect depth capped at 3
M2: File downloads use attachment disposition (not inline)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-17 14:18:28 +07:00
grabowski dbfd229ba8 Link service accounts to recurring bills with dropdown and display chip
Deploy to LXC / deploy (push) Successful in 2m1s
Validate / validate (push) Successful in 35s
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-17 13:58:43 +07:00
grabowski 1ce614186d Add service accounts page with CRUD, filter pills, and nav tab
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-17 13:52:33 +07:00
grabowski 493ffa4097 Add service accounts schema, enum, audit events, recurringBills FK
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-17 13:49:16 +07:00
grabowski fef69b653c Add inline rename/edit on project detail page
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 16:09:52 +07:00
grabowski 57f3d42133 Redesign company overview: 4 compact KPIs, side-by-side projects + recent expenses
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 16:06:19 +07:00
grabowski f51e156539 Restructure company nav: 8 primary tabs + HR/Ops/Admin dropdowns with active highlight
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 15:59:25 +07:00
grabowski 03526ff3b9 Restore pointer cursor on buttons (Tailwind v4 Preflight reset)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 15:35:46 +07:00
grabowski b43924f527 Add recurring bills UI with full CRUD, filters, overdue highlight, amount override
Validate / validate (push) Successful in 33s
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 15:23:53 +07:00
grabowski b611207d25 Add recurring bills poster, scheduler boot, and manual run stub
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 15:17:38 +07:00
grabowski bd87cd09f5 Add recurring bills schema and cycle math helper
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 15:13:38 +07:00
grabowski 70bb5954a0 Make entire account card clickable to open detail
Stretched-link pattern: absolute-positioned overlay anchor covers the
card; action controls (edit/archive/delete + inline forms) get
`relative z-10` so they float above and stay clickable.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 14:25:03 +07:00
grabowski c1a575241f Fix zero balance on accounts list page
The correlated subquery in the SELECT was returning 0 for every row.
Replaced with a separate grouped-sum query joined in JS — same data, more
reliable SQL generation.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 14:17:33 +07:00
grabowski 77c5d72e43 Reconciliation link, account CSVs in export, drop legacy bank/card tables
Validate / validate (push) Successful in 31s
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 14:06:53 +07:00
grabowski 0d4fdb6fd7 Add account detail page with transaction history, filters, and CSV export
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 13:58:44 +07:00
grabowski 3a095851e9 Auto-post expenses and invoice payments to accounts ledger
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 12:04:15 +07:00
grabowski d75fe6ed95 Add opening balance, manual transactions, and cross-currency transfers
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 11:54:10 +07:00
grabowski aea6dbc06e Add accounts list page with CRUD, Accounts nav tab, profile deprecation banner
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 11:50:40 +07:00
grabowski 57e72e5b6c Add companyAccounts schema, ledger helper, legacy migration script
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 11:34:57 +07:00
grabowski 2c2353e2e7 Wire favicon fetch and refresh action into links page
Validate / validate (push) Successful in 27s
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 11:56:31 +07:00
grabowski 1ef68a4d0d Add personal bookmarks CRUD on links page
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 11:54:11 +07:00
grabowski ca0335671c Add company links page with CRUD and Links nav tab
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 11:49:04 +07:00
grabowski 84a98efd6e Add companyLinks schema, favicon helper with SSRF guard, URL validator
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 11:45:08 +07:00
grabowski 5d9c0f0249 Add Documents tab and include document metadata in financial export
Validate / validate (push) Successful in 26s
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 11:11:54 +07:00
grabowski 2489b092af Add document detail page with versions and download endpoint
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 11:10:41 +07:00
grabowski a198bae9be Add company documents list and upload page
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 10:59:50 +07:00
grabowski f69313bf33 Add company documents schema, uploads helper, and env wiring
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 10:52:06 +07:00
grabowski eceda5f007 Add company bank/cards/addresses to financial export ZIP
Validate / validate (push) Successful in 24s
Three new CSVs in the export bundle, with matching README entries:
- company_bank_accounts.csv
- company_cards.csv (last4 only, joined with linked bank account name)
- company_addresses.csv

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 10:34:52 +07:00
grabowski 504fbadec4 Add Profile tab to company nav
Visible to admin, manager, and accountant. Placed between
Integrations and Export so the broader-audience tab appears first.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 10:32:44 +07:00
grabowski 7d58a1a1c6 Add company Profile page with bank accounts, cards, and addresses
New /companies/[id]/profile route. Single page, three sections:

Bank Accounts:
- Table view with masked account numbers (••••1234)
- Add form (always-on inline) with bank, account holder, number,
  type, branch, SWIFT/BIC, IBAN, currency, primary toggle, notes
- Inline edit row (admin only) and Set Primary / Remove actions
- Audit log: bank_account_added/updated/removed

Cards:
- Add form gates input to last 4 digits (maxlength=4 + regex)
- Amber warning: "Last 4 digits only — never enter full PAN"
- Optional link to a bank account
- Brand select (Visa/Mastercard/Amex/JCB/UnionPay/Discover/Other)
- Audit log: card_added/removed (no edit — remove + re-add)

Addresses:
- Type enum (Legal/Shipping/Billing/Other) with type-coloured badges
- Grouped by type, default flag scoped per type
- Full Thai address fields plus contact person/phone
- Inline edit per row, Set Default per type, Remove
- Audit log: address_added/updated/removed

Read access: admin/manager/accountant. Edit: admin only.
All forms use enhance with reset:false to preserve state on error.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 10:31:24 +07:00
grabowski 92a07685b0 Add company profile schema (bank accounts, cards, addresses)
Three new per-company tables backing the upcoming Profile page:

- company_bank_accounts: bank/account name, account number, type,
  branch, SWIFT/BIC, IBAN, currency (default THB), isPrimary,
  isActive, notes
- company_cards: brand (visa/mastercard/amex/jcb/unionpay/discover/
  other), last4 (varchar(4)), cardholder, expiry month/year,
  nickname, optional FK to a bank account. Stores ONLY last 4
  digits — never the full PAN, to avoid PCI-DSS scope.
- company_addresses: type enum (legal/shipping/billing/other),
  label, recipient, full Thai address fields (subdistrict/district/
  province/postal code), country defaulting to Thailand, contact
  person + phone, isDefault, notes

Eight new audit events in companyLogEventEnum cover add/update/
remove operations on each. Page UI and export integration follow.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 10:15:56 +07:00
grabowski 51e8cfc536 Include personal/address/emergency columns in financial export employees.csv
Validate / validate (push) Successful in 25s
Extends the employees.csv builder to include all 14 new fields
(DOB, gender, nationality, marital status, full Thai address,
emergency contact). Order matches the on-screen detail page so
auditors and accountants get the complete record.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 10:02:17 +07:00
grabowski f12c901a97 Show personal/address/emergency on employee detail and in edit modal
Detail page now has three new cards beneath the main employee block:
- Personal: DOB (with computed age), gender, nationality, marital status
- Address: combined one-line address plus a labelled grid for the
  Thai-specific subdistrict/district/province/postal code parts
- Emergency Contact: name, phone, relationship

Edit modal extends with matching sections so HR/admin can update
all 14 new fields. updateEmployee server action passes the new
fields through to the employees table.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 10:01:09 +07:00
grabowski f222ac3989 Add personal/address/emergency sections to new-employee form
Three new form blocks slot in between Tax & Bank and Salary:

- Personal: date of birth, gender (select), nationality (defaults
  to Thai), marital status (select)
- Address: line 1/2, subdistrict (Tambon), district (Amphoe),
  province (Changwat), postal code, country (defaults to Thailand)
- Emergency Contact: name, phone, relationship

Server action pulls each new field from formData (all optional)
and includes them in the employees insert.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 09:58:38 +07:00
grabowski ed98aefecd Add personal, Thai address, and emergency contact columns to employees
14 new nullable columns on the employees table:

Personal:
- dateOfBirth, gender, nationality, maritalStatus

Address (Thai-specific):
- addressLine1, addressLine2, subdistrict (Tambon),
  district (Amphoe), province (Changwat), postalCode, country

Emergency contact:
- emergencyContactName, emergencyContactPhone,
  emergencyContactRelationship

All nullable to leave existing rows intact. Constrained sets
(gender, marital status) live in the UI selects rather than
pgEnums for flexibility. Form/UI/export updates follow.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 09:56:48 +07:00
grabowski 1754b99909 Fix financial export array binding and add SVG favicon
Validate / validate (push) Successful in 25s
- Replace raw `ANY(${array})` SQL with drizzle's inArray() in
  src/lib/server/export/financial.ts; the raw form sent UUID arrays
  in a malformed Postgres array literal causing 500 on download
- Add static/favicon.svg (Thai baht symbol on blue square) and point
  app.html at it; remove the empty favicon.png
- Redirect /favicon.ico to /favicon.svg in hooks.server.ts so
  browsers' implicit fallback request stops 404'ing

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 09:50:14 +07:00
grabowski 39ac9d3928 Add financial export ZIP download for admin and accountant
Validate / validate (push) Successful in 27s
- New /companies/[id]/export page with year selector and big download button
- GET /export/zip endpoint generates the financial-export-{name}-{year}.zip
  by calling buildFinancialExport, then logs financial_exported in
  the company audit trail
- New "Export" tab in company nav, visible to admin or accountant
- Page lists all included files and warns about sensitive PII

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 09:43:26 +07:00
grabowski 843ed96aaa Add jszip and financial export builder module
- Install jszip dependency (~100KB, pure JS)
- New src/lib/server/export/financial.ts builds a year-scoped ZIP
  with one CSV per logical table: company, projects, parties (incl
  archived), employees (incl terminated), budget_allocations, expenses,
  invoices + line items, salary_history (effective on/before year end),
  payslips + line items, packages (with carrier label and customs link),
  external_transactions (with provider label and matched expense),
  company_log
- All CSVs prefixed with UTF-8 BOM for Excel/Thai support
- Reference tables include soft-deleted rows so historical FKs resolve
- Routes and UI to follow in next commit

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 09:41:46 +07:00
grabowski 0bfbcef043 Add accountant role and financial_exported audit event
- New 'accountant' role in companyRoleEnum (orthogonal like 'hr')
- meetsMinRole and requireCompanyRole now exclude accountant from
  hierarchy along with hr
- Settings UI exposes accountant in the role checkbox lists for both
  add-member and edit-member forms
- New 'financial_exported' value added to companyLogEventEnum, ready
  for the upcoming export feature

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 09:39:18 +07:00
grabowski 23b00b2cfc Add feature requests page with upvotes and admin status workflow
Validate / validate (push) Successful in 22s
- New /feature-requests route accessible to all logged-in users via sidebar nav
- feature_requests + feature_request_votes tables (one vote per user per request)
- Submit form (modal), upvote toggle, filter by status, sort by votes/newest
- System admins can change status (open / in_review / waiting_for_checks / in_progress / resolved / closed) with optional note
- Submitter auto-votes their own request on creation
- Admin or original submitter can delete a request

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 16:42:50 +07:00
grabowski b6f07fe4df 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>
2026-04-14 16:35:13 +07:00
grabowski 2c4dfed8db Fix remaining dark mode black text on amounts
- Projects tab: budget amounts on project cards
- Budget tab: allocated/spent/remaining in project table
- Reports tab: category amounts, project names, budget vs actual values
- Company overview: expense amounts in recent expenses table

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 13:28:38 +07:00
grabowski 49e500fdda Fix dark mode text color on budget amounts
Budget/expense amounts on dashboard, project view, and expenses page
now show white text in dark mode instead of black.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 13:25:55 +07:00
grabowski 80e02030d6 Add light/dark mode toggle across all pages
- Theme store with localStorage persistence and system preference detection
- Inline script in app.html to prevent flash of wrong theme
- Sun/moon toggle button in top bar and auth pages
- Tailwind v4 dark mode via @custom-variant with class strategy
- Dark mode classes applied to all 20+ pages: sidebar, auth forms,
  dashboard, companies, projects, expenses, budget, categories,
  reports, import, settings, admin pages, and all modals

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 13:23:15 +07:00
grabowski 1c7166adc5 Add user disable and permanent delete for system admins
- Added disabledAt column to users table
- Disabled users are blocked at login and session validation (immediate logout)
- Admin users page shows Active/Disabled status badges
- Disable/Enable toggle button per user (kills all sessions on disable)
- Permanent delete with confirmation modal (removes user, sessions, memberships)
- Self-protection: admins cannot disable or delete themselves

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 13:14:03 +07:00
grabowski d58443ed73 Add soft-delete (archive) for companies, admin-only
- Added deletedAt column to companies table for soft delete
- System admins see a trash icon on each company card with confirmation modal
- Archived companies are filtered from sidebar, dashboard, company list, and direct access
- Audit log entry created on archive

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 13:09:27 +07:00
grabowski 7a4ba0537f Initial commit: Buildfor Life Budget app
Multi-company budget/project tracking tool built with SvelteKit 5,
PostgreSQL (Drizzle ORM), and Tailwind CSS v4.

Features:
- Auth: local (email/password with Argon2) + generic OIDC
- 4 roles per company: admin, manager, user, viewer
- Multi-company with per-company user membership
- Projects with budget allocation from company pool
- Expense submission with approval workflow
- Categories and tags for expense organization
- Reports with spending breakdowns (by category, project, time)
- CSV import for Actual Budget migration
- Company audit log tracking all budget and admin actions
- Remaining budget hero display on overview and budget pages
- Admin-only company creation; new users wait for invitation
- Deployment configs for systemd + nginx (bare metal/Proxmox)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 11:51:32 +07:00