Make custom Tags relation filterable on all four entities

Expose the custom many-to-many CTag "Tags" relation as a linkMultiple
field (cTags) so it can be added to the list-view Search Filters, and
build the full Tags setup on Lead from scratch in parity with the others.

Opportunity / Contact / Account (relation already existed):
- add fields.cTags (linkMultiple) to entityDefs
- add cTags to the Search-Filters layout (Opportunity appended, preserving
  cAccount1; Contact/Account get a full filters.json incl. cTags)

Lead (built from scratch):
- create Lead<->CTag many-to-many relation via EntityManager (relationName
  cLeadTag, join table c_lead_tag); link normalized to cTags to mirror the
  other three (API auto-prefix had produced cCTags)
- add cTags field, de_DE/en_US labels, clientDefs panel polish (create:false,
  unlink-only row actions), bottomPanelsDetail entry, and filters.json
- CTag side: links.interessenten label de "Interessenten" / en "Leads"

Verified via API/DB: cTags linkMultiple present on all four scopes; Lead
link/CTag.interessenten resolve; Any of / All of / None of filters execute
and return correct results end-to-end; existing data intact.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
mkt
2026-06-16 16:31:30 +02:00
parent cebe829dcd
commit e61af83d25
16 changed files with 489 additions and 327 deletions

View File

@@ -1,15 +1,17 @@
{
"labels": {
"Create CTag": "Tag erstellen"
},
"fields": {
"verkaufschancen": "Verkaufschancen",
"kontakte": "Kontakte",
"firmen": "Firmen"
},
"links": {
"verkaufschancen": "Verkaufschancen",
"kontakte": "Kontakte",
"firmen": "Firmen"
}
"labels": {
"Create CTag": "Tag erstellen"
},
"fields": {
"verkaufschancen": "Verkaufschancen",
"kontakte": "Kontakte",
"firmen": "Firmen",
"interessenten": "Interessenten"
},
"links": {
"verkaufschancen": "Verkaufschancen",
"kontakte": "Kontakte",
"firmen": "Firmen",
"interessenten": "Interessenten"
}
}

View File

@@ -0,0 +1,8 @@
{
"fields": {
"cTags": "Tags"
},
"links": {
"cTags": "Tags"
}
}

View File

@@ -1,15 +1,17 @@
{
"labels": {
"Create CTag": "Create Tag"
},
"fields": {
"verkaufschancen": "Verkaufschancen",
"kontakte": "Kontakte",
"firmen": "Firmen"
},
"links": {
"verkaufschancen": "Verkaufschancen",
"kontakte": "Kontakte",
"firmen": "Firmen"
}
"labels": {
"Create CTag": "Create Tag"
},
"fields": {
"verkaufschancen": "Verkaufschancen",
"kontakte": "Kontakte",
"firmen": "Firmen",
"interessenten": "Leads"
},
"links": {
"verkaufschancen": "Verkaufschancen",
"kontakte": "Kontakte",
"firmen": "Firmen",
"interessenten": "Leads"
}
}

View File

@@ -0,0 +1,8 @@
{
"fields": {
"cTags": "Tags"
},
"links": {
"cTags": "Tags"
}
}

View File

@@ -0,0 +1,20 @@
[
"assignedUser",
"teams",
"createdAt",
"createdBy",
"modifiedAt",
"streamUpdatedAt",
"type",
"industry",
"description",
"emailAddress",
"phoneNumber",
"targetLists",
"billingAddressCountry",
"billingAddress",
"shippingAddress",
"website",
"campaign",
"cTags"
]

View File

@@ -0,0 +1,16 @@
[
"assignedUser",
"teams",
"createdAt",
"createdBy",
"modifiedAt",
"streamUpdatedAt",
"accounts",
"address",
"emailAddress",
"phoneNumber",
"title",
"targetLists",
"campaign",
"cTags"
]

View File

