> For the complete documentation index, see [llms.txt](https://developer.vario-software.de/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://developer.vario-software.de/documentation/fundamentals/custom-fields.md).

# Custom Fields

Custom Fields are the primary way for apps to extend the VARIO Cloud ERP data model. They let you attach your own fields to any ERP entity — articles, accounts, documents, CRM activities, and more. This is central to most integrations: storing external reference IDs, tracking synchronization state, mapping data between systems, or adding any app-specific information directly on ERP records.

Once registered, Custom Fields appear automatically in the ERP UI under the "Zusatzfelder" tab and are fully queryable via the API and VQL.

{% hint style="info" %}
In the API, Custom Fields are represented as **EAV Groups** (Entity-Attribute-Value) containing **Attributes**.
{% endhint %}

```mermaid
graph TD
  G["EAV Group «myapp»\nexternalId (TEXT)\nisActive (BOOLEAN)"]
  G -->|"custom.myapp.<attributeKey>"| A["Article"]
  G -->|"custom.myapp.<attributeKey>"| D["Document"]
```

Access pattern: `custom.{groupKey}.{attributeKey}`

***

### Definition

Define an EAV group as a JSON file in `migrations/{n}/static/erp/eav-groups/`:

```json
{
  "key": "myapp",
  "label": "My App",
  "description": "Custom fields for the integration",
  "attributes": [
    {
      "key": "externalId",
      "label": "External ID",
      "description": "Reference ID in the external system",
      "position": 1,
      "type": "TEXT",
      "viewMode": "EDITABLE",
      "active": true,
      "fulltextField": true,
      "searchIndex": true
    }
  ],
  "uiSettings": {},
  "entities": ["document"]
}
```

### Group Properties

| Field         | Type     | Description                                                      |
| ------------- | -------- | ---------------------------------------------------------------- |
| `key`         | `string` | Unique identifier — determines the access path `custom.{key}.*`  |
| `label`       | `string` | Display name in the ERP                                          |
| `description` | `string` | Description                                                      |
| `attributes`  | `array`  | The fields in this group                                         |
| `uiSettings`  | `object` | UI display configuration (optional)                              |
| `entities`    | `array`  | Target entities, e.g. `["account"]` or `["account", "document"]` |

### Attribute Properties

| Field           | Type           | Description                                     |
| --------------- | -------------- | ----------------------------------------------- |
| `key`           | `string`       | Unique key within the group                     |
| `label`         | `string`       | Display name                                    |
| `description`   | `string`       | Description                                     |
| `position`      | `number`       | Sort order                                      |
| `type`          | `string`       | Data type (see below)                           |
| `viewMode`      | `string`       | `"EDITABLE"`, `"READ_ONLY"`, or `"NOT_VISIBLE"` |
| `active`        | `boolean`      | Whether the field is active                     |
| `fulltextField` | `boolean`      | Include in fulltext search                      |
| `searchIndex`   | `boolean`      | Create a search index                           |
| `textGroupRef`  | `string\|null` | Reference to a text enum group (for dropdowns)  |

### Attribute Types

| Type        | Description   |
| ----------- | ------------- |
| `TEXT`      | Text          |
| `MEMO`      | Long text     |
| `BOOLEAN`   | Boolean       |
| `INTEGER`   | Integer       |
| `DECIMAL`   | Decimal       |
| `CURRENCY`  | Currency      |
| `DATE`      | Date          |
| `DATE_TIME` | Date and time |
| `JSON`      | JSON object   |

***

## Migration

The following section on Custom Field migration is only relevant for developers building VARIO apps. If you are working exclusively with the API, you may skip ahead to the next chapter. Custom Fields are registered during app installation via the migration system.

```
backend/services/maintenance/install/migrations/
  1/
    index.js
    static/erp/eav-groups/myapp.json
  2/                                    ← later update
    index.js
```

### Creating Fields

```javascript
const MigratorErp = require('@vario-software/vario-app-framework-backend/utils/migrator.js');
const eavGroup = require('./static/erp/eav-groups/myapp.json');

const handle = async function()
{
  const migrator = new MigratorErp('migration1');

  await migrator.setMigration('createEav', async (methods) =>
  {
    await methods.createEavGroup(eavGroup);
  });
};

module.exports = handle;
```

### Updating Fields

Add a new migration folder (e.g. `migrations/2/`) and use `changeEavGroup`:

```javascript
// migrations/2/index.js
const MigratorErp = require('@vario-software/vario-app-framework-backend/utils/migrator.js');

const handle = async function()
{
  const migrator = new MigratorErp('migration2');

  await migrator.setMigration('addStatusField', async (methods) =>
  {
    await methods.changeEavGroup('myapp', (group) =>
    {
      group.attributes.push({
        key: 'syncStatus',
        label: 'Sync Status',
        type: 'TEXT',
        viewMode: 'EDITABLE',
        active: true,
        searchIndex: false,
      });
      return group;
    });
  });
};

module.exports = handle;
```

### Migration Methods

| Method                                            | Description                              |
| ------------------------------------------------- | ---------------------------------------- |
| `createEavGroup(json)`                            | Create a group                           |
| `getEavGroup(groupKey)`                           | Read a group                             |
| `changeEavGroup(groupKey, callback)`              | Modify a group via callback              |
| `deleteEavGroup(groupKey)`                        | Delete a group including its data        |
| `removeDataFromEavGroup(groupKey, attributeKeys)` | Remove specific attributes or their data |

***

## API Access

Custom Fields live under the `custom` namespace on every entity they are bound to:

```javascript
// Read
const { data } = await app.erp.fetch('/crm/activities/123', {
  useInternalApi: true,
});
const value = data.custom.myapp.externalId;
```

```javascript
// Create
await app.erp.fetch('/crm/activities', {
  method: 'POST',
  useInternalApi: true,
  body: {
    comment: 'Created via app',
    custom: { myapp: { externalId: 'EXT-456' } },
  },
});
```

```javascript
// Update
await app.erp.fetch('/crm/activities/123', {
  method: 'PUT',
  useInternalApi: true,
  body: {
    custom: { myapp: { externalId: 'EXT-789' } },
  },
});
```

### VQL

```javascript
const { data } = await app.erp.vql({
  statement: `
    SELECT id, comment, custom.myapp.externalId
    FROM crm.activities
    WHERE custom.myapp.externalId NOT NULL
    LIMIT 50
  `,
});
```

***

## Permissions

Required in `app-manifest.json`:

```json
{
  "requirements": {
    "permissions": [
      { "resource": "eav", "verb": "read" },
      { "resource": "eav", "verb": "create" },
      { "resource": "eav", "verb": "update" },
      { "resource": "eav", "verb": "delete" }
    ]
  }
}
```


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://developer.vario-software.de/documentation/fundamentals/custom-fields.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
