feat(contacts): default contact type
This commit is contained in:
@@ -49,12 +49,12 @@ Hermes task landing checklist:
|
||||
|
||||
## Current Baseline
|
||||
|
||||
- Current tracking checkpoint: 2026-06-07 P3.73 conversation filter permission parity, prepared as `feat(conversations): scope filter permissions`.
|
||||
- Latest implementation checkpoint: this checkpoint, prepared as `feat(conversations): scope filter permissions`.
|
||||
- Latest documentation/tooling checkpoint: this tracker update records Chatwoot `Conversations::PermissionFilterService` parity for the advanced conversation filter path. No route artifacts change.
|
||||
- Current tracking checkpoint: 2026-06-07 P3.74 contact type default parity, prepared as `feat(contacts): default contact type`.
|
||||
- Latest implementation checkpoint: this checkpoint, prepared as `feat(contacts): default contact type`.
|
||||
- Latest documentation/tooling checkpoint: this tracker update records Chatwoot contact default/lead sync parity for direct create and import paths. No route artifacts change.
|
||||
- Plan landing status: complete for the current known Hermes plans and user-confirmed scope. Future work should update this file directly instead of opening a parallel tracker.
|
||||
- Worktree status at this implementation checkpoint: advanced conversation filtering now applies the same inbox visibility gate as Chatwoot `Conversations::PermissionFilterService`: administrators see all account conversations, agents see only conversations from inboxes they are assigned to, and account users missing from the account see no filter results. Existing P3.72 account enabled-feature serialization parity, P3.71 contact referer filter parity, P5.8c contact import search indexing, P3.70 global contact search resolved-scope parity, P3.69 contact index/filter/export resolved-scope parity, P3.68 contact filter payload parity, P3.64-P3.67 conversation filter payload parity, P3.63 search message-order parity, P3.62 trimmed search payload fields, P3.61 sender-name, P3.60 numeric message-type parity, and prior report/profile/channel/contact checkpoints remain implemented. Live API/browser/enterprise smoke still needs the full PostgreSQL/Redis/Meilisearch/GoChat/Vite/Chrome stack.
|
||||
- Next executable implementation checkpoint: continue Phase 2/3 drift audit for message filters, import paths creating unexpected contact types, the next reused-frontend mismatch, or B12 live smoke when the full PostgreSQL/Redis/Meilisearch/GoChat/Vite/Chrome stack is available. Re-run Phase 6 placeholder audit after future route/smoke changes.
|
||||
- Worktree status at this implementation checkpoint: contacts now follow Chatwoot default contact type semantics on all create/import paths: blank `contact_type` becomes `visitor`, and visitor contacts with email, phone, or social additional attributes upgrade to `lead`. This closes the CRM v2/import drift where CSV-created contacts without an explicit `contact_type` could remain empty and disappear from lead-only CRM surfaces. Existing P3.73 conversation filter permission parity, P3.72 account enabled-feature serialization parity, P3.71 contact referer filter parity, P5.8c contact import search indexing, P3.70 global contact search resolved-scope parity, P3.69 contact index/filter/export resolved-scope parity, P3.68 contact filter payload parity, P3.64-P3.67 conversation filter payload parity, P3.63 search message-order parity, P3.62 trimmed search payload fields, P3.61 sender-name, P3.60 numeric message-type parity, and prior report/profile/channel/contact checkpoints remain implemented. Live API/browser/enterprise smoke still needs the full PostgreSQL/Redis/Meilisearch/GoChat/Vite/Chrome stack.
|
||||
- Next executable implementation checkpoint: continue Phase 2/3 drift audit for message filters, the next reused-frontend mismatch, or B12 live smoke when the full PostgreSQL/Redis/Meilisearch/GoChat/Vite/Chrome stack is available. Re-run Phase 6 placeholder audit after future route/smoke changes.
|
||||
- `go test ./...` passes when run outside the restricted socket sandbox for the latest implementation baseline; the latest docs/tooling checkpoint verified `scripts/parity_frontend_smoke.sh --check` with workspace-local temp/cache dirs after `/tmp` was full.
|
||||
- Route dump succeeds with `967` registered routes after profile MFA route tracking.
|
||||
- Route parity artifacts now exist under `docs/parity/` and are generated by `cmd/route_parity`.
|
||||
@@ -156,6 +156,7 @@ This table is the shortest authoritative handoff view. If an older lower section
|
||||
|
||||
| Priority | Workstream | Current state | Next checkpoint | Commit close rule |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| 0 | P3.74 contact type default parity | Implemented for direct create and CSV import paths: blank GoChat contacts now default to Chatwoot `visitor`, and visitor contacts with email, phone, or social details upgrade to `lead`. | Keep in Review; reopen from B12 CRM/import smoke or fresh reference evidence for channel-specific social detail keys or bulk-import merge edge cases. | Focused contact model/import tests, full `go test ./...`, and `git diff --check` must pass. No route artifacts change. |
|
||||
| 0 | P3.73 conversation filter permission parity | Implemented for reused dashboard advanced filters: `POST /conversations/filter` now scopes results and meta counts through Chatwoot-style inbox visibility before applying payload/status/label filters. Administrators see all account conversations; agents see only assigned inbox conversations. | Keep in Review; reopen from B12 conversation filter smoke or fresh reference evidence for enterprise custom-role overrides beyond the current non-SSO scope. | Focused conversation service filter permission tests, full `go test ./...`, and `git diff --check` must pass. No route artifacts change. |
|
||||
| 0 | P3.72 account enabled-features serialization parity | Implemented for reused frontend account feature gates: GoChat account payloads now serialize `features` like Chatwoot `Account#enabled_features`, seeding default enabled flags from `config/features.yml`, including explicit true flags such as `crm_v2`, and omitting false flags. | Keep in Review; reopen from B12 account/CRM smoke or fresh reference evidence for installation-config default feature drift, cloud-only feature filtering, or bitfield migration needs. | Focused account handler tests, full `go test ./...`, and `git diff --check` must pass. No route artifacts change. |
|
||||
| 0 | P3.71 contact referer filter parity | Implemented for reused CRM contact advanced filters: GoChat now accepts frontend `referer` payload conditions and filters `contacts.additional_attributes.referer` with equality and containment operators. | Keep in Review; reopen from B12 CRM filter smoke or fresh reference/frontend evidence for exact referer location drift, link case-sensitivity expectations, or export filter reuse gaps. | Focused contact filter handler tests, full `go test ./...`, and `git diff --check` must pass. No route artifacts change. |
|
||||
@@ -237,6 +238,7 @@ These rows are the executable development plan from this point forward. A checkp
|
||||
|
||||
| ID | Owner files | Reference files | Work to land | Exit gate |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| P3.74 contact type default parity | `internal/model/contact.go`, `internal/model/model_test.go`, `internal/service/contact_service_g3_test.go` | `reference/chatwoot/app/models/contact.rb`, `reference/chatwoot/app/services/contacts/sync_attributes.rb`, `reference/chatwoot/spec/models/contact_spec.rb`, `reference/chatwoot/spec/services/contacts/sync_attributes_spec.rb`, `reference/chatwoot/app/services/data_import/contact_manager.rb` | Contact persistence now mirrors Chatwoot enum/default behavior before CRM scopes see imported records: blank `contact_type` is normalized to `visitor`, and `visitor` contacts with email, phone number, or social additional attributes become `lead`. CSV imports without a `contact_type` column therefore create CRM-v2-visible leads when identity details are present and true visitors when they are absent. | Review by `feat(contacts): default contact type`; focused model/import tests assert default visitor, detail-based lead upgrade, and CSV import defaults; full `go test ./...` and `git diff --check`. No route artifacts change. |
|
||||
| P3.73 conversation filter permission parity | `internal/service/conversation_service.go`, `internal/service/conversation_service_test.go` | `reference/chatwoot/app/services/conversations/filter_service.rb`, `reference/chatwoot/app/services/conversations/permission_filter_service.rb`, `reference/chatwoot/app/controllers/api/v1/accounts/conversations_controller.rb#filter` | Advanced conversation filters now apply the Chatwoot permission base relation before payload/status/label filters. Non-admin agents are restricted to conversations whose inbox is in their account-scoped assigned inboxes; administrators keep full account visibility; missing account memberships return an empty result set. | Review by `feat(conversations): scope filter permissions`; focused service tests assert agent filtering hides unassigned-inbox matches while administrators still see all matching inboxes; full `go test ./...` and `git diff --check`. No route artifacts change. |
|
||||
| P3.72 account enabled-features serialization parity | `internal/handler/api/v1/account_handler.go`, `internal/handler/api/v1/account_handler_test.go` | `reference/chatwoot/app/models/concerns/featurable.rb`, `reference/chatwoot/app/views/api/v1/models/_account.json.jbuilder`, `reference/chatwoot/config/features.yml`, `reference/chatwoot/app/javascript/dashboard/store/modules/accounts.js` | Account show/list payloads now serialize `features` as Chatwoot `enabled_features` instead of raw GoChat `feature_flags`: default enabled flags are present for empty accounts, explicit true flags like `crm_v2` are included, and false flags are omitted so reused frontend feature gates see the same truthy-key contract. | Review by `feat(accounts): serialize enabled features`; focused handler tests assert default enabled flags, explicit `crm_v2`, and false-flag omission; full `go test ./...` and `git diff --check`. No route artifacts change. |
|
||||
| P3.71 contact referer filter parity | `internal/repository/contact_repo.go`, `internal/handler/api/v1/contact_handler_crud_test.go` | `reference/chatwoot/app/javascript/dashboard/components-next/filter/contactProvider.js`, `reference/chatwoot/app/javascript/dashboard/components-next/filter/helper/filterHelper.js`, `reference/chatwoot/app/services/contacts/filter_service.rb`, `reference/chatwoot/lib/filters/filter_keys.yml` | Contact advanced filters now accept the reused frontend's built-in `referer` contact filter and evaluate it against `contacts.additional_attributes.referer` with `equal_to`, `not_equal_to`, `contains`, and `does_not_contain`. This closes the prior frontend/reference drift where the current dashboard exposed `referer` even though the backend filter key list does not declare it under contacts. | Review by `feat(contacts): filter by referer`; focused handler tests assert `referer contains` matches only contacts whose additional attributes carry the referer URL; full `go test ./...` and `git diff --check`. No route artifacts change. |
|
||||
@@ -336,6 +338,7 @@ This ledger records the committed parity checkpoints that future slices should b
|
||||
|
||||
| Commit | Scope | Verification summary | Follow-up state |
|
||||
| --- | --- | --- | --- |
|
||||
| `feat(contacts): default contact type` | Advances P3.74 with Chatwoot contact default/sync parity. Contact persistence now normalizes blank `contact_type` to `visitor`, then upgrades visitor contacts with email, phone number, or social additional attributes to `lead`, so CSV imports without an explicit contact type no longer create empty-type records that drift from CRM v2 lead-only behavior. | `go test ./internal/model ./internal/service -run 'TestContact_Create|TestContactService_ImportCSV_DefaultsContactTypeLikeChatwoot' -count=1`; full `go test ./...`; `git diff --check`. No route artifacts change. | Move P3.74 to Review; continue Phase 2/3 drift audit for message filters, Phase 6 placeholder audit, or B12 live smoke. |
|
||||
| `feat(conversations): scope filter permissions` | Advances P3.73 with Chatwoot `Conversations::PermissionFilterService` parity for `POST /conversations/filter`. Advanced filters now apply assigned-inbox visibility before filter payloads and meta counts, so non-admin agents cannot see conversations from inboxes they do not belong to while administrators retain account-wide visibility. | `go test ./internal/service -run 'TestConversationService_Filter_(RestrictsAgentToAssignedInboxes|AllowsAdministratorAllInboxes)' -count=1`; full `go test ./...`; `git diff --check`. No route artifacts change. | Move P3.73 to Review; continue Phase 2/3 drift audit for message filters, import/contact-type drift, Phase 6 placeholder audit, or B12 live smoke. |
|
||||
| `feat(accounts): serialize enabled features` | Advances P3.72 with Chatwoot account `enabled_features` serialization parity. GoChat account payloads now seed default enabled features from the local reference `config/features.yml`, include explicit true flags such as `crm_v2`, and omit false flags instead of exposing raw `feature_flags` to reused dashboard feature gates. | `go test ./internal/handler/api/v1 -run 'AccountHandlerSuite/TestGet_(FeaturesUseChatwootEnabledFeatures|DefaultFeaturesMatchChatwootDefaults)' -count=1`; full `go test ./...`; `git diff --check`. No route artifacts change. | Move P3.72 to Review; continue Phase 2/3 drift audit for message filters, conversation permission-filter depth, import/contact-type drift, Phase 6 placeholder audit, or B12 live smoke. |
|
||||
| `feat(contacts): filter by referer` | Advances P3.71 with reused CRM contact filter parity for the frontend-exposed `referer` attribute. Contact advanced filters now evaluate `referer` against `contacts.additional_attributes.referer` with equality and containment operators instead of rejecting the current dashboard payload as an unknown attribute. | `go test ./internal/handler/api/v1 -run 'ContactHandlerCRUDTestSuite/TestFilter_ContactRefererFromFrontendProvider\|ContactHandlerCRUDTestSuite/TestFilter_ChatwootPayloadStandardAndAdditionalAttributes' -count=1`; full `go test ./...`; `git diff --check`. No route artifacts change. | Move P3.71 to Review; continue Phase 2/3 drift audit for message filters, conversation permission-filter depth, CRM v2 feature-flag serialization, Phase 6 placeholder audit, or B12 live smoke. |
|
||||
@@ -2677,3 +2680,4 @@ Verification milestone gates:
|
||||
- 2026-06-07: P3.71 contact referer filter checkpoint prepared as `feat(contacts): filter by referer`; audited reused `contactProvider.js`, `filterHelper.js`, `Contacts::FilterService`, and `filter_keys.yml`. GoChat contact advanced filters now accept the frontend-exposed `referer` attribute and match it against `contacts.additional_attributes.referer`, so CRM filter payloads using `contains` no longer fail as unknown attributes. Focused contact filter tests passed; full `go test ./...` and `git diff --check` passed. No route artifacts change.
|
||||
- 2026-06-07: P3.72 account enabled-features serialization checkpoint prepared as `feat(accounts): serialize enabled features`; audited Chatwoot `Featurable#enabled_features`, account model serializer, `config/features.yml`, and reused dashboard account feature getter. GoChat account payloads now return Chatwoot-style truthy `features`: default enabled flags are present for empty accounts, explicit true flags such as `crm_v2` are included, and false flags are omitted. Focused account handler tests passed; full `go test ./...` and `git diff --check` passed. No route artifacts change.
|
||||
- 2026-06-07: P3.73 conversation filter permission checkpoint prepared as `feat(conversations): scope filter permissions`; audited Chatwoot `Conversations::FilterService`, `Conversations::PermissionFilterService`, and the reused `POST /conversations/filter` path. GoChat advanced conversation filters now apply assigned-inbox visibility for non-admin agents before payload/status/label filters and meta counts, while administrators keep full account visibility. Focused conversation service permission tests passed; full `go test ./...` and `git diff --check` passed. No route artifacts change.
|
||||
- 2026-06-07: P3.74 contact type default checkpoint prepared as `feat(contacts): default contact type`; audited Chatwoot contact enum defaults, `Contacts::SyncAttributes`, model specs, sync specs, and CSV import behavior. GoChat contacts now default blank `contact_type` to `visitor` and upgrade visitor contacts with email, phone, or social details to `lead`, closing the import/CRM-v2 drift where CSV-created contacts without an explicit type could stay empty. Focused contact model/import tests passed; full `go test ./...` and `git diff --check` passed. No route artifacts change.
|
||||
|
||||
+40
-37
@@ -12,23 +12,23 @@ import (
|
||||
// Reference: Chatwoot app/models/contact.rb — P2B M4 spec
|
||||
type Contact struct {
|
||||
Base
|
||||
AccountID uint `gorm:"index;not null" json:"account_id"`
|
||||
Name string `gorm:"size:255" json:"name"`
|
||||
MiddleName string `gorm:"size:255;default:''" json:"middle_name,omitempty"`
|
||||
LastName string `gorm:"size:255;default:''" json:"last_name,omitempty"`
|
||||
Email string `gorm:"size:255;index" json:"email,omitempty"`
|
||||
PhoneNumber string `gorm:"size:50;index" json:"phone_number,omitempty"`
|
||||
AvatarURL string `gorm:"size:512" json:"avatar_url,omitempty"`
|
||||
Identifier string `gorm:"size:255;index" json:"identifier,omitempty"`
|
||||
CountryCode string `gorm:"size:10;default:''" json:"country_code,omitempty"`
|
||||
Location string `gorm:"size:255;default:''" json:"location,omitempty"`
|
||||
AccountID uint `gorm:"index;not null" json:"account_id"`
|
||||
Name string `gorm:"size:255" json:"name"`
|
||||
MiddleName string `gorm:"size:255;default:''" json:"middle_name,omitempty"`
|
||||
LastName string `gorm:"size:255;default:''" json:"last_name,omitempty"`
|
||||
Email string `gorm:"size:255;index" json:"email,omitempty"`
|
||||
PhoneNumber string `gorm:"size:50;index" json:"phone_number,omitempty"`
|
||||
AvatarURL string `gorm:"size:512" json:"avatar_url,omitempty"`
|
||||
Identifier string `gorm:"size:255;index" json:"identifier,omitempty"`
|
||||
CountryCode string `gorm:"size:10;default:''" json:"country_code,omitempty"`
|
||||
Location string `gorm:"size:255;default:''" json:"location,omitempty"`
|
||||
AdditionalAttributes datatypes.JSON `gorm:"type:jsonb;default:'{}'" json:"additional_attributes,omitempty"`
|
||||
CustomAttributes datatypes.JSON `gorm:"type:jsonb;default:'{}'" json:"custom_attributes,omitempty"`
|
||||
Blocked bool `gorm:"default:false" json:"blocked"`
|
||||
ContactType string `gorm:"size:50;default:''" json:"contact_type,omitempty"`
|
||||
SourceID string `gorm:"size:255" json:"source_id,omitempty"`
|
||||
CompanyID *uint `json:"company_id,omitempty"`
|
||||
LastActivityAt *int64 `gorm:"index" json:"last_activity_at,omitempty"`
|
||||
Blocked bool `gorm:"default:false" json:"blocked"`
|
||||
ContactType string `gorm:"size:50;default:''" json:"contact_type,omitempty"`
|
||||
SourceID string `gorm:"size:255" json:"source_id,omitempty"`
|
||||
CompanyID *uint `json:"company_id,omitempty"`
|
||||
LastActivityAt *int64 `gorm:"index" json:"last_activity_at,omitempty"`
|
||||
}
|
||||
|
||||
func (Contact) TableName() string { return "contacts" }
|
||||
@@ -45,21 +45,24 @@ func (c *Contact) BeforeSave(tx *gorm.DB) error {
|
||||
// Reference: Chatwoot Contacts::SyncAttributes#update_contact_location_and_country_code
|
||||
func (c *Contact) syncLocationAndCountryCode() {
|
||||
if c.AdditionalAttributes != nil {
|
||||
attrs := make(map[string]interface{})
|
||||
if err := json.Unmarshal(c.AdditionalAttributes, &attrs); err == nil {
|
||||
if city, ok := attrs["city"].(string); ok && city != "" {
|
||||
c.Location = city
|
||||
}
|
||||
if country, ok := attrs["country"].(string); ok && country != "" {
|
||||
c.CountryCode = country
|
||||
}
|
||||
}
|
||||
attrs := make(map[string]interface{})
|
||||
if err := json.Unmarshal(c.AdditionalAttributes, &attrs); err == nil {
|
||||
if city, ok := attrs["city"].(string); ok && city != "" {
|
||||
c.Location = city
|
||||
}
|
||||
if country, ok := attrs["country"].(string); ok && country != "" {
|
||||
c.CountryCode = country
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// syncContactType upgrades visitor → lead if email/phone/social details present.
|
||||
// syncContactType defaults blank contacts to visitor and upgrades visitor to lead if email/phone/social details are present.
|
||||
// Reference: Chatwoot Contacts::SyncAttributes#set_contact_type
|
||||
func (c *Contact) syncContactType() {
|
||||
if strings.TrimSpace(c.ContactType) == "" {
|
||||
c.ContactType = "visitor"
|
||||
}
|
||||
if c.ContactType != "visitor" {
|
||||
return
|
||||
}
|
||||
@@ -69,16 +72,16 @@ func (c *Contact) syncContactType() {
|
||||
}
|
||||
// Check social details in additional_attributes (social_* keys)
|
||||
if c.AdditionalAttributes != nil {
|
||||
attrs := make(map[string]interface{})
|
||||
if err := json.Unmarshal(c.AdditionalAttributes, &attrs); err == nil {
|
||||
for key, val := range attrs {
|
||||
if strings.HasPrefix(key, "social_") {
|
||||
if s, ok := val.(string); ok && s != "" {
|
||||
c.ContactType = "lead"
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
attrs := make(map[string]interface{})
|
||||
if err := json.Unmarshal(c.AdditionalAttributes, &attrs); err == nil {
|
||||
for key, val := range attrs {
|
||||
if strings.HasPrefix(key, "social_") {
|
||||
if s, ok := val.(string); ok && s != "" {
|
||||
c.ContactType = "lead"
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"gorm.io/driver/sqlite"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
@@ -114,11 +115,11 @@ func TestAccount_FeatureFlags(t *testing.T) {
|
||||
defer cleanupDB(t, db)
|
||||
|
||||
acc := &Account{
|
||||
Name: "FlagsOrg",
|
||||
Locale: "en",
|
||||
Timezone: "UTC",
|
||||
Active: true,
|
||||
FeatureFlags: `{"auto_resolve":true,"copilot":false}`,
|
||||
Name: "FlagsOrg",
|
||||
Locale: "en",
|
||||
Timezone: "UTC",
|
||||
Active: true,
|
||||
FeatureFlags: `{"auto_resolve":true,"copilot":false}`,
|
||||
AutoResolveDuration: 7,
|
||||
}
|
||||
err := db.Create(acc).Error
|
||||
@@ -225,9 +226,9 @@ func TestAccountUser_Create(t *testing.T) {
|
||||
db.Create(user)
|
||||
|
||||
au := &AccountUser{
|
||||
UserID: user.ID,
|
||||
AccountID: acc.ID,
|
||||
Role: string(AccountUserRoleAgent),
|
||||
UserID: user.ID,
|
||||
AccountID: acc.ID,
|
||||
Role: string(AccountUserRoleAgent),
|
||||
Availability: "online",
|
||||
}
|
||||
err := db.Create(au).Error
|
||||
@@ -255,10 +256,10 @@ func TestInbox_Create(t *testing.T) {
|
||||
db.Create(acc)
|
||||
|
||||
inbox := &Inbox{
|
||||
AccountID: acc.ID,
|
||||
Name: "Support",
|
||||
ChannelType: "web_widget",
|
||||
ChannelID: 1,
|
||||
AccountID: acc.ID,
|
||||
Name: "Support",
|
||||
ChannelType: "web_widget",
|
||||
ChannelID: 1,
|
||||
EnableAutoAssignment: false,
|
||||
AutoAssignmentLimit: 0,
|
||||
}
|
||||
@@ -357,6 +358,20 @@ func TestContact_Create(t *testing.T) {
|
||||
assert.NoError(t, err)
|
||||
assert.NotZero(t, contact.ID)
|
||||
assert.Equal(t, "Jane Doe", contact.Name)
|
||||
assert.Equal(t, "lead", contact.ContactType)
|
||||
}
|
||||
|
||||
func TestContact_CreateDefaultsToVisitorWithoutDetails(t *testing.T) {
|
||||
db := setupModelTestDB(t)
|
||||
defer cleanupDB(t, db)
|
||||
|
||||
acc := &Account{Name: "ContactDefaultOrg", Locale: "en", Timezone: "UTC", Active: true}
|
||||
require.NoError(t, db.Create(acc).Error)
|
||||
|
||||
contact := &Contact{AccountID: acc.ID, Name: "Visitor"}
|
||||
err := db.Create(contact).Error
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "visitor", contact.ContactType)
|
||||
}
|
||||
|
||||
// --- Enum Tests ---
|
||||
@@ -410,4 +425,4 @@ func TestPermissionDimension_Constants(t *testing.T) {
|
||||
assert.Equal(t, "report_manage", string(DimensionReportManage))
|
||||
assert.Equal(t, "knowledge_base_manage", string(DimensionKnowledgeBaseManage))
|
||||
assert.Equal(t, "automation_manage", string(DimensionAutomationManage))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -438,6 +438,24 @@ func TestContactService_ImportCSV_ImportsValidRows(t *testing.T) {
|
||||
assert.Equal(t, []string{"contact", "contact"}, indexer.indexed)
|
||||
}
|
||||
|
||||
func TestContactService_ImportCSV_DefaultsContactTypeLikeChatwoot(t *testing.T) {
|
||||
db, _, svc := setupContactService(t)
|
||||
account := createTestAccount(t, db)
|
||||
|
||||
csvData := "name,email,phone_number\nLead,lead@test.com,+111\nVisitor,,\n"
|
||||
result, err := svc.ImportCSV(context.Background(), account.ID, strings.NewReader(csvData))
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, 2, result.Imported)
|
||||
|
||||
var lead model.Contact
|
||||
require.NoError(t, db.Where("account_id = ? AND name = ?", account.ID, "Lead").First(&lead).Error)
|
||||
assert.Equal(t, "lead", lead.ContactType)
|
||||
|
||||
var visitor model.Contact
|
||||
require.NoError(t, db.Where("account_id = ? AND name = ?", account.ID, "Visitor").First(&visitor).Error)
|
||||
assert.Equal(t, "visitor", visitor.ContactType)
|
||||
}
|
||||
|
||||
func TestContactService_ImportCSV_AllowsRowsWithoutNameWhenIdentityPresent(t *testing.T) {
|
||||
db, _, svc := setupContactService(t)
|
||||
account := createTestAccount(t, db)
|
||||
|
||||
Reference in New Issue
Block a user