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": { "labels": {
"Create CTag": "Tag erstellen" "Create CTag": "Tag erstellen"
}, },
"fields": { "fields": {
"verkaufschancen": "Verkaufschancen", "verkaufschancen": "Verkaufschancen",
"kontakte": "Kontakte", "kontakte": "Kontakte",
"firmen": "Firmen" "firmen": "Firmen",
}, "interessenten": "Interessenten"
"links": { },
"verkaufschancen": "Verkaufschancen", "links": {
"kontakte": "Kontakte", "verkaufschancen": "Verkaufschancen",
"firmen": "Firmen" "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": { "labels": {
"Create CTag": "Create Tag" "Create CTag": "Create Tag"
}, },
"fields": { "fields": {
"verkaufschancen": "Verkaufschancen", "verkaufschancen": "Verkaufschancen",
"kontakte": "Kontakte", "kontakte": "Kontakte",
"firmen": "Firmen" "firmen": "Firmen",
}, "interessenten": "Leads"
"links": { },
"verkaufschancen": "Verkaufschancen", "links": {
"kontakte": "Kontakte", "verkaufschancen": "Verkaufschancen",
"firmen": "Firmen" "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": { "firmen": {
"layout": null, "layout": null,
"selectPrimaryFilterName": 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": { "links": {
"cAgentur": { "cAgentur": {
"type": "hasMany", "type": "hasMany",
"foreign": "cAccount1", "foreign": "cAccount1",
"entity": "Opportunity", "entity": "Opportunity",
"audited": false, "audited": false,
"isCustom": true "isCustom": true
}, },
"cTags": { "cTags": {
"type": "hasMany", "type": "hasMany",
"relationName": "cAccountTag", "relationName": "cAccountTag",
"foreign": "firmen", "foreign": "firmen",
"entity": "CTag", "entity": "CTag",
"audited": false, "audited": false,
"isCustom": true "isCustom": true
}
} }
} },
"fields": {
"cTags": {
"type": "linkMultiple",
"view": "views/fields/link-multiple",
"isCustom": true
}
}
}

View File

@@ -1,113 +1,121 @@
{ {
"fields": { "fields": {
"name": { "name": {
"type": "varchar", "type": "varchar",
"required": true, "required": true,
"pattern": "$noBadCharacters" "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"
}
}, },
"links": { "description": {
"createdBy": { "type": "text"
"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
}
}, },
"collection": { "createdAt": {
"orderBy": "createdAt", "type": "datetime",
"order": "desc" "readOnly": true
}, },
"indexes": { "modifiedAt": {
"name": { "type": "datetime",
"columns": [ "readOnly": true
"name", },
"deleted" "createdBy": {
] "type": "link",
}, "readOnly": true,
"assignedUser": { "view": "views/fields/user"
"columns": [ },
"assignedUserId", "modifiedBy": {
"deleted" "type": "link",
] "readOnly": true,
}, "view": "views/fields/user"
"createdAt": { },
"columns": [ "assignedUser": {
"createdAt" "type": "link",
] "required": false,
}, "view": "views/fields/assigned-user"
"createdAtId": { },
"unique": true, "teams": {
"columns": [ "type": "linkMultiple",
"createdAt", "view": "views/fields/teams"
"id"
]
}
} }
} },
"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": { "links": {
"cTags": { "cTags": {
"type": "hasMany", "type": "hasMany",
"relationName": "cContactTag", "relationName": "cContactTag",
"foreign": "kontakte", "foreign": "kontakte",
"entity": "CTag", "entity": "CTag",
"audited": false, "audited": false,
"isCustom": true "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": { "fields": {
"stage": { "stage": {
"options": [ "options": [
"Interessant", "Interessant",
"Abgelehnt", "Abgelehnt",
"Angefragt", "Angefragt",
"Agentur prüft", "Agentur prüft",
"Kunde prüft", "Kunde prüft",
"Identifiziert", "Identifiziert",
"Brief versendet", "Brief versendet",
"Anruf-Wiedervorlage", "Anruf-Wiedervorlage",
"Angerufen", "Angerufen",
"Ersttermin vereinbart", "Ersttermin vereinbart",
"Ersttermin durchgeführt", "Ersttermin durchgeführt",
"Angebot", "Angebot",
"Verhandlung", "Verhandlung",
"Unterschriftsreif", "Unterschriftsreif",
"Gewonnen", "Gewonnen",
"In Durchführung", "In Durchführung",
"Abgeschl. / Bestandsk.", "Abgeschl. / Bestandsk.",
"Verloren", "Verloren",
"Ghosted" "Ghosted"
], ],
"default": "Interessant", "default": "Interessant",
"probabilityMap": { "probabilityMap": {
"Interessant": 5, "Interessant": 5,
"Abgelehnt": 0, "Abgelehnt": 0,
"Angefragt": 15, "Angefragt": 15,
"Agentur prüft": 20, "Agentur prüft": 20,
"Kunde prüft": 30, "Kunde prüft": 30,
"Identifiziert": 5, "Identifiziert": 5,
"Brief versendet": 8, "Brief versendet": 8,
"Anruf-Wiedervorlage": 10, "Anruf-Wiedervorlage": 10,
"Angerufen": 12, "Angerufen": 12,
"Ersttermin vereinbart": 20, "Ersttermin vereinbart": 20,
"Ersttermin durchgeführt": 30, "Ersttermin durchgeführt": 30,
"Angebot": 40, "Angebot": 40,
"Verhandlung": 60, "Verhandlung": 60,
"Unterschriftsreif": 80, "Unterschriftsreif": 80,
"Gewonnen": 100, "Gewonnen": 100,
"In Durchführung": 100, "In Durchführung": 100,
"Abgeschl. / Bestandsk.": 100, "Abgeschl. / Bestandsk.": 100,
"Verloren": 0, "Verloren": 0,
"Ghosted": 0 "Ghosted": 0
}, },
"style": { "style": {
"Interessant": null, "Interessant": null,
"Abgelehnt": "info", "Abgelehnt": "info",
"Angefragt": null, "Angefragt": null,
"Agentur prüft": null, "Agentur prüft": null,
"Kunde prüft": null, "Kunde prüft": null,
"Identifiziert": null, "Identifiziert": null,
"Brief versendet": "warning", "Brief versendet": "warning",
"Anruf-Wiedervorlage": "warning", "Anruf-Wiedervorlage": "warning",
"Angerufen": "warning", "Angerufen": "warning",
"Ersttermin vereinbart": "warning", "Ersttermin vereinbart": "warning",
"Ersttermin durchgeführt": "warning", "Ersttermin durchgeführt": "warning",
"Angebot": null, "Angebot": null,
"Verhandlung": null, "Verhandlung": null,
"Unterschriftsreif": null, "Unterschriftsreif": null,
"Gewonnen": "success", "Gewonnen": "success",
"In Durchführung": "primary", "In Durchführung": "primary",
"Abgeschl. / Bestandsk.": "info", "Abgeschl. / Bestandsk.": "info",
"Verloren": "danger", "Verloren": "danger",
"Ghosted": "info" "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
}
}, },
"links": { "cLeadQuelle": {
"cAccount1": { "type": "enum",
"type": "belongsTo", "required": true,
"foreign": "cAgentur", "options": [
"entity": "Account", "Inbound Agentur",
"audited": false, "Inbound Plattform-Lead",
"isCustom": true "Aktive Plattform-Suche",
}, "Aktive Akquise",
"cTags": { "Empfehlung / Netzwerk",
"type": "hasMany", "Sonstiges"
"relationName": "cOpportunityTag", ],
"foreign": "verkaufschancen", "style": {
"entity": "CTag", "Inbound Agentur": null,
"audited": false, "Inbound Plattform-Lead": null,
"isCustom": true "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
}
}
}