PSA/ee/docs/plans/inbound-ticket-defaults-implementation.md
Hermes 284313f908
Some checks are pending
Bidi Control Character Guard / bidi-control-guard (push) Waiting to run
Circular Dependency Check / Check for new circular dependencies (push) Waiting to run
Citus Migration Smoke / Combined migrations on single-node Citus (push) Waiting to run
E2E Fresh Install Tests / fresh-install-e2e (push) Waiting to run
ext-v2 guardrails / Run ext-v2 guard and ESLint (push) Waiting to run
Integration Tests / Check for relevant changes (push) Waiting to run
Integration Tests / ${{ (github.event_name == 'schedule' || github.event.inputs.suite == 'full') && 'Full integration suite' || 'Tier-1 integration subset' }} (push) Blocked by required conditions
Mobile checks / Mobile lint + typecheck (push) Waiting to run
Mobile checks / Mobile unit tests (push) Waiting to run
Mobile checks / Mobile dependency audit (report) (push) Waiting to run
Mobile checks / Mobile reproducibility checks (push) Waiting to run
Secrets guard (env backups) / Ensure no tracked env backup files (push) Waiting to run
Temporal Readiness / fast-readiness (push) Waiting to run
Temporal Readiness / docker-parity (push) Waiting to run
TypeScript Type Check / Nx affected typecheck (push) Waiting to run
Unit Tests / Skipped-test budget (push) Waiting to run
Unit Tests / Nx affected unit tests (push) Waiting to run
Unit Tests / Server unit coverage (informational) (push) Waiting to run
Validate Tenant Management Schema / Check for relevant changes (push) Waiting to run
Validate Tenant Management Schema / Validate Tenant Management Schema (push) Blocked by required conditions
EE Workflows Build Guard / ee-workflows-build-guard (push) Waiting to run
Initial import of AlgaPSA codebase from PSA server
Excluded: .git, node_modules, secrets/, compose.env, assemblyscript tgz

Source: /opt/alga-psa on psa.joliet.tech
2026-06-22 16:12:17 -05:00

190 lines
6.8 KiB
Markdown

