## Activities

Activities define security permissions and access control for nodes in the Xtrem framework. They establish what operations users can perform on different data entities and provide the foundation for role-based access control. Activities are essential for securing business data and ensuring proper authorization throughout the application.

### Basic Activity Definition

The simplest activity definition demonstrates the core pattern from the ShowCase package:

```typescript
import { Activity } from '@sage/xtrem-core';
import { ShowCaseProvider } from '../nodes/show-case-provider';

export const showCaseProviderActivity = new Activity({
    description: 'Showcase provider activity',
    node: () => ShowCaseProvider,
    __filename,
    permissions: ['lookup', 'read', 'create', 'update', 'delete'],
});
```

```typescript
import { Activity } from '@sage/xtrem-core';
import { ShowCaseInvoice } from '../nodes/show-case-invoice';

export const showCaseInvoiceActivity = new Activity({
    description: 'Showcase invoice activity',
    node: () => ShowCaseInvoice,
    __filename,
    permissions: ['lookup', 'read', 'create', 'update', 'delete'],
});
```

### Activity with Operation Grants

Activities can grant specific operations on related nodes:

```typescript
import { Activity } from '@sage/xtrem-core';
import { ShowCaseCustomer } from '../nodes/show-case-customer';
import { ShowCaseInvoice } from '../nodes/show-case-invoice';

export const showCaseCustomerActivity = new Activity({
    description: 'Showcase customer activity',
    node: () => ShowCaseCustomer,
    __filename,
    permissions: ['lookup', 'read', 'create', 'update', 'delete'],
    operationGrants: {
        testMutation: [
            {
                on: [() => ShowCaseInvoice],
                operations: ['read'],
            },
        ],
    },
});
```

### Activity Definition Properties

Activities support several key properties:

- **description**: Human-readable description of the activity
- **node**: Function returning the primary node class this activity controls
- **\_\_filename**: Required for proper activity registration and debugging
- **permissions**: Array of basic permissions granted on the primary node
- **operationGrants**: Object mapping permission names to operation grants on other nodes
- **permissionGrants**: Object mapping permission names to permission grants on other activities
- **noCascadeNodes**: Function returning nodes that should not inherit cascaded permissions

### Permission Types

#### Basic Permissions

Standard permissions that can be granted on nodes:

- **lookup**: Basic read access for dropdowns and references
- **read**: Full read access to node data
- **create**: Permission to create new instances
- **update**: Permission to modify existing instances
- **delete**: Permission to remove instances

#### Custom Permissions

Activities can define custom permissions for specific business operations:

```typescript
export const role = new Activity({
    description: 'Role',
    node: () => Role,
    __filename,
    permissions: ['read', 'manage'],
    operationGrants: {
        read: [
            { operations: ['all', 'getEffectivePermissions'], on: [() => Role] },
            { operations: ['lookup'], on: [() => ActivityGrant] },
        ],
        manage: [
            { operations: ['create', 'update', 'delete', 'all'], on: [() => Role] },
            { operations: ['roleActivitiesCreateUpdate'], on: [() => RoleActivity] },
            { operations: ['lookup'], on: [() => ActivityGrant] },
        ],
    },
});
```

### Operation Grants

Operation grants allow activities to grant specific operations on other nodes:

```typescript
const readGrants: OperationGrant[] = [
    { operations: ['lookup'], on: [() => AccountsReceivableOpenItem] },
    { operations: ['lookup'], on: [() => BankAccount] },
    { operations: ['lookup'], on: [() => BasePaymentDocument] },
    { operations: ['lookup'], on: [() => BaseDocumentLine] },
    { operations: ['lookup'], on: [() => Site] },
];

export const receipt = new Activity({
    description: 'Receipt',
    node: () => Receipt,
    __filename,
    permissions: ['lookup', 'read', 'create', 'update', 'delete'],
    operationGrants: {
        read: readGrants,
        create: [...readGrants, ...createGrants],
        update: [...readGrants, ...updateGrants],
        delete: [...readGrants, ...deleteGrants],
    },
});
```

