Skip to main content

Feature Design Doc: Implementation Portal v1

Overview

A native Professional Services Automation (PSA) module for FlowPOS that enables any service business to manage client onboarding and project delivery via structured, phase-based implementation boards. Includes a public client-facing portal, billable step tracking, manual time entry, and invoice draft generation — all connected to the existing Stripe and SendGrid infrastructure.


Problem Statement

FlowPOS merchants who deliver services (IT consultancies, law firms, marketing agencies, architects, and FlowPOS's own onboarding team) lack a structured tool to:

  • Define repeatable delivery templates by industry/vertical
  • Share a client-facing view of project progress without requiring a FlowPOS login
  • Track billable work at the task level and generate invoices from completed steps
  • Create accountability through assignee roles, due dates, and notifications

The closest commercial tools (Rocketlane, Dock, Vitally) are standalone SaaS products. Building this natively inside FlowPOS makes it available to all tenants and positions FlowPOS as a platform beyond POS.


Target Users

  • Internal implementers (FlowPOS team or merchant's operations staff): create boards, manage progress, log hours, issue invoices
  • External clients (the merchant's own customers or the business being onboarded): view progress, complete assigned steps, upload documents — without a FlowPOS account

Data Model

GuideTemplate

Reusable template scoped to a business. Ships with starter templates per vertical.

GuideTemplate
id uuid PK
businessId uuid FK — tenant scope
name string — e.g. "Restaurant Onboarding", "Corporate Registration"
vertical enum: retail | restaurant | legal | accounting | agency | it | generic
description string?
isDefault boolean — starter templates shipped by FlowPOS
createdAt timestamp
updatedAt timestamp

Phase

Ordered phases within a template.

Phase
id uuid PK
templateId uuid FK → GuideTemplate
name string
order integer
description string?

Step

Individual work items within a phase.

Step
id uuid PK
phaseId uuid FK → Phase
name string
description string?
order integer
type enum: task | document | form | approval
assigneeRole enum: implementer | client
billableType enum: fixed | hourly | milestone | non_billable
fixedAmount decimal? — used when billableType = fixed
hourlyRate decimal? — used when billableType = hourly
isVisibleToClient boolean — controls whether amount shows on client portal
dueOffsetDays integer? — relative due date offset from board start date

ImplementationBoard

One board per client engagement, derived from a template.

ImplementationBoard
id uuid PK
businessId uuid FK — tenant scope
templateId uuid FK → GuideTemplate (snapshot reference)
clientName string
clientEmail string?
clientLogoUrl string?
startDate date?
shareToken string UNIQUE — public read-only access token (UUID v4)
status enum: draft | active | completed | archived
internalNotes string?
createdBy uuid FK → User
createdAt timestamp
updatedAt timestamp

BoardPhase

Phase instance on a specific board (copied from template at board creation).

BoardPhase
id uuid PK
boardId uuid FK → ImplementationBoard
name string
order integer
targetDate date?
status enum: pending | in_progress | completed
completedAt timestamp?

BoardStep

Step instance on a specific board.

BoardStep
id uuid PK
boardPhaseId uuid FK → BoardPhase
name string
description string?
order integer
type enum: task | document | form | approval
assigneeRole enum: implementer | client
billableType enum: fixed | hourly | milestone | non_billable
fixedAmount decimal?
hourlyRate decimal?
isVisibleToClient boolean
dueDate date?
status enum: pending | in_progress | completed | approved | rejected
completedAt timestamp?
completedBy uuid FK → User?

TimeEntry

Manual hour log against hourly-type steps.

TimeEntry
id uuid PK
boardStepId uuid FK → BoardStep
userId uuid FK → User
hours decimal — e.g. 1.5
description string?
loggedAt date
createdAt timestamp

StepAttachment

File uploads on document-type steps, stored in GCS.

StepAttachment
id uuid PK
boardStepId uuid FK → BoardStep
uploadedBy uuid FK → User? — null if uploaded by client via shareToken
fileName string
gcsKey string
mimeType string
sizeBytes integer
createdAt timestamp

StepComment

Per-step communication with internal/client visibility control.

StepComment
id uuid PK
boardStepId uuid FK → BoardStep
authorId uuid FK → User? — null if posted by client via shareToken
authorName string — denormalized for client comments
body string
visibility enum: internal | client
createdAt timestamp

InvoiceDraft

Auto-generated invoice from completed billable steps.

InvoiceDraft
id uuid PK
boardId uuid FK → ImplementationBoard
businessId uuid FK
status enum: draft | sent | paid | void
lineItems jsonb — snapshot of billable steps at generation time
totalAmount decimal
currency string — default: businessId currency (GTQ, USD, etc.)
generatedAt timestamp
stripeInvoiceId string? — populated when pushed to Stripe

Feature Scope

1. GuideTemplate Management (internal)

  • CRUD for templates scoped to businessId
  • CRUD for phases and steps within a template
  • Seed 4 starter templates at tenant creation:
    • FlowPOS Retail Onboarding (8 phases)
    • FlowPOS Restaurant Onboarding (8 phases)
    • Legal Matter (generic law firm template, 4 phases)
    • IT Project Delivery (agency/consultancy, 5 phases)
  • Templates can be cloned and customized per tenant

2. ImplementationBoard Management (internal)

  • Create board from a template: copies all phases + steps into BoardPhase/BoardStep instances
  • Set clientName, clientEmail, startDate, internalNotes
  • Due dates auto-calculated from startDate + step.dueOffsetDays at creation time
  • Board status transitions: draft → active → completed → archived
  • Implementer dashboard: list all active boards with health indicators
    • Green: no overdue steps
    • Yellow: 1–2 overdue steps
    • Red: 3+ overdue steps or no activity in 7 days

3. Step Execution (internal)

  • Mark steps complete, in-progress, or rejected
  • For approval steps: separate approve/reject action with optional comment
  • Manual time entry on hourly steps: hours + description + loggedAt
  • File upload on document steps: signed GCS URL flow
  • Per-step comments: visibility toggle (internal vs. client-visible)
  • Inline due date editing per step

4. Public Client Portal (shareToken)

Accessible via /portal/:shareToken — no FlowPOS login required.

Displays:

  • Business name + logo (from board config)
  • Overall progress bar (% of steps completed)
  • Phase-by-phase breakdown with status
  • "What's next" card: next open step assigned to client
  • Per-step detail: description, due date, status, client-visible comments
  • Client can:
    • Mark their assigned task steps complete
    • Upload files on document steps
    • Post comments (stored with visibility: client, authorName from a prompt)
  • Billable steps show amount only if isVisibleToClient = true

Portal is read-only for implementer-assigned steps from the client's perspective.

5. Invoice Draft Generation

  • "Generate Invoice" action on any active or completed board
  • Collects all BoardStep where:
    • billableType != non_billable
    • status = completed OR approved
    • Not already included in a previous InvoiceDraft
  • Line item calculation:
    • fixed: uses fixedAmount
    • hourly: sums all TimeEntry.hours × hourlyRate
    • milestone: fixed amount, triggered only when a designated approval step is approved
  • Produces InvoiceDraft with lineItems jsonb snapshot + totalAmount
  • Draft can be reviewed, edited (line items only), then pushed to Stripe as a Stripe Invoice
  • Status tracked: draft → sent → paid → void

6. Email Notifications (SendGrid)

Events that trigger email:

EventRecipient
Board activatedClient (welcome email with portal link)
Step assigned to client is completed by implementerClient (next action available)
Step assigned to implementer is completed by clientImplementer
Step is overdue by 1 dayAssignee
Phase completedBoth parties
Board completedBoth parties
Invoice draft sentClient

All emails include the portal link. Templated in SendGrid, i18n-ready (es/en).


API Endpoints (NestJS)

Templates

GET    /businesses/:businessId/guide-templates
POST /businesses/:businessId/guide-templates
GET /businesses/:businessId/guide-templates/:id
PUT /businesses/:businessId/guide-templates/:id
DELETE /businesses/:businessId/guide-templates/:id

POST /guide-templates/:id/phases
PUT /guide-templates/:id/phases/:phaseId
DELETE /guide-templates/:id/phases/:phaseId

POST /guide-templates/:id/phases/:phaseId/steps
PUT /guide-templates/:id/phases/:phaseId/steps/:stepId
DELETE /guide-templates/:id/phases/:phaseId/steps/:stepId

Boards

GET    /businesses/:businessId/implementation-boards
POST /businesses/:businessId/implementation-boards
GET /businesses/:businessId/implementation-boards/:id
PUT /businesses/:businessId/implementation-boards/:id
DELETE /businesses/:businessId/implementation-boards/:id

PATCH /implementation-boards/:id/status

Steps

PATCH  /board-steps/:stepId/status
POST /board-steps/:stepId/time-entries
GET /board-steps/:stepId/time-entries
POST /board-steps/:stepId/attachments
GET /board-steps/:stepId/attachments
POST /board-steps/:stepId/comments
GET /board-steps/:stepId/comments

Client Portal (public, no auth)

GET    /portal/:shareToken
PATCH /portal/:shareToken/steps/:stepId/complete
POST /portal/:shareToken/steps/:stepId/attachments
POST /portal/:shareToken/steps/:stepId/comments

Invoices

POST   /implementation-boards/:boardId/invoice-drafts
GET /implementation-boards/:boardId/invoice-drafts
PUT /invoice-drafts/:id
POST /invoice-drafts/:id/send — push to Stripe + email client

Hexagonal Architecture Layout

modules/
implementation-portal/
domain/
entities/
guide-template.entity.ts
phase.entity.ts
step.entity.ts
implementation-board.entity.ts
board-phase.entity.ts
board-step.entity.ts
time-entry.entity.ts
step-attachment.entity.ts
step-comment.entity.ts
invoice-draft.entity.ts
value-objects/
step-type.vo.ts
billable-type.vo.ts
assignee-role.vo.ts
ports/
guide-template.repository.port.ts
implementation-board.repository.port.ts
board-step.repository.port.ts
invoice-draft.repository.port.ts
storage.port.ts — GCS upload/signed URL
notification.port.ts — SendGrid events
payment.port.ts — Stripe Invoice push
services/
board-factory.service.ts — creates BoardPhase/BoardStep from template
invoice-calculator.service.ts — computes line items from completed steps
overdue-checker.service.ts — domain logic for health status

application/
use-cases/
create-guide-template.use-case.ts
create-implementation-board.use-case.ts
complete-board-step.use-case.ts
log-time-entry.use-case.ts
upload-step-attachment.use-case.ts
post-step-comment.use-case.ts
generate-invoice-draft.use-case.ts
send-invoice-draft.use-case.ts
get-client-portal.use-case.ts
complete-client-step.use-case.ts

infrastructure/
persistence/
kysely/
guide-template.repository.ts
implementation-board.repository.ts
board-step.repository.ts
invoice-draft.repository.ts
storage/
gcs-storage.adapter.ts
notifications/
sendgrid-notification.adapter.ts
payment/
stripe-invoice.adapter.ts

interfaces/
http/
guide-template.controller.ts
implementation-board.controller.ts
board-step.controller.ts
client-portal.controller.ts
invoice-draft.controller.ts
dto/
(request/response DTOs per endpoint)

Multi-Tenancy

  • All templates and boards are scoped by businessId
  • Starter templates are seeded as isDefault: true and copied per tenant at provisioning
  • shareToken URLs are tenant-agnostic — the token itself identifies the board
  • Client portal does NOT expose businessId in the URL

i18n

  • All UI strings in es (primary) and en
  • Step type labels, phase status labels, and notification emails localized
  • Locale determined by board's businessId locale setting
  • Client portal defaults to es, respects browser Accept-Language as fallback

Storage (GCS)

  • Step attachments stored under: gs://{bucket}/portal/{boardId}/steps/{stepId}/{filename}
  • Signed URLs generated server-side (15-minute expiry for client portal uploads)
  • Max file size: 10MB per attachment
  • Allowed MIME types: pdf, png, jpg, docx, xlsx

Implementation Ordering (for AI coding assistants)

Follow this sequence to avoid dependency issues:

  1. Database migrations — all tables in order: guide_template, phase, step, implementation_board, board_phase, board_step, time_entry, step_attachment, step_comment, invoice_draft
  2. Domain entities + value objects — pure TypeScript, no framework
  3. Repository ports — interfaces only
  4. Kysely repository adapters — implement ports
  5. Domain servicesBoardFactory, InvoiceCalculator, OverdueChecker
  6. Use cases (application layer) — one at a time, in order listed above
  7. Storage adapter (GCS signed URL)
  8. Notification adapter (SendGrid templates)
  9. NestJS module wiring — providers, exports, imports
  10. Controllers + DTOs — internal endpoints first
  11. Client portal controller — public routes, shareToken guard
  12. Invoice draft → Stripe adapter
  13. React PWA — Template management UI
  14. React PWA — Board management + implementer dashboard
  15. React PWA — Step execution UI (complete, log hours, upload, comment)
  16. React PWA — Invoice draft review + send UI
  17. Public client portal page (Next.js or standalone React route under /portal/:shareToken)
  18. Overdue notification cron job (Cloud Scheduler → Cloud Run job)
  19. Seed starter templates (migration seed or admin script)
  20. Import Center adaptersGuideTemplateImportAdapter, BoardImportAdapter, TimeEntryImportAdapter
  21. Import Center UI — register new import types with upload → validate → preview → confirm flow
  22. Unit tests — domain layer — entities, value objects, BoardFactory, InvoiceCalculator, OverdueChecker
  23. Unit tests — application layer — all use cases with mocked ports
  24. Unit tests — infrastructure adapters — repositories, GCS, SendGrid, Stripe, import adapters
  25. Integration/e2e tests — controllers — all endpoints via supertest
  26. Unit tests — React PWA components and hooks
  27. Unit tests — client portal components
  28. Bruno API collection for all endpoints
  29. Swagger documentation on all controllers

Data Migration (Import Center)

The Implementation Portal integrates with the existing FlowPOS Import Center so merchants can migrate existing project data, client lists, or template structures into the module rather than starting from scratch.

Supported Import Types

GuideTemplate import

  • Source format: CSV or JSON
  • Columns/fields: templateName, vertical, phaseName, phaseOrder, stepName, stepOrder, stepType, assigneeRole, billableType, fixedAmount, hourlyRate, dueOffsetDays
  • Behavior: creates a new GuideTemplate with nested Phase and Step records scoped to the tenant's businessId
  • Use case: teams who already have their delivery process documented in a spreadsheet

ImplementationBoard import

  • Source format: CSV
  • Columns: clientName, clientEmail, templateName, startDate, status
  • Behavior: creates boards in draft status from the matching template; steps are instantiated but not completed
  • Use case: migrating active client projects from a previous tool (spreadsheet, Trello, Asana, etc.)

TimeEntry import

  • Source format: CSV
  • Columns: boardName, stepName, hours, description, loggedAt, userEmail
  • Behavior: appends historical time entries to matched BoardStep records
  • Use case: teams moving from a separate time tracking tool who want billing history intact

Import Center Integration Points

  • New import types registered in the Import Center module under category: implementation-portal
  • Each import type follows the existing Import Center pattern: upload file → validate → preview → confirm → process
  • Validation errors reported per row (e.g. unknown templateName, invalid stepType value, missing required fields)
  • Import runs async via existing job queue; status tracked in Import Center UI
  • Partial imports allowed: valid rows are processed, invalid rows reported in the error summary

Import Adapter (Hexagonal)

infrastructure/
import/
guide-template-import.adapter.ts — implements ImportPort<GuideTemplateRow>
board-import.adapter.ts — implements ImportPort<BoardRow>
time-entry-import.adapter.ts — implements ImportPort<TimeEntryRow>

Each adapter maps raw CSV/JSON rows to domain commands and delegates to the corresponding use case.


Testing Requirements

Unit tests are required for all new code in this module. Tests must be written alongside implementation, not after.

Backend (NestJS — Jest)

Domain layer — 100% coverage target

  • All domain entities: construction, state transitions, invariant enforcement
  • All value objects: valid/invalid input, equality
  • BoardFactory: verify correct BoardPhase/BoardStep instances are created from a template, due dates calculated correctly from startDate + dueOffsetDays
  • InvoiceCalculator: fixed amount, hourly (hours × rate), milestone gating, non-billable exclusion, multi-step aggregation, already-invoiced step exclusion
  • OverdueChecker: health status logic (green/yellow/red thresholds)

Application layer — use case tests

  • Each use case tested in isolation with mocked ports (repository, storage, notification, payment)
  • Key scenarios per use case:
    • Happy path
    • Entity not found (throws expected domain error)
    • Authorization failure (wrong businessId)
    • Invalid state transition (e.g. completing an already-completed step)
  • GetClientPortal: valid shareToken, expired/invalid token, board in draft status (should not be publicly accessible)
  • GenerateInvoiceDraft: no billable steps, partial billable steps, all steps billable, steps already included in prior draft excluded

Infrastructure layer — adapter tests

  • Repository adapters: tested against an in-memory or test database (not mocked) to verify query correctness
  • GcsStorageAdapter: mock GCS client, verify signed URL generation and key construction
  • SendgridNotificationAdapter: mock SendGrid client, verify correct template IDs and recipient resolution per event type
  • StripeInvoiceAdapter: mock Stripe client, verify line item mapping and invoice creation call
  • Import adapters: valid CSV rows, malformed rows, missing required fields, unknown reference values

Controller layer — e2e/integration tests

  • All endpoints tested via NestJS supertest with seeded test data
  • Auth guard behavior (protected vs. public shareToken routes)
  • Request validation (invalid DTOs return 400)
  • Correct HTTP status codes per scenario

Frontend PWA (React — Vitest + React Testing Library)

Unit tests per component/hook

  • useImplementationBoard: loading, error, and success states; step completion mutation
  • useTimeEntry: hour logging, validation (negative hours, zero hours rejected)
  • useInvoiceDraft: generation, line item display, send action
  • GuideTemplateForm: field validation, phase/step add-remove interactions
  • BoardStepCard: renders correct UI per step type and status; billable amount visibility toggle
  • StepCommentThread: internal vs. client-visible rendering; post comment action
  • ImplementerDashboard: health status badge rendering (green/yellow/red); board list filtering

Client portal components (public route)

  • ClientPortalView: renders progress bar, phases, and steps correctly from mock board data
  • ClientStepAction: correct action per step type (checkbox for task, upload for document, approve/reject for approval)
  • ClientCommentBox: posts comment with author name prompt; internal comments not rendered

Import Center UI

  • GuideTemplateImportFlow: file upload step, validation error display, confirm step
  • BoardImportFlow: same pattern

Testing Conventions

  • Test files colocated: *.spec.ts (backend), *.test.tsx (frontend)
  • No test depends on external network calls — all adapters mocked at the port boundary
  • Factories/fixtures provided for each domain entity to keep test setup concise
  • CI enforces: all tests pass before merge, coverage thresholds enforced per layer (domain: 100%, application: 90%, infrastructure: 80%, UI components: 80%)

Out of Scope for v1

  • Start/stop timer (manual hour entry only — timer in v2)
  • Customer accounts / client login (shareToken is sufficient for v1)
  • Recurring boards or board templates with variable pricing tiers
  • Multi-currency per board (uses business default currency)
  • Delivery tracking or GPS
  • Native mobile app for client portal
  • Automated Stripe subscription billing (invoice-based only in v1)

Speckit Command

/speckit.specify A Professional Services Automation (PSA) module for FlowPOS called Implementation Portal. Enables any service-based FlowPOS tenant (or FlowPOS itself) to manage client project delivery via structured GuideTemplates (phases + steps), ImplementationBoards (one per client engagement), per-step billable tracking (fixed | hourly | milestone | non-billable), manual time entry on hourly steps, auto-generated InvoiceDrafts pushed to Stripe, per-step file attachments (GCS), per-step comments with internal/client visibility, email notifications via SendGrid for key events, and a public client-facing portal accessible via shareToken (no login required). Integrates with the existing FlowPOS Import Center to support data migration of templates, boards, and time entries from CSV/JSON. Follows hexagonal architecture with businessId multi-tenant scoping. Ships with 4 starter GuideTemplates. Unit tests required across all layers: domain entities and services (100% coverage), application use cases (90%), infrastructure adapters (80%), and React PWA components and hooks (80%). React PWA covers template management, board management, step execution, time logging, and invoice drafting. A public Next.js or React route serves the client portal at /portal/:shareToken.