From 1ffd1d5708e46974f55d8b9d3eb19ba6a12e7995 Mon Sep 17 00:00:00 2001 From: Rogee Date: Fri, 5 Jun 2026 04:31:12 +0800 Subject: [PATCH] docs: record contact export email checkpoint --- docs/CHATWOOT_PARITY_DEVELOPMENT_PLAN.md | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/docs/CHATWOOT_PARITY_DEVELOPMENT_PLAN.md b/docs/CHATWOOT_PARITY_DEVELOPMENT_PLAN.md index 8e0fe190..db328538 100644 --- a/docs/CHATWOOT_PARITY_DEVELOPMENT_PLAN.md +++ b/docs/CHATWOOT_PARITY_DEVELOPMENT_PLAN.md @@ -16,7 +16,7 @@ Build GoChat as a Go backend that can directly reuse the frontend from `referenc ## Current Baseline -- Latest implementation checkpoint: `653db81 feat(crm): validate imported contact labels`. +- Latest implementation checkpoint: `c2766fe feat(crm): email contact export completions`. - Worktree status at this planning checkpoint: clean. - `go test ./...` passes. - Route dump succeeds with `TOTAL: 820` after adding the Chatwoot-compatible `PUT` contact note update route. @@ -82,16 +82,17 @@ This ledger records the committed parity checkpoints that future slices should b | `fb775b7 feat(crm): align nested conversation payloads` | Advanced B4 nested CRM conversation parity: contact and company conversations now render Chatwoot's `{ payload: [conversation partial] }` shape with nested `meta.sender`, `messages`, `last_non_activity_message`, account/inbox IDs, timestamps, priority, labels, and unread fields from the shared conversation serializer. Company conversations now follow the enterprise controller's latest-20, `last_activity_at DESC` behavior instead of paginated raw models. | Focused nested conversation handler tests passed; focused handler/service/repository/router tests passed; escalated full `go test ./...` passed after sandbox socket limits blocked local-only auth/middleware tests; `git diff --check` passed. No route changes; route dump remains `TOTAL: 820`. | Continue B4 with CRM Meilisearch search shape, stricter import label validation, frontend smoke fixtures, and deferred export email delivery. | | `202da19 feat(crm): route crm search through meilisearch` | Advanced B4 CRM search parity: contact and company service search now use the configured search service for non-empty queries, preserving Chatwoot CRM `{ meta, payload }` serializers by resolving Meilisearch result IDs back through account-scoped repositories. Bootstrap wires the Meilisearch-backed search service into contact/company services, and DB search remains only the no-reader/test fallback. Global search DB fallback now includes companies so the company document shape has a repository equivalent. | Focused search/service/repository/API tests passed; full `go test ./...` passed; `git diff --check` passed. No route changes; route dump remains `TOTAL: 820`. | Continue B4 with stricter import label validation, frontend smoke fixtures, and deferred export email delivery. | | `653db81 feat(crm): validate imported contact labels` | Completed the remaining B4 import validation gap: contact CSV import now validates `labels` against existing account labels before creating/updating contacts, rejects rows with unknown labels, records failed row counts, preserves canonical existing label names, and no longer creates arbitrary account tags from import input. | Focused import/service/API tests passed; full `go test ./...` passed; `git diff --check` passed. No route changes; route dump remains `TOTAL: 820`. | Continue B4 with frontend CRM smoke fixtures and deferred export email delivery. | +| `c2766fe feat(crm): email contact export completions` | Completed B4 contact export email delivery: the completed export artifact now triggers a Chatwoot-style `contact_export_complete` mailer boundary for the requesting user's email, using the same subject and download URL semantics while preserving the existing persisted notification. The default mailer reads Chatwoot-compatible SMTP env vars and no-ops when SMTP is not configured. | Focused export/service/API tests passed; full `go test ./...` passed; `git diff --check` passed. No route changes; route dump remains `TOTAL: 820`. | Continue B4 with frontend CRM smoke fixtures. | ## Next Slice Contract Completed implementation slice: B4 contact merge, contact import, contact export, and notes checkpoints now cover contact/company CRUD payloads, company-contact relations, contact labels, label filtering, avatar deletion, selected custom-attribute deletion, company destroy-custom-attributes, dashboard contact merge behavior, persisted contact import lifecycle behavior, persisted contact export artifacts, and Chatwoot-shaped contact/company notes for the CRM flows. -Next implementation slice: continue B4 with frontend CRM smoke coverage. Keep B3 in review for delivery/storage side-effect parity, and keep real SMTP/email delivery for export completion tied to the broader mailer infrastructure work. +Next implementation slice: continue B4 with frontend CRM smoke coverage. Keep B3 in review for delivery/storage side-effect parity. | Step | Required result | Reference source | Verification | | --- | --- | --- | --- | -| N1 | Finish remaining contact/company behavior gaps: frontend smoke validation and any export email-delivery side effects not covered by the persisted notification. | Chatwoot contacts and enterprise companies controllers/Jbuilder views/frontend API clients. | Handler/service tests assert status codes, persistence, and exact `{ payload }` or `{ meta, payload }` shape. | +| N1 | Finish remaining contact/company behavior gaps with frontend smoke validation. | Chatwoot contacts and enterprise companies controllers/Jbuilder views/frontend API clients. | Handler/service tests assert status codes, persistence, and exact `{ payload }` or `{ meta, payload }` shape. | | N2 | Harden CRM Meilisearch behavior by extending indexed contact/company fields as new frontend cases are found. | Chatwoot search usage and local Meilisearch decision. | Search tests run against mocked Meilisearch engine and preserve CRM payload contracts. | | N3 | Preserve completed auth/profile/conversation/message fixtures while expanding the CRM suite. | Existing focused tests and Chatwoot frontend clients. | Existing auth/profile/conversation/message focused tests remain green. | | N4 | Regenerate route artifacts after route changes; current route dump is `TOTAL: 820` and tracked route parity is `262 exact, 0 missing`. | `cmd/dump_routes`, `cmd/route_parity`. | Route commands run when applicable. | @@ -141,8 +142,9 @@ Current B4 contacts/companies checkpoint: - Added nested contact/company conversation serializer parity: both endpoints now return `{ payload: [...] }` arrays built from the shared Chatwoot conversation partial, including nested sender metadata, latest message payloads, `last_non_activity_message`, labels, timestamps, priority, and unread fields. Company conversations are limited to the latest 20 by `last_activity_at DESC` like the enterprise controller. - Routed CRM contact/company non-empty search through the configured Meilisearch-backed search service while preserving existing Chatwoot CRM list serializers by resolving hit IDs through account-scoped repositories. Service tests now prove contact/company search reader usage, and global search has a company repository fallback for document-shape parity in local DB mode. - Added Chatwoot-style contact import label validation: CSV rows with labels not already present on the account are rejected, failed row counts are recorded on `data_imports`, valid labels use existing canonical tag names, and imports no longer create unknown labels from user-provided CSV values. +- Added contact export completion email delivery: after the persisted CSV artifact is generated, a Chatwoot-style mailer boundary sends `Your contact's export file is available to download.` to the requesting user when SMTP is configured, while keeping the in-app notification metadata. - Route dump is now `TOTAL: 820` and tracked route parity is `262 exact, 0 missing`. -- Remaining B4 gaps: real SMTP/email delivery for export completion and frontend smoke validation. +- Remaining B4 gap: frontend smoke validation. Active B4 task board: @@ -155,7 +157,7 @@ Active B4 task board: | B4.5 | Contact merge behavior and response shape. | `actions/contact_merges_controller.rb`, `ContactMergeAction`, contact merge Jbuilder view. | Done | `1e3bf47`; merge route/handler/service tests. | | B4.6a | Contact import/data-import persistence. | Contacts import controller, data import model/job, `DataImport::ContactManager`. | Done | `8eed2f1`; import handler/service tests cover status codes, lifecycle persistence, merge/update matching, labels, and custom attributes. | | B4.6b | Contacts export artifact/download behavior. | Contacts export controller and `Account::ContactsExportJob`. | Done | `47c2b80`; export request persists CSV artifact, download URL, label/filter data, row count, and notification metadata. | -| B4.6c | Contacts export email delivery. | `AdministratorNotifications::AccountNotificationMailer#contact_export_complete`. | Todo | Real mailer delivery is wired once mail infrastructure work reaches P5.4; current checkpoint persists notification metadata only. | +| B4.6c | Contacts export email delivery. | `AdministratorNotifications::AccountNotificationMailer#contact_export_complete`. | Done | `c2766fe`; export completion calls a mailer boundary with the Chatwoot subject, requesting user's email, and export download URL. SMTP env mailer no-ops when not configured. | | B4.6d | Contact import label validation. | `DataImportJob#build_contact_from_row` approved-label validation. | Done | `653db81`; import rejects unknown labels, keeps valid rows, records failed counts, and avoids creating arbitrary tags. | | B4.7a | Contact/company notes payload depth. | Contact notes controller/Jbuilder, company notes frontend consumer. | Done | `af57482`; raw contact note array/object fixtures, nested user payloads, empty destroy response, and company note user metadata tests. | | B4.7b | Contact/company conversations payload depth. | Nested conversations controllers and Jbuilder views. | Done | `fb775b7`; nested CRM conversation payload fixtures cover shared conversation partial fields, latest messages, sender metadata, and company latest-20 ordering. | @@ -697,3 +699,4 @@ Verification milestone gates: - 2026-06-05: B4 nested contact/company conversations checkpoint committed as `fb775b7 feat(crm): align nested conversation payloads`; contact and company nested conversation endpoints now return Chatwoot `{ payload: [...] }` conversation partials with nested sender metadata, latest message payloads, `last_non_activity_message`, labels, timestamps, priority, account/inbox fields, and company latest-20 ordering by `last_activity_at DESC`. Focused nested conversation tests, focused handler/service/repository/router tests, escalated full `go test ./...`, and `git diff --check` passed. Route dump unchanged at `TOTAL: 820`. - 2026-06-05: B4 CRM Meilisearch checkpoint committed as `202da19 feat(crm): route crm search through meilisearch`; contact and company CRM search now routes non-empty queries through the configured search service, resolves Meilisearch result IDs back through account-scoped repositories to preserve Chatwoot CRM list payloads, wires the search reader in app bootstrap, and adds company document fallback coverage to global DB search. Focused search/service/repository/API tests, full `go test ./...`, and `git diff --check` passed. Route dump unchanged at `TOTAL: 820`. - 2026-06-05: B4 contact import label validation checkpoint committed as `653db81 feat(crm): validate imported contact labels`; CSV import now matches Chatwoot's approved-label guard by rejecting rows whose `labels` column contains labels absent from the account, preserving valid rows, recording failed row counts on `data_imports`, and preventing arbitrary tag creation from CSV input. Focused import/service/API tests, full `go test ./...`, and `git diff --check` passed. Route dump unchanged at `TOTAL: 820`. +- 2026-06-05: B4 contact export email checkpoint committed as `c2766fe feat(crm): email contact export completions`; completed contact exports now invoke a Chatwoot-style `contact_export_complete` mailer boundary for the requesting user's email, using the export download URL and subject `Your contact's export file is available to download.`, while retaining the persisted notification. Focused export/service/API tests, full `go test ./...`, and `git diff --check` passed. Route dump unchanged at `TOTAL: 820`.