### Complex Activity Examples

#### Customer Activity with Multi-Entity Grants

```typescript
const coreEntities = [
    () => Customer,
    () => BusinessEntityAddress,
    () => BusinessEntityContact,
    () => ItemCustomerPrice,
];

const lookupGrants: OperationGrant[] = [
    {
        operations: ['lookup'],
        on: [...coreEntities, () => BusinessEntity, () => AttachmentAssociation],
    },
    { operations: ['read'], on: [() => SysNoteAssociation] },
];

const createEntityGrant = (operations: string[]) => ({
    operations,
    on: coreEntities,
});

export const customer = new Activity({
    description: 'Customer',
    node: () => Customer,
    __filename,
    permissions: ['lookup', 'read', 'create', 'update', 'delete'],
    operationGrants: {
        lookup: lookupGrants,
        read: [...lookupGrants, createEntityGrant(['read'])],
        create: [...lookupGrants, createEntityGrant(['create', 'read'])],
        update: [...lookupGrants, createEntityGrant(['update', 'read'])],
        delete: [...lookupGrants, createEntityGrant(['delete', 'read'])],
    },
});
```

#### Stock Activity with Common Grants

```typescript
export const commonStockActivities: OperationGrant[] = [
    { operations: ['lookup'], on: [() => Company] },
    { operations: ['lookup'], on: [() => Site] },
    { operations: ['lookup'], on: [() => Currency] },
    { operations: ['lookup'], on: [() => Item] },
    { operations: ['lookup'], on: [() => ItemSite] },
];

export const stockMovement = new Activity({
    description: 'Stock Movement',
    node: () => StockMovement,
    __filename,
    permissions: ['lookup', 'read', 'create', 'update', 'delete'],
    operationGrants: {
        read: commonStockActivities,
        create: [...commonStockActivities, ...additionalCreateGrants],
        update: [...commonStockActivities, ...additionalUpdateGrants],
    },
});
```

### Permission Inheritance

Activities automatically inherit permissions based on relationships:

1. **Vital Child Nodes**: Operations on parent nodes grant corresponding operations on vital children
2. **Foreign References**: Operations grant lookup access on referenced nodes
3. **Permission Cascading**: Higher-level operations grant lower-level operations (create grants read and lookup)

### Activity Organization

Organize activities using index files for clean exports:

```typescript
// activities/index.ts
export * from './show-case-customer-activity';
export * from './show-case-invoice-activity';
export * from './show-case-provider-activity';
```

### Activity Registration

Activities are automatically registered through the module system:

```typescript
// Main module index
import * as activities from './activities/_index';
import * as dataTypes from './data-types/_index';
import * as enums from './enums/_index';
import * as menuItems from './menu-items/_index';
import * as nodes from './nodes/_index';
import * as serviceOptions from './service-options/_index';

export { activities, enums, dataTypes, nodes, serviceOptions, menuItems };
```

### Testing Activities

Activities can be tested to ensure proper permission resolution:

```typescript
describe('ShowCase Activities', () => {
    it('should grant proper permissions for provider activity', () => {
        const activity = showCaseProviderActivity;
        expect(activity.permissions).to.include('read');
        expect(activity.permissions).to.include('create');
        expect(activity.permissions).to.include('update');
        expect(activity.permissions).to.include('delete');
        expect(activity.permissions).to.include('lookup');
    });

    it('should grant operation grants for customer activity', () => {
        const activity = showCaseCustomerActivity;
        expect(activity.operationGrants?.testMutation).to.exist;
        expect(activity.operationGrants?.testMutation[0].operations).to.include('read');
    });
});
```

### Advanced Permission Patterns

#### Permission Grants to Other Activities

