Fields
π Overviewβ
internal/api/v1/crm/services/field.service.js governs custom property definitions that extend contacts, businesses, and deals. It merges system defaults with account-specific additions, enforces user-config locks, deduplicates titles, spreads option lists (including deal currency choices), and writes both Account.crm.additional_info and UserConfig.fields so UI config stays aligned. Controllers only validate payloads; this service coordinates lookups, locks, and persistence.
ποΈ Collections Usedβ
π Full Schema: See Database Collections Documentation
_accountsβ
- Operations: Reads/writes
crm.additional_infoblobs when fields are added, updated, or removed - Model:
shared/models/account.js - Usage Context: Stores the authoritative list of custom field definitions per entity type
user-configβ
- Operations: Fetches lock state, stores per-user page config (
fields[type]ordering and visibility) - Model:
shared/models/user-config.js - Usage Context: Mirrors field metadata so dashboards know ordering, widths, mandatory flags
Utility Layerβ
fieldUtils.getDefaultFields()supplies baseline definitions that cannot be mutated or deletedfieldUtils.getCurrencyOption()augments deal currency dropdowns with account-specific ratesfieldUtils.getFieldCopyTitle()resolves duplicate labels by auto-appending counters
π Data Flowβ
flowchart TD
Request -->|Validate| Controller
Controller -->|delegate| FieldService
FieldService -->|load defaults| DefaultFields[(Shared Defaults)]
FieldService -->|merge| AccountBlob[(Account.crm.additional_info)]
FieldService -->|persist| AccountsDB[(Accounts)]
FieldService -->|sync config| UserConfigDB[(UserConfig)]
FieldService --> Response
π§ Service Entry Pointsβ
Listing & Retrievalβ
-
getTypeField({ type, additional_info, account_id, uid })- Hydrates the accountβs custom
additional_infoblob or seeds defaults if absent - Marks custom entries with
custom=trueand injects deal currency options - Supports
typescoping (people, businesses, deals) or returns all when omitted
- Hydrates the accountβs custom
-
getOneField({ type, id, additional_info })- Locates a specific field definition inside the cached
additional_info - Returns 404 when a custom ID is missing or type mismatch occurs
- Locates a specific field definition inside the cached
Creation & Updatesβ
-
postField({ type, title, valueType, options, uid, account_id, additional_info, field_options })- Rejects writes when
UserConfigis locked for the requesting user - Normalises titles via
getFieldCopyTitleto avoid duplicates - Persists new field object to
Account.crm.additional_infoand appends layout config with incrementedorder - Returns the saved field with
custom=trueand the computed config payload
- Rejects writes when
-
updateField({ type, id, title, options, uid, account_id, additional_info })- Blocks edits to default fields and checks per-user lock state
- Validates unique titles inside the same entity type
- Updates name and selectable options, then persists the full blob back to the account
Deletion & Cleanupβ
deleteOneField({ type, id, additional_info, account_id, uid })- Prevents deletions of system defaults and ensures the ID exists in the custom list
- Removes the entry from
crm.additional_info, rewritesUserConfig.fieldsorder values, and returnsSUCCESS
π Integration Pointsβ
Internal Helpersβ
fieldUtilsbundle for defaults, currency options, and unique title generationUserConfigdocuments track per-user UI locks and column orderingAccountwrites guarantee all users inherit custom field mutations immediately
External Servicesβ
- None directlyβattachments and validation happen upstream before service invocation
β οΈ Edge Cases & Guardrailsβ
- Locked configuration: When
UserConfig.is_lockedis true, create/update requests are denied withACCESS_DENIED - Default protections: System fields from
getDefaultFields()cannot be updated or deleted - Collision handling:
getFieldCopyTitleauto-appends counters so duplicate names remain distinct - Option integrity: Only option-bearing types (
dropdown,singleCheck,multipleCheck) persist options arrays - Ordering: Deletions reindex
UserConfig.fields[type]to keep sequential order values