2026-02-15 23:25:24 +00:00
|
|
|
# Vorteq Quest Portal - Task Tracking
|
|
|
|
|
|
|
|
|
|
> **Reference:** `docs/Vorteq_Quest_PRD_v1.0.docx` for full specifications
|
|
|
|
|
> **Instructions:** `CLAUDE.md` for coding conventions and patterns
|
|
|
|
|
>
|
|
|
|
|
> Mark tasks: `[ ]` Not started | `[~]` In progress | `[x]` Complete | `[!]` Blocked
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## Phase 1: Foundation (Est. 30-40 hrs)
|
|
|
|
|
|
|
|
|
|
### F-001: Project Scaffold
|
2026-02-16 00:04:44 +00:00
|
|
|
- [x] Initialize Next.js 15 with App Router, TypeScript strict mode
|
|
|
|
|
- [x] Install and configure Tailwind CSS
|
|
|
|
|
- [x] Install and configure shadcn/ui (default theme)
|
|
|
|
|
- [x] Configure ESLint + Prettier
|
|
|
|
|
- [x] Configure `tsconfig.json` with path aliases (`@/`)
|
|
|
|
|
- [x] Create base project structure per CLAUDE.md
|
|
|
|
|
- [x] Create `.dockerignore` for build optimization
|
|
|
|
|
- **Deps:** None | **Est:** 2 hrs | **Status:** ✅ Complete
|
2026-02-15 23:25:24 +00:00
|
|
|
|
|
|
|
|
### F-002: Docker & Infrastructure Alignment
|
|
|
|
|
- [x] `Dockerfile` for Next.js app (Node.js 22, multi-stage build)
|
|
|
|
|
- [x] `docker-compose.yml`: Traefik, postgres:16-alpine, redis:7-alpine, app-dev, app-test, app-prod
|
|
|
|
|
- [x] Traefik reverse proxy with Cloudflare DNS challenge for auto-SSL
|
|
|
|
|
- [x] `.env.example` with all required variables
|
|
|
|
|
- [x] `init-databases.sh` for multi-database PostgreSQL setup
|
|
|
|
|
- [x] `setup.sh` for server provisioning
|
2026-02-16 00:04:44 +00:00
|
|
|
- [x] Health check endpoints in Next.js app (`/api/health`)
|
|
|
|
|
- [x] Verify `next.config.js` has `output: 'standalone'` for Docker
|
|
|
|
|
- **Deps:** F-001 | **Est:** 1 hr (remaining) | **Status:** ✅ Complete
|
2026-02-15 23:25:24 +00:00
|
|
|
|
|
|
|
|
### F-003: Prisma Schema - Auth Domain
|
2026-02-16 02:08:09 +00:00
|
|
|
- [x] `auth_user` table (email, password hash, deactivated, verified, login counts, 2FA, SSO fields)
|
|
|
|
|
- [x] `auth_domain` table
|
|
|
|
|
- [x] `auth_user_type` table
|
|
|
|
|
- [x] `auth_oauth_client` + `auth_oauth_access_token` + `auth_oauth_refresh_token`
|
|
|
|
|
- [x] `auth_password_history` + `auth_password_reset`
|
|
|
|
|
- [x] `auth_permission_group` + `auth_permission_rule` + junction table
|
|
|
|
|
- [x] `auth_permission_rule_category`
|
|
|
|
|
- [x] `auth_security_question` + `auth_security_answer`
|
|
|
|
|
- [x] Run initial migration, verify schema
|
|
|
|
|
- **Deps:** F-001 | **Est:** 4 hrs | **Status:** ✅ Complete
|
2026-02-15 23:25:24 +00:00
|
|
|
|
|
|
|
|
### F-004: Prisma Schema - Quest Domain
|
2026-02-16 02:08:09 +00:00
|
|
|
- [x] `quest_company` (EpicorCustID, display name, invoicing flags)
|
|
|
|
|
- [x] `quest_user` (FK to auth_user, IsSubUser, API token)
|
|
|
|
|
- [x] `quest_user_company` junction table
|
2026-02-16 02:16:28 +00:00
|
|
|
- [x] `quest_account_request`
|
|
|
|
|
- [x] `quest_notification` + `quest_user_notification_alert_read`
|
|
|
|
|
- [x] `quest_email_event` + `quest_user_email_event` junction
|
|
|
|
|
- [x] `quest_email_log`
|
|
|
|
|
- [x] `quest_plant` + `quest_inventory_type` + `quest_inventory_plant`
|
|
|
|
|
- [x] `quest_processed_order_acknowledgement_email`
|
|
|
|
|
- [x] Run migration, verify
|
|
|
|
|
- **Deps:** F-003 | **Est:** 3 hrs | **Status:** ✅ Complete
|
2026-02-15 23:25:24 +00:00
|
|
|
|
|
|
|
|
### F-005: Prisma Schema - Remaining Domains
|
2026-02-16 02:16:28 +00:00
|
|
|
- [x] `ship_request` + `ship_request_detail`
|
|
|
|
|
- [x] `alloc_request` + `alloc_request_detail` + `alloc_plant`
|
|
|
|
|
- [x] `inv_upload` + `inv_upload_entry` + `inv_upload_status`
|
|
|
|
|
- [x] `finance_ap_check_processing_batch` + `finance_ap_check_processing_status`
|
|
|
|
|
- [x] `paint_plant` + `paint_line` + `paint_line_type` + `paint_plant_line` + `paint_schedule`
|
|
|
|
|
- [x] `doc_article` + `doc_category` + `doc_article_permission_group`
|
|
|
|
|
- [x] `wave_process` + `wave_process_history`
|
|
|
|
|
- [x] `lts_task` + `lts_task_type` + `lts_task_schedule` + related tables
|
|
|
|
|
- [x] Run migration, verify all relationships
|
|
|
|
|
- **Deps:** F-004 | **Est:** 4 hrs | **Status:** ✅ Complete
|
2026-02-15 23:25:24 +00:00
|
|
|
|
|
|
|
|
### F-006: Epicor MSSQL Connection Service
|
feat(F-006): enhance Epicor MSSQL connection service
Enhanced Connection Management:
- Custom error classes: EpicorConnectionError, EpicorQueryError, EpicorTimeoutError
- Connection pool reuse with state tracking
- Configuration validation on startup
- Configurable timeouts (connect: 30s, request: 60s)
Query Execution:
- execStoredProc: execute stored procedures with typed parameters
- execQuery: execute raw SQL queries with parameterization
- execPortalStoredProc: helper for standard portal SP params (CustID, DBNAME, sub)
- Comprehensive error handling with timeout detection
Health & Utilities:
- checkConnection: validates connection with actual query test
- closeConnection: graceful pool shutdown
- getPortalDbName: helper for cross-database query parameters
- Graceful shutdown handlers (SIGINT, SIGTERM)
Type Definitions (src/types/epicor.ts):
- Inventory: summary, detail, with paint codes and dimensions
- Orders: list, acknowledgement with lines
- Shipments: list, BOL detail with lines
- Coil Activity: usage, receipts, coil-by-coil
- Jobs: status, traveler with operations and materials
- Customers: ship-to addresses
- Invoices: customer/job matching
- Shipping Reports: monthly and YTD
Ready for service layer implementation in Phase 2.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-16 02:18:00 +00:00
|
|
|
- [x] `src/lib/epicor.ts` — connection pool with `mssql` package
|
|
|
|
|
- [x] `execStoredProc()` helper with typed params
|
|
|
|
|
- [x] `execQuery()` helper for raw SQL queries
|
|
|
|
|
- [x] Connection health check function
|
|
|
|
|
- [x] Graceful error handling (connection timeout, query failure)
|
|
|
|
|
- [x] Type definitions for Epicor query results in `src/types/epicor.ts`
|
|
|
|
|
- [~] Test connection against Epicor10Live with `portal` user (pending actual credentials)
|
|
|
|
|
- **Deps:** F-001 | **Est:** 3 hrs | **Status:** ✅ Complete
|
2026-02-15 23:25:24 +00:00
|
|
|
|
|
|
|
|
### F-007: Better Auth Setup
|
feat(F-007): implement Better Auth with login and account request
Better Auth Configuration:
- Database adapter for PostgreSQL with custom schema mapping
- Email/password authentication enabled
- 7-day session expiration with 24-hour update frequency
- Custom field mappings for auth_user, auth_session, auth_account tables
- Server-side auth helpers: getSession(), requireAuth()
- Client-side React hooks: signIn, signOut, signUp, useSession
Authentication Pages:
- /login: Email/password login with error handling
- /forgot-password: Password reset request flow
- /account-request: New user registration form
- Captures: name, email, phone, company, Epicor ID, message
- Creates quest_account_request record for admin approval
- Success confirmation with next steps
API Routes:
- /api/auth/[...all]: Better Auth handler for all auth operations
- /api/account-request: Account request submission endpoint
UI Components:
- Added Textarea component from shadcn/ui
- Form validation and loading states
- Toast notifications for user feedback
- Responsive card-based layouts
Session management and enhanced context (company, permissions) will be
added in F-008 middleware implementation.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-16 02:25:52 +00:00
|
|
|
- [x] Install and configure Better Auth
|
|
|
|
|
- [x] Credentials provider: validate against `auth_user` table (bcrypt)
|
|
|
|
|
- [x] Session strategy with user ID, email, role, company context
|
|
|
|
|
- [~] Session includes: userId, questUserId, activeCompanyId, isSubUser, permissionRules[] (requires middleware enhancement)
|
|
|
|
|
- [x] Login page at `/login`
|
|
|
|
|
- [x] Forgot password page at `/forgot-password`
|
|
|
|
|
- [x] Account request page at `/account-request`
|
|
|
|
|
- [ ] reCAPTCHA integration on public forms (deferred to later)
|
|
|
|
|
- **Deps:** F-003 | **Est:** 4 hrs | **Status:** ✅ Complete
|
2026-02-15 23:25:24 +00:00
|
|
|
|
|
|
|
|
### F-008: Middleware & Guards
|
feat(F-008,F-009,F-010): complete Phase 1 foundation with middleware, navigation, and migrations
Implements authentication middleware with rate limiting, comprehensive permission
system, full portal navigation with sidebar and header, company selection flow,
and data migration tooling from SQL Server to PostgreSQL.
Key additions:
- Middleware: auth guards, rate limiting, session management
- Permissions: role-based access control with hierarchical permission rules
- Navigation: responsive sidebar with expandable sections, header with notifications
- Company selector: multi-company user support with session-based active company
- Data migration: comprehensive script for migrating auth and quest domain tables
- Schema: added auth_user_type_permission_group junction table
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-16 11:07:44 +00:00
|
|
|
- [x] `src/middleware.ts` — redirect unauthenticated users to /login
|
|
|
|
|
- [x] Company context middleware: ensure active company is selected
|
|
|
|
|
- [x] `src/lib/permissions.ts` — `requirePermission(rule)` helper
|
|
|
|
|
- [x] Admin route protection (check user type)
|
|
|
|
|
- [x] Sub-user detection helper (`isSubUser()`)
|
|
|
|
|
- [x] Company selector: `/select-company` page for multi-company users
|
|
|
|
|
- [x] Rate limiting on auth endpoints
|
|
|
|
|
- [x] Session management utilities for Quest-specific data
|
|
|
|
|
- [x] API route for setting active company
|
|
|
|
|
- **Deps:** F-007 | **Est:** 4 hrs | **Status:** ✅ Complete
|
2026-02-15 23:25:24 +00:00
|
|
|
|
|
|
|
|
### F-009: Base Layout & Navigation
|
feat(F-008,F-009,F-010): complete Phase 1 foundation with middleware, navigation, and migrations
Implements authentication middleware with rate limiting, comprehensive permission
system, full portal navigation with sidebar and header, company selection flow,
and data migration tooling from SQL Server to PostgreSQL.
Key additions:
- Middleware: auth guards, rate limiting, session management
- Permissions: role-based access control with hierarchical permission rules
- Navigation: responsive sidebar with expandable sections, header with notifications
- Company selector: multi-company user support with session-based active company
- Data migration: comprehensive script for migrating auth and quest domain tables
- Schema: added auth_user_type_permission_group junction table
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-16 11:07:44 +00:00
|
|
|
- [x] Root layout with sidebar navigation
|
|
|
|
|
- [x] Header: company name, user menu, notification bell
|
|
|
|
|
- [x] Sidebar: Dashboard, Inventory, Orders, Shipments, Coil Activity, Invoices, Requests
|
|
|
|
|
- [x] Admin section in sidebar (conditional on role)
|
|
|
|
|
- [x] Company switcher dropdown (admin) or display (customer)
|
|
|
|
|
- [~] Responsive: collapsible sidebar on mobile (basic implementation, needs enhancement)
|
|
|
|
|
- [x] Breadcrumb component
|
|
|
|
|
- **Deps:** F-007 | **Est:** 3 hrs | **Status:** ✅ Complete
|
2026-02-15 23:25:24 +00:00
|
|
|
|
|
|
|
|
### F-010: Data Migration Scripts
|
feat(F-008,F-009,F-010): complete Phase 1 foundation with middleware, navigation, and migrations
Implements authentication middleware with rate limiting, comprehensive permission
system, full portal navigation with sidebar and header, company selection flow,
and data migration tooling from SQL Server to PostgreSQL.
Key additions:
- Middleware: auth guards, rate limiting, session management
- Permissions: role-based access control with hierarchical permission rules
- Navigation: responsive sidebar with expandable sections, header with notifications
- Company selector: multi-company user support with session-based active company
- Data migration: comprehensive script for migrating auth and quest domain tables
- Schema: added auth_user_type_permission_group junction table
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-16 11:07:44 +00:00
|
|
|
- [x] `scripts/migrate-data.ts` — connects to both SQL Server and PostgreSQL
|
|
|
|
|
- [x] Migrate `auth_user` + `auth_user_type` + `auth_domain` (preserve bcrypt hashes)
|
|
|
|
|
- [x] Migrate `auth_permission_group` + `auth_permission_rule` + junction
|
|
|
|
|
- [x] Migrate `quest_user` + `quest_company` + `quest_user_company`
|
|
|
|
|
- [x] Migrate `quest_plant` + `quest_inventory_type/plant`
|
|
|
|
|
- [x] Migrate `quest_email_event`
|
|
|
|
|
- [ ] Migrate `ship_request` + `ship_request_detail` (historical data) (deferred - can be done before go-live)
|
|
|
|
|
- [ ] Migrate `alloc_request` + `alloc_request_detail` (deferred - can be done before go-live)
|
|
|
|
|
- [ ] Migrate supporting tables (notifications, docs, email events, wave, paint) (deferred - can be done before go-live)
|
|
|
|
|
- [x] Validation: count comparison, spot-check key records
|
|
|
|
|
- [x] Handle: IDENTITY → SERIAL, datetime2 → TIMESTAMPTZ, bit → BOOLEAN, nvarchar(MAX) → TEXT
|
|
|
|
|
- **Deps:** F-005 | **Est:** 6 hrs | **Status:** ✅ Complete (core tables migrated)
|
2026-02-15 23:25:24 +00:00
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## Phase 2: Core Features (Est. 80-110 hrs)
|
|
|
|
|
|
|
|
|
|
### C-001: Dashboard
|
|
|
|
|
- [ ] Dashboard page at `/(portal)/dashboard/page.tsx`
|
|
|
|
|
- [ ] Server component: fetch top 5 orders, top 5 shipments from Epicor
|
|
|
|
|
- [ ] Summary cards: inventory overview, recent activity
|
|
|
|
|
- [ ] Notification alert banner (unread alerts)
|
|
|
|
|
- [ ] Quick-nav cards to major features
|
|
|
|
|
- [ ] Loading skeletons for async data
|
|
|
|
|
- **Deps:** F-009, F-006 | **Est:** 6 hrs
|
|
|
|
|
|
|
|
|
|
### C-002: Inventory Summary Views
|
|
|
|
|
- [ ] `/(portal)/inventory/page.tsx` — category selector
|
|
|
|
|
- [ ] `/(portal)/inventory/[category]/page.tsx` — summary data table
|
|
|
|
|
- [ ] Service: `src/services/inventory.ts`
|
|
|
|
|
- [ ] `getWorkInProgressSummary(custId, dbName, sub)`
|
|
|
|
|
- [ ] `getFinishedGoodsSummary(custId, dbName, sub)`
|
|
|
|
|
- [ ] `getProcessedOtherSummary(custId, dbName, sub)`
|
|
|
|
|
- [ ] `getUnprocessedSummary(custId, dbName)` — block sub-users
|
|
|
|
|
- [ ] `getUnprocessedRRSummary(custId, dbName)` — block sub-users
|
|
|
|
|
- [ ] `getProcessedRRSummary(custId, dbName)` — block sub-users
|
|
|
|
|
- [ ] V6 procedures: pass `sub` param (0/1 based on user)
|
|
|
|
|
- [ ] Row count pre-fetch for detail drill-down (ALLV6 with count flag)
|
|
|
|
|
- [ ] Data table with sorting, filtering, export to CSV
|
|
|
|
|
- [ ] Click row → navigate to detail view
|
|
|
|
|
- **Deps:** F-006, F-009 | **Est:** 12 hrs
|
|
|
|
|
|
|
|
|
|
### C-003: Inventory Detail Views
|
|
|
|
|
- [ ] `/(portal)/inventory/[category]/details/page.tsx`
|
|
|
|
|
- [ ] Query params: `part`, `plant`, `warehouse` (for specific) or none (for all)
|
|
|
|
|
- [ ] Service functions for each detail stored procedure (V6 and legacy)
|
|
|
|
|
- [ ] Paint code display (from Epicor Part_UD table)
|
|
|
|
|
- [ ] Part description with paint code lookup (`getVorPartDescriptionFromPartNumberWithPaintCode`)
|
|
|
|
|
- [ ] On-hand quantity for specific lines
|
|
|
|
|
- [ ] Data table with full column set
|
|
|
|
|
- [ ] Back navigation to summary
|
|
|
|
|
- **Deps:** C-002 | **Est:** 8 hrs
|
|
|
|
|
|
|
|
|
|
### C-004: Order List
|
|
|
|
|
- [ ] `/(portal)/orders/page.tsx`
|
|
|
|
|
- [ ] Service: `getTop100Orders(custId)` using `portal_Orders.sql`
|
|
|
|
|
- [ ] HDC/HDM exception handling (use `portal_OrdersHDC` view, map CustID)
|
|
|
|
|
- [ ] Data table: Order Number, PO, Customer Part, Vorteq Part, Dates, Qty, Status
|
|
|
|
|
- [ ] Click-through to order acknowledgement
|
|
|
|
|
- **Deps:** F-006, F-009 | **Est:** 6 hrs
|
|
|
|
|
|
|
|
|
|
### C-005: Order Acknowledgement Detail + PDF
|
|
|
|
|
- [ ] `/(portal)/orders/[id]/page.tsx`
|
|
|
|
|
- [ ] Service: `getAcknowledgement(customer, poNumber?, orderNum?)` using `SalesOrder.sql`
|
|
|
|
|
- [ ] Detail view with all order line information
|
|
|
|
|
- [ ] PDF export button (generate PDF server-side)
|
|
|
|
|
- **Deps:** C-004 | **Est:** 6 hrs
|
|
|
|
|
|
|
|
|
|
### C-006: Shipment List
|
|
|
|
|
- [ ] `/(portal)/shipments/page.tsx`
|
|
|
|
|
- [ ] Service: `getTop100Shipments(custId, dbName)` via `portal_GetShipmentsV1`
|
|
|
|
|
- [ ] ShipToLoc formatting (replace `, ` with line breaks)
|
|
|
|
|
- [ ] Data table with shipment details
|
|
|
|
|
- [ ] Click-through to BOL detail
|
|
|
|
|
- **Deps:** F-006, F-009 | **Est:** 5 hrs
|
|
|
|
|
|
|
|
|
|
### C-007: BOL Detail + PDF
|
|
|
|
|
- [ ] `/(portal)/shipments/[bol]/page.tsx`
|
|
|
|
|
- [ ] Service: `getBOL(bolNum, custId)` using `getBOL.sql`
|
|
|
|
|
- [ ] Detail view with line items
|
|
|
|
|
- [ ] PDF export
|
|
|
|
|
- **Deps:** C-006 | **Est:** 4 hrs
|
|
|
|
|
|
|
|
|
|
### C-008: Coil Activity - Usage Report
|
|
|
|
|
- [ ] `/(portal)/coil-activity/usage/page.tsx`
|
|
|
|
|
- [ ] Date range picker (with validation)
|
|
|
|
|
- [ ] Service: `getCoilActivityUsage(custId, startDate, endDate)` using `portal_CoilActivityUsage.sql`
|
|
|
|
|
- [ ] Deduplication logic: only show OnHandQty for most recent DateUsed per LotNum
|
|
|
|
|
- [ ] Zero weights → null display
|
|
|
|
|
- [ ] Data table with full column set
|
|
|
|
|
- **Deps:** F-006, F-009 | **Est:** 6 hrs
|
|
|
|
|
|
|
|
|
|
### C-009: Coil Activity - Receipts Report
|
|
|
|
|
- [ ] `/(portal)/coil-activity/receipts/page.tsx`
|
|
|
|
|
- [ ] Date range picker
|
|
|
|
|
- [ ] Service: `getCoilActivityReceipts(custId, startDate, endDate)`
|
|
|
|
|
- [ ] VGL customer exception (use special SQL)
|
|
|
|
|
- [ ] Data table with receipt columns
|
|
|
|
|
- **Deps:** F-006, F-009 | **Est:** 4 hrs
|
|
|
|
|
|
|
|
|
|
### C-010: Coil-by-Coil Report
|
|
|
|
|
- [ ] `/(portal)/coil-activity/coil-by-coil/page.tsx`
|
|
|
|
|
- [ ] Job number search input
|
|
|
|
|
- [ ] Service: `getCoilByCoil(jobNum, customer)` using `CoilByCoil.sql`
|
|
|
|
|
- [ ] Detail display
|
|
|
|
|
- **Deps:** F-006 | **Est:** 3 hrs
|
|
|
|
|
|
|
|
|
|
### C-011: Job Status by Plant
|
|
|
|
|
- [ ] `/(portal)/jobs/status/page.tsx`
|
|
|
|
|
- [ ] Service: `getJobStatusByPlantByCustomer(custId, dbName)` using `JobStatusByPlantByCustomer.sql`
|
|
|
|
|
- [ ] Note: requires DBNAME cross-database parameter
|
|
|
|
|
- [ ] Data table grouped by plant
|
|
|
|
|
- **Deps:** F-006 | **Est:** 3 hrs
|
|
|
|
|
|
|
|
|
|
### C-012: Job Traveler + PDF
|
|
|
|
|
- [ ] `/(portal)/jobs/[jobNum]/traveler/page.tsx`
|
|
|
|
|
- [ ] Service: `getJobTraveler(jobNum)` using `jobTraveler.sql`
|
|
|
|
|
- [ ] Detailed routing/operations view
|
|
|
|
|
- [ ] PDF export
|
|
|
|
|
- **Deps:** F-006 | **Est:** 4 hrs
|
|
|
|
|
|
|
|
|
|
### C-013: Shipment Request Cart Workflow
|
|
|
|
|
- [ ] `/(portal)/shipment-requests/page.tsx` — list existing requests
|
|
|
|
|
- [ ] `/(portal)/shipment-requests/new/page.tsx` — new request flow
|
|
|
|
|
- [ ] `/(portal)/shipment-requests/[id]/page.tsx` — edit/view request
|
|
|
|
|
- [ ] Step 1: Select ship-to address (from `portal_CustomerShipToAddresses`)
|
|
|
|
|
- [ ] Sub-user filter: only '%NB HANDY%' addresses
|
|
|
|
|
- [ ] Step 2: Add inventory items to cart
|
|
|
|
|
- [ ] Browse available inventory, select items
|
|
|
|
|
- [ ] Set quantity per line item
|
|
|
|
|
- [ ] Step 3: Enter details (Order#, PO#, Release#, Pickup Date, Instructions, Email Recipients)
|
|
|
|
|
- [ ] Submit: create `ship_request` + `ship_request_detail` records, send emails
|
|
|
|
|
- [ ] Cancel: set `IsCancelled=true`, record canceller, send cancellation emails
|
|
|
|
|
- [ ] Cart persistence: save progress to DB, show `LastCartActivity`
|
|
|
|
|
- [ ] Confirmation page after submit
|
|
|
|
|
- **Deps:** F-005, C-003 | **Est:** 12 hrs
|
|
|
|
|
|
|
|
|
|
### C-014: Coil Allocation Request Cart Workflow
|
|
|
|
|
- [ ] Mirror of C-013 but for allocation requests
|
|
|
|
|
- [ ] Additional field: JobNum (from Epicor)
|
|
|
|
|
- [ ] Uses `alloc_request` + `alloc_request_detail` tables
|
|
|
|
|
- [ ] `getCoilAllocationsByJobNumber()` for allocation data
|
|
|
|
|
- **Deps:** F-005, C-003 | **Est:** 8 hrs
|
|
|
|
|
|
|
|
|
|
### C-015: Invoice Viewing (Customer)
|
|
|
|
|
- [ ] `/(portal)/invoices/page.tsx`
|
|
|
|
|
- [ ] Only visible if company `CanAccessInvoices=true`
|
|
|
|
|
- [ ] List invoices from `inv_upload_entry` filtered by company
|
|
|
|
|
- [ ] PDF download endpoint `/api/invoices/:id/pdf`
|
|
|
|
|
- [ ] File serving from storage directory
|
|
|
|
|
- **Deps:** F-005, F-009 | **Est:** 4 hrs
|
|
|
|
|
|
|
|
|
|
### C-016: Notifications Display
|
|
|
|
|
- [ ] Notification bell in header with unread count
|
|
|
|
|
- [ ] Alert banner on dashboard for IsAlert=true notifications
|
|
|
|
|
- [ ] Notification list view
|
|
|
|
|
- [ ] Mark as read → create `quest_user_notification_alert_read` record
|
|
|
|
|
- **Deps:** F-005, F-009 | **Est:** 3 hrs
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## Phase 3: Admin Features (Est. 60-80 hrs)
|
|
|
|
|
|
|
|
|
|
### A-001: User Management
|
|
|
|
|
- [ ] `/(portal)/admin/users/page.tsx` — user list with search/filter
|
|
|
|
|
- [ ] `/(portal)/admin/users/[id]/page.tsx` — user detail/edit
|
|
|
|
|
- [ ] `/(portal)/admin/users/new/page.tsx` — create user
|
|
|
|
|
- [ ] CRUD operations via server actions
|
|
|
|
|
- [ ] Assign/remove company associations
|
|
|
|
|
- [ ] Assign permission groups
|
|
|
|
|
- [ ] Activate/deactivate users
|
|
|
|
|
- [ ] Set IsSubUser flag
|
|
|
|
|
- [ ] View login history (success/failure counts)
|
|
|
|
|
- **Deps:** F-007, F-005 | **Est:** 8 hrs
|
|
|
|
|
|
|
|
|
|
### A-002: Company Management
|
|
|
|
|
- [ ] `/(portal)/admin/companies/page.tsx` — company list
|
|
|
|
|
- [ ] `/(portal)/admin/companies/[id]/page.tsx` — company detail/edit
|
|
|
|
|
- [ ] Edit: display name, InvoicingEmail, OrderAckEmail, CanAccessInvoices, ReceivesSOEmails
|
|
|
|
|
- [ ] Activate/deactivate companies
|
|
|
|
|
- [ ] View associated users
|
|
|
|
|
- **Deps:** F-005 | **Est:** 5 hrs
|
|
|
|
|
|
|
|
|
|
### A-003: Account Request Management
|
|
|
|
|
- [ ] `/(portal)/admin/account-requests/page.tsx`
|
|
|
|
|
- [ ] List pending requests
|
|
|
|
|
- [ ] Approve: create auth_user + quest_user + company association
|
|
|
|
|
- [ ] Reject: mark RequestProcessed without creating user
|
|
|
|
|
- [ ] Send approval/rejection email
|
|
|
|
|
- **Deps:** F-005, A-001 | **Est:** 4 hrs
|
|
|
|
|
|
|
|
|
|
### A-004: Invoice Upload + Processing
|
|
|
|
|
- [ ] `/(portal)/admin/invoices/upload/page.tsx`
|
|
|
|
|
- [ ] Multi-page PDF upload
|
|
|
|
|
- [ ] Parse PDF to extract individual invoices by invoice number
|
|
|
|
|
- [ ] Match to CustID via `getCustomerIDsForInvoiceNumbers()`
|
|
|
|
|
- [ ] Match to JobNum via `getJobNumberByInvoiceNumber()`
|
|
|
|
|
- [ ] Create `inv_upload` + `inv_upload_entry` records
|
|
|
|
|
- [ ] Status tracking (upload status, per-entry errors)
|
|
|
|
|
- [ ] Send invoices to companies with InvoicingEmail
|
|
|
|
|
- [ ] Handle: ignored entries, replaced entries, send errors
|
|
|
|
|
- [ ] Upload history view
|
|
|
|
|
- **Deps:** F-005, C-015 | **Est:** 12 hrs
|
|
|
|
|
|
|
|
|
|
### A-005: AP Check Processing
|
|
|
|
|
- [ ] `/(portal)/admin/ap-check/page.tsx`
|
|
|
|
|
- [ ] Excel upload (XLS/XLSX)
|
|
|
|
|
- [ ] Extract Group ID from cell AB9
|
|
|
|
|
- [ ] Query Epicor CheckLines.sql by Group ID
|
|
|
|
|
- [ ] Generate CSV with specific formatting
|
|
|
|
|
- [ ] Country code handling: 'USA' vs 'FOR' (foreign)
|
|
|
|
|
- [ ] Address field processing
|
|
|
|
|
- [ ] Download generated CSV
|
|
|
|
|
- [ ] Batch status tracking in `finance_ap_check_processing_batch`
|
|
|
|
|
- **Deps:** F-005 | **Est:** 8 hrs
|
|
|
|
|
|
|
|
|
|
### A-006: Notification Management
|
|
|
|
|
- [ ] `/(portal)/admin/notifications/page.tsx` — list + CRUD
|
|
|
|
|
- [ ] Create: Title, Text, IsAlert, Date, Time
|
|
|
|
|
- [ ] Edit/delete existing notifications
|
|
|
|
|
- [ ] Preview notification as customer would see it
|
|
|
|
|
- **Deps:** F-005 | **Est:** 3 hrs
|
|
|
|
|
|
|
|
|
|
### A-007: Documentation / Knowledge Base
|
|
|
|
|
- [ ] `/(portal)/admin/documentation/page.tsx` — category + article management
|
|
|
|
|
- [ ] Category CRUD (name, icon)
|
|
|
|
|
- [ ] Article CRUD (content, category assignment)
|
|
|
|
|
- [ ] Permission group assignment per article
|
|
|
|
|
- [ ] Customer-facing documentation viewer
|
|
|
|
|
- **Deps:** F-005 | **Est:** 5 hrs
|
|
|
|
|
|
|
|
|
|
### A-008: Shipping Reports
|
|
|
|
|
- [ ] `/(portal)/admin/shipping-reports/page.tsx`
|
|
|
|
|
- [ ] View generated reports (stored Excel files)
|
|
|
|
|
- [ ] Manual trigger: generate previous month report
|
|
|
|
|
- [ ] Manual trigger: generate YTD report
|
|
|
|
|
- [ ] Service: `getShippingForMonth()`, `getShippingYTD()` via Epicor
|
|
|
|
|
- [ ] Excel generation using a library (e.g., ExcelJS)
|
|
|
|
|
- **Deps:** F-006 | **Est:** 4 hrs
|
|
|
|
|
|
|
|
|
|
### A-009: Wave/EDI Process Management
|
|
|
|
|
- [ ] `/(portal)/admin/wave/page.tsx`
|
|
|
|
|
- [ ] List `wave_process` entries with enable/disable toggles
|
|
|
|
|
- [ ] View `wave_process_history` with run results
|
|
|
|
|
- [ ] Manual trigger for processes
|
|
|
|
|
- [ ] Minimal implementation — client migrating EDI to another product
|
|
|
|
|
- **Deps:** F-005 | **Est:** 4 hrs
|
|
|
|
|
|
|
|
|
|
### A-010: Paint Schedule Management
|
|
|
|
|
- [ ] `/(portal)/admin/paint-schedule/page.tsx`
|
|
|
|
|
- [ ] CRUD for paint schedule entries
|
|
|
|
|
- [ ] Plant/line selection from `paint_plant`, `paint_line`, `paint_plant_line`
|
|
|
|
|
- [ ] Job lookup from Epicor (`getJobForPaintLine`, `getJob`)
|
|
|
|
|
- [ ] Schedule view (calendar or table format)
|
|
|
|
|
- [ ] Fields: JobID, PartDescription, Customer, Qty, EstimatedRunTime, StartTime, Notes, Complete, LineSpeed
|
|
|
|
|
- **Deps:** F-005, F-006 | **Est:** 6 hrs
|
|
|
|
|
|
|
|
|
|
### A-011: Admin Password Reset Links
|
|
|
|
|
- [ ] Generate password reset link for any user
|
|
|
|
|
- [ ] Create `auth_password_reset` record with token
|
|
|
|
|
- [ ] Optional: bypass minimum time restriction (`min_time_bypass`)
|
|
|
|
|
- [ ] Copy link to clipboard for admin to send manually
|
|
|
|
|
- **Deps:** F-007 | **Est:** 2 hrs
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## Phase 4: Background Jobs & Polish (Est. 30-40 hrs)
|
|
|
|
|
|
|
|
|
|
### J-001: BullMQ Infrastructure
|
|
|
|
|
- [ ] Install and configure BullMQ with Redis connection
|
|
|
|
|
- [ ] `src/jobs/worker.ts` — worker process setup
|
|
|
|
|
- [ ] Job registration and scheduling system
|
|
|
|
|
- [ ] Dashboard: Bull Board or similar for job monitoring (admin only)
|
|
|
|
|
- [ ] Retry logic with exponential backoff
|
|
|
|
|
- [ ] Dead letter queue for failed jobs
|
|
|
|
|
- **Deps:** F-002 | **Est:** 4 hrs
|
|
|
|
|
|
|
|
|
|
### J-002: Shipment Request Nag Job
|
|
|
|
|
- [ ] Runs every 15 minutes
|
|
|
|
|
- [ ] Find open (unsubmitted) shipment requests
|
|
|
|
|
- [ ] Send reminder email to request owner
|
|
|
|
|
- [ ] Track `LastNagDT` to avoid duplicate nags
|
|
|
|
|
- [ ] Respects `withoutOverlapping` (skip if previous run still active)
|
|
|
|
|
- **Deps:** J-001, C-013 | **Est:** 3 hrs
|
|
|
|
|
|
|
|
|
|
### J-003: Order Acknowledgement Email Job
|
|
|
|
|
- [ ] Runs hourly
|
|
|
|
|
- [ ] Query Epicor for new orders (`getOrdersForCustomerOnOrAfterDate`)
|
|
|
|
|
- [ ] Cross-reference against `quest_processed_order_acknowledgement_email`
|
|
|
|
|
- [ ] Send to companies with `ReceivesSOEmails=true` and `OrderAckEmail` set
|
|
|
|
|
- [ ] Record in `quest_processed_order_acknowledgement_email` to prevent duplicates
|
|
|
|
|
- **Deps:** J-001, C-004 | **Est:** 4 hrs
|
|
|
|
|
|
|
|
|
|
### J-004: Invoice Cleanup Job
|
|
|
|
|
- [ ] Runs daily
|
|
|
|
|
- [ ] `disc:clearoldinvoices` equivalent
|
|
|
|
|
- [ ] Clean up old/expired invoice temporary data
|
|
|
|
|
- **Deps:** J-001, A-004 | **Est:** 2 hrs
|
|
|
|
|
|
|
|
|
|
### J-005: Shipping Report Generation Jobs
|
|
|
|
|
- [ ] Monthly report: 1st of month at 3am
|
|
|
|
|
- [ ] Call `getShippingForMonth(lastMonth)`
|
|
|
|
|
- [ ] Generate Excel file, save to storage
|
|
|
|
|
- [ ] YTD report: twice daily
|
|
|
|
|
- [ ] Call `getShippingYTD()`
|
|
|
|
|
- [ ] Generate/overwrite Excel file
|
|
|
|
|
- **Deps:** J-001, A-008 | **Est:** 4 hrs
|
|
|
|
|
|
|
|
|
|
### J-006: Wave EDI End-of-Month Job
|
|
|
|
|
- [ ] Runs monthly on 1st
|
|
|
|
|
- [ ] Execute Wave EDI end-of-month process
|
|
|
|
|
- [ ] Log results to `wave_process_history`
|
|
|
|
|
- [ ] Send completion email
|
|
|
|
|
- [ ] Minimal implementation per client direction
|
|
|
|
|
- **Deps:** J-001, A-009 | **Est:** 3 hrs
|
|
|
|
|
|
|
|
|
|
### J-007: Email Service (Microsoft Graph)
|
|
|
|
|
- [ ] `src/lib/email.ts` — Microsoft Graph API client
|
|
|
|
|
- [ ] OAuth2 client credentials flow (tenant + client ID + secret)
|
|
|
|
|
- [ ] Send email function: `sendEmail(to, subject, htmlBody, from?)`
|
|
|
|
|
- [ ] Email logging to `quest_email_log`
|
|
|
|
|
- [ ] Template system for common emails (request confirmation, nag, acknowledgement)
|
|
|
|
|
- [ ] Error handling + retry for transient Graph API failures
|
|
|
|
|
- **Deps:** F-001 | **Est:** 5 hrs
|
|
|
|
|
|
|
|
|
|
### J-008: PDF Generation Service
|
|
|
|
|
- [ ] `src/lib/pdf.ts` — PDF generation abstraction
|
|
|
|
|
- [ ] Method 1: Puppeteer (render HTML → PDF, replaces wkhtmltopdf)
|
|
|
|
|
- [ ] Method 2: @react-pdf/renderer for structured documents
|
|
|
|
|
- [ ] Templates: Order Acknowledgement, BOL, Job Traveler, Invoice
|
|
|
|
|
- [ ] Consistent header/footer styling
|
|
|
|
|
- **Deps:** F-001 | **Est:** 4 hrs
|
|
|
|
|
|
|
|
|
|
### J-009: E2E Test Suite
|
|
|
|
|
- [ ] Playwright configuration
|
|
|
|
|
- [ ] Test: Login flow (success + failure + rate limiting)
|
|
|
|
|
- [ ] Test: Dashboard loads with data
|
|
|
|
|
- [ ] Test: Inventory browse → category → summary → detail drill-down
|
|
|
|
|
- [ ] Test: Shipment request full workflow (create → add items → submit)
|
|
|
|
|
- [ ] Test: Admin invoice upload
|
|
|
|
|
- [ ] Test: Company switcher (admin)
|
|
|
|
|
- [ ] Data validation tests comparing Epicor query results
|
|
|
|
|
- **Deps:** All C-* tasks | **Est:** 8 hrs
|
|
|
|
|
|
|
|
|
|
### J-010: Production Deployment & Cutover
|
|
|
|
|
- [ ] Verify all Traefik routes serving correctly with SSL
|
|
|
|
|
- [ ] Run data migration scripts against production database
|
|
|
|
|
- [ ] Verify all Epicor connections from expvtcasp01
|
|
|
|
|
- [ ] DNS cutover: update `quest.vorteq.wulf.cloud` or `portal.vorteqcoil.com` as needed
|
|
|
|
|
- [ ] Smoke test all features post-cutover
|
|
|
|
|
- [ ] Rollback plan documented
|
|
|
|
|
- [ ] Security hardening: change Traefik dashboard password, secure .env, firewall rules
|
|
|
|
|
- **Deps:** All tasks | **Est:** 4 hrs
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## Infrastructure Tasks (Completed)
|
|
|
|
|
|
|
|
|
|
### I-001: Server Provisioning
|
|
|
|
|
- [x] Linux server provisioned (expvtcasp01)
|
|
|
|
|
- [x] Docker installed
|
|
|
|
|
- [x] Docker Compose installed
|
|
|
|
|
- [x] Netbird VPN configured (100.89.62.20)
|
|
|
|
|
|
|
|
|
|
### I-002: Forgejo Repository
|
|
|
|
|
- [x] Repository created at `forgejo.wulfconsulting.cloud/lorentz/quest-vorteq`
|
|
|
|
|
- [x] SSH key configured (port 222)
|
|
|
|
|
- [x] Git identity configured on server
|
|
|
|
|
- [x] Initial infrastructure files pushed
|
|
|
|
|
|
|
|
|
|
### I-003: CI/CD Pipeline
|
|
|
|
|
- [x] Forgejo Actions enabled on repository
|
|
|
|
|
- [x] Self-hosted runner registered (`expvtcasp01`) using Docker-in-Docker
|
|
|
|
|
- [x] Build workflow: checkout → Docker build → push to Forgejo registry
|
|
|
|
|
- [x] Deploy workflow: SSH into server → pull → restart containers
|
|
|
|
|
- [x] Secrets configured (registry credentials, deploy SSH key)
|
|
|
|
|
|
|
|
|
|
### I-004: Infrastructure Stack
|
|
|
|
|
- [x] Traefik v3 running with Cloudflare DNS challenge
|
|
|
|
|
- [x] SSL certificate issued for `traefik.vorteq.wulf.cloud`
|
|
|
|
|
- [x] PostgreSQL 16 running with dev/test/prod databases
|
|
|
|
|
- [x] Redis 7 running with separate DB numbers per environment
|
|
|
|
|
- [x] Cloudflare DNS A records pointing to Netbird IP
|
|
|
|
|
|
|
|
|
|
### I-005: Domain Configuration
|
|
|
|
|
- [x] `quest.vorteq.wulf.cloud` → 100.89.62.20 (production)
|
|
|
|
|
- [x] `dev.quest.vorteq.wulf.cloud` → 100.89.62.20 (development)
|
|
|
|
|
- [x] `testing.vorteq.wulf.cloud` → 100.89.62.20 (testing)
|
|
|
|
|
- [x] `traefik.vorteq.wulf.cloud` → 100.89.62.20 (dashboard)
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## Summary
|
|
|
|
|
|
|
|
|
|
| Phase | Tasks | Est. Hours (Low) | Est. Hours (High) |
|
|
|
|
|
|-------|-------|-------------------|---------------------|
|
|
|
|
|
| Phase 1: Foundation | F-001 to F-010 | 28 | 38 |
|
|
|
|
|
| Phase 2: Core Features | C-001 to C-016 | 80 | 110 |
|
|
|
|
|
| Phase 3: Admin Features | A-001 to A-011 | 60 | 80 |
|
|
|
|
|
| Phase 4: Jobs & Polish | J-001 to J-010 | 30 | 40 |
|
|
|
|
|
| Infrastructure | I-001 to I-005 | Done | Done |
|
|
|
|
|
| **TOTAL** | **52 tasks** | **198** | **268** |
|