```typescript
export const roleActivity = new Activity({
    description: 'Role Management',
    node: () => Role,
    __filename,
    permissions: ['read', 'manage'],
    permissionGrants: {
        manage: [
            {
                permissions: ['read'],
                on: [() => userActivity, () => systemActivity],
            },
        ],
    },
});
```

#### No Cascade Nodes

Prevent permission cascading to specific nodes:

```typescript
export const restrictedActivity = new Activity({
    description: 'Restricted Access',
    node: () => SensitiveNode,
    __filename,
    permissions: ['read'],
    noCascadeNodes: () => [SecretChildNode, PrivateDataNode],
});
```

### Security Considerations

1. **Principle of Least Privilege**: Grant only the minimum permissions required
2. **Explicit Grants**: Be explicit about operation grants rather than relying on inheritance
3. **Regular Review**: Periodically review activity definitions for unnecessary permissions
4. **Testing**: Thoroughly test permission scenarios to ensure proper access control
5. **Documentation**: Document the business reasoning behind complex permission structures

### Best Practices for Activities

1. **Use descriptive names**: Make activity purposes clear through naming
2. **Group related permissions**: Organize operation grants logically
3. **Leverage inheritance**: Use permission cascading effectively for vital relationships
4. **Avoid over-granting**: Don't grant permissions that aren't actually needed
5. **Consider user roles**: Design activities with specific user roles in mind
6. **Use shared grants**: Create reusable grant patterns for common scenarios
7. **Validate configurations**: Test activity configurations thoroughly
8. **Document complex logic**: Explain unusual permission patterns in comments

### Common Patterns

#### Read-Only Activity

```typescript
export const readOnlyActivity = new Activity({
    description: 'Read-only access',
    node: () => ReportNode,
    __filename,
    permissions: ['lookup', 'read'],
});
```

#### Full Access Activity

```typescript
export const fullAccessActivity = new Activity({
    description: 'Full administrative access',
    node: () => AdminNode,
    __filename,
    permissions: ['lookup', 'read', 'create', 'update', 'delete'],
});
```

#### Lookup Only Activity

```typescript
export const lookupOnlyActivity = new Activity({
    description: 'Lookup access for references',
    node: () => ReferenceNode,
    __filename,
    permissions: ['lookup'],
});
```

### Workflows

The Xtrem framework includes a powerful workflow engine for business process automation. Workflows consist of event steps, action steps, condition steps, and other workflow components that automate business operations.

### Workflow Step Types

#### Event Steps

Event steps listen for entity events and trigger workflow execution. They're typically based on entity lifecycle events like creation, updates, or status changes.

```typescript
// Sales Order Created Event Step
import * as xtremWorkflow from '@sage/xtrem-workflow';

export class SalesOrderCreatedEvent extends xtremWorkflow.workflowSteps.EntityCreatedEventStep {
    static override readonly descriptor = {
        ...xtremWorkflow.workflowSteps.EntityCreatedEventStep.descriptor,
        key: 'sales-order-created',
        title: 'Sales order created',
        description: 'Triggers when a sales order is created.',
        serviceOptions: () => [],

        defaultConfig: {
            title: { base: 'Sales order created' },
            subtitle: '',
            startTopics: ['SalesOrder/created'],
            stepVariables: [
                {
                    node: 'SalesOrder',
                    path: 'salesOrder._id',
                    type: 'reference',
                    title: 'salesOrder',
                },
                {
                    node: 'SalesOrder',
                    path: 'salesOrder.number',
                    type: 'text',
                    title: 'salesOrderNumber',
                },
                {
                    node: 'SalesOrder',
                    path: 'salesOrder.totalAmountExcludingTax',
                    type: 'decimal',
                    title: 'totalAmountExcludingTax',
                },
                {
                    node: 'Customer',
                    path: 'salesOrder.billToCustomer.creditLimit',
                    type: 'decimal',
                    title: 'creditLimit',
                },
                {
                    node: 'BusinessEntityAddress',
                    path: 'salesOrder.billToLinkedAddress.emailAddress',
                    type: 'text',
                    title: 'emailAddress',
                },
                {
                    node: 'Contact',
                    path: 'salesOrder.billToContact._id',
                    type: 'reference',
                    title: 'billToContact',
                },
                {
                    node: 'Site',
                    path: 'salesOrder.site._id',
                    type: 'reference',
                    title: 'site',
                },
            ],
            outputVariables: [],
        },
    };
}
```