@@ -0,0 +1,8 @@
{
"targetLists": {
"index": 5
},
"cTags": {
"index": 6
}
}

View File

@@ -0,0 +1,20 @@
[
"assignedUser",
"teams",
"createdAt",
"createdBy",
"modifiedAt",
"streamUpdatedAt",
"address",
"accountName",
"campaign",
"emailAddress",
"phoneNumber",
"status",
"source",
"opportunityAmountConverted",
"industry",
"targetLists",
"createdAccount",
"cTags"
]

View File

@@ -0,0 +1,20 @@
[
"assignedUser",
"teams",
"createdAt",
"createdBy",
"modifiedAt",
"streamUpdatedAt",
"account",
"cAccount1",
"amountConverted",
"amountWeightedConverted",
"campaign",
"closeDate",
"contacts",
"leadSource",
"probability",
"stage",
"description",
"cTags"
]

View File

@@ -15,6 +15,10 @@
"firmen": {
"layout": null,
"selectPrimaryFilterName": null
},
"interessenten": {
"layout": null,
"selectPrimaryFilterName": null
}
}
}

View File

@@ -0,0 +1,8 @@
{
"relationshipPanels": {
"cTags": {
"create": false,
"rowActionsView": "custom:views/record/row-actions/tags-unlink-only"
}
}
}

View File

