Tags
π Overviewβ
internal/api/v1/crm/services/tag.service.js exposes CRUD helpers for account-level tags shared between contacts and deals. It handles pagination, case-insensitive uniqueness checks, respects lock flags (preventing non-admin updates), and keeps tag references tidy across CRM entities during deletions. Controllers supply validated inputs; this service executes Mongo queries, permission checks, and cascading updates.
ποΈ Collections Usedβ
π Full Schema: See Database Collections Documentation
crm-tagβ
- Operations: All CRUD plus lock flag checks (
is_locked) - Model:
shared/models/crm-tag.js - Usage Context: Stores tag text, account ownership, creator, lock state, and metadata
crm.contactsβ
- Operations:
$pull/$pullAllcleanup when tags are removed - Model:
shared/models/contact.js - Usage Context: Ensures contacts no longer reference deleted tags
crm.dealsβ
- Operations:
$pull/$pullAllupdates to strip removed tag IDs - Model:
shared/models/deal.js - Usage Context: Keeps deal tag arrays aligned after deletes
π Data Flowβ
flowchart TD
Client --> Controller
Controller --> TagService
TagService -->|list/create| Tags[(crm-tag)]
TagService -->|cleanup| Contacts[(crm.contacts)]
TagService -->|cleanup| Deals[(crm.deals)]
Tags --> Response
π§ Service Entry Pointsβ
Listing & Detailβ
-
getTag({ account_id, uid, page, limit, search, sortOrder })- Coerces pagination args, applies account scoping, and filters by
searchusing case-insensitive regex - Returns sorted results with pagination metadata from
utilities.generatePagination
- Coerces pagination args, applies account scoping, and filters by
-
getOneTag({ account_id, uid, id })- Fetches a single tag by
_idand account, raising an error when missing
- Fetches a single tag by
Creation & Updateβ
-
postTag({ account_id, uid, tag })- Rejects duplicates via regex match and enforces
is_lockedguard (locked tags ban creation for non-admin contexts) - Persists the tag document with creator metadata
- Rejects duplicates via regex match and enforces
-
updateOneTag({ account_id, uid, tag, id })- Confirms existence, honors
is_locked, then validates uniqueness before updating thetagstring
- Confirms existence, honors
Deletionβ
-
deleteOneTag({ account_id, id })- Verifies the tag belongs to the account, deletes it, and
$pulls the ID from related deals and contacts
- Verifies the tag belongs to the account, deletes it, and
-
deleteMultiTag({ account_id, tags })- Converts incoming IDs to
ObjectId, ensures all exist for the account, and runsdeleteManyplus$pullAllcleanup on both contacts and deals
- Converts incoming IDs to
π Integration Pointsβ
Internalβ
utilities.generatePaginationdelivers consistent paging payloads for list responsesmongooseconversions are used before bulk deletes to match stored ObjectId types
Externalβ
- None directlyβthe service strictly manipulates Mongo collections
β οΈ Edge Cases & Guardrailsβ
- Lock enforcement: Documents flagged
is_lockedblock create/update attempts, returningACCESS_DENIED - Case-insensitive uniqueness: Regex ensure
Tagandtagare treated as duplicates - Bulk validation:
deleteMultiTagvalidates every ID belongs to the callerβs account before deleting - Cleanup: Both deletion paths remove tag IDs from contact and deal arrays to prevent orphan references