Excluded: .git, node_modules, secrets/, compose.env, assemblyscript tgz Source: /opt/alga-psa on psa.joliet.tech
8.6 KiB
PRD: Tax Import Service Porting + BackNav Fix + Notification Reconciliation
Date: 2026-02-20 PR scope: Single PR combining 3 cleanup items from the ongoing server-to-package migration
Problem Statement
The codebase has accumulated stale code patterns from the server-to-package migration:
- CSV tax import services remain in
server/src/lib/services/despite having a natural home in@alga-psa/integrations(whereXeroCsvTaxImportServicealready lives as a reference) - BackNav.tsx has a package-to-server boundary violation — imports
UnsavedChangesContextfromserver/src/contexts/despite an identical copy already existing in the same package atpackages/ui/src/context/ - Notification email files are duplicated between
server/src/lib/notifications/andpackages/notifications/src/notifications/with diverged implementations (server has inline rate limiting that's been centralized elsewhere) - An orphan file
server/src/lib/adapters/invoiceAdapters.tshas zero callers
Goals
- Port
csvTaxImportValidator.tsandcsvTaxImportService.ts(+ dependencycsvFieldNormalizer.ts) to@alga-psa/integrations - Fix BackNav.tsx boundary violation (1 import change)
- Reconcile notification dual-copies: delete server
email.tsandemailChannel.ts, update callers to use package versions - Delete orphan
server/src/lib/adapters/invoiceAdapters.ts
Non-Goals
- Moving
externalTaxImportService.ts(depends on accounting adapters which have a circular dependency with@alga-psa/billing— deferred) - Moving accounting adapters from
server/src/lib/adapters/accounting/(circular dep: integrations→billing→integrations — needs separate strategy) - Moving
accountingExportService.tsto a package (has many server-internal deps) - Moving notification server-only files (
emailService.ts,sendEventEmail.ts,NotificationAccumulator.ts) - Deleting server
emailLocaleResolver.ts(still imported locally bysendEventEmail.ts) - Creating new facade packages
Detailed Changes
Work Item 1: CSV Tax Import Services → @alga-psa/integrations
Files to move:
| Source | Destination | LOC |
|---|---|---|
server/src/lib/utils/csvFieldNormalizer.ts |
packages/integrations/src/lib/csvFieldNormalizer.ts |
323 |
server/src/lib/services/csvTaxImportValidator.ts |
packages/integrations/src/services/csvTaxImportValidator.ts |
745 |
server/src/lib/services/csvTaxImportService.ts |
packages/integrations/src/services/csvTaxImportService.ts |
595 |
Import changes needed in moved files:
| Old Import | New Import | Files Affected |
|---|---|---|
createTenantKnex from ../db |
@alga-psa/db |
csvTaxImportService |
TaxSource from ../../interfaces/tax.interfaces |
@alga-psa/types |
csvTaxImportService |
parseCSV from ../utils/csvParser |
@alga-psa/core (already used by XeroCsvTaxImportService) |
csvTaxImportService |
csvFieldNormalizer from ../utils/csvFieldNormalizer |
../lib/csvFieldNormalizer (relative within package) |
csvTaxImportValidator |
csvTaxImportValidator from ./csvTaxImportValidator |
stays relative | csvTaxImportService |
Callers to update:
| File | Old Import | New Import |
|---|---|---|
server/src/lib/api/controllers/ApiCSVAccountingController.ts |
../../services/csvTaxImportService |
@alga-psa/integrations/services |
Barrel updates:
packages/integrations/src/services/index.ts— add exports for csvTaxImportValidator, csvTaxImportService
Package.json: No new dependencies needed. @alga-psa/core, @alga-psa/db, @alga-psa/types are already in integrations package.json.
Work Item 2: BackNav.tsx Fix
Single import change in packages/ui/src/components/BackNav.tsx:
- import { UnsavedChangesContext } from 'server/src/contexts/UnsavedChangesContext';
+ import { UnsavedChangesContext } from '../context/UnsavedChangesContext';
The context at packages/ui/src/context/UnsavedChangesContext.tsx has identical API: setHasUnsavedChanges, hasAnyUnsavedChanges, confirmNavigation, unregister, useUnsavedChanges(), useRegisterUnsavedChanges().
Work Item 3: Notification Dual-Copy Reconciliation
Delete server copies (adopt package versions):
server/src/lib/notifications/email.ts(561 LOC) — package version (504 LOC) is correct; inline rate limiting was moved to TenantEmailServiceserver/src/lib/notifications/emailChannel.ts(7 LOC) — identical to package version, already exported from@alga-psa/notificationsbarrel
Keep in server (not touched):
emailLocaleResolver.ts— still imported bysendEventEmail.tslocallyemailService.ts,sendEventEmail.ts,NotificationAccumulator.ts— server infrastructure
Package changes needed:
- Export
getEmailNotificationServicefrom@alga-psa/notificationsbarrel (currently only accessible via relative import within package)
Server callers to update:
| File | What it imports | New import source |
|---|---|---|
server/src/lib/jobs/handlers/expiringCreditsNotificationHandler.ts |
getEmailNotificationService |
@alga-psa/notifications |
server/src/lib/eventBus/subscribers/ticketEmailSubscriber.ts |
getEmailEventChannel |
@alga-psa/notifications |
server/src/lib/eventBus/subscribers/projectEmailSubscriber.ts |
getEmailEventChannel |
@alga-psa/notifications |
server/src/lib/eventBus/publishers/index.ts |
getEmailEventChannel |
@alga-psa/notifications |
server/src/lib/api/services/TicketService.ts |
getEmailEventChannel |
@alga-psa/notifications |
server/src/test/integration/ticketEmailDelimiters.test.ts |
EMAIL_EVENT_CHANNEL |
@alga-psa/notifications |
Work Item 4: Orphan Deletion
Delete server/src/lib/adapters/invoiceAdapters.ts — zero callers. The billing package has its own independent copy at packages/billing/src/lib/adapters/invoiceAdapters.ts.
Implementation Order
- Move
csvFieldNormalizer.tsto integrations package (prerequisite for validator) - Move
csvTaxImportValidator.tsto integrations package (update csvFieldNormalizer import) - Move
csvTaxImportService.tsto integrations package (update all imports: db, types, parseCSV, validator) - Update integrations services barrel to export new modules
- Update
ApiCSVAccountingController.tscaller - Delete server copies of tax import services + csvFieldNormalizer
- Fix BackNav.tsx import
- Add
getEmailNotificationServiceexport to notifications package barrel - Update 6 notification callers to import from
@alga-psa/notifications - Delete server
email.tsandemailChannel.ts - Delete orphan
invoiceAdapters.ts - Verify build
Risks
- csvTaxImportService uses
parseCSVfrom server utils — must switch to@alga-psa/core(same function, already used by XeroCsvTaxImportService in the same package). Verify function signature compatibility. - Notification rate limiting removal — The server email.ts had inline rate limiting that the package version doesn't. Risk mitigated: TenantEmailService already handles rate limiting centrally.
- Package notifications barrel change — Adding
getEmailNotificationServiceexport. Verify no naming conflicts.
Acceptance Criteria
npm run buildsucceedsnpm run build:sharedsucceedsserver/src/lib/services/csvTaxImportValidator.tsdeletedserver/src/lib/services/csvTaxImportService.tsdeletedserver/src/lib/utils/csvFieldNormalizer.tsdeletedserver/src/lib/notifications/email.tsdeletedserver/src/lib/notifications/emailChannel.tsdeletedserver/src/lib/adapters/invoiceAdapters.tsdeletedBackNav.tsximports from../context/UnsavedChangesContextpackages/integrations/src/services/csvTaxImportValidator.tsexistspackages/integrations/src/services/csvTaxImportService.tsexistspackages/integrations/src/lib/csvFieldNormalizer.tsexistsgetEmailNotificationServiceimportable from@alga-psa/notifications- Grep for boundary violation patterns returns 0 source matches (outside docs/)
Deferred Items (future PRs)
These were explored but deferred due to circular dependency blockers:
- externalTaxImportService.ts — depends on concrete adapter classes (QuickBooksOnlineAdapter, XeroAdapter) which import from
@alga-psa/billing - Accounting adapters (7 files, 3,369 LOC) — create circular dep: integrations→billing→integrations. Need to extract shared types to
@alga-psa/typesfirst, or use runtime subpath strategy. - accountingExportAdapter.ts interface types — candidate for
@alga-psa/typesin future PR