# CRM Modules

Custom modules allow you to change CRM behavior without editing core source files.

## What Is a Module

A module is a ZIP archive with:

- `module.json` in archive root
- one or more PHP hook files referenced from `module.json`

After installation, enabled module hooks can modify validated payload for all core CRM entities.

## module.json Example

```json
{
  "name": "Priority Automation",
  "slug": "priority-automation",
  "version": "1.0.0",
  "description": "Force task priority and fill deal source",
  "hooks": {
    "activities.store": "hooks/activities-store.php",
    "activities.update": "hooks/activities-update.php",
    "companies.store": "hooks/companies-store.php",
    "companies.update": "hooks/companies-update.php",
    "contacts.store": "hooks/contacts-store.php",
    "contacts.update": "hooks/contacts-update.php",
    "deals.store": "hooks/deals-store.php",
    "deals.update": "hooks/deals-update.php",
    "disks.folders.store": "hooks/disks-folders-store.php",
    "disks.store": "hooks/disks-store.php",
    "disks.update": "hooks/disks-update.php",
    "forms.store": "hooks/forms-store.php",
    "forms.update": "hooks/forms-update.php",
    "hr.requests.store": "hooks/hr-requests-store.php",
    "hr.requests.update": "hooks/hr-requests-update.php",
    "news.store": "hooks/news-store.php",
    "pipelines.store": "hooks/pipelines-store.php",
    "pipelines.update": "hooks/pipelines-update.php",
    "products.store": "hooks/products-store.php",
    "products.update": "hooks/products-update.php",
    "projects.store": "hooks/projects-store.php",
    "projects.update": "hooks/projects-update.php",
    "tasks.store": "hooks/tasks-store.php",
    "tasks.update": "hooks/tasks-update.php",
    "onec.exchanges.store": "hooks/onec-exchanges-store.php",
    "onec.exchanges.update": "hooks/onec-exchanges-update.php",
    "telephony.store": "hooks/telephony-store.php",
    "telephony.update": "hooks/telephony-update.php",
    "messengers.channels.store": "hooks/messengers-channels-store.php",
    "messengers.channels.update": "hooks/messengers-channels-update.php",
    "messengers.conversations.store": "hooks/messengers-conversations-store.php",
    "messengers.conversations.update": "hooks/messengers-conversations-update.php",
    "messengers.messages.store": "hooks/messengers-messages-store.php",
    "messengers.messages.update": "hooks/messengers-messages-update.php",
    "warehouses.store": "hooks/warehouses-store.php",
    "warehouses.update": "hooks/warehouses-update.php"
  }
}
```

## Supported Hooks

- `activities.store`
- `activities.update`
- `companies.store`
- `companies.update`
- `contacts.store`
- `contacts.update`
- `deals.store`
- `deals.update`
- `disks.folders.store`
- `disks.store`
- `disks.update`
- `forms.store`
- `forms.update`
- `hr.requests.store`
- `hr.requests.update`
- `news.store`
- `pipelines.store`
- `pipelines.update`
- `products.store`
- `products.update`
- `projects.store`
- `projects.update`
- `tasks.store`
- `tasks.update`
- `onec.exchanges.store`
- `onec.exchanges.update`
- `telephony.store`
- `telephony.update`
- `messengers.channels.store`
- `messengers.channels.update`
- `messengers.conversations.store`
- `messengers.conversations.update`
- `messengers.messages.store`
- `messengers.messages.update`
- `warehouses.store`
- `warehouses.update`

## Hook Script Format

You can return:

1. array of changed fields, or
2. callable `(array $payload, array $context, array $manifest): array`

Example:

```php
<?php

return static function (array $payload, array $context, array $manifest): array {
    if (($payload['priority'] ?? '') === 'low') {
        $payload['priority'] = 'high';
    }

    return $payload;
};
```

The returned fields are merged into the entity payload before saving.

## Archive Structure

```text
priority-automation.zip
├── module.json
├── README.md
└── hooks
    ├── activities-store.php
    ├── companies-store.php
    ├── contacts-store.php
    ├── deals-store.php
    ├── disks-store.php
    ├── forms-store.php
    ├── hr-requests-store.php
    ├── pipelines-store.php
    ├── products-store.php
    ├── projects-store.php
    ├── tasks-store.php
    ├── telephony-store.php
    ├── messengers-channels-store.php
    └── warehouses-store.php
```

## Lifecycle

1. Open `Profile -> Modules`.
2. Generate scaffold ZIP.
3. Implement your hooks.
4. Archive files as ZIP.
5. Upload ZIP in Modules settings.
6. Enable or disable module.
7. Upload new ZIP with same `slug` to update module.
