KB: suitecrm-dev
← All workspaces4075 results — page 2 of 82
| Title | Domain | Type | Severity | Source | Freshness | Updated |
|---|---|---|---|---|---|---|
| Aktive tilpasninger | suitecrm | knowledge | medium | CURRENT-module-contacts.md | 100 | 2026-03-20 02:00:01 |
|
Body:
# Contacts — Tilpasninger
**Siste SC-ID:** SC-0039
**Antall endringer:** 6
**Dato:** 2026-03-17 (verified + GOTCHA-C-19 orphan)
**Load:** Når oppgaven involverer Contacts-modulen (felt, metadata, labels, modal)
> Se også: `CURRENT-leads-contacts-architecture.md` for Leads↔Contacts relasjon (SC-0022, SC-0024)
---
## Aktive tilpasninger
### SC-0025: Contacts LBL_TITLE + LBL_LIST_TITLE — oversatt til "Stilling"
- **Dato:** 2026-03-16
- **Endring:**
- `set_label(key="LBL_TITLE", value="Stilling", module="Contacts", language="nb_NO")`
- `set_label(key="LBL_LIST_TITLE", value="Stilling", module="Contacts", language="nb_NO")`
- **Hvorfor:** "Job Title" er engelsk — norsk bruker "Stilling" i CRM-kontekst
- **Filer:** `custom/Extension/modules/Contacts/Ext/Language/nb_NO.custom_agent.php`
- **Oppgraderingssikker:** Ja — Extension language override
### SC-0031: Contacts editviewdefs — eksplisitte labels for modal-kompatibilitet
- **Dato:** 2026-03-17
- **Fil:** `public/legacy/custom/modules/Contacts/metadata/editviewdefs.php`
- **Endring:** La til eksplisitte `label`-attributter på 5 felt: `first_name` (LBL_FIRST_NAME), `last_name` (LBL_LAST_NAME), `department` (LBL_DEPARTMENT), `account_name` (LBL_ACCOUNT_NAME), `campaign_name` (LBL_CAMPAIGN). Fjernet legacy `customCode` på `first_name`.
- **Hvorfor:** RecordModal (create-modus) viser felt uten labels hvis editviewdefs mangler eksplisitt `label`-attributt. Se GOTCHA-C-18.
- **Deploy-metode:** MCP `deploy_metadata_file` + `cache_clear`
- **Oppgraderingssikker:** Ja — custom metadata override
### SC-0028: contact-relate modal bredde — modalOptions size: xl (MP-0005 BUGFIX-5)
- **Dato:** 2026-03-17
- **Filer:**
- `extensions/magitekExt/app/src/fields/contact-relate/contact-relate-edit.component.ts`
- `extensions/magitekExt/app/src/fields/contact-relate/contact-relate-detail.component.ts`
- **Endring:** `modalOptions: { size: 'xl' }` lagt til i `showModal()`-kall. Gir 1140px max-width ved ≥1200px viewport.
- **Rotårsak:** Top-level `size`-felt i `RecordModalOptions` ignoreres av RecordModalService. Se GOTCHA-C-15.
- **Oppgraderingssikker:** Ja — Extension-filer
|
||||||
| 11. summaryTemplates — Record-tittel i Angular-header | suitecrm | knowledge | medium | CURRENT-metadata-patterns.md | 100 | 2026-03-20 02:00:01 |
|
Body:
## 11. summaryTemplates — Record-tittel i Angular-header
`summaryTemplates` i detailviewdefs styrer hvilke felt som vises som record-tittel øverst på detaljsiden (over fanelistene).
### Standard oppsett (person-navn)
```php
'summaryTemplates' => array(
'edit' => 'LBL_SUMMARY_PERSON',
'detail' => 'LBL_SUMMARY_PERSON',
)
```
Labelen `LBL_SUMMARY_PERSON` har Angular-template-verdien `{{fields.first_name.value}} {{fields.last_name.value}}`.
### GOTCHA: Kun label-nøkler fungerer — ikke Smarty-syntaks
`{$fields.xxx.value}` (Smarty) fungerer IKKE — Angular rendrer ingenting og tittelen blir tom.
Angular-laget leser label-nøkkelen via `RecordViewDefinitionHandler::getFieldLabel()` og interpolerer
`{{fields.xxx.value}}`-mønsteret mot record-data. Legg ALDRI rå template-strenger direkte i viewdefs.
### Slik endrer du record-tittelen (to-stegs prosedyre)
**Steg 1** — Opprett ny label med Angular-template-syntaks (begge språk!):
```python
# Via set_label MCP:
set_label(key="LBL_SUMMARY_LEAD_COMPANY", value="{{fields.account_name.value}}",
string_type="app_strings", language="nb_NO")
set_label(key="LBL_SUMMARY_LEAD_COMPANY", value="{{fields.account_name.value}}",
string_type="app_strings", language="en_us")
```
**Steg 2** — Pek summaryTemplates til label-nøkkelen i detailviewdefs:
```php
'summaryTemplates' => array(
'edit' => 'LBL_SUMMARY_LEAD_COMPANY',
'detail' => 'LBL_SUMMARY_LEAD_COMPANY',
)
```
### Kombinere felt
Labelverdien kan kombinere flere felt: `{{fields.first_name.value}} {{fields.last_name.value}}`
### Referanse
- Implementert i SC-0025 (Leads: FIRMANAVN som record-tittel)
- Mekanisme dokumentert i EX-0023 (`coordination/explore/EX-0023-260316-lead-record-title/`)
- Kildekode: `core/backend/ViewDefinitions/LegacyHandler/RecordViewDefinitionHandler.php`
---
## 12. Referanser
- `CURRENT-architecture.md` — Dual-layer system, Angular + Symfony
- `CURRENT-extensions.md` — Extension framework, QR&R, custom fields
- `CONTEXT.md` i MP-0001 — DEV-gotchas fra implementering
- SuiteCRM kildekode: `core/backend/ViewDefinitions/LegacyHandler/RecordViewDefinitionHandler.php`
- SuiteCRM kildekode: `core/app/fields/base/base.component.ts` (displayLogic, requiredLogic)
|
||||||
| 10. Gotchas og kjente fallgruver | suitecrm | knowledge | medium | CURRENT-metadata-patterns.md | 100 | 2026-03-20 02:00:01 |
|
Body:
## 10. Gotchas og kjente fallgruver
| Gotcha | Konsekvens | Loesning |
|--------|-----------|---------|
| **Kun endre detailviewdefs og ikke editviewdefs** | Feltlogikk virker i detail men ikke edit | Endre BEGGE filer |
| **Glemme theme TPL-sletting** | Endringer vises ikke selv etter cache_clear | `rm` TPL-filene manuelt |
| **`cache_clear` uten `rebuild_extensions` for Extension-filer** | Vardefs/labels/layoutdefs ikke lastet | ALLTID rebuild foerst |
| **Handler namespace stemmer ikke med mappestruktur** | Symfony finner ikke handleren | Maa matche eksakt |
| **`remoteEntry` utelatt fra extension.php** | Symfony krasjer ved container-bygging | Sett til `''` for backend-only |
| **Skrive direkte til `*.ext.php`-filer** | Overskrives ved neste QR&R | Skriv til kildefiler i `Ext/` |
| **`customCode` i viewdefs** | Angular ignorerer det helt | Bruk displayLogic eller CSS |
| **`type: 'address'` composite widget** | Rendrer BEGGE adresseblokker uansett | Bruk individuelle adressefelt |
| **`source => 'custom_fields'` mangler i vardef** | Felt vises ikke i EditView | Alltid med for `_c`-felt |
| **Bruke SSH istedenfor MCP for viewdefs** | Sakte, ingen audit trail | Bruk deploy_metadata_file |
| **`headerColumnClass` paa top-level (ikke under `metadata`)** | Ignoreres stille, ingen feilmelding | ALLTID nest under `'metadata' => [...]` key |
| **Bootstrap col-klasser for pixel-presis alignment** | Prosent-basert, varierer med viewport | Bruk CSS eller Angular-komponent for full kontroll |
| **`get_view_layout` MCP feil med `sugarEntry`-guard** | Returnerer PHP guard-feil for listview | Kjent MCP-bug, fikset i server — bruk `get_angular_view_definition` for det Angular-laget faktisk bruker |
| **Custom `_c`-felt mangler i `$listViewDefs`** | Felt usynlig i "Choose Columns"-dialogen | Legg feltet til i `$listViewDefs['{Module}']` med `'default' => false` |
| **`address_group_c` (non-db) vises ikke i detailview** | Virtuelt felt uten verdi rendres tomt | Bruk standard adressefelter for detail-visning — address_group_c er kun for edit-modus rendering |
---
|
||||||
| 9. Detailviewdefs.php — Minimal komplett struktur | suitecrm | knowledge | info | CURRENT-metadata-patterns.md | 100 | 2026-03-20 02:00:01 |
|
Body:
## 9. Detailviewdefs.php — Minimal komplett struktur
```php
<?php
// Fil: public/legacy/custom/modules/Leads/metadata/detailviewdefs.php
$viewdefs['Leads']['DetailView'] = [
'templateMeta' => [
'form' => ['required_fields' => []],
'maxColumns' => 2,
'useTabs' => false,
'tabDefs' => ['DEFAULT' => ['newTab' => false, 'panelDefault' => 'expanded']],
],
'panels' => [
[
'name' => 'LBL_LEADS_INFORMATION',
'columns' => 2,
'labels' => true,
'labelsOnTop' => false,
'placeholders' => true,
'fields' => [
// --- Enkel rad med to felt ---
[
0 => ['name' => 'status'],
1 => ['name' => 'lead_source'],
],
// --- Felt med displayLogic ---
[
0 => [
'name' => 'description',
'displayLogic' => [
'hide_when_dead' => [
'key' => 'displayType',
'modes' => ['detail', 'edit', 'create'],
'params' => [
'fieldDependencies' => ['status'],
'targetDisplayType' => 'none',
'activeOnFields' => ['status' => ['Dead']]
]
]
]
],
1 => '', // Tom kolonne
],
// --- Felt med requiredLogic ---
[
0 => [
'name' => 'phone_work',
'requiredLogic' => [
'required_when_in_dialog' => [
'key' => 'required',
'modes' => ['edit', 'create'],
'params' => [
'fieldDependencies' => ['status'],
'activeOnFields' => ['status' => ['In_Dialog']]
]
]
]
],
1 => ['name' => 'phone_mobile'],
],
]
]
]
];
```
---
|
||||||
| Etter oppgradering — sjekkliste | suitecrm | knowledge | medium | CURRENT-metadata-patterns.md | 100 | 2026-03-20 02:00:01 |
|
Body:
### Etter oppgradering — sjekkliste
1. `public/dist/index.html` — verifiser at `custom-overrides.css`-linken er til stede. Kjoer `/var/www/suitecrm/scripts/inject-custom-css.sh` hvis den mangler.
2. `extensions/magitekExt/` — kjoer `yarn build:extension magitekExt` paa nytt hvis Angular-versjonen er oppdatert (Phase 2).
3. Kjoer `rebuild_extensions` + `cache_clear` etter alle oppgraderinger.
4. Verifiser displayLogic og requiredLogic fungerer i nettleser.
---
## 8. Komplett felt-array i detailviewdefs.php
Et felt i detailviewdefs kan ha alle disse egenskapene samtidig:
```php
'phone_work' => [
'name' => 'phone_work',
'comment' => 'Arbeidstelfon', // Valgfritt — kun for dokumentasjon
// --- Display Logic ---
'displayLogic' => [
'hide_when_dead' => [
'key' => 'displayType',
'modes' => ['detail', 'edit', 'create'],
'params' => [
'fieldDependencies' => ['status'],
'targetDisplayType' => 'none',
'activeOnFields' => ['status' => ['Dead', 'Converted']]
]
]
],
// --- Required Logic ---
'requiredLogic' => [
'required_when_in_dialog' => [
'key' => 'required',
'modes' => ['edit', 'create'],
'params' => [
'fieldDependencies' => ['status'],
'activeOnFields' => ['status' => ['In_Dialog']]
]
]
],
// --- Update Value Logic ---
'updateValueLogic' => [
'clear_when_dead' => [
'key' => 'value',
'modes' => ['edit'],
'params' => [
'fieldDependencies' => ['status'],
'activeOnFields' => ['status' => ['Dead']],
'value' => ''
]
]
],
// --- Field Actions (knapp ved feltet) ---
'fieldActions' => [
'actions' => [
[
'key' => 'copy-address',
'labelKey' => 'LBL_COPY_ADDRESS',
'asyncProcess' => true,
'params' => [
'payload' => [
'module' => 'Leads',
'field' => 'phone_work',
]
]
]
]
]
]
```
---
|
||||||
| Komplett deploy-sekvens for metadata-endring | suitecrm | knowledge | medium | CURRENT-metadata-patterns.md | 100 | 2026-03-20 02:00:01 |
|
Body:
### Komplett deploy-sekvens for metadata-endring
```
1. Les eksisterende fil (SSH cat eller les fra minne hvis nettopp skrevet)
2. Modifiser PHP-innholdet
3. Deploy:
mcp__suitecrm__deploy_metadata_file(...)
4. Rydd theme TPL (SSH):
ssh suitecrm 'rm -f /var/www/suitecrm/public/legacy/cache/themes/suite8/modules/{Mod}/DetailView.tpl'
ssh suitecrm 'rm -f /var/www/suitecrm/public/legacy/cache/themes/suite8/modules/{Mod}/EditView.tpl'
5. Rydd Symfony-cache:
mcp__suitecrm__cache_clear()
6. Verifiser i nettleser
```
### Komplett deploy-sekvens for backend handler (extension)
```
1. Deploy extension.php:
mcp__suitecrm__deploy_extension_file(
module="_global", ext_type="Config",
filename="extension.php", content=...
)
-- ELLER via SSH scp-pattern (se CONTEXT.md)
2. Deploy handler PHP-fil:
-- Via SSH scp-pattern (3-stegs: local /tmp -> scp -> sudo cp)
3. Rydd cache:
mcp__suitecrm__cache_clear()
-- Symfony re-scanner extensions/ og auto-registrerer ny handler
4. Verifiser handler er tilgjengelig:
ssh suitecrm 'grep -r "getProcessType" /var/www/suitecrm/extensions/magitekExt/'
```
---
## 7. Oppgraderingssikkerhet
### Overlever SuiteCRM-oppgradering (SIKKERT)
| Sti | Type | Risiko |
|-----|------|--------|
| `public/legacy/custom/modules/{M}/metadata/` | Viewdefs | Lav |
| `public/legacy/custom/Extension/modules/{M}/Ext/Vardefs/` | Custom fields | Lav |
| `public/legacy/custom/Extension/modules/{M}/Ext/Language/` | Labels | Lav |
| `public/legacy/custom/Extension/modules/{M}/Ext/Layoutdefs/` | Subpanels | Lav |
| `public/legacy/custom/modules/{M}/logic_hooks.php` | Logic hooks | Lav |
| `extensions/magitekExt/` (hele mappen) | Vaar extension | Middels |
| `extensions/magitekExt/modules/*/Service/*.php` | Handlers | Lav |
| `public/dist/custom-overrides.css` | CSS-filen | Lav |
| DB: `fields_meta_data`, `*_cstm`-tabeller | Custom field data | Lav |
### Overskrives ved oppgradering (IKKE SIKKERT)
| Sti | Risiko | Handling |
|-----|--------|---------|
| `public/dist/index.html` | HOeY | Re-injiser CSS-link etter oppgradering |
| `core/backend/` | FORBUDT | Aldri endre |
| `core/app/` (Angular) | FORBUDT | Aldri endre |
| `public/legacy/modules/{M}/` (core) | FORBUDT | Aldri endre |
|
||||||
| 6. MCP Deployment Commands | suitecrm | knowledge | medium | CURRENT-metadata-patterns.md | 100 | 2026-03-20 02:00:01 |
|
Body:
## 6. MCP Deployment Commands
### `deploy_metadata_file` — Viewdefs (layout + logic)
Bruk for alle filer i `public/legacy/custom/modules/{Module}/metadata/`:
```
mcp__suitecrm__deploy_metadata_file(
module = "Leads",
view = "detailview", // "detailview" | "editview" | "listview"
content = "<?php\n$viewdefs['Leads']['DetailView'] = [\n ..."
)
```
**Naar bruke det:**
- `detailviewdefs.php` — layout, displayLogic, requiredLogic, updateValueLogic, fieldActions
- `editviewdefs.php` — redigerbarhet (legacy modus)
- `listviewdefs.php` — kolonner i listemodus
**Etter deploy:**
1. Slett theme-cache TPL manuelt (MCP-gap — se nedenfor)
2. Kjoer `cache_clear`
### `deploy_extension_file` — Vardefs, Language, Layoutdefs
Bruk for filer i `public/legacy/custom/Extension/modules/{Module}/Ext/`:
```
mcp__suitecrm__deploy_extension_file(
module = "Leads",
ext_type = "Vardefs", // "Vardefs" | "Language" | "Layoutdefs"
filename = "custom_fields.php",
content = "<?php\n$dictionary['Lead']['fields']['myfelt_c'] = ..."
)
```
**Etter deploy:** Kjoer ALLTID `rebuild_extensions` FOER `cache_clear`.
### `rebuild_extensions` — Kompiler Extension-filer
```
mcp__suitecrm__rebuild_extensions()
```
Kjoer etter ENHVER `deploy_extension_file`-operasjon. Uten dette vises ikke endringen.
### `cache_clear` — Rydd Symfony + Smarty cache
```
mcp__suitecrm__cache_clear()
```
Kjoer alltid SIST i sekvensen, etter rebuild_extensions (hvis brukt).
### Manuelle SSH-kommandoer — Smarty cache og theme TPL
**`cache_clear` rydder IKKE theme module TPL** (kjent MCP-gap).
Disse kommandoene maa kjoeres manuelt via SSH etter `deploy_metadata_file`:
```bash
# Slett kompilert EditView-template for modulen
ssh suitecrm 'rm -f /var/www/suitecrm/public/legacy/cache/themes/suite8/modules/Leads/EditView.tpl'
# Slett kompilert DetailView-template for modulen
ssh suitecrm 'rm -f /var/www/suitecrm/public/legacy/cache/themes/suite8/modules/Leads/DetailView.tpl'
# Slett alle Smarty templates_c (optional, bredere)
ssh suitecrm 'rm -rf /var/www/suitecrm/public/legacy/cache/smarty/templates_c/*'
```
**Begrunn:** `TemplateHandler::buildTemplate()` gjenbruker `.tpl`-filen uten aa sjekke om kilde-metadata er endret. `checkTemplate()` sjekker KUN om filen finnes — ikke om den er utdatert.
|
||||||
| extension.php — Backend-only modus (ingen frontend ennaa) | suitecrm | knowledge | medium | CURRENT-metadata-patterns.md | 100 | 2026-03-20 02:00:01 |
|
Body:
### extension.php — Backend-only modus (ingen frontend ennaa)
```php
<?php
// Fil: extensions/magitekExt/config/extension.php
// Backend-only: remoteEntry er tom streng (IKKE utelatt — maa vaere med)
use Symfony\Component\DependencyInjection\Container;
if (!isset($container)) {
return;
}
/** @var Container $container */
$extensions = $container->getParameter('extensions') ?? [];
$extensions['magitekExt'] = [
'remoteEntry' => '', // Tom streng = ingen Angular-frontend ennaa
'remoteName' => 'magitekExt',
'enabled' => true,
'extension_name' => 'Magitek Extension',
'version' => '1.0.0',
'author' => 'Magitek',
'author_uri' => 'https://magitek.no',
'license' => 'Proprietary'
];
$container->setParameter('extensions', $extensions);
```
### extension.php — Med Angular-frontend (Phase 2)
```php
<?php
// Fil: extensions/magitekExt/config/extension.php
// Med Angular-frontend: remoteEntry peker paa bygget bundle
use Symfony\Component\DependencyInjection\Container;
if (!isset($container)) {
return;
}
/** @var Container $container */
$extensions = $container->getParameter('extensions') ?? [];
$extensions['magitekExt'] = [
'remoteEntry' => '../extensions/magitekExt/remoteEntry.js',
'remoteName' => 'magitekExt',
'enabled' => true,
'extension_name' => 'Magitek Extension',
'version' => '1.0.0',
'author' => 'Magitek',
'author_uri' => 'https://magitek.no',
'license' => 'Proprietary'
];
$container->setParameter('extensions', $extensions);
```
### Composer PSR-4 auto-loading
Backend-handler-klasser i `extensions/*/` lastes automatisk av Symfon via `core_services.yaml`:
```yaml
# Symfony scanner automatisk: extensions/*/
# Ingen manuell registrering av handler noedvendig.
# Forutsetning: namespace maa matche mappe-struktur.
```
**Namespace -> mappe-mapping:**
- `App\Extensions\magitekExt\Modules\Leads\Service\CopyAddressHandler`
- = `extensions/magitekExt/modules/Leads/Service/CopyAddressHandler.php`
**Etter deploy av ny handler:** Kjoer alltid `cache_clear` saa Symfony re-scanner services.
---
|
||||||
| MCP-deploy for label | suitecrm | knowledge | medium | CURRENT-metadata-patterns.md | 100 | 2026-03-20 02:00:01 |
|
Body:
### MCP-deploy for label
```
# Sett label foer deploy av viewdefs:
mcp__suitecrm__set_label(
module="Leads",
key="LBL_COPY_ADDRESS",
value="Kopier adresse",
language="nb_NO"
)
```
---
## 5. Backend Process Handler — Komplett mal
Process handlers kjoeres naar en `fieldAction` med `asyncProcess: true` utloeses fra frontenden.
### Namespace-konvensjon
```
App\Extensions\{extName}\Modules\{Module}\Service\{HandlerName}Handler
```
Eksempel: `App\Extensions\magitekExt\Modules\Leads\Service\CopyAddressHandler`
### Filplassering
```
extensions/
magitekExt/
modules/
Leads/
Service/
CopyAddressHandler.php
config/
extension.php (se nedenfor)
services.yaml (tom eller minimal)
backend/ (.gitkeep — holder mappen)
```
### Komplett ProcessHandlerInterface-mal (alle 6 metoder paakrevd)
```php
<?php
// Fil: extensions/magitekExt/modules/Leads/Service/CopyAddressHandler.php
namespace App\Extensions\magitekExt\Modules\Leads\Service;
use App\Process\Entity\Process;
use App\Process\Service\ProcessHandlerInterface;
class CopyAddressHandler implements ProcessHandlerInterface
{
/**
* Definerer prosess-noekkelen som matcher fieldActions['key']
*/
public function getProcessType(): string
{
return 'copy-address'; // Maa matche 'key' i fieldActions
}
/**
* Minimum rolle for aa utloese denne prosessen.
* Standard: 'ROLE_USER' (alle innloggede brukere)
*/
public function requiredAuthRole(): string
{
return 'ROLE_USER';
}
/**
* SuiteCRM ACL-sjekker. Returner tomt array for ingen ekstra ACL-sjekk.
*/
public function getRequiredACLs(Process $process): array
{
return [];
}
/**
* Sett prosess-opsjoner og standardverdier foer validate() og run().
*/
public function configure(Process $process): void
{
// Eksempel: sett standard modul
// $process->setOptions(['module' => 'Leads']);
}
/**
* Valider at noedvendige parametere er tilstede.
* Kast \InvalidArgumentException ved manglende data.
*/
public function validate(Process $process): void
{
$options = $process->getOptions();
if (empty($options['payload']['module'])) {
throw new \InvalidArgumentException('Missing required payload.module');
}
}
/**
* Selve prosess-logikken.
* Returner resultat via $process->setMessages() og $process->setStatus().
*/
public function run(Process $process): void
{
$options = $process->getOptions();
$payload = $options['payload'] ?? [];
// --- Din logikk her ---
// Eksempel: kopier adressedata
$result = [
'copied' => true,
'field' => $payload['field'] ?? '',
];
// Sett resultat-status og meldinger
$process->setStatus('success');
$process->setMessages(['LBL_COPY_ADDRESS_SUCCESS']);
// Evt. returner data til frontenden:
// $process->setData(['fieldValues' => ['description' => 'Ny verdi']]);
}
}
```
|
||||||
| Kombinert eksempel (postnr + poststed inline) | suitecrm | knowledge | medium | CURRENT-metadata-patterns.md | 100 | 2026-03-20 02:00:01 |
|
Body:
### Kombinert eksempel (postnr + poststed inline)
```php
// Rad i panels-arrayen:
[
0 => [
'name' => 'primary_address_postalcode',
'metadata' => [
'headerColumnClass' => 'col-sm-12 col-md-12 col-lg-auto',
'valueColumnClass' => 'col-sm-12 col-md-12 col-lg-auto',
],
],
1 => [
'name' => 'primary_address_city',
'metadata' => [
'labelDisplay' => 'none',
'headerColumnClass' => 'col-sm-12 col-md-12 col-lg-auto',
'valueColumnClass' => 'col-sm-12 col-md-12 col-lg-auto',
],
],
]
```
**Begrensning:** Metadata-klasser endrer Bootstrap-kolonnebredde, men full visuell kontroll (f.eks. viewport-uavhengig alignment) krever tilleggs-CSS eller Angular-komponent.
---
## 4. Field Actions — Feltknapper med backend-prosess
`fieldActions` legger til en knapp ved siden av et felt. Klikk utloser en backend `ProcessHandler`.
### Komplett syntaks
```php
'feltNavn' => [
'name' => 'feltNavn',
'fieldActions' => [
'actions' => [
[
'key' => 'unik-prosess-noekkel', // Matcher ProcessHandler::getProcessType()
'labelKey' => 'LBL_KNAPP_TEKST', // Oversettelsesnoekkel for knappetekst
'asyncProcess' => true, // ALLTID true for backend-kall
'params' => [
'payload' => [
'module' => '{ModuleNavn}', // Modul-kontekst for handleren
// Andre parametere som sendes til handleren:
'field' => 'feltNavn',
]
]
]
]
]
]
```
### Eksempel fra TASK-005: "Kopier adresse"-knapp
```php
// I detailviewdefs.php — paa primary_address_street-feltet
'primary_address_street' => [
'name' => 'primary_address_street',
'fieldActions' => [
'actions' => [
[
'key' => 'copy-address',
'labelKey' => 'LBL_COPY_ADDRESS',
'asyncProcess' => true,
'params' => [
'payload' => [
'module' => 'Leads',
'field' => 'primary_address_street',
]
]
]
]
]
]
```
|
||||||
| Eksempel: Sett `lead_source` automatisk naar `status = New` | suitecrm | knowledge | medium | CURRENT-metadata-patterns.md | 100 | 2026-03-20 02:00:01 |
|
Body:
### Eksempel: Sett `lead_source` automatisk naar `status = New`
```php
'lead_source' => [
'name' => 'lead_source',
'updateValueLogic' => [
'default_source_for_new' => [
'key' => 'value',
'modes' => ['create'],
'params' => [
'fieldDependencies' => ['status'],
'activeOnFields' => [
'status' => ['New']
],
'value' => 'Web Site'
]
]
]
]
```
### Eksempel: Toemm felt naar betingelse er oppfylt
```php
'description' => [
'name' => 'description',
'updateValueLogic' => [
'clear_when_dead' => [
'key' => 'value',
'modes' => ['edit'],
'params' => [
'fieldDependencies' => ['status'],
'activeOnFields' => [
'status' => ['Dead']
],
'value' => '' // Tom streng toemmer feltet
]
]
]
]
```
---
## 3b. Per-felt kolonne-klasser og label-skjuling (metadata key)
SuiteCRM 8 `field-layout.component.ts` stoetter per-felt overrides av Bootstrap-klasser via `metadata` sub-key i viewdefs.
### headerColumnClass / valueColumnClass
Overstyrer standard `col-lg-3` (label) og `col-lg-9` (value) paa enkeltfelt.
```php
'primary_address_postalcode' => [
'name' => 'primary_address_postalcode',
'metadata' => [
'headerColumnClass' => 'col-sm-12 col-md-12 col-lg-auto',
'valueColumnClass' => 'col-sm-12 col-md-12 col-lg-auto',
],
]
```
**KRITISK:** MÅ nestes under `'metadata'` key. Top-level `headerColumnClass` ignoreres STILLE (SC-GOTCHA-11).
**Kilde:** `field.builder.ts:178`, bekreftet mot `Emails/metadata/modalcomposeviewdefs.php`.
### labelDisplay — skjul label
```php
'primary_address_city' => [
'name' => 'primary_address_city',
'metadata' => [
'labelDisplay' => 'none', // Skjuler label-kolonnen helt
],
]
```
Nativt stoettet i field-layout template (`*ngIf` paa label-wrapper). Nyttig naar to felt vises inline og andre felt kun trenger input uten label.
|
||||||
| Eksempel fra TASK-004: `phone_work` paakrevd naar `status = In_Dialog` | suitecrm | knowledge | medium | CURRENT-metadata-patterns.md | 100 | 2026-03-20 02:00:01 |
|
Body:
### Eksempel fra TASK-004: `phone_work` paakrevd naar `status = In_Dialog`
```php
// I detailviewdefs.php — Leads-modulen
'phone_work' => [
'name' => 'phone_work',
'requiredLogic' => [
'required_when_in_dialog' => [
'key' => 'required',
'modes' => ['edit', 'create'],
'params' => [
'fieldDependencies' => ['status'],
'activeOnFields' => [
'status' => ['In_Dialog']
]
]
]
]
]
```
### Kombinert displayLogic + requiredLogic
Et felt kan ha begge logikktyper samtidig:
```php
'phone_work' => [
'name' => 'phone_work',
'displayLogic' => [
'hide_when_dead' => [
'key' => 'displayType',
'modes' => ['detail', 'edit', 'create'],
'params' => [
'fieldDependencies' => ['status'],
'targetDisplayType' => 'none',
'activeOnFields' => ['status' => ['Dead', 'Converted']]
]
]
],
'requiredLogic' => [
'required_when_in_dialog' => [
'key' => 'required',
'modes' => ['edit', 'create'],
'params' => [
'fieldDependencies' => ['status'],
'activeOnFields' => ['status' => ['In_Dialog']]
]
]
]
]
```
---
## 3. Update Value Logic — Auto-oppdatering av feltverdier
`updateValueLogic` fyller automatisk ut ett felt basert paa verdien av et annet.
### Komplett syntaks
```php
'maalfelt' => [
'name' => 'maalfelt',
'updateValueLogic' => [
'regel_noekkelnavn' => [
'key' => 'value', // ALLTID 'value' for value-oppdatering
'modes' => ['edit', 'create'],
'params' => [
'fieldDependencies' => ['kildefelt'],
'activeOnFields' => [
'kildefelt' => ['TriggerVerdi']
],
'value' => 'Standardverdi som settes' // Statisk verdi
]
]
]
]
```
|
||||||
| Negasjon — vis KUN naar en spesifikk verdi er satt | suitecrm | knowledge | medium | CURRENT-metadata-patterns.md | 100 | 2026-03-20 02:00:01 |
|
Body:
### Negasjon — vis KUN naar en spesifikk verdi er satt
For "vis feltet kun naar status er X" — skjul det naar status IKKE er X.
Dette krever at du setter feltet som skjult som standard og bruker `'targetDisplayType' => 'show'`:
```php
// Tilnaerming: ett skjul-regel per "andre" verdi — eller bruk JS logic hooks
// Enklest: skjul felt og vis kun naar betingelse er oppfylt:
'feltNavn' => [
'name' => 'feltNavn',
'displayLogic' => [
'show_only_in_dialog' => [
'key' => 'displayType',
'modes' => ['edit', 'create'],
'params' => [
'fieldDependencies' => ['status'],
'targetDisplayType' => 'none',
'activeOnFields' => [
'status' => ['New', 'Assigned', 'Dead', 'Converted', 'Recycled', 'In_Review']
// List alle verdier UNNTATT den du vil vise for
]
]
]
]
]
```
---
## 2. Required Logic — Betinget paakrevd validering
`requiredLogic` gjor et felt paakrevd basert paa verdier i andre felt. Angular-frontenden viser roed asterisk og blokkerer lagring.
### Komplett syntaks
```php
'feltNavn' => [
'name' => 'feltNavn',
'requiredLogic' => [
'regel_noekkelnavn' => [
'key' => 'required', // ALLTID 'required' for required logic
'modes' => ['edit', 'create'], // Validering gjelder kun i redigering
'params' => [
'fieldDependencies' => ['avhengigFelt'],
'activeOnFields' => [
'avhengigFelt' => ['Verdi1', 'Verdi2']
]
]
]
]
]
```
### `modes` for requiredLogic
| Verdi | Anbefaling |
|-------|-----------|
| `['edit', 'create']` | Standard — validering ved redigering og oppretting |
| `['create']` | Kun ved oppretting (feks onboarding-felt) |
| `['edit']` | Kun ved endring av eksisterende post |
**Merk:** `'detail'` i modes gir ingen effekt for required-logikk.
|
||||||
| `modes` verdier | suitecrm | knowledge | medium | CURRENT-metadata-patterns.md | 100 | 2026-03-20 02:00:01 |
|
Body:
### `modes` verdier
| Verdi | Naar det gjelder |
|-------|-----------------|
| `'detail'` | Visning av post (les-modus) |
| `'edit'` | Redigering av eksisterende post |
| `'create'` | Oppretting av ny post |
| `'list'` | Listemodus (sjelden brukt for displayLogic) |
Typisk kombinasjon: `['detail', 'edit', 'create']` for aa dekke alle modi.
### Eksempel fra TASK-003: Skjul `description` naar `status = Dead`
```php
// I detailviewdefs.php — Leads-modulen
'description' => [
'name' => 'description',
'displayLogic' => [
'hide_when_dead' => [
'key' => 'displayType',
'modes' => ['detail', 'edit', 'create'],
'params' => [
'fieldDependencies' => ['status'],
'targetDisplayType' => 'none',
'activeOnFields' => [
'status' => ['Dead']
]
]
]
]
]
```
### Flere betingelser (multiple verdier)
```php
'feltNavn' => [
'name' => 'feltNavn',
'displayLogic' => [
'hide_when_inactive' => [
'key' => 'displayType',
'modes' => ['detail', 'edit', 'create'],
'params' => [
'fieldDependencies' => ['status'],
'targetDisplayType' => 'none',
'activeOnFields' => [
'status' => ['Dead', 'Converted', 'Recycled'] // Skjul for ALLE disse verdiene
]
]
]
]
]
```
> **FALLGRUVE: displayLogic kan IKKE filtrere paa modus alene**
> `displayLogic` med `modes: [edit, create]` uten `fieldDependencies`/`activeOnFields` har INGEN effekt.
> Feltet vises i alle moduser uavhengig. `activeOnFields` med en faktisk feltverdi er alltid paakrevd.
>
> Vil du vise et felt KUN i detail-visning (ikke i edit/create)?
> → Dette stoettes IKKE via metadata alene i SuiteCRM 8.
> → Alternativer: CSS `[data-mode='detail']`-selektor, eller Angular-komponent.
> → Eskaler til `/architect` hvis dette behovet oppstaar.
|
||||||
| Grunnleggende Regler | suitecrm | knowledge | medium | CURRENT-metadata-patterns.md | 100 | 2026-03-20 02:00:01 |
|
Body:
# SuiteCRM 8 PHP Metadata Patterns — Agent Reference
> Oppdatert: 2026-03-16 (v1.2 — summaryTemplates record-tittel mønster, to-stegs label-nøkkel prosedyre)
> Kilde: MP-0001 TASK-003/004/005 + MP-0002 SC-0015 (headerColumnClass, labelDisplay discovery) + /suitecrm sesjon (SC-0020/SC-0021)
> Formål: Komplett referanse for LLM-agenter som skal gjøre PHP metadata-tilpasninger i SuiteCRM 8
---
## Grunnleggende Regler
**KRITISK — les dette foer du skriver metadata:**
1. **Layout er i `detailviewdefs.php`** — Angular-frontenden leser layout fra detailviewdefs. `editviewdefs.php` styrer redigerbarhet (legacy modus). Endre BEGGE for fullstendig tilpasning.
2. **Aldri endre core-filer** — Skriv alltid til `public/legacy/custom/modules/{Module}/metadata/`
3. **Les hele filen foerst** — Custom override erstatter HELE filen. Alt du utelater forsvinner.
4. **To cache-lag maa ryddes** etter metadata-endringer (se seksjon 6).
5. **Deploy via MCP** — Bruk `deploy_metadata_file` for alle viewdefs-filer.
---
## 1. Display Logic — Vis/skjul felt basert paa andre feltverdier
`displayLogic` skjuler eller viser et felt dynamisk basert paa verdier i andre felt.
### Komplett syntaks
```php
// I detailviewdefs.php, inne i feltets array:
'feltNavn' => [
'name' => 'feltNavn',
'displayLogic' => [
'regel_noekkelnavn' => [ // Valgfritt navn paa regelen (streng-noekkel)
'key' => 'displayType', // ALLTID 'displayType' for display logic
'modes' => ['detail', 'edit', 'create'], // Se liste nedenfor
'params' => [
'fieldDependencies' => ['avhengigFelt'], // Felt som trigger-sjekk
'targetDisplayType' => 'none', // 'none' = skjul, 'show' = vis
'activeOnFields' => [
'avhengigFelt' => ['Verdi1', 'Verdi2'] // Aktiver naar dette feltet har disse verdiene
]
]
]
]
]
```
### `targetDisplayType` verdier
| Verdi | Effekt |
|-------|--------|
| `'none'` | Skjuler feltet (display: none) |
| `'show'` | Viser feltet (standard) |
**Merk:** `'none'` er den praktisk nyttige verdien. `'show'` kan brukes til "vis KUN naar"-logikk ved aa kombinere med standard skjult felt.
|
||||||
| Aktive tilpasninger | suitecrm | knowledge | medium | CURRENT-module-accounts.md | 100 | 2026-03-20 02:00:01 |
|
Body:
# Accounts — Tilpasninger
**Siste SC-ID:** SC-0012
**Antall endringer:** 1
**Dato:** 2026-03-17
**Load:** Når oppgaven involverer Accounts-modulen
---
## Aktive tilpasninger
### SC-0012: org_nr_c custom felt på Accounts
- **Dato:** 2026-03-15
- **Filer:**
- Opprettet via MCP `create_custom_field` på Accounts-modulen
- DB: `accounts_cstm.org_nr_c` VARCHAR(9)
- `fields_meta_data` entry for Accounts/org_nr_c
- **Endring:** Organisasjonsnummer-felt for norske bedrifter. 9 tegn, bruker SC-0010 orgnr-komponent for validering og Brreg-oppslag.
- **Oppgraderingssikker:** Ja — custom field via Studio/MCP
---
## Filkart
```
/var/www/suitecrm/public/legacy/custom/
├── Extension/modules/Accounts/Ext/
│ └── Vardefs/ (org_nr_c vardef — auto-generert av MCP)
└── modules/Accounts/
└── metadata/
├── searchdefs.php ← pre-eksisterende override
└── SearchFields.php ← pre-eksisterende override
```
---
## Pre-eksisterende tilpasninger
- `_override_sugarfield_testheine_c.php` — Testfelt, kan sannsynligvis fjernes
- `accounts_prospectlists_1` relasjon (M2M med ProspectLists)
---
## Fremtidige tilpasninger (planlagt)
Accounts er "Kunder" i B2B-flyten — vil bli utvidet etterhvert som leads konverteres. Potensielle tilpasninger:
- Org-nr som primær identifikator (Brreg-integrasjon)
- Kobling til Leads via konverteringsflyt
- Norske feltnavn for adresseblokk
---
## Gotchas (Accounts-spesifikke)
Ingen modul-spesifikke gotchas ennå. Se `CURRENT-customizations.md` for globale gotchas.
|
||||||
| Implementert: `SyncLeadRelateToM2MHook` (after_save paa Contacts) — SC-0039 | suitecrm | knowledge | medium | CURRENT-leads-contacts-architecture.md | 100 | 2026-03-20 02:00:01 |
|
Body:
### Implementert: `SyncLeadRelateToM2MHook` (after_save paa Contacts) — SC-0039
Hook registrert i `public/legacy/custom/Extension/modules/Contacts/Ext/LogicHooks/sync_lead_relate_to_m2m.php`.
Klassen `SyncLeadRelateToM2MHook` ligg i `public/legacy/custom/modules/Contacts/SyncLeadRelateToM2MHook.php`.
**Logikk (speilet av SC-0035, men fraa Contact-sida):**
1. Naar `lead_id` endres paa eit Contact (via lead_name relate-felt i editview)
2. Hook sjekker om ny lead allerede er i `leads_contacts_1_c` join-tabellen
3. Hvis IKKJE: legg til rad (synk felt→M2M subpanel)
4. Setter `contact_id` paa Lead-kortet viss det er tomt (bidireksjonell kobling)
5. Hvis gammal `lead_id` fjernes: fjern raden fraa join-tabellen
### Implementert: lead-relate Angular komponenter — SC-0040
`lead-relate-edit.component.ts` set `sessionStorage.setItem('lead_relate_reload', '1')` naar lead_id endres.
`lead-relate-detail.component.ts` sjekkar flagget i `ngOnInit` og kaller `window.location.reload()` for aa oppdatere subpanel etter save.
Registrert i `extension.module.ts` for module='contacts', field='lead_name'.
---
## Fremtidig arbeid
### Subpanel→felt synkronisering (SC-0035 ULØST)
Naar kontakt leggast til/fjernast fraa Kontakter-subpanelet, maa contact_name-feltet oppdaterast i detailview utan full reload. Krever arkitektonisk tilnærming — RecordViewStore er ikkje tilgjengeleg fraa extensions.
### Lead-konvertering med kontaktpersoner
Naar "Konverter lead" brukes, boer alle kontaktpersoner fra `leads_contacts_1` automatisk flyttes til det nye Account-kortet. Dette er IKKE implementert ennaa — krever custom ConvertLead logic hook eller ProcessHandler.
### Cleanup av ForLeadsContacts1 subpanel
Custom subpanel-fil `ForLeadsContacts1.php` ble opprettet men er ikke i aktiv bruk (layoutdefs-override ble fjernet). Kan slettes ved opprydding.
|
||||||
| Endre primaer lead paa kontakt | suitecrm | knowledge | medium | CURRENT-leads-contacts-architecture.md | 100 | 2026-03-20 02:00:01 |
|
Body:
### Endre primaer lead paa kontakt
1. Gaa til kontaktkortet → Rediger
2. Klikk popup-knappen ved "NAVN PAa LEAD" → velg ny lead
3. Lagre
### Se alle leads for en kontakt
1. Gaa til kontaktkortet → Leads-subpanel (relationship-bar)
### Se alle kontaktpersoner for et lead
1. Gaa til Lead-kortet → Kontakter-subpanel
---
## SC-0035 + SC-0039 + SC-0040: Toveis synk KONTAKTPERSON ↔ Kontakter-subpanel
**Dato:** 2026-03-17
**Status:** Delvis implementert — felt→subpanel fungerer, subpanel→felt ULØST
### Implementert: `SyncContactRelateToM2MHook` (after_save paa Leads)
Hook registrert i `public/legacy/custom/Extension/modules/Leads/Ext/LogicHooks/sync_contact_relate_to_m2m.php`.
Klassen `SyncContactRelateToM2MHook` ligg i `public/legacy/custom/modules/Leads/SyncContactRelateToM2MHook.php`.
**Logikk:**
1. Naar `contact_id` endres paa eit Lead (via contact_name relate-felt i editview)
2. Hook sjekker om ny contact allerede er i `leads_contacts_1_c` join-tabellen
3. Hvis IKKJE: legg til relasjon (synk felt→subpanel)
4. Hvis gammal `contact_id` fjernes: fjern raden fraa join-tabellen (synk sletting)
### Implementert: SessionStorage-flagg + ngOnInit reload (contact-relate-detail)
`contact-relate-edit.component.ts` set `sessionStorage.setItem('contact_relate_reload', '1')` naar contact legges til eller fjernes via relate-feltet.
`contact-relate-detail.component.ts` sjekkar flagget i `ngOnInit` og kaller `window.location.reload()` for aa oppdatere Kontakter-subpanelet etter save (edit→detail overgang).
### ULØST: Subpanel→felt retningen
Naar bruker legg til/fjernar en kontakt via Kontakter-subpanelet DIREKTE (uten aa redigere feltet), oppdateres IKKJE contact_name-feltet i detailview utan full page refresh. Problemet:
- `RecordViewStore` er IKKJE tilgjengeleg fraa extensions (ikkje i `public-api.ts`)
- Direkte import fraa `core/app/core/src/...` bryt Angular-kompilatoren (NG8001)
- 5+ tilnærmingar prøvd (Injector._records, ngOnDestroy+router, MutationObserver, fetch-intercept, XHR-intercept) — alle feilet eller foraarsaka produksjonsskade
**Neste steg:** Eskaler til `/architect` for arkitektonisk løysing (event bus, signal, eller public API-utviding).
|
||||||
| GOTCHA-L8: Direkte SQL i hooks for aa unngaa rekursive after_save-loops | suitecrm | knowledge | medium | CURRENT-leads-contacts-architecture.md | 100 | 2026-03-20 02:00:01 |
|
Body:
### GOTCHA-L8: Direkte SQL i hooks for aa unngaa rekursive after_save-loops
`SyncLeadRelateToM2MHook` (Contacts, SC-0039) bruker direkte `$db->query("UPDATE leads SET contact_id=...")` i stedet for `$leadBean->save()`. Grunnen: bean-save trigger `SyncContactRelateToM2MHook` paa Leads, som ville trigget tilbake. Direkte SQL unngaar dette, men `date_modified` oppdateres ikke paa Lead-kortet. Akseptabel trade-off. Samme moenster boer brukes i alle toveis synk-hooks.
**Sources:** Sesjon 2026-03-17 (SC-0039)
### GOTCHA-L9: ALDRI bruk XHR/fetch-intercept eller MutationObserver for auto-reload
SC-0035 forsøkte window.fetch monkey-patching og XMLHttpRequest interceptor for aa auto-reloade subpanel naar relate-felt endres. BEGGE foraarsaket reload-loop i produksjon (commit 57294af lagt til, 083a137 fjernet). MutationObserver var ogsaa testet — ustabil.
**Akseptabel løysing:** SessionStorage-flagg i edit-komponent, sjekka i ngOnInit av detail-komponent. Trigger `window.location.reload()` KUN ved edit→detail overgang (ikkje fraa ngOnDestroy).
**Forbudt:** `window.fetch` monkey-patching, `XMLHttpRequest` interceptor, `MutationObserver` for auto-reload, `window.location.reload()` fraa `ngOnDestroy`.
**Ref:** feedback_escalate_angular_hacks, feedback_never_reload_from_destroy
### GOTCHA-L6: Extension language-filer kompileres i alfabetisk filnavn-rekkefølge
`nb_NO.lang.ext.php` bygges ved aa konkatenere alle Extension-filer i `Ext/Language/` i **alfabetisk filnavn-rekkefølge**. Ved duplikate keys vinner **siste fil**. Eksempel: `nb_NO.custom_agent.php` (c) kompileres FOER `nb_NO.old_kontakt_label.php` (o). Hvis begge har `$mod_strings['LBL_LIST_NAME']`, vinner `old_kontakt_label.php`. For aa endre en eksisterende label: endre verdien **i den opprinnelige filen**, ikke opprett ny fil.
---
## Arbeidsflyt for brukeren
### Koble kontaktperson til Lead
1. Gaa til Lead-kortet → Kontakter-subpanel → Handlinger → Velg kontakt
2. Kontakten faar automatisk `lead_id` satt (logic hook)
3. Paa kontaktkortet vises leadet i "NAVN PAa LEAD"-feltet
|
||||||
| GOTCHAS — ting som har gaatt galt | suitecrm | knowledge | medium | CURRENT-leads-contacts-architecture.md | 100 | 2026-03-20 02:00:01 |
|
Body:
## GOTCHAS — ting som har gaatt galt
### GOTCHA-L1: `side => 'right'` paa link-felt er KRITISK
Link-feltet `leads_contacts_1` paa Contacts MAATTE ha `'side' => 'right'` for at SugarBean skulle vite at Contact er RHS i many-to-many relasjonen. Uten dette returnerte subpanelet 0 resultater fra Contact-siden, selv om join-tabellen hadde data.
### GOTCHA-L2: Relasjon MAA registreres i `relationships` DB-tabell
`leads_contacts_lead_id`-relasjonen (for relate-feltets typeahead-soek) fungerte IKKE foer den ble lagt til i `relationships`-tabellen via SQL INSERT. Vardef-definisjon alene er ikke nok — SuiteCRM 8s GraphQL-lag leser relasjoner fra DB.
### GOTCHA-L3: `lead_id` MAA vaere `type => 'id'` (ikke `type => 'relate'`)
Foerste forsook brukte `type => 'relate'` med `source => 'non-db'` for lead_id. SugarBean lagret IKKE verdien til DB. Endret til `type => 'id'` (ekte DB-kolonne) — da fungerte lagring.
### GOTCHA-L4: `rname => 'name'` fungerer for relate-felt typeahead
For person-type moduler (Leads, Contacts) fungerer `rname => 'name'` for typeahead-soek, MEN inline dropdown (klikk uten aa skrive) viser ingen resultater. Dette er standard SuiteCRM 8-oppfoersel — identisk med account_name. Brukeren maa skrive minst noen bokstaver, eller bruke popup-knappen.
### GOTCHA-L5: Core 'leads' subpanel bruker feil link
Core subpaneldefs for Contacts har `'leads' => ['get_subpanel_data' => 'leads']` som bruker `contact_leads` (one-to-many via `leads.contact_id`). Vi overstyrer dette i layoutdefs til `'get_subpanel_data' => 'leads_contacts_1'` for aa bruke vaart many-to-many subpanel.
### GOTCHA-L7: `rname` i relate-vardef styrer visningsverdi OG typeahead-soek
Relate-felt bruker `rname` for aa bestemme hvilken kolonne fra maaltabellen som vises og soekes i. `lead_name` hadde `rname => 'name'` (personnavn). Endret til `rname => 'account_name'` (firmanavn) i SC-0036. Endring krever rebuild_extensions + cache_clear. Typeahead-soek matcher ogsaa paa den nye kolonnen.
**Sources:** Sesjon 2026-03-17 (SC-0036)
|
||||||
| Logic Hooks — automatisk synkronisering | suitecrm | knowledge | medium | CURRENT-leads-contacts-architecture.md | 100 | 2026-03-20 02:00:01 |
|
Body:
## Logic Hooks — automatisk synkronisering
### Naar kontakt knyttes til lead via subpanel (fra Lead-siden)
1. `SyncLeadIdFromLeadHook::afterRelationshipAdd()` trigges
2. Sjekker: er `lead_id` tom paa kontakten?
3. Hvis ja: setter `lead_id = dette leadets ID` → kontakten faar "primaer lead"
4. Brukeren kan deretter endre primaer lead manuelt via "NAVN PAa LEAD"-feltet
### Naar kontakt fjernes fra lead via subpanel
1. `SyncLeadIdFromLeadHook::afterRelationshipDelete()` trigges
2. Sjekker: er kontaktens `lead_id` lik dette leadets ID?
3. Hvis ja: nullstiller `lead_id` → kontakten mister primaer lead
### Samme logikk fra Contact-siden
- `SyncLeadIdHook` paa Contacts-modulen haandterer add/delete fra kontaktens Leads-subpanel
### Naar lead_id endres paa Contact via relate-felt i editview (SC-0039)
1. `SyncLeadRelateToM2MHook::afterSave()` trigges paa Contact etter lagring
2. Sjekker om ny `lead_id` allerede er i `leads_contacts_1_c` join-tabellen
3. Hvis IKKJE: legg til rad (synk felt→M2M subpanel)
4. Setter `contact_id` paa Lead-kortet hvis det er tomt (bidireksjonell kobling)
5. Hvis gammal `lead_id` fjernes: fjern raden fraa join-tabellen
6. Angular: `lead-relate-edit.component.ts` set sessionStorage-flagg etter save
7. Angular: `lead-relate-detail.component.ts` sjekkar flagget i ngOnInit og kaller `window.location.reload()` for aa oppdatere subpanel
### Naar kontakt knyttes til lead — auto-sett contact_id paa Lead (SC-0024)
1. `SyncContactIdOnLeadHook::afterRelationshipAdd()` trigges
2. Sjekker: er `contact_id` tom paa leadet?
3. Hvis ja: setter `contact_id = denne kontaktens ID` → leadet faar "primaer kontakt"
4. Brukeren kan deretter endre primaer kontakt manuelt via "KONTAKTPERSON"-feltet
### Naar kontakt fjernes fra lead — nullstill contact_id (SC-0024)
1. `SyncContactIdOnLeadHook::afterRelationshipDelete()` trigges
2. Sjekker: er leadets `contact_id` lik denne kontaktens ID?
3. Hvis ja: nullstiller `contact_id` → leadet mister primaer kontakt
---
|
||||||
| Leads-modulen | suitecrm | knowledge | medium | CURRENT-leads-contacts-architecture.md | 100 | 2026-03-20 02:00:01 |
|
Body:
### Leads-modulen
```
public/legacy/custom/Extension/modules/Leads/Ext/
Vardefs/
leads_contacts_1_Leads.php ← link-felt for M2M
contact_relate_fields.php ← contact_name, contact_link (SC-0024)
last_name_not_required.php ← last_name required=false (SC-0024)
LogicHooks/
sync_lead_id_to_contact.php ← Hook-registrering (fra Lead-siden)
sync_contact_id_on_lead.php ← Hook-registrering: auto-sett contact_id (SC-0024)
public/legacy/custom/modules/Leads/
SyncLeadIdFromLeadHook.php ← PHP-klasse: setter lead_id paa Contact naar lenket fra Lead
SyncContactIdOnLeadHook.php ← PHP-klasse: setter contact_id paa Lead naar Contact lenkes (SC-0024)
metadata/subpanels/
default.php ← Custom subpanel: viser account_name istedenfor name (SC-0038)
```
### Angular Extension (magitekExt) — lead-relate komponenter (SC-0040)
```
extensions/magitekExt/app/src/fields/lead-relate/
lead-relate-edit.component.ts ← Sett sessionStorage-flagg naar lead_id endres
lead-relate-edit.component.html
lead-relate-detail.component.ts ← Sjekk flagg i ngOnInit, kall reload()
lead-relate-detail.component.html
lead-relate.module.ts ← Angular modul
```
Registrert i `extension.module.ts` for module='contacts', field='lead_name'.
SessionStorage-nøkkel: `lead_relate_reload` — flagget sett av edit, konsumert av detail.
### Relationship metadata
```
public/legacy/custom/metadata/
leads_contacts_1MetaData.php ← M2M relationship-definisjon + join-tabell schema
```
### Database
```sql
-- Join-tabell for many-to-many
leads_contacts_1_c (id, date_modified, deleted, leads_contacts_1leads_ida, leads_contacts_1contacts_idb)
-- FK paa contacts-tabellen for primaer lead
contacts.lead_id VARCHAR(36) NULL
-- Relasjoner registrert i relationships-tabellen (BEGGE maa finnes!)
-- 1. leads_contacts_1: lhs=Leads, rhs=Contacts, type=many-to-many
-- 2. leads_contacts_lead_id: lhs=Leads, rhs=Contacts, type=one-to-many, rhs_key=lead_id
```
---
|
||||||
| 3. `contact_leads` (CORE — one-to-many, GJENBRUKT av SC-0024) | suitecrm | knowledge | medium | CURRENT-leads-contacts-architecture.md | 100 | 2026-03-20 02:00:01 |
|
Body:
### 3. `contact_leads` (CORE — one-to-many, GJENBRUKT av SC-0024)
- **Type:** Innebygd SuiteCRM one-to-many via `leads.contact_id`
- **Opprinnelig bruk:** Lead-konvertering (intern SuiteCRM-funksjon)
- **Naa GJENBRUKT:** SC-0024 gjenbruker `leads.contact_id` for `contact_name` relate-felt (se system 4)
- **Link-felt paa Contact:** `leads` (core vardefs)
### 4. `leads.contact_id` (GJENBRUKT — primaer kontakt relate-felt, SC-0024)
- **Type:** Gjenbruk av core FK-kolonne `leads.contact_id` (CHAR(36))
- **Brukes til:** "KONTAKTPERSON"-feltet paa Lead-kortet (relate-felt som lenker til Contact)
- **Vardef:** `contact_name` (type=relate, module=Contacts, id_name=contact_id, link=contact_link)
- **Link-felt:** `contact_link` (relationship=contact_leads, link_type=one, side=right)
- **DB:** Gjenbruker eksisterende `leads.contact_id` — INGEN ny kolonne
- **DB-registrering:** `contact_leads` finnes allerede i `relationships`-tabellen (core)
- **Logic hook:** `SyncContactIdOnLeadHook` auto-setter `contact_id` naar Contact legges til via `leads_contacts_1` subpanel
- **Detailview:** Viser Contact-navn med klikkbar lenke til `#/contacts/record/{id}` — FUNGERER
- **Listview:** CONTACT_NAME lagt til i `$listViewDefs` men Angular ignorerer kolonnen (SC-GOTCHA-14). Workaround: bruk Choose Columns i UI
---
## Filkart — alle filer involvert
### Contacts-modulen (paa serveren /var/www/suitecrm/)
```
public/legacy/custom/Extension/modules/Contacts/Ext/
Vardefs/
lead_relate_fields.php ← lead_name (rname=account_name), lead_id (type=id). NB: ingen lead_link vardef
leads_contacts_1_Contacts.php ← link-felt for M2M (MED side => 'right'!)
Layoutdefs/
leads_subpanel_fix.php ← Override core 'leads' subpanel til aa bruke leads_contacts_1
LogicHooks/
sync_lead_id.php ← Hook-registrering (after_relationship_add/delete)
sync_lead_relate_to_m2m.php ← Hook-registrering for SyncLeadRelateToM2MHook (SC-0039)
Language/
nb_NO.custom_agent.php ← LBL_LEAD_NAME = "Navn paa lead"
public/legacy/custom/modules/Contacts/
SyncLeadIdHook.php ← PHP-klasse: synkroniser lead_id ved relationship-endring
SyncLeadRelateToM2MHook.php ← PHP-klasse: felt→M2M+contact_id fraa Contact-siden (SC-0039)
metadata/
detailviewdefs.php ← lead_name felt i layout (etter account_name)
editviewdefs.php ← lead_name felt i edit-layout
subpanels/
ForLeadsContacts1.php ← Custom subpanel-kolonner (med lead_name-kolonne)
```
|
||||||
| Forretningskontekst | suitecrm | knowledge | medium | CURRENT-leads-contacts-architecture.md | 100 | 2026-03-20 02:00:01 |
|
Body:
# Leads som Firmakort — Kontaktperson-arkitektur
**Version:** 1.3
**Date:** 2026-03-17 (added GOTCHA-L9 XHR anti-pattern, verified all hooks)
**Load:** Alltid les denne filen naar oppgaven involverer Leads, Contacts, eller relasjoner mellom dem.
---
## Forretningskontekst
Magitek driver B2B-salg. Leads brukes som **firmakort i tidlig fase** — identisk konsept som Account (Kunde), men for prospekter som ennaa ikke er kvalifisert. Hver Lead kan ha **flere kontaktpersoner** (Contacts), og hver Contact kan ha en **primaer Lead**.
Denne arkitekturen er en bevisst utvidelse av standard SuiteCRM, som kun stoetter een kontaktperson per Lead (via navn-feltet paa Lead-kortet).
---
## Relasjonsarkitektur — FIRE uavhengige systemer
### 1. `leads_contacts_1` (VAaR — many-to-many)
- **Type:** Many-to-many via join-tabell `leads_contacts_1_c`
- **Join keys:** `leads_contacts_1leads_ida` (Lead ID) + `leads_contacts_1contacts_idb` (Contact ID)
- **Opprettet:** Studio (2026-03-14)
- **Brukes til:** Koble flere kontaktpersoner til et Lead, og omvendt
- **Subpanel paa Lead:** "Kontakter" — viser alle koblede kontaktpersoner
- **Subpanel paa Contact:** "Leads" — viser alle koblede leads
- **Link-felt paa Contact:** `leads_contacts_1` (type=link, `side => 'right'` — KRITISK!)
- **Link-felt paa Lead:** `leads_contacts_1` (type=link)
### 2. `contacts.lead_id` (VAaR — primaer lead FK)
- **Type:** Direkte FK-kolonne i `contacts`-tabellen
- **Brukes til:** "NAVN PAa LEAD"-feltet paa kontaktkortet (relate-felt)
- **Vardef:** `lead_name` (type=relate, module=Leads, id_name=lead_id, rname=account_name, table=leads)
- **MERK:** Vardef har IKKJE `link`-attributt (verifisert 2026-03-17). Relate-feltet fungerer via `table` + `id_name`. `lead_link` vardef eksisterer IKKJE — dokumentasjonen i tidlegare versjoner var feil.
- **Relasjon:** `leads_contacts_lead_id` (one-to-many, Leads→Contacts via contacts.lead_id)
- **DB-registrering:** Finnes i `relationships`-tabellen (KRITISK for typeahead-soek!)
- **Logic hook (subpanel→felt):** Auto-setter `lead_id` naar kontakt knyttes til lead via `leads_contacts_1` subpanel
- **Logic hook (felt→subpanel):** `SyncLeadRelateToM2MHook` synker `lead_id`-endring til M2M join-tabell + setter `contact_id` paa Lead (SC-0039)
|
||||||
| Gotchas (Leads-spesifikke) | suitecrm | knowledge | medium | CURRENT-module-leads.md | 100 | 2026-03-20 02:00:01 |
|
Body:
## Gotchas (Leads-spesifikke)
### GOTCHA-L-02: Listview krever BEGGE variabel-blokker
Custom listview-filer MÅ inneholde både `$viewdefs` (SuiteCRM 8 sidebar/bulk actions) OG `$listViewDefs` (Legacy kolonne-definisjoner). Utelater du én, brekker layouten.
### GOTCHA-L-07: Composite address widget — alt-eller-ingenting
`'type' => 'address'` rendrer ALLE sub-felt som fieldset-gruppe. For enkeltfelt: bruk plain strenger i panels-arrayen uten composite type.
### GOTCHA-L-12: Custom `_c`-felt vises ikke i "Choose Columns" uten `$listViewDefs`-entry
Selv om et custom felt har vardef og er deployet, vises det IKKE i "Choose Columns" uten eksplisitt oppføring i `$listViewDefs['Leads']`.
```php
'FELTNAME_C' => [
'width' => '10%',
'label' => 'LBL_FELTNAME_C',
'default' => false,
],
```
### GOTCHA-L-13: `address_group_c` er en UI-container, ikke et data-felt
`address_group_c` har `'source' => 'non-db'` og er kun en Angular edit-komponent (AddressGroupComponent). Den rendrer ingenting i detailview. Bruk standard enkeltfelt for detailview-visning.
### GOTCHA-L-14: Relate-felt i `$listViewDefs` — Angular ignorerer og faller tilbake til `name`
`CONTACT_NAME` i listview faller tilbake til core `name`-feltet (first_name+last_name). Angular's `ListViewDefinitionHandler` mapper relate-kolonne til `name`. Workaround: brukeren velger contact_name via Choose Columns i UI.
**Status:** Uløst — SC-0024.
|
||||||
| SC-0037: Leads LBL_NAME + LBL_LIST_NAME → "Kundenavn" | suitecrm | knowledge | medium | CURRENT-module-leads.md | 100 | 2026-03-20 02:00:01 |
|
Body:
### SC-0037: Leads LBL_NAME + LBL_LIST_NAME → "Kundenavn"
- **Dato:** 2026-03-17
- **Fil:** `public/legacy/custom/Extension/modules/Leads/Ext/Language/nb_NO.custom_agent.php`
- **Endring:** LBL_NAME og LBL_LIST_NAME endret fra "OLD_kontakt" til "Kundenavn"
- **Hvorfor:** Leads er firmakort i B2B-arkitektur — feltet viser firmanavn og skal hete "Kundenavn". Erstatter midlertidig verdi fra SC-0023/SC-0024.
- **Løser:** SC-0023 (ULØST → LØST)
- **Erstatter:** SC-0020 (LBL_LIST_NAME "Kontaktperson" → "Kundenavn")
- **Oppgraderingssikker:** Ja — Extension language override
### SC-0038: Leads default subpanel override — account_name-kolonne
- **Dato:** 2026-03-17
- **Fil:** `public/legacy/custom/modules/Leads/metadata/subpanels/default.php`
- **Endring:** Custom subpanel-definisjon som viser `account_name` (firmanavn) istedenfor `name` (personnavn) i Leads-subpanelet.
- **Hvorfor:** Leads er firmakort — subpanelet (f.eks. på Contacts) skal vise firmanavn, ikke personnavn.
- **Oppgraderingssikker:** Ja — custom metadata override
### SC-0026: Leads detailviewdefs — FIRMANAVN som record-tittel
- **Dato:** 2026-03-16
- **Endring:** Global label `LBL_SUMMARY_LEAD_COMPANY = {{fields.account_name.value}}` opprettet, og `summaryTemplates` i Leads detailviewdefs oppdatert.
- **Hvorfor:** Leads viste kontaktpersonens navn som header — firmanavnet er mer meningsfullt i B2B.
- **Filer:**
- `custom/include/language/nb_NO.lang.php` — LBL_SUMMARY_LEAD_COMPANY
- `custom/include/language/en_us.lang.php` — LBL_SUMMARY_LEAD_COMPANY
- `custom/modules/Leads/metadata/detailviewdefs.php` — summaryTemplates
- **Mønster:** Se CURRENT-metadata-patterns.md seksjon 11
- **Oppgraderingssikker:** Ja — custom metadata override + global language override
---
## Filkart — alle custom filer for Leads på serveren
```
/var/www/suitecrm/public/legacy/custom/
├── modules/Leads/
│ ├── metadata/
│ │ ├── detailviewdefs.php ← Hoved-layout (SC-0006,11,13,14,15,16,17,23,24,26)
│ │ ├── editviewdefs.php ← Edit-layout (SC-0006,11,13,14,15,16,17,23,24)
│ │ ├── listviewdefs.php ← Liste-layout (SC-0001,21,24)
│ │ └── subpanels/
│ │ └── default.php ← account_name-kolonne i Leads-subpanel (SC-0038)
│ ├── SyncLeadIdFromLeadHook.php ← Logic hook (SC-0022)
│ └── SyncContactIdOnLeadHook.php ← Logic hook (SC-0024)
├── Extension/modules/Leads/Ext/
│ ├── Vardefs/
│ │ ├── notes_c.php ← SC-0005
│ │ ├── kommune_c.php ← SC-0006
│ │ ├── kontaktstrategi_c_vardef.php ← SC-0013
│ │ ├── address_group_c.php ← SC-0016
│ │ ├── contact_relate_fields.php ← SC-0024
│ │ └── last_name_not_required.php ← SC-0024
│ ├── Layoutdefs/
│ │ └── notes_subpanel.php ← SC-0004
│ ├── Language/
│ │ ├── nb_NO.custom_agent.php ← SC-0004,19,20,23,24,37
│ │ ├── en_us.custom_agent.php ← SC-0004
│ │ ├── nb_NO.kontaktstrategi_dropdown.php ← SC-0013
│ │ ├── en_us.kontaktstrategi_dropdown.php ← SC-0013
│ │ └── nb_NO.old_kontakt_label.php ← SC-0024
│ └── LogicHooks/
│ ├── sync_lead_id_to_contact.php ← SC-0022
│ └── sync_contact_id_on_lead.php ← SC-0024
└── include/language/
└── nb_NO.lang.php ← SC-0003 (lead_status_dom), SC-0026 (LBL_SUMMARY_LEAD_COMPANY)
```
---
|
||||||
| SC-0017: notater_c varchar-felt på Leads (ved siden av Avdeling) | suitecrm | knowledge | info | CURRENT-module-leads.md | 100 | 2026-03-20 02:00:01 |
|
Body:
### SC-0017: notater_c varchar-felt på Leads (ved siden av Avdeling)
- **Dato:** 2026-03-16
- **Filer:**
- Vardef via MCP create_custom_field
- `public/legacy/custom/modules/Leads/metadata/detailviewdefs.php` (rad 3: department | notater_c)
- `public/legacy/custom/modules/Leads/metadata/editviewdefs.php`
- DB: `leads_cstm.notater_c` VARCHAR(255)
- Label: LBL_NOTATER_C = "Notater" (nb_NO)
- **Kontekst:** notes_c (SC-0005) er et textarea. notater_c er et kort enlinjes felt.
- **Oppgraderingssikker:** Ja — custom field + custom metadata
### SC-0019: LBL_NOTES_C label endret fra "Notater" til "Tilleggsinfo"
- **Dato:** 2026-03-16
- **Filer:**
- `public/legacy/custom/Extension/modules/Leads/Ext/Language/nb_NO.custom_agent.php`
- **Endring:** Label for notes_c endret for å skille det fra notater_c varchar-feltet.
- **Oppgraderingssikker:** Ja — Extension language override
### SC-0020: LBL_LIST_NAME i Leads listview — label endret til "Kontaktperson" (ERSTATTET av SC-0037)
- **Dato:** 2026-03-16
- **Endring:** `set_label(key="LBL_LIST_NAME", value="Kontaktperson", module="Leads", language="nb_NO")`
- **Erstattet:** SC-0037 endret LBL_LIST_NAME til "Kundenavn" (2026-03-17)
- **Oppgraderingssikker:** Ja — Extension language override (lag 4)
### SC-0021: Leads listview — custom _c-felt lagt til kolonnevalg
- **Dato:** 2026-03-16
- **Fil:** `public/legacy/custom/modules/Leads/metadata/listviewdefs.php`
- **Endring:** kontaktstrategi_c, kommune_c lagt til `$listViewDefs['Leads']` med `'default' => false`.
- **Bakgrunn:** Custom felt er usynlig i Choose Columns uten eksplisitt oppføring. Se GOTCHA-L-12.
- **Oppgraderingssikker:** Ja — custom metadata override
### SC-0023: LBL_NAME i Leads — label endret (LØST via SC-0037)
- **Dato:** 2026-03-16
- **Opprinnelig:** `set_label(key="LBL_NAME", value="Kontaktperson")`
- **Oppdatert av SC-0024:** LBL_NAME og LBL_LIST_NAME satt til "OLD_kontakt" i nb_NO.custom_agent.php
- **LØST av SC-0037:** Begge labels endret til "Kundenavn" (2026-03-17)
- **Oppgraderingssikker:** Ja — Extension language override
|
||||||
| SC-0013: Kontaktstrategi dropdown-felt på Leads | suitecrm | knowledge | medium | CURRENT-module-leads.md | 100 | 2026-03-20 02:00:01 |
|
Body:
### SC-0013: Kontaktstrategi dropdown-felt på Leads
- **Dato:** 2026-03-16
- **Filer:**
- `public/legacy/custom/Extension/modules/Leads/Ext/Vardefs/kontaktstrategi_c_vardef.php`
- `public/legacy/custom/Extension/modules/Leads/Ext/Language/nb_NO.kontaktstrategi_dropdown.php`
- `public/legacy/custom/Extension/modules/Leads/Ext/Language/en_us.kontaktstrategi_dropdown.php`
- `public/legacy/custom/modules/Leads/metadata/detailviewdefs.php`
- `public/legacy/custom/modules/Leads/metadata/editviewdefs.php`
- DB: `leads_cstm.kontaktstrategi_c` VARCHAR(100)
- **Dropdown-verdier:** `'' => ''`, `'Kun E-post'`, `'Mulig besøk'`, `'Tlf'`
- **Deploy-metode:** MCP `deploy_extension_file(ext_type="Language")` + `rebuild_extensions` + `cache_clear`
- **Viktig:** `$app_list_strings` MÅ ligge i Ext/Language-fil (lag 4), IKKE i vardef-filen.
- **Oppgraderingssikker:** Ja — custom field + Extension Language + custom metadata
### SC-0014: account_name øverst i Leads editview + label FIRMANAVN
- **Dato:** 2026-03-16
- **Filer:**
- `public/legacy/custom/modules/Leads/metadata/detailviewdefs.php`
- `public/legacy/custom/modules/Leads/metadata/editviewdefs.php`
- MCP `set_label(key="LBL_ACCOUNT_NAME", value="Firmanavn", module="Leads", language="nb_NO")`
- **Endring:** account_name er nå første felt i layout, over navn-feltet. Label endret til "Firmanavn".
- **Oppgraderingssikker:** Ja — custom metadata + custom label
### SC-0015: Postnr+poststed inline layout (Leads editview/detailview)
- **Dato:** 2026-03-16
- **Masterplan:** MP-0002 (metadata + CSS), MP-0003 (Angular AddressGroupComponent)
- **Filer:**
- `public/legacy/custom/modules/Leads/metadata/detailviewdefs.php`
- `public/legacy/custom/modules/Leads/metadata/editviewdefs.php`
- `public/dist/custom-overrides.css` (SC-0015 CSS)
- `public/dist/index.html` (cache-buster)
- **Endring:** Postnr og poststed vises inline på samme rad via metadata `headerColumnClass`/`valueColumnClass` + CSS.
- **Begrensninger:** Visuelt ok ved ~1400px, men IKKE viewport-uavhengig. Full løsning: SC-0016.
- **Oppgraderingssikker:** Ja — custom metadata + custom-overrides.css
|
||||||
| SC-0005: Leads — inline notes_c custom field | suitecrm | knowledge | info | CURRENT-module-leads.md | 100 | 2026-03-20 02:00:01 |
|
Body:
### SC-0005: Leads — inline notes_c custom field
- **Dato:** 2026-03-15
- **Filer:**
- `public/legacy/custom/Extension/modules/Leads/Ext/Vardefs/notes_c.php`
- `public/legacy/custom/modules/Leads/Ext/Vardefs/vardefs.ext.php` (kompilert)
- `public/legacy/custom/modules/Leads/metadata/editviewdefs.php`
- `public/legacy/custom/modules/Leads/metadata/detailviewdefs.php`
- DB: `leads_cstm.notes_c TEXT`
- **Endring:** Inline textarea for raske notater direkte i lead create/edit-skjemaet.
- **Plassering:** Under BESKRIVELSE-feltet i "Informasjon om leaden"-fanen
- **Label:** LBL_NOTES_C = "Tilleggsinfo" (nb_NO) — se SC-0019
- **Oppgraderingssikker:** Ja — custom Extension vardef + custom metadata-filer
### SC-0006: Leads — enkel adresseblokk + kommune_c custom field
- **Dato:** 2026-03-15
- **Filer:**
- `public/legacy/custom/modules/Leads/metadata/detailviewdefs.php`
- `public/legacy/custom/modules/Leads/metadata/editviewdefs.php`
- `public/legacy/custom/Extension/modules/Leads/Ext/Vardefs/kommune_c.php`
- `public/legacy/custom/Extension/modules/Leads/Ext/Language/nb_NO.lang.ext.php` (LBL_KOMMUNE_C = 'Kommune')
- DB: `leads_cstm.kommune_c` VARCHAR(100)
- **Endring:** Fjernet composite address-widgets. Erstattet med enkeltfelt: gatenavn, postnr, poststed, land + kommune_c. Alt_address-felt fjernet.
- **Auto-populering (SC-0041):** kommune_c fylles automatisk via postnummer-oppslag (statisk JSON i magitekExt). Fylke lagres i `primary_address_state` (standard SuiteCRM-felt, ikke custom).
- **Oppgraderingssikker:** Ja — custom metadata + Extension vardef
### SC-0011: Leads display/required logic (metadata)
- **Dato:** 2026-03-15
- **Filer:** Deployet via MCP til Leads detailviewdefs/editviewdefs
- **Endring:**
- `displayLogic` på `description`: skjul når status=Dead
- `requiredLogic` på `phone_work`: påkrevd når status=In_Dialog
- `fieldActions` på `primary_address_street`: copy-address-knapp (ProcessHandler: CopyAddressHandler)
- **Backend:** `extensions/magitekExt/modules/Leads/Service/CopyAddressHandler.php`
- **Oppgraderingssikker:** Ja — custom metadata + extension ProcessHandler
|
||||||
| Aktive tilpasninger | suitecrm | knowledge | low | CURRENT-module-leads.md | 100 | 2026-03-20 02:00:01 |
|
Body:
# Leads — Tilpasninger
**Siste SC-ID:** SC-0038
**Antall endringer:** 17
**Dato:** 2026-03-17
**Load:** Når oppgaven involverer Leads-modulen (felt, metadata, layout, labels, subpaneler)
> Se også: `CURRENT-leads-contacts-architecture.md` for Leads↔Contacts relasjon (SC-0022, SC-0024)
---
## Aktive tilpasninger
### SC-0001: Leads listview — klikkbar kundenavn-kolonne
- **Dato:** 2026-03-14
- **Fil:** `public/legacy/custom/modules/Leads/metadata/listviewdefs.php`
- **Endring:** Lagt til `'link' => true` på `ACCOUNT_NAME`-feltet
- **Hvorfor:** Bruker ønsket at "Navn på Kunde"-kolonnen skal være klikkbar (rød link) som "Navn"-kolonnen, og lenke til Lead-detaljvisningen
- **Oppgraderingssikker:** Ja — custom metadata override
### SC-0003: Leads status-dropdown — ny salgspipeline
- **Dato:** 2026-03-15
- **Fil:** `public/legacy/custom/include/language/nb_NO.lang.php` (app_list_strings → lead_status_dom)
- **Endring:** Erstattet standard SuiteCRM lead-statuser med salgsfokusert pipeline
- **Gamle verdier:** New, Assigned, In Process, Converted, Recycled, Dead
- **Nye verdier:**
| Key | nb_NO |
|-----|-------|
| New | Ny |
| Contacted | Kontaktet |
| Contacted_Low_Interest | Kontaktet, lite interessert |
| In_Dialog | I dialog |
| Converted | Konvertert til kunde |
| On_Hold | Avventende - vanskelig å nå |
| Dead | Tapt lead |
- **Hvorfor:** Standard-statusene handler om intern prosessflyt. Nye verdier reflekterer salgstemperatur og engasjement.
- **Pipeline:** Ny → Kontaktet → I dialog → Konvertert til kunde (happy path)
- **Oppgraderingssikker:** Ja — custom language override
### SC-0004: Leads — dedikert Notes-subpanel
- **Dato:** 2026-03-15
- **Filer:**
- `public/legacy/custom/Extension/modules/Leads/Ext/Layoutdefs/notes_subpanel.php`
- `public/legacy/custom/Extension/modules/Leads/Ext/Language/nb_NO.custom_agent.php`
- `public/legacy/custom/Extension/modules/Leads/Ext/Language/en_us.custom_agent.php`
- **Endring:** Eget Notes-subpanel på Lead record view (order 15, over Activities). Create + Select-knapper.
- **Hvorfor:** Notes fantes kun innbakt i History-collection (blandet med møter, samtaler, e-post).
- **Label:** LBL_NOTES_SUBPANEL_TITLE = "Notater" (nb_NO) / "Notes" (en_us)
- **Oppgraderingssikker:** Ja — Extension framework (Layoutdefs + Language)
- **MERK:** Subpanelet er deployet og kompilert, men ikke synlig i UI per 2026-03-15. Krever Quick Repair & Rebuild via Admin UI.
|
||||||
| Navbar DOM-struktur (etter SC-0034 statisk navbar) | suitecrm | knowledge | high | CURRENT-angular-css.md | 100 | 2026-03-20 02:00:01 |
|
Body:
## Navbar DOM-struktur (etter SC-0034 statisk navbar)
Statisk navbar bruker `[class.active]` binding paa `*ngFor`-items istedenfor separat rendering av `navbar.current`:
```html
<!-- Statisk (SC-0034): Alle moduler i naturlig rekkefoelje, aktiv markert med CSS-klasse -->
<li *ngFor="let item of navbar.menu; let i = index"
class="top-nav nav-item dropdown non-grouped"
[class.active]="navbar.current && item.module === navbar.current.module">
<scrm-menu-item [item]="item" [index]="i+1"></scrm-menu-item>
</li>
```
**Viktig:** `.active`-klassen eksisterer allerede i SuiteCRM SCSS. Ingen ekstra CSS trengs for standard highlighting.
**Mobile:** Uendret — viser kun `navbar.current` (aktiv modul) i top-bar, resten i sidebar. Dette er tilsiktet design.
---
## Fallgruver
1. **`col-lg-3` er prosent-basert** -- 25% av col-bredde, IKKE av rad eller panel. Hvis du endrer col-bredde, endres label-bredde.
2. **`flex-wrap: wrap` er default** paa `form-group.row` -- uten `nowrap` stacker label og input vertikalt naar col er for smal.
3. **Browser-caching** -- custom-overrides.css har ingen hash. Bruk `?v=` parameter i index.html etter endring.
4. **Extension SCSS gir falsk trygghet** -- `deploy_css` MCP skriver til extension SCSS som ALDRI treffer host-DOM.
5. **`!important` er noedvendig** -- Bootstrap og Angular inline-styles har hoey specificity.
6. **MCP `deploy_css` mode='replace' bug** -- Returnerer file_size: 0, filen blir tom. Bruk scp/tee i stedet.
7. **NOPASSWD sudo konfigurert** -- bruk `sudo -n` for alle sudo-kommandoer. For `public/dist/` har www-data skrivetilgang — vanlig `tee` fungerer ogsaa.
8. **`headerColumnClass`/`valueColumnClass` maa nestes under `metadata` key** -- top-level ignoreres stille. Se SC-GOTCHA-11.
9. **`labelDisplay => 'none'` under `metadata` key** -- skjuler label nativt i field-layout template. Nyttig for inline-felt.
|
||||||
| CSS Custom Properties for Theming (SC-0033) | suitecrm | knowledge | medium | CURRENT-angular-css.md | 100 | 2026-03-20 02:00:01 |
|
Body:
## CSS Custom Properties for Theming (SC-0033)
custom-overrides.css bruker CSS custom properties for multi-tema stoette:
```css
/* Default-verdier (Nordic) */
:root {
--nav-bg: #374151;
--nav-text: #F9FAFB;
--link-color: #4a6fa5;
/* ... alle variabler, se CURRENT-theme-system.md seksjon 2 */
}
/* Tema-spesifikke overrides */
[data-theme="hubspot"] {
--nav-bg: #2D3E50;
--nav-text: #C1D6E6;
/* ... */
}
/* Bruk variabler i regler */
nav.navbar {
background-color: var(--nav-bg) !important;
color: var(--nav-text) !important;
}
```
### Viktige regler for CSS custom properties
- `:root` definerer default-verdier (Nordic-tema)
- `[data-theme="X"]` paa `<html>` overstyrer variabler per tema
- `var(--variabel)` brukes i selektorer — ALDRI hardkodede hex-verdier for tema-elementer
- Legacy iframe har IKKE tilgang til CSS custom properties — bruker hardkodede verdier i colourSelector.php
- Komplett variabel-referanse: CURRENT-theme-system.md seksjon 2
---
## Deploy-metode for custom-overrides.css
**Anbefalt:** `scp` eller `ssh suitecrm "tee"` direkte til `public/dist/custom-overrides.css`.
**ADVARSEL:** MCP `deploy_css` med `mode='replace'` har en bug -- returnerer `file_size: 0` og toemmer filen. Bruk IKKE denne metoden for globale CSS-overrides. MCP `deploy_css` uten mode fungerer for extension SCSS (som uansett er scoped og ikke treffer host-DOM).
**Etter deploy:** Oppdater cache-buster i `public/dist/index.html`: `?v=YYYYMMDD-tag`
## Bootstrap Grid-begrensninger for felt-alignment
Bootstrap 4 `col-lg-*` er prosent-basert (`col-lg-3` = 25%). Dette gjoer det UMULIG aa oppnaa konsistent vertikal alignment naar:
- En rad har annen flex-kontekst enn andre rader (f.eks. inline-felt vs standard 2-kolonne)
- Fast px-bredde (f.eks. 168px label) fungerer KUN ved en spesifikk viewport-bredde
- 6+ CSS-iterasjoner under SC-0015 bekreftet at CSS-only + metadata `headerColumnClass` IKKE gir viewport-uavhengig alignment
**Loesningsalternativer:**
1. **Metadata `headerColumnClass`/`valueColumnClass` + CSS** -- funksjonelt men ikke pixel-perfekt responsivt (MP-0002)
2. **Angular AddressGroupComponent** -- CSS Grid med full frihet, ingen Bootstrap-avhengighet (MP-0003, planlagt)
|
||||||
| DOM-struktur i Record View (editview/detailview) | suitecrm | knowledge | medium | CURRENT-angular-css.md | 100 | 2026-03-20 02:00:01 |
|
Body:
## DOM-struktur i Record View (editview/detailview)
```html
<form class="field-layout edit"> <!-- hele panelet -->
<div class="field-layout-row form-row"> <!-- en rad (2 kolonner) -->
<div class="field-layout-col col form-group"> <!-- venstre kolonne -->
<div class="field-layout-field-group-wrapper form-group row"> <!-- label+input wrapper -->
<div class="col-lg-3 field-layout-field-label-wrapper"> <!-- label (25% av col) -->
<div class="col-lg-9 field-layout-field-wrapper"> <!-- input (75% av col) -->
<scrm-field class="field-name-{fieldname} field-type-{type}">
</div>
</div>
<div class="field-layout-col col form-group"> <!-- hoeyre kolonne -->
...
</div>
</div>
</form>
```
### Viktige CSS-klasser
| Klasse | Element | Notat |
|--------|---------|-------|
| `.field-layout-row` | Rad | `display: flex`, `form-row` |
| `.field-layout-col` | Kolonne | `.col` = `flex: 1 1 0%` (50/50) |
| `.field-column-bordered` | Venstre col (naar 2 cols) | Har border-right |
| `.field-layout-field-group-wrapper` | Label+input container | `.form-group.row` |
| `.col-lg-3` | Label wrapper | 25% av col-bredde |
| `.col-lg-9` | Input wrapper | 75% av col-bredde |
| `.field-name-{name}` | scrm-field element | Satt av Angular Field component |
| `.full-row` | Naar kun 1 col i raden | Full bredde |
### Breddeberegninger (1920px viewport)
- Panel/form bredde: ~1860px
- Standard rad: ~1860px (full bredde)
- Standard col: ~930px (50%)
- Standard label (col-lg-3): ~233px (25% av 930)
- Standard input (col-lg-9): ~697px (75% av 930)
- Input left position: ~260px fra venstre
## CSS-moenstre: To felt paa samme rad i venstre kolonne
Brukes naar to felt (f.eks. postnr + poststed) skal vises inline i venstre halvdel.
```css
/* Raden: halv bredde, ingen wrapping */
.field-layout-row:has(.field-name-FELT1) {
max-width: 50% !important;
flex-wrap: nowrap !important;
}
/* Foerste col: auto-bredde, fast label-bredde for alignment */
.field-layout-row:has(.field-name-FELT1) > .field-column-bordered {
flex: 0 0 auto !important;
width: auto !important;
max-width: none !important;
}
/* Foerste col label: fast bredde = standard label (233px) */
.field-layout-row:has(.field-name-FELT1) > .field-column-bordered .field-layout-field-label-wrapper {
flex: 0 0 233px !important;
max-width: 233px !important;
}
/* Foerste col: horisontal label+input */
.field-layout-row:has(.field-name-FELT1) > .field-column-bordered .field-layout-field-group-wrapper {
flex-wrap: nowrap !important;
align-items: center !important;
}
/* Andre col: tar resten */
.field-layout-row:has(.field-name-FELT1) > .field-layout-col:has(.field-name-FELT2) {
flex: 1 1 auto !important;
max-width: none !important;
min-width: 0 !important;
}
/* Andre col: kompakt label + flex input */
.field-layout-row:has(.field-name-FELT1) > .field-layout-col:has(.field-name-FELT2) .field-layout-field-label-wrapper {
flex: 0 0 auto !important;
white-space: nowrap !important;
padding-right: 8px !important;
}
.field-layout-row:has(.field-name-FELT1) > .field-layout-col:has(.field-name-FELT2) .field-layout-field-group-wrapper {
flex-wrap: nowrap !important;
align-items: center !important;
}
.field-layout-row:has(.field-name-FELT1) > .field-layout-col:has(.field-name-FELT2) .field-layout-field-wrapper {
flex: 1 1 auto !important;
max-width: none !important;
}
```
|
||||||
| Arkitektur | suitecrm | knowledge | medium | CURRENT-angular-css.md | 100 | 2026-03-20 02:00:01 |
|
Body:
# SuiteCRM 8 Angular Frontend CSS -- Ekspertfil
> Versjon: 1.2 | Sist oppdatert: 2026-03-17
> Kilde: Audit av SC-0015 (postnr+poststed layout), bug-crusher Opus-sesjon, SC-0033 multi-tema system, SC-0034 statisk navbar
## Arkitektur
SuiteCRM 8 bruker Angular med Module Federation for extensions.
### CSS-lag (prioritetsrekkefoelje)
1. **Angular theme SCSS** -- `core/app/shell/src/themes/suite8/` (ALDRI endre)
2. **Bootstrap 4** -- Grid-system, `.col`, `.form-row`, `.col-lg-*`
3. **Extension SCSS** -- `extensions/{ext}/app/src/styles.scss` (SCOPED -- kun egne komponenter)
4. **custom-overrides.css** -- `public/dist/custom-overrides.css` (GLOBAL -- treffer alt)
### KRITISK: Extension SCSS scoping
Extension SCSS kompileres til `styles.<hash>.css` i extension-bundelen, men:
- Module Federation laster KUN JS-chunks fra extensions, IKKE CSS fra extension index.html
- Extension CSS treffer ALDRI host-appens DOM-elementer
- `:has()`, `.field-layout-row`, `.field-name-*`, `scrm-field` -- INGEN av disse treffer fra extension SCSS
**ENESTE mekanisme for globale CSS-overrides: `custom-overrides.css`**
### custom-overrides.css
- Injisert i `public/dist/index.html` via `<link>` tag
- Overskrives ved SuiteCRM-oppgradering (recovery via `inject-custom-css.sh`)
- Har INGEN automatisk cache-busting -- bruk `?v=YYYYMMDD-tag` parameter
- Inneholder CSS custom properties for multi-tema stoette (`[data-theme]` selektorer, SC-0033)
- Lokal kopi: `coordination/themes/custom-overrides.css`
- Deploy via: `deploy_css` MCP-tool (target='global') eller SSH heredoc — se CURRENT-theme-system.md for full prosedyre
## Frontend Internals
### Angular HttpClient bruker XMLHttpRequest, IKKE fetch
SuiteCRM 8 Angular bruker Angular HttpClient (via Apollo GraphQL) som er basert paa
`XMLHttpRequest`, IKKE native `window.fetch`. Monkey-patching av `window.fetch` fanger
INGEN GraphQL-kall.
`XMLHttpRequest.prototype.send`-intercept treffer, men er risikabelt — matcher ogsaa
ikke-relaterte kall som `batchedStatistics`, `createProcess` (user preferences), og
annen bakgrunnstrafikk. Filtrering paa request body er upaalitelig fordi modulnavn
("Leads", "Contacts") forekommer i mange ikke-relaterte kall.
**Konklusjon:** Frontend-intercept av GraphQL-kall fra extension-kode er IKKE en
levedyktig tilnaerming for aa detektere subpanel-endringer. Bruk heller
sessionStorage-flagg satt av custom komponenter, eller arkitektonisk loesning via `/architect`.
**Sources:** SC-0035 debug-sesjon
---
|
||||||
| suitecrm-specialist commit/push permissions | _claude/agent-system | knowledge | info | CURRENT.md | 100 | 2026-03-20 02:00:01 |
|
Body:
### suitecrm-specialist commit/push permissions
- Commit in suitecrm-dev workspace: directly via Bash tool (git add, git commit, git push)
- Commit in another workspace: delegate to `git-coordinator` subagent
- Must NEVER refuse commit/push with "outside scope" when working in own workspace
### General scope-guard pattern
When a task falls outside an agent's scope, the agent must:
1. Identify which command/agent is correct for the task
2. Inform the user with the specific `/command` to use
3. NOT attempt a partial solution
|
||||||
| How agents use it | _claude/agent-system | knowledge | medium | CURRENT.md | 100 | 2026-03-20 02:00:01 |
|
Body:
### How agents use it
1. Agent reads **indeks** first (Steg 1a) — finds which domain file to load
2. Agent reads **only the relevant domain file** — saves ~400 lines of context
3. Self-learning writes to the **correct domain file** + updates one line in the indeks
### Scaling
New modules: create `CURRENT-module-{module}.md` + add one row in indeks SC-register + modul-oversikt table.
### Agent parts updated
- `02-knowledge-gate.md` — oppgave-router points to domain files per task type
- `03-state-check.md` — Steg 1 reads indeks + relevant domain file
- `12-self-learning.md` — writes to correct domain file + updates indeks
All 7 SuiteCRM agents rebuilt after these changes.
### Known issue: KB FTS5 hyphen bug
KB queries with hyphens crash: `kb query "SC-GOTCHA"` gives `PDOException: no such column: GOTCHA` because FTS5 interprets `-` as MINUS operator. Workaround: avoid hyphens in KB queries (use `SC GOTCHA` or `SC0034`).
---
## Known Anti-Patterns (Ongoing)
### Playwright screenshots in project root (Fixed 2026-03-16)
**Problem:** Playwright screenshots saved directly to `/var/www/suitecrm-dev/` pollute the git working tree with untracked `.png` files.
**Fix:** `suitecrm-specialist.md` rule 9b requires all screenshots go to `/var/www/suitecrm-dev/tmp/screenshots/` (gitignored). Agents must `mkdir -p` the directory before saving. Applies to all agents that use Playwright MCP in this workspace.
**Affected agents:** `suitecrm-specialist.md`, and any agent using Playwright screenshots.
---
## Scope Guards (Established 2026-03-16)
### agent-ops / agent-ops-suitecrm scope
- **IN scope:** Agent files, prompts, command files, knowledge files, expert files, coordination docs
- **OUT of scope:** Application code, MCP server code (Python), SuiteCRM customizations
- MCP bugs discovered during audit -> always delegate to `mcp-server-builder`, never fix directly
- **CAN spawn subagents directly:** `mcp-server-builder`, `magitek-server-infra-ops`, `magitek-proxmox-maintenance`, `security-auditor`, `bug-crusher`, `git-coordinator` -- these are globally available regardless of workspace
|
||||||
| Build command | _claude/agent-system | knowledge | medium | CURRENT.md | 100 | 2026-03-20 02:00:01 |
|
Body:
### Build command
```bash
~/.claude/agents/build-agent.sh suitecrm all # Rebuild all 7 variants
~/.claude/agents/build-agent.sh suitecrm specialist # Rebuild one variant
```
Each built agent = variant core + all 12 shared parts injected. Agents went from 270-330 lines to 660-730 lines.
**Manuelt vedlikeholdte pipeline-filer:** Disse filene er IKKE auto-generert fra parts og må fikses manuelt ved infrastruktur-endringer: `review-masterplan.md`, `patch-revise-masterplan.md`, `implement-masterplan.md`, `includes/pipeline-safety-rules.md`. `build-agent.sh suitecrm all` oppdaterer IKKE disse.
### Self-Learning Protocol (shared/12-self-learning.md)
Agents now self-report after deploys:
- Update the correct **domain file** (e.g., `CURRENT-module-leads.md`) with SC-XXXX entries
- Update `CURRENT-customizations.md` **indeks** with one line in the SC-register table
- Log gotchas to `coordination/maintenance/gotcha-log.md`
- Log patterns to `coordination/maintenance/patterns-log.md`
- Expert-training agent reviews and integrates into knowledge files
### Impact on delegation map
The delegation map above remains correct — commands still delegate to the same agent files. The difference is those agent files are now **generated** from parts, not hand-maintained. Direct edits to generated agents will be overwritten by the next `build-agent.sh suitecrm all`.
---
## Domain File Split for Customization Registry (Established 2026-03-17)
### Problem
`CURRENT-customizations.md` grew to 603 lines — every agent had to load the entire monolith even when working on a single module. Token waste and context pollution.
### Solution
Split into an **indeks** (~120 lines) + **7 domain files**:
```
CURRENT-customizations.md ← INDEKS (SC-register + routing table)
CURRENT-module-leads.md ← SC-0001,03-06,11,13-15,17,19-21,23,26 (15 entries)
CURRENT-module-contacts.md ← SC-0025,28,31,32 (4 entries)
CURRENT-module-accounts.md ← SC-0012 (1 entry)
CURRENT-leads-contacts-architecture.md ← SC-0022,24 (existed before split)
CURRENT-extension-magitekext.md ← SC-0008-10,16,18,27,28,32,34 (8 entries)
CURRENT-theme-system.md ← SC-0007,29,30,33,34 (existed before split)
CURRENT-translations-global.md ← SC-0002,03 (2 entries, global)
```
|
||||||
| Agent-ops fixes made during MP-0004 session | _claude/agent-system | knowledge | info | CURRENT.md | 100 | 2026-03-20 02:00:01 |
|
Body:
### Agent-ops fixes made during MP-0004 session
| Fix | Target | Description |
|-----|--------|-------------|
| Coordination-monitor graceful | `~/.claude/commands/orchestrate.md` | Script absence no longer causes error |
| Subagent prompt checklist | `~/.claude/commands/orchestrate.md` | KB + schema + rapport checklist mandatory |
| Git push session-end | `~/.claude/commands/orchestrate.md` | Added to session-end checklist |
| Memory: push after commit | `feedback_push_after_commit.md` | New memory file |
| Memory: uncommitted side-effects | `feedback_uncommitted_sideeffects.md` | New memory file |
### MCP server fixes (mcp-server-builder, external repo)
These were done in `/var/www/mcp-servers/` (pushed as `5ca630b`):
1. Fixed mutable default `dict = {}` to `dict | None = None` in `run_graphql_query`
2. Extended `get_module_metadata`/`get_app_metadata` field selection via live schema introspection
3. Both fixes address audit advisories A-001 and A-002
---
## Modular Agent Build System for SuiteCRM (Established 2026-03-17)
### Architecture
All SuiteCRM agent variants are now built from modular parts:
```
~/.claude/agents/parts/suitecrm/
├── shared/ # 12 shared knowledge modules (~400 lines total)
│ ├── 01-version-warning.md # SuiteCRM 8 ≠ 7 ≠ SugarCRM
│ ├── 02-knowledge-gate.md # Steg 0 hard gate
│ ├── 03-state-check.md # Steg 1 existing state check
│ ├── 04-mcp-first.md # MCP-first policy + decision tree
│ ├── 05-instance-info.md # Server info, paths, SSH
│ ├── 06-ssh-safety.md # File size checks, SCP pattern
│ ├── 07-cache-clearing.md # rebuild_extensions vs cache:clear
│ ├── 08-anti-patterns.md # 11+ known anti-patterns
│ ├── 09-escalation.md # 3-attempt rule, when to stop
│ ├── 10-mcp-gap-logging.md # Log MCP gaps to feedback file
│ ├── 11-screenshots.md # Screenshots to tmp/screenshots/
│ └── 12-self-learning.md # Update customizations.md + maintenance logs
├── specialist/core.md → suitecrm-specialist.md
├── bug-crusher/core.md → bug-crusher-suitecrm.md
├── ui-redesign/core.md → ui-redesign-suitecrm.md
├── ui-designer/core.md → ui-designer-suitecrm.md
├── explore/core.md → explore-suitecrm.md
├── architect/core.md → architect-suitecrm.md
└── implement-masterplan/core.md → implement-masterplan-suitecrm.md
```
|
||||||
| suitecrm-specialist Changes (E-01, E-02) | _claude/agent-system | knowledge | low | CURRENT.md | 100 | 2026-03-20 02:00:01 |
|
Body:
### suitecrm-specialist Changes (E-01, E-02)
Changes made to `~/.claude/agents/suitecrm-specialist.md`:
| Change | What | Why |
|--------|------|-----|
| **E-01** | Obligatory `## Rapport` section with task_id, status, files_deployed, errors | Prevents FM-03 (incomplete reports) |
| **E-02** | Knowledge files split: 5 KJERNE (always load) + 3 OPT-IN (load on demand) | Prevents FM-01 (context overflow); orchestrator can activate compact mode |
### Cross-reference
- Orchestrator command file: `.claude/commands/orchestrate.md` (F-01 to F-05 inline)
- Specialist agent file: `~/.claude/agents/suitecrm-specialist.md` (E-01, E-02 inline)
- Expert file: `coordination/experts/suitecrm/CURRENT-orchestration.md` (full reference)
---
## Orchestrator Learnings (MP-0004, 2026-03-16)
Full documentation: `coordination/experts/suitecrm/CURRENT-orchestration.md` v1.1
### 2 New Orchestrator Rules (F-06, F-07)
Added after MP-0004 orchestration session revealed subagent inefficiency and shallow API integration:
| Rule | Problem | Fix |
|------|---------|-----|
| **F-06** | Steg 1 agent used 206 tool calls / 139k tokens for one file — trial-and-error without KB | Mandatory KB-query + expert file read before implementation |
| **F-07** | Steg 2-4 agent wrote thin queries (3 scalars) for metadata tools despite docstrings promising full data | Mandatory schema introspection before writing API code |
### Subagent Prompt Checklist (New)
Every implementation delegation prompt now MUST include a 4-item checklist:
1. KB-query before implementation
2. Read expert files pointed to by KB
3. Schema introspection for API work
4. Mandatory rapport format (F-01)
### Session-End Checklist (New)
Before ending orchestration session:
1. Git push in ALL repos where commits were made (not just primary workspace)
2. Report uncommitted side-effects to user (subagent-modified files excluded from commits)
3. Move completed masterplans
4. Flag expert file updates needed
### 4 New Failure Modes (FM-06 to FM-09)
| FM | Problem | Prevention |
|----|---------|------------|
| **FM-06** | Trial-and-error implementation (200+ tool calls, no KB) | F-06 KB-query rule |
| **FM-07** | Thin API queries (docstring-implementation mismatch) | F-07 schema introspection |
| **FM-08** | Unpushed commits in external repos | Session-end checklist |
| **FM-09** | Silent uncommitted side-effects from subagents | Session-end checklist |
|
||||||
| Post-Session Audit Commands (Added 2026-03-16) | _claude/agent-system | knowledge | low | CURRENT.md | 100 | 2026-03-20 02:00:01 |
|
Body:
## Post-Session Audit Commands (Added 2026-03-16)
Three global commands for reviewing completed agent sessions. Located in `~/.claude/commands/`.
| Command | Purpose | When to run |
|---------|---------|-------------|
| `/prompt-agent-chat-meta-audit` | Instruks-etterlevelse, ressursbruk, MCP-utnyttelse, tokenokonomi | Always after a session |
| `/prompt-agent-chat-mcp-audit` | MCP bugs, mangler, workarounds, utviklingsmuligheter | When session had MCP activity |
| `/prompt-agent-chat-knowledge-harvest` | Ekstraher kunnskap til ekspertfiler og memories | After complex sessions |
**Recommended order:** meta-audit (1) -> mcp-audit (2) -> knowledge-harvest (3).
### Key design decisions
- **meta-audit** outputs both a human-readable summary and a structured LLM section
- **meta-audit** produces a three-part action plan: agent-ops scope / subagent-delegatable / new session needed
- **mcp-audit** updates `coordination/feedback/suitecrm-mcp-gaps.md` directly
- **mcp-audit** can spawn `mcp-server-builder` for immediate MCP fixes
- **knowledge-harvest** targets expert files and memories, catches gotchas and outdated information
---
## Orchestrator Learnings (MP-0003 Postmortem, 2026-03-16)
Full documentation: `coordination/experts/suitecrm/CURRENT-orchestration.md`
### 5 Orchestrator Rules (F-01 to F-05)
Added to `.claude/commands/orchestrate.md` after MP-0003 postmortem audit revealed
5 systematic failures in the orchestrator role:
| Rule | Problem | Fix |
|------|---------|-----|
| **F-01** | Subagents delivered incomplete reports (MCP gap logs without task_id/status) | Mandatory 4-field report format |
| **F-02** | Playwright test against empty record gave false "not rendering" | Require `search_records` to find record WITH data before UI test |
| **F-03** | Two context overflows from passing full session into debug prompts | Debug prompt max ~1500 chars; use `generic-development` not `suitecrm-specialist` |
| **F-04** | Orchestrator ran SSH analysis directly after subagent failures | Explicit "coordinator not implementor" rule; delegate to `generic-development` |
| **F-05** | Re-delegated already-completed task due to missing status in report | Check state (MCP/SSH) before re-running |
|
||||||
| SSH Safety Rules (Standard Across All Agents) | _claude/agent-system | knowledge | medium | CURRENT.md | 100 | 2026-03-20 02:00:01 |
|
Body:
## SSH Safety Rules (Standard Across All Agents)
These rules are now embedded in every agent that uses SSH:
1. **Check size before cat:** `ssh suitecrm "wc -l /path"` -- if >100 lines, use `head -N`
2. **Known large files:** Never cat index.html (minified), style.css (699KB)
3. **No parallel dependent SSH:** Chain with `&&` in single command
4. **No hardcoded passwords:** `sudo -n` for SSH sudo, env-vars for credentials, never plaintext
5. **No heredoc+sudo over SSH:** Use SCP 5-step pattern (local tmp, scp, sudo -n cp, sudo -n chown, verify)
6. **SCP deploy pattern:** Write locally to /tmp, scp to remote /tmp, sudo -n cp to target, sudo -n chown
## Credential Policy
- **SSH sudo:** `sudo -n` (non-interactive, NOPASSWD via `/etc/sudoers.d/heine-nopasswd`)
- **Playwright login:** `$SUITECRM_ADMIN_USER` / `$SUITECRM_ADMIN_PASS` env-vars (project settings.json)
- **`$SUDO_PASS` er UTDATERT** — NOPASSWD er konfigurert, denne env-var brukes ikke lenger
- Never write passwords in masterplan files, command output, or commit messages
- Enforced in: all 7 generated SuiteCRM agents (via shared parts), all self-contained commands
- **ALDRI les hele filer med secrets:** Bruk aldri Read-tool eller `cat` på `settings.json`, `.mcp.json`, eller `.env`-filer — disse inneholder passord i klartekst som eksponeres i samtaleloggen. Bruk `grep -c "VAR_NAME" fil` for å sjekke om en variabel finnes, eller `grep "KEY" fil` for å lese enkeltverdier.
## Trust Chain Between Pipeline Stages
```
/explore --[findings]--> /architect --[masterplan]--> /review-masterplan
|
[review findings]
|
v
/patch-revise-masterplan
|
[revised masterplan]
|
v
/implement-masterplan
|
[deployed code]
|
v
/audit-masterplan
```
Each stage trusts the previous stage's verified findings. Re-verification is only needed for:
- Facts explicitly flagged as uncertain
- New state created by the current stage's changes
- Data that may have changed since the previous stage ran (time-sensitive)
---
|
||||||
| Global vs. Local Rule Split (Established 2026-03-15) | _claude/agent-system | knowledge | low | CURRENT.md | 100 | 2026-03-20 02:00:01 |
|
Body:
## Global vs. Local Rule Split (Established 2026-03-15)
### Architecture
```
~/.claude/agents/includes/pipeline-safety-rules.md <-- 5 universal rules
^ ^ ^
| | |
| | +-- ~/.claude/agents/patch-revise-masterplan.md (includes file)
| +----- ~/.claude/agents/review-masterplan.md (includes file)
+-------- ~/.claude/agents/implement-masterplan.md (includes file)
.claude/commands/ (this workspace)
orchestrate.md <-- self-contained, blocks global agent
review-masterplan.md <-- self-contained, blocks global agent
patch-revise-masterplan.md <-- self-contained, blocks global agent
implement-masterplan.md <-- self-contained, blocks global agent
audit-masterplan.md <-- self-contained, blocks global agent
quality-audit-masterplan.md <-- self-contained, blocks global agent
```
### Key Decision
- **Global includes file** holds rules that are workspace-agnostic (SSH safety, trust chain, credentials, script verification)
- **Local command files** hold rules that are SuiteCRM-specific (MCP tools, file blocklists, deploy patterns, rebuild commands)
- Self-contained commands in this workspace **block** their global counterpart to avoid ReportMaker/Laravel pattern leakage (see "Cross-Workspace Conflict" section below)
---
## Cross-Workspace Conflict: Global Agent Mismatch
**Problem discovered:** Some commands in this workspace share names with global agents designed for ReportMaker/Laravel (e.g., `implement-masterplan`, `review-masterplan`, `audit-masterplan`). When Claude auto-reads the global agent file, it follows ReportMaker patterns (Laravel, Apache, Serena MCP, MySQL) instead of SuiteCRM patterns.
**Fix:** Self-contained commands now include an explicit block:
```
**DO NOT** read or follow `~/.claude/agents/{name}.md` -- that file is for a different workspace
```
This affects 5 commands: `implement-masterplan`, `review-masterplan`, `patch-revise-masterplan`, `audit-masterplan`, `quality-audit-masterplan`.
---
|
||||||
| Command File Convention: [Anbefalt: modell | Effort | Thinking] | _claude/agent-system | knowledge | high | CURRENT.md | 100 | 2026-03-20 02:00:01 |
|
Body:
## Command File Convention: [Anbefalt: modell | Effort | Thinking]
**Established:** 2026-03-16
All command files in `.claude/commands/` MUST end with:
```
[Anbefalt: {modell} | Effort: {level} | Thinking: {ja/nei}]
```
This line is shown to the user when a role is loaded, so they can verify session settings match the recommendation.
**Source of truth:** CLAUDE.md in suitecrm-dev workspace (table under "Available Commands").
| Command | Modell | Effort | Thinking |
|---------|--------|--------|---------|
| `/suitecrm`, `/agent-ops`, `/solo-dev-light`, `/refactor`, `/audit-masterplan`, `/quality-audit-masterplan`, `/review-masterplan`, `/implement-masterplan`, `/orchestrate`, `/patch-revise-masterplan` | Sonnet | medium | nei |
| `/explore`, `/bug-crusher`, `/ui-design`, `/architect` | Opus | high | ja |
| `/ui-redesign` | Sonnet | high | ja |
| `/expert-training` | Opus | high | nei |
### Prompt-format for obligatorisk output i command-filer
**Problemet:** Blockquote-format (`> **tekst**`) er for svakt — modellen behandler det som valgfritt og dropper linjer den ikke anser som "kjernesvar".
```markdown
# FUNGERER IKKE (modellen dropper andre linje):
> **Klar som SuiteCRM-spesialist. Hva trenger du hjelp med?**
> `[Anbefalt: Sonnet | Effort: medium | Thinking: nei]`
```
**Løsningen:** Imperativ med eksplisitt "no more, no less":
```markdown
# FUNGERER (etablert 2026-03-16):
Your COMPLETE response must be EXACTLY these two lines (no more, no less, no bullet points):
Klar som SuiteCRM-spesialist. Hva trenger du hjelp med?
`[Anbefalt: Sonnet | Effort: medium | Thinking: nei]`
```
Alle 16 command-filer i `.claude/commands/` bruker nå dette formatet.
---
### Grep-First Pattern for Batch File Status
When checking whether multiple command files already contain a specific line/pattern, use Grep BEFORE reading files individually:
```bash
# Example: find which command files already have [Anbefalt:] line
Grep pattern="\[Anbefalt:" path=".claude/commands/"
# Then read only the files that DON'T match
```
This avoids N individual Read calls when 1 Grep reveals which files need updating.
Apply to: any "which files have/lack X" question across multiple files.
---
|
||||||
| Oppgave-router integration | _claude/agent-system | knowledge | medium | CURRENT.md | 100 | 2026-03-20 02:00:01 |
|
Body:
### Oppgave-router integration
The oppgave-router in Steg 0 now points UI-relevant task types (listview/detailview/editview, displayLogic/requiredLogic, CSS/SCSS, Angular extension) to `CURRENT-customizations.md + Steg 1`.
---
## Pipeline Anti-Patterns (Fixed 2025-03-15, Refactored 2026-03-15)
Five systematic anti-patterns were identified across all pipeline agents and fixed (2025-03-15). In 2026-03-15, the 5 universal rules were extracted to a global includes file, separating workspace-agnostic rules from SuiteCRM-specific rules.
### Global Rules (in `~/.claude/agents/includes/pipeline-safety-rules.md`)
These 5 rules apply to ALL workspaces and are included by reference in global pipeline agents:
1. **Parallel SSH dependency rule** -- never run dependent SSH commands in parallel, chain with `&&`
2. **Pipeline trust chain** -- each stage trusts previous stage's verified findings, no re-verification
3. **Credential policy** -- `sudo -n` (NOPASSWD), `$SUITECRM_ADMIN_USER`/`$SUITECRM_ADMIN_PASS` for Playwright, never hardcoded passwords
4. **SSH file size check** -- `wc -l` before `cat` on any SSH file
5. **Non-existent scripts** -- verify scripts exist before referencing them
**Global agents that include the file:** `implement-masterplan.md`, `review-masterplan.md`, `patch-revise-masterplan.md`.
### SuiteCRM-Specific Rules (remain in local command files)
These rules are NOT in the global includes because they are workspace-specific:
- **MCP-first policy** -- use `suitecrm`, `context7`, `playwright` MCP servers before SSH. Decision tree in `CLAUDE.md`.
- **699KB file blocklist** -- never `cat` these known-large files:
- `/var/www/suitecrm/public/dist/index.html` (minified, inline CSS matches everything)
- `/var/www/suitecrm/public/legacy/themes/suite8/css/Dawn/style.css` (699KB)
- `grep "custom.*css" index.html` (matches entire inline `<style>` block)
- **SCP 3-step deploy pattern** -- write locally to /tmp, scp to remote /tmp, sudo cp to target
- **`rebuild_extensions` vs `cache:clear`** -- distinct operations, not interchangeable
- **MCP tool tables** -- tool reference for suitecrm/context7/playwright servers
- **Non-existent resources specific to this workspace:**
- `fix-permissions.sh`, `QUALITY-GATES.md`, `merge-angular-json` -- do not exist
- MySQL MCP -- not available in this workspace (Serena MCP IS available: `serena` local + `serena-suitecrm` remote)
**Where enforced:** Self-contained command files (`orchestrate.md`, `review-masterplan.md`, `patch-revise-masterplan.md`, `implement-masterplan.md`, `audit-masterplan.md`, `quality-audit-masterplan.md`).
---
|
||||||
| Knowledge Files | _claude/agent-system | knowledge | low | CURRENT.md | 100 | 2026-03-20 02:00:01 |
|
Body:
## Knowledge Files
`~/.claude/agents/suitecrm-specialist/` contains 8 knowledge files:
| File | Domain |
|------|--------|
| `knowledge-architecture.md` | Dual-layer system, file structure |
| `knowledge-extensions.md` | Extension framework, upgrade safety |
| `knowledge-metadata.md` | View definitions, layouts, detailviewdefs |
| `knowledge-mcp-tools.md` | All 3 MCP servers, tool reference |
| `knowledge-translations.md` | Language files, label system |
| `knowledge-ssh-patterns.md` | SSH deploy patterns, SCP workflow |
| `knowledge-import.md` | Data import patterns |
| `knowledge-upgrade-safety.md` | What survives upgrades, what does not |
---
## SuiteCRM Specialist: Two-Gate Architecture (Established 2026-03-16)
The `suitecrm-specialist.md` agent has two sequential hard gates before any work begins:
| Gate | Name | Purpose | Applies to |
|------|------|---------|------------|
| **Steg 0** | Last kunnskap | Load knowledge files (MCP tools, SSH patterns, metadata, etc.) | ALL tasks |
| **Steg 1** | Sjekk eksisterende tilstand | Check existing customizations, recent commits, current overrides on server | UI/metadata/felt/CSS tasks |
### Why two gates
Steg 0 (knowledge loading) prevents technical mistakes (wrong SSH hostname, missing MCP tools, unknown gotchas). But it does NOT prevent the most destructive failure: overwriting existing customizations with "clean" default metadata.
Steg 1 was added after an agent received a UI task involving a module with recent overrides (Angular components, metadata, CSS), ignored all existing work, and deployed standard SuiteCRM layout -- destroying all previous customizations. The user had to redo everything.
### Steg 1 substeps
- **1a:** Read `CURRENT-customizations.md` (indeks, ~120 lines) then relevant domain file
- **1b:** Check recent git commits on server (`git log --oneline -20`)
- **1c:** Check existing overrides for the target module (metadata, extensions, Angular)
- **1d:** NEVER deploy "clean" metadata -- always fetch current layout first, then modify
|
||||||
| Global post-session audit commands (no delegation, self-contained prompts) | _claude/agent-system | knowledge | medium | CURRENT.md | 100 | 2026-03-20 02:00:01 |
|
Body:
### Global post-session audit commands (no delegation, self-contained prompts)
| Command | Purpose |
|---------|---------|
| `/prompt-agent-chat-meta-audit` | Meta-revision: instruks-etterlevelse, tokenokonomi, handlingsplan |
| `/prompt-agent-chat-mcp-audit` | MCP-fokusert audit, oppdaterer mcp-gaps.md |
| `/prompt-agent-chat-knowledge-harvest` | Kunnskap-ekstraksjon til ekspertfiler og memories |
These are global (`~/.claude/commands/`) and work across all workspaces.
### Self-contained with knowledge file references
| Command | References |
|---------|-----------|
| `/ui-design` | `suitecrm-specialist/knowledge-extensions.md`, `knowledge-architecture.md` |
| `/ui-redesign` | `suitecrm-specialist/knowledge-metadata.md`, `knowledge-extensions.md` |
| `/solo-dev-light` | `suitecrm-specialist/knowledge-*.md` (all) |
## Global Agent Files (SuiteCRM-Specific)
Located in `~/.claude/agents/`:
| File | Source | Key additions |
|------|--------|---------------|
| `explore-suitecrm.md` | **Generated** from `parts/suitecrm/explore/core.md` + shared | SSH safety, trust previous reports |
| `architect-suitecrm.md` | **Generated** from `parts/suitecrm/architect/core.md` + shared | Trust-explore-reports, credentials policy |
| `suitecrm-specialist.md` | **Generated** from `parts/suitecrm/specialist/core.md` + shared | 8 knowledge files, Steg 0+1 gates, self-learning |
| `bug-crusher-suitecrm.md` | **Generated** from `parts/suitecrm/bug-crusher/core.md` + shared | Dual-layer debugging, cache investigation |
| `ui-redesign-suitecrm.md` | **Generated** from `parts/suitecrm/ui-redesign/core.md` + shared | DOM inspection, Playwright verification |
| `ui-designer-suitecrm.md` | **Generated** from `parts/suitecrm/ui-designer/core.md` + shared | Extension UI, Angular components |
| `implement-masterplan-suitecrm.md` | **Generated** from `parts/suitecrm/implement-masterplan/core.md` + shared | Sequential deploy, MCP deploy tools |
| `agent-ops-suitecrm.md` | Hand-maintained | Agent system maintenance |
|
||||||
| Architecture Overview | _claude/agent-system | knowledge | low | CURRENT.md | 100 | 2026-03-20 02:00:01 |
|
Body:
# Agent System — SuiteCRM Dev Workspace
> Expert file for `_claude/agent-system` domain.
> Last updated: 2026-03-19 — NOPASSWD sudo migration (credential policy updated, $SUDO_PASS deprecated). Previous: Domain file split (CURRENT-customizations.md monolith split into indeks + 7 domain files), KB FTS5 hyphen bug documented. Modular agent build system for SuiteCRM (12 shared parts, 7 variant cores, `build-agent.sh suitecrm all`), self-learning protocol. MP-0004 orchestration learnings (F-06, F-07, FM-06 to FM-09). MP-0003 postmortem (F-01 to F-05), suitecrm-specialist report format (E-01) + knowledge opt-in (E-02).
## Architecture Overview
The SuiteCRM dev workspace uses a two-tier agent system:
1. **Local commands** in `.claude/commands/` — entry points invoked via `/command`
2. **Global agents** in `~/.claude/agents/` — shared across workspaces, workspace-specific variants
Commands either delegate to a global agent (thin wrappers) or are fully self-contained with SuiteCRM-specific instructions.
## Command Delegation Map
### Delegates to global SuiteCRM-specific agents (thin wrappers)
| Command | Delegates to | Notes |
|---------|-------------|-------|
| `/explore` | `~/.claude/agents/explore-suitecrm.md` | SSH safety rules baked in |
| `/architect` | `~/.claude/agents/architect-suitecrm.md` | Trust-explore-reports baked in |
| `/suitecrm` | `~/.claude/agents/suitecrm-specialist.md` | + 8 knowledge files in `suitecrm-specialist/` |
| `/bug-crusher` | `~/.claude/agents/bug-crusher.md` | Generic, not SuiteCRM-specific yet |
| `/agent-ops` | `~/.claude/agents/agent-ops-suitecrm.md` | + reads router `agent-ops.md` for shared sections |
| `/expert-training` | `~/.claude/agents/expert-training.md` | Router, detects workspace |
### Self-contained SuiteCRM-specific (DO NOT delegate to global)
These commands explicitly block their global counterpart with "DO NOT read or follow ~/.claude/agents/{name}.md":
| Command | Why self-contained |
|---------|-------------------|
| `/orchestrate` | No git worktrees, sequential remote deploy, MCP-first |
| `/review-masterplan` | 12-category SuiteCRM checklist, upgrade-safety focus |
| `/patch-revise-masterplan` | Read-whole-file-first rule, trust review findings |
| `/implement-masterplan` | Remote deploy pattern, MCP deploy tools, NOPASSWD sudo |
| `/audit-masterplan` | Remote verification, MCP-first audit |
| `/quality-audit-masterplan` | Remote code quality checks |
|
||||||
| [Workflow] SKILL: Dashboard | claude/commands/SKILL | pattern | medium | SKILL.md | 88 | 2026-03-20 02:00:04 |
|
Body:
All gaps visible at: https://syncrovanis.magitek.no/gaps
|
||||||
| [Workflow] SKILL: After Logging (MANDATORY) | claude/commands/SKILL | pattern | medium | SKILL.md | 88 | 2026-03-20 02:00:04 |
|
Body:
Tell the user:
> **GAP logged:** {GAP-NNN} [{priority}] {title} -> {workspace}
|
||||||
| [Tool usage] SKILL: Valid Enums | claude/commands/SKILL | api_note | medium | SKILL.md | 88 | 2026-03-20 02:00:04 |
|
Body:
**syncrovanis types:** bug, feature, quality, docs, performance
**mcp-servers types:** gap, error, perf, ux, bug, feature, quality, docs, performance
**Priority:** critical, high, medium, low
To see all current enums: `~/.claude/lib/gap-cli.sh enums`
|
||||||
Ingestion History
Loading…