@@ -1,19 +1,26 @@
{
"links": {
"cAgentur": {
"type": "hasMany",
"foreign": "cAccount1",
"entity": "Opportunity",
"audited": false,
"isCustom": true
},
"cTags": {
"type": "hasMany",
"relationName": "cAccountTag",
"foreign": "firmen",
"entity": "CTag",
"audited": false,
"isCustom": true
}
"links": {
"cAgentur": {
"type": "hasMany",
"foreign": "cAccount1",
"entity": "Opportunity",
"audited": false,
"isCustom": true
},
"cTags": {
"type": "hasMany",
"relationName": "cAccountTag",
"foreign": "firmen",
"entity": "CTag",
"audited": false,
"isCustom": true
}
},
"fields": {
"cTags": {
"type": "linkMultiple",
"view": "views/fields/link-multiple",
"isCustom": true
}
}
}

View File

@@ -1,113 +1,121 @@
{
"fields": {
"name": {
"type": "varchar",
"required": true,
"pattern": "$noBadCharacters"
},
"description": {
"type": "text"
},
"createdAt": {
"type": "datetime",
"readOnly": true
},
"modifiedAt": {
"type": "datetime",
"readOnly": true
},
"createdBy": {
"type": "link",
"readOnly": true,
"view": "views/fields/user"
},
"modifiedBy": {
"type": "link",
"readOnly": true,
"view": "views/fields/user"
},
"assignedUser": {
"type": "link",
"required": false,
"view": "views/fields/assigned-user"
},
"teams": {
"type": "linkMultiple",
"view": "views/fields/teams"
}
"fields": {
"name": {
"type": "varchar",
"required": true,
"pattern": "$noBadCharacters"
},
"links": {
"createdBy": {
"type": "belongsTo",
"entity": "User"
},
"modifiedBy": {
"type": "belongsTo",
"entity": "User"
},
"assignedUser": {
"type": "belongsTo",
"entity": "User"
},
"teams": {
"type": "hasMany",
"entity": "Team",
"relationName": "entityTeam",
"layoutRelationshipsDisabled": true
},
"verkaufschancen": {
"type": "hasMany",
"relationName": "cOpportunityTag",
"foreign": "cTags",
"entity": "Opportunity",
"audited": false,
"isCustom": true
},
"kontakte": {
"type": "hasMany",
"relationName": "cContactTag",
"foreign": "cTags",
"entity": "Contact",
"audited": false,
"isCustom": true
},
"firmen": {
"type": "hasMany",
"relationName": "cAccountTag",
"foreign": "cTags",
"entity": "Account",
"audited": false,
"isCustom": true
}
"description": {
"type": "text"
},
"collection": {
"orderBy": "createdAt",
"order": "desc"
"createdAt": {
"type": "datetime",
"readOnly": true
},
"indexes": {
"name": {
"columns": [
"name",
"deleted"
]
},
"assignedUser": {
"columns": [
"assignedUserId",
"deleted"
]
},
"createdAt": {
"columns": [
"createdAt"
]
},
"createdAtId": {
"unique": true,
"columns": [
"createdAt",
"id"
]
}
"modifiedAt": {
"type": "datetime",
"readOnly": true
},
"createdBy": {
"type": "link",
"readOnly": true,
"view": "views/fields/user"
},
"modifiedBy": {
"type": "link",
"readOnly": true,
"view": "views/fields/user"
},
"assignedUser": {
"type": "link",
"required": false,
"view": "views/fields/assigned-user"
},
"teams": {
"type": "linkMultiple",
"view": "views/fields/teams"
}
},
"links": {
"createdBy": {
"type": "belongsTo",
"entity": "User"
},
"modifiedBy": {
"type": "belongsTo",
"entity": "User"
},
"assignedUser": {
"type": "belongsTo",
"entity": "User"
},
"teams": {
"type": "hasMany",
"entity": "Team",
"relationName": "entityTeam",
"layoutRelationshipsDisabled": true
},
"verkaufschancen": {
"type": "hasMany",
"relationName": "cOpportunityTag",
"foreign": "cTags",
"entity": "Opportunity",
"audited": false,
"isCustom": true
},
"kontakte": {
"type": "hasMany",
"relationName": "cContactTag",
"foreign": "cTags",
"entity": "Contact",
"audited": false,
"isCustom": true
},
"firmen": {
"type": "hasMany",
"relationName": "cAccountTag",
"foreign": "cTags",
"entity": "Account",
"audited": false,
"isCustom": true
},
"interessenten": {
"type": "hasMany",
"relationName": "cLeadTag",
"foreign": "cTags",
"entity": "Lead",
"audited": false,
"isCustom": true
}
},
"collection": {
"orderBy": "createdAt",
"order": "desc"
},
"indexes": {
"name": {
"columns": [
"name",
"deleted"
]
},
"assignedUser": {
"columns": [
"assignedUserId",
"deleted"
]
},
"createdAt": {
"columns": [
"createdAt"
]
},
"createdAtId": {
"unique": true,
"columns": [
"createdAt",
"id"
]
}
}
}

View File

@@ -1,12 +1,19 @@
{
"links": {
"cTags": {
"type": "hasMany",
"relationName": "cContactTag",
"foreign": "kontakte",
"entity": "CTag",
"audited": false,
"isCustom": true
}
"links": {
"cTags": {
"type": "hasMany",
"relationName": "cContactTag",
"foreign": "kontakte",
"entity": "CTag",
"audited": false,
"isCustom": true
}
},
"fields": {
"cTags": {
"type": "linkMultiple",
"view": "views/fields/link-multiple",
"isCustom": true
}
}
}

View File

@@ -0,0 +1,19 @@
{
"fields": {
"cTags": {
"type": "linkMultiple",
"view": "views/fields/link-multiple",
"isCustom": true
}
},
"links": {
"cTags": {
"type": "hasMany",
"relationName": "cLeadTag",
"foreign": "interessenten",
"entity": "CTag",
"audited": false,
"isCustom": true
}
}
}

View File

@@ -1,167 +1,172 @@
{
"fields": {
"stage": {
"options": [
"Interessant",
"Abgelehnt",
"Angefragt",
"Agentur prüft",
"Kunde prüft",
"Identifiziert",
"Brief versendet",
"Anruf-Wiedervorlage",
"Angerufen",
"Ersttermin vereinbart",
"Ersttermin durchgeführt",
"Angebot",
"Verhandlung",
"Unterschriftsreif",
"Gewonnen",
"In Durchführung",
"Abgeschl. / Bestandsk.",
"Verloren",
"Ghosted"
],
"default": "Interessant",
"probabilityMap": {
"Interessant": 5,
"Abgelehnt": 0,
"Angefragt": 15,
"Agentur prüft": 20,
"Kunde prüft": 30,
"Identifiziert": 5,
"Brief versendet": 8,
"Anruf-Wiedervorlage": 10,
"Angerufen": 12,
"Ersttermin vereinbart": 20,
"Ersttermin durchgeführt": 30,
"Angebot": 40,
"Verhandlung": 60,
"Unterschriftsreif": 80,
"Gewonnen": 100,
"In Durchführung": 100,
"Abgeschl. / Bestandsk.": 100,
"Verloren": 0,
"Ghosted": 0
},
"style": {
"Interessant": null,
"Abgelehnt": "info",
"Angefragt": null,
"Agentur prüft": null,
"Kunde prüft": null,
"Identifiziert": null,
"Brief versendet": "warning",
"Anruf-Wiedervorlage": "warning",
"Angerufen": "warning",
"Ersttermin vereinbart": "warning",
"Ersttermin durchgeführt": "warning",
"Angebot": null,
"Verhandlung": null,
"Unterschriftsreif": null,
"Gewonnen": "success",
"In Durchführung": "primary",
"Abgeschl. / Bestandsk.": "info",
"Verloren": "danger",
"Ghosted": "info"
}
},
"cLeadQuelle": {
"type": "enum",
"required": true,
"options": [
"Inbound Agentur",
"Inbound Plattform-Lead",
"Aktive Plattform-Suche",
"Aktive Akquise",
"Empfehlung / Netzwerk",
"Sonstiges"
],
"style": {
"Inbound Agentur": null,
"Inbound Plattform-Lead": null,
"Aktive Plattform-Suche": null,
"Aktive Akquise": null,
"Empfehlung / Netzwerk": null,
"Sonstiges": null
},
"default": "Inbound Agentur",
"maxLength": 100,
"isCustom": true,
"displayAsLabel": true
},
"cVerlustgrund": {
"type": "enum",
"options": [
"Agentur Absage",
"Kunde Absage",
"Keine Einigung",
"Eigene Absage",
"Sonstiges",
"Agentur Ghosted",
"Kunde Ghosted"
],
"style": {
"Agentur Absage": null,
"Kunde Absage": null,
"Keine Einigung": null,
"Eigene Absage": null,
"Sonstiges": null,
"Agentur Ghosted": null,
"Kunde Ghosted": null
},
"default": "Kunde Absage",
"maxLength": 100,
"isCustom": true
},
"cProjektlink": {
"type": "url",
"isCustom": true
},
"cVerguetungsmodell": {
"type": "enum",
"options": [
"Agentur Honorar",
"Direkt Festpreis",
"Direkt Honorar",
"Direkt Nutzenbasiert"
],
"style": {
"Agentur Honorar": null,
"Direkt Festpreis": null,
"Direkt Honorar": null,
"Direkt Nutzenbasiert": null
},
"default": "Agentur Honorar",
"displayAsLabel": true,
"maxLength": 100,
"isCustom": true
},
"cAccount1": {
"type": "link"
},
"amount": {
"required": false
},
"closeDate": {
"required": false
}
"fields": {
"stage": {
"options": [
"Interessant",
"Abgelehnt",
"Angefragt",
"Agentur prüft",
"Kunde prüft",
"Identifiziert",
"Brief versendet",
"Anruf-Wiedervorlage",
"Angerufen",
"Ersttermin vereinbart",
"Ersttermin durchgeführt",
"Angebot",
"Verhandlung",
"Unterschriftsreif",
"Gewonnen",
"In Durchführung",
"Abgeschl. / Bestandsk.",
"Verloren",
"Ghosted"
],
"default": "Interessant",
"probabilityMap": {
"Interessant": 5,
"Abgelehnt": 0,
"Angefragt": 15,
"Agentur prüft": 20,
"Kunde prüft": 30,
"Identifiziert": 5,
"Brief versendet": 8,
"Anruf-Wiedervorlage": 10,
"Angerufen": 12,
"Ersttermin vereinbart": 20,
"Ersttermin durchgeführt": 30,
"Angebot": 40,
"Verhandlung": 60,
"Unterschriftsreif": 80,
"Gewonnen": 100,
"In Durchführung": 100,
"Abgeschl. / Bestandsk.": 100,
"Verloren": 0,
"Ghosted": 0
},
"style": {
"Interessant": null,
"Abgelehnt": "info",
"Angefragt": null,
"Agentur prüft": null,
"Kunde prüft": null,
"Identifiziert": null,
"Brief versendet": "warning",
"Anruf-Wiedervorlage": "warning",
"Angerufen": "warning",
"Ersttermin vereinbart": "warning",
"Ersttermin durchgeführt": "warning",
"Angebot": null,
"Verhandlung": null,
"Unterschriftsreif": null,
"Gewonnen": "success",
"In Durchführung": "primary",
"Abgeschl. / Bestandsk.": "info",
"Verloren": "danger",
"Ghosted": "info"
}
},
"links": {
"cAccount1": {
"type": "belongsTo",
"foreign": "cAgentur",
"entity": "Account",
"audited": false,
"isCustom": true
},
"cTags": {
"type": "hasMany",
"relationName": "cOpportunityTag",
"foreign": "verkaufschancen",
"entity": "CTag",
"audited": false,
"isCustom": true
}
"cLeadQuelle": {
"type": "enum",
"required": true,
"options": [
"Inbound Agentur",
"Inbound Plattform-Lead",
"Aktive Plattform-Suche",
"Aktive Akquise",
"Empfehlung / Netzwerk",
"Sonstiges"
],
"style": {
"Inbound Agentur": null,
"Inbound Plattform-Lead": null,
"Aktive Plattform-Suche": null,
"Aktive Akquise": null,
"Empfehlung / Netzwerk": null,
"Sonstiges": null
},
"default": "Inbound Agentur",
"maxLength": 100,
"isCustom": true,
"displayAsLabel": true
},
"cVerlustgrund": {
"type": "enum",
"options": [
"Agentur Absage",
"Kunde Absage",
"Keine Einigung",
"Eigene Absage",
"Sonstiges",
"Agentur Ghosted",
"Kunde Ghosted"
],
"style": {
"Agentur Absage": null,
"Kunde Absage": null,
"Keine Einigung": null,
"Eigene Absage": null,
"Sonstiges": null,
"Agentur Ghosted": null,
"Kunde Ghosted": null
},
"default": "Kunde Absage",
"maxLength": 100,
"isCustom": true
},
"cProjektlink": {
"type": "url",
"isCustom": true
},
"cVerguetungsmodell": {
"type": "enum",
"options": [
"Agentur Honorar",
"Direkt Festpreis",
"Direkt Honorar",
"Direkt Nutzenbasiert"
],
"style": {
"Agentur Honorar": null,
"Direkt Festpreis": null,
"Direkt Honorar": null,
"Direkt Nutzenbasiert": null
},
"default": "Agentur Honorar",
"displayAsLabel": true,
"maxLength": 100,
"isCustom": true
},
"cAccount1": {
"type": "link"
},
"amount": {
"required": false
},
"closeDate": {
"required": false
},
"cTags": {
"type": "linkMultiple",
"view": "views/fields/link-multiple",
"isCustom": true
}
},
"links": {
"cAccount1": {
"type": "belongsTo",
"foreign": "cAgentur",
"entity": "Account",
"audited": false,
"isCustom": true
},
"cTags": {
"type": "hasMany",
"relationName": "cOpportunityTag",
"foreign": "verkaufschancen",
"entity": "CTag",
"audited": false,
"isCustom": true
}
}
}