# Inbound Ticket Defaults Implementation Plan ✅ COMPLETED
## Table of Contents
1. [Problem Summary](#problem-summary)
2. [Solution Architecture](#solution-architecture)
3. [Implementation Phases](#implementation-phases)
- [Phase 1: Database Schema](#phase-1-database-schema)
- [Phase 2: Backend API Development](#phase-2-backend-api-development)
- [Phase 3: Workflow Integration](#phase-3-workflow-integration)
- [Phase 4: UI Components](#phase-4-ui-components)
4. [Database Schema Details](#database-schema-details)
5. [TypeScript Interfaces](#typescript-interfaces)
6. [File Changes Summary](#file-changes-summary)
## Problem Summary
The email-to-ticket workflow is failing with validation errors because required ticket fields (`channel_id`, `company_id`, `status_id`, `priority_id`, `entered_by`, etc.) are not being provided. The workflow receives email data but cannot create tickets without these mandatory database fields.
## Solution Architecture
Create a flexible `inbound_ticket_defaults` table with named default configurations that can be referenced by email providers. This design allows for reusable default configurations and enables future domain-based routing capabilities.
## Implementation Phases
### Phase 1: Database Schema ✅
- [x] Create `inbound_ticket_defaults` table migration
- [x] Add reference column to `email_providers` table
- [x] Create default inbound ticket defaults seed data
### Phase 2: Backend API Development ✅
- [x] Create inbound ticket defaults actions (CRUD operations)
- [x] Create ticket field options actions (for dropdowns)
- [x] Update email provider actions to handle defaults reference
- [x] Add TypeScript interfaces for new types
- [x] Update TicketModel to allow null `entered_by` for system-generated tickets
### Phase 3: Workflow Integration ✅
- [x] Update `createTicketFromEmail()` to accept default field values
- [x] Create helper function to resolve email provider's ticket defaults
- [x] Update email processing workflow to retrieve and use defaults
- [x] Validate workflow execution with configured defaults
### Phase 4: UI Components ✅
- [x] Create InboundTicketDefaultsManager component
- [x] Create InboundTicketDefaultsForm component
- [x] Update provider forms to select from existing defaults
- [x] Integrate defaults management into EmailProviderConfiguration
## Database Schema Details
### New Table: `inbound_ticket_defaults`
```sql
CREATE TABLE inbound_ticket_defaults (
id UUID NOT NULL,
tenant UUID NOT NULL,
short_name VARCHAR(100) NOT NULL,
display_name VARCHAR(255) NOT NULL,
description TEXT,
defaults JSONB NOT NULL DEFAULT '{}',
is_active BOOLEAN DEFAULT true,
created_at TIMESTAMP NOT NULL DEFAULT NOW(),
updated_at TIMESTAMP NOT NULL DEFAULT NOW(),
PRIMARY KEY (id, tenant),
FOREIGN KEY (tenant) REFERENCES tenants(tenant),
UNIQUE (tenant, short_name)
);
```
### Email Providers Table Update
```sql
ALTER TABLE email_providers
ADD COLUMN inbound_ticket_defaults_id UUID;
```
### Default JSON Structure
```json
{
"channel_id": "uuid",
"status_id": "uuid",
"priority_id": "uuid",
"company_id": "uuid",
"entered_by": null,
"category_id": "uuid",
"subcategory_id": "uuid",
"location_id": "uuid"
}
```
## TypeScript Interfaces
```typescript
interface InboundTicketDefaults {
id: string;
tenant: string;
short_name: string;
display_name: string;
description?: string;
defaults: {
channel_id: string;
status_id: string;
priority_id: string;
company_id?: string;
entered_by?: string | null;
category_id?: string;
subcategory_id?: string;
location_id?: string;
};
is_active: boolean;
created_at: string;
updated_at: string;
}
interface TicketFieldOptions {
channels: Array<{ id: string; name: string; is_default: boolean }>;
statuses: Array<{ id: string; name: string }>;
priorities: Array<{ id: string; name: string }>;
categories: Array<{ id: string; name: string; parent_id?: string }>;
users: Array<{ id: string; name: string; username: string }>;
}
```
## File Changes Summary
### New Files ✅
- `/server/migrations/20250713002000_create_inbound_ticket_defaults_table.cjs`
- `/server/migrations/20250713002001_add_defaults_ref_to_email_providers.cjs`
- `/server/seeds/dev/005_default_inbound_ticket_defaults.cjs`
- `/server/src/lib/actions/email-actions/inboundTicketDefaultsActions.ts`
- `/server/src/lib/actions/email-actions/ticketFieldOptionsActions.ts`
- `/server/src/components/admin/InboundTicketDefaultsManager.tsx`
- `/server/src/components/forms/InboundTicketDefaultsForm.tsx`
### Modified Files ✅
- `/server/src/lib/actions/email-actions/emailProviderActions.ts`
- `/server/src/components/EmailProviderConfiguration.tsx`
- `/server/src/components/MicrosoftProviderForm.tsx`
- `/server/src/components/GmailProviderForm.tsx` ⏳ (Pending - similar changes as Microsoft)
- `/shared/workflow/actions/emailWorkflowActions.ts`
- `/shared/models/ticketModel.ts` - Update validation to allow null `entered_by`
- `/server/seeds/dev/004_email_processing_workflow_from_source.cjs`
- `/server/src/types/email.types.ts`
### Key Implementation Details
#### Database Migration Strategy
1. Create `inbound_ticket_defaults` table with tenant partitioning
2. Add foreign key reference from `email_providers`
3. Seed with default "email-general" configuration
#### Backend API Pattern
- Follow existing email actions pattern with tenant isolation
- Use consistent error handling and validation
- Return structured responses with proper TypeScript types
- Update TicketModel validation to allow `entered_by: null` for system tickets
#### Workflow Integration Points
- Resolve email provider → inbound ticket defaults in workflow
- Pass defaults as additional parameters to ticket creation
- Merge email data with configured defaults before validation
#### UI Component Architecture
- Reusable form components for ticket field selection
- Manager component for CRUD operations on defaults
- Integration into existing email provider configuration flow
#### System-Generated Ticket Handling
For tickets created from email processing, `entered_by` will be `null` to indicate system generation:
- Update TicketModel validation schema to allow `entered_by: null`
- Modify ticket creation logic to handle null `entered_by` gracefully
- UI components should display "System" when `entered_by` is null
- Audit logs should show system-generated entries appropriately
#### TicketModel Changes Required
```typescript
// Update validation to allow null for system tickets
entered_by: z.string().uuid().nullable().optional()
// In createTicket logic:
const auditUser = data.entered_by || 'system';
```
This implementation focuses strictly on solving the immediate validation issue while providing a solid foundation for the email-to-ticket workflow.