#### Entity Update Event Steps

Event steps that trigger on entity updates with specific conditions:

```typescript
// Sales Order Confirmed Event Step
import * as xtremWorkflow from '@sage/xtrem-workflow';

export class SalesOrderConfirmedEvent extends xtremWorkflow.workflowSteps.EntityUpdatedEventStep {
    static override readonly descriptor = {
        ...xtremWorkflow.workflowSteps.EntityUpdatedEventStep.descriptor,
        key: 'sales-order-confirmed',
        title: 'Sales order confirmed',
        description: 'Triggers when a sales order status changes from quote to pending.',
        serviceOptions: () => [],

        defaultConfig: {
            title: { base: 'Sales order confirmed' },
            subtitle: '',
            startTopics: ['SalesOrder/updated'],
            conditions: [
                {
                    path: 'salesOrder.status',
                    value: 'quote',
                    operator: 'wasEqualTo',
                },
                {
                    path: 'salesOrder.status',
                    value: 'pending',
                    operator: 'equalTo',
                },
            ],
            stepVariables: [
                {
                    node: 'SalesOrder',
                    path: 'salesOrder._id',
                    type: 'reference',
                    title: 'salesOrder',
                },
            ],
            outputVariables: [],
        },
    };
}
```

#### Condition Actions

Condition actions evaluate business rules and control workflow flow:

```typescript
// Credit Limit Condition Action
import * as xtremWorkflow from '@sage/xtrem-workflow';

export class SalesOrderWithinCreditLimitCondition extends xtremWorkflow.workflowSteps.ConditionAction {
    static override readonly descriptor = {
        ...xtremWorkflow.workflowSteps.ConditionAction.descriptor,
        key: 'sales-order-within-credit-limit',
        title: 'Is sales order within credit limit',
        description:
            "Compares the sales order's total amount excluding tax with the credit limit of the bill to customer.",
        serviceOptions: () => [xtremWorkflow.serviceOptions.workflowAdvanced],

        defaultConfig: {
            title: { base: 'Is within credit limit?' },
            subtitle: '',
            inputVariableName: 'salesOrder',
            conditions: [
                {
                    path: 'salesOrder.totalAmountExcludingTax',
                    value: 'salesOrder.billToCustomer.creditLimit',
                    operator: 'lessThan',
                    useParameter: true,
                } as const,
            ],
            outputVariables: [],
            outputVariableName: '',
        },
    };
}
```

#### Print Actions

Print actions generate documents and reports:

```typescript
// Print Document Action
import * as xtremWorkflow from '@sage/xtrem-workflow';

export class SalesOrderPrintAction extends xtremWorkflow.workflowSteps.PrintDocumentAction {
    static override readonly descriptor = {
        ...xtremWorkflow.workflowSteps.PrintDocumentAction.descriptor,
        key: 'sales-order-print',
        title: 'Print sales order',
        description: 'Prints a sales order report.',
        serviceOptions: () => [],

        defaultConfig: {
            title: { base: 'Print sales order' },
            subtitle: '',
            inputVariableName: 'salesOrder',
            reportParameters: [
                {
                    name: 'salesOrder',
                    node: 'SalesOrder',
                    type: 'reference',
                    value: 'salesOrder._id',
                    origin: 'fromVariable',
                },
                {
                    name: 'site',
                    node: 'Site',
                    type: 'reference',
                    value: 'site',
                    origin: 'fromVariable',
                },
            ],
            outputVariables: [],
            outputVariableName: '',
            reportKey: 'sales-order',
        },
    };
}
```

#### Email Actions

Email actions send automated notifications:

```typescript
// Send Email Action
import * as xtremWorkflow from '@sage/xtrem-workflow';

export class SalesOrderSendRejectionEmailAction extends xtremWorkflow.workflowSteps.SendEmailAction {
    static override readonly descriptor = {
        ...xtremWorkflow.workflowSteps.SendEmailAction.descriptor,
        key: 'sales-order-send-rejection-email',
        title: 'Send rejection email',
        description: 'Sends a rejection email for a sales order.',
        serviceOptions: () => [],

        defaultConfig: {
            title: { base: 'Send rejection email' },
            subtitle: '',
            inputVariableName: 'salesOrder',
            addressList: [
                {
                    name: 'to',
                    addresses: [
                        {
                            emailAddress: 'emailAddress',
                            name: '',
                            origin: 'fromVariable',
                        },
                    ],
                },
            ],
            localizationParameters: [
                {
                    name: 'salesOrderNumber',
                    value: 'salesOrderNumber',
                    origin: 'fromVariable',
                },
            ],
            subject: {
                localizationKey: '@sage/xtrem-sales/workflow__sales_order_send_rejection_email__subject',
                localizationDefaultValue: 'Sales order {{salesOrderNumber}} rejected',
            },
            body: {
                localizationKey: '@sage/xtrem-sales/workflow__sales_order_send_rejection_email__body',
                localizationDefaultValue:
                    'Your sales order {{salesOrderNumber}} has been rejected due to credit limit constraints.',
            },
            outputVariables: [],
            outputVariableName: '',
        },
    };
}
```

### Workflow Configuration

#### Workflow Templates

Workflow templates define reusable step configurations:

```json
{
    "selector": "{_id status}",
    "localizedTitle": {
        "base": "Sales Order - confirm",
        "en-US": "Sales Order - confirm"
    },
    "stepVariables": [
        {
            "node": "SalesOrder",
            "path": "salesOrder._id",
            "type": "reference",
            "title": "salesOrder"
        }
    ],
    "actionParameters": [],
    "mutationArguments": [
        {
            "name": "salesOrder",
            "node": "SalesOrder",
            "type": "reference",
            "value": "salesOrder._id",
            "origin": "fromVariable"
        },
        {
            "name": "isSafeToRetry",
            "type": "boolean",
            "value": true,
            "origin": "manual",
            "isMandatory": false
        }
    ]
}
```

#### Workflow Definitions

Workflow definitions connect events, conditions, and actions:

```csv
"id";"name";"is_active";"status";"test_user";"diagram";"start_topic"
"retailDefaultFlow";"{""en"":""Retail default flow"",""base"":""Retail default flow"",""en-US"":""Retail default flow""}";"Y";"test";"unit.test@acme.com";"5";"SalesOrder/created"
```

### Workflow Step Variables

Workflow steps can define variables that flow between steps:

```typescript
stepVariables: [
    {
        node: 'SalesOrder',
        path: 'salesOrder._id',
        type: 'reference',
        title: 'salesOrder',
    },
    {
        node: 'SalesOrder',
        path: 'salesOrder.number',
        type: 'text',
        title: 'salesOrderNumber',
    },
    {
        node: 'SalesOrder',
        path: 'salesOrder.totalAmountExcludingTax',
        type: 'decimal',
        title: 'totalAmountExcludingTax',
    },
    {
        node: 'Customer',
        path: 'salesOrder.billToCustomer.creditLimit',
        type: 'decimal',
        title: 'creditLimit',
    },
];
```

### Service Options

Workflow steps can specify service options for advanced features:

```typescript
serviceOptions: () => [xtremWorkflow.serviceOptions.workflowAdvanced, xtremWorkflow.serviceOptions.orderToOrderOption];
```

