Extending Objects

Add or change HotCRM objects, fields, hooks, flows, and sharing metadata.

Extending Objects

HotCRM objects live in src/objects/ and are registered from src/objects/index.ts.

Add an object

// src/objects/warranty.object.ts
import { ObjectSchema, Field } from '@objectstack/spec/data';

export const Warranty = ObjectSchema.create({
  name: 'crm_warranty',
  label: 'Warranty',
  pluralLabel: 'Warranties',
  icon: 'shield',
  fields: {
    warranty_number: Field.autonumber({ label: 'Warranty Number', format: 'WR-{000000}' }),
    crm_account: Field.lookup('crm_account', { label: 'Account', required: true }),
    crm_product: Field.lookup('crm_product', { label: 'Product' }),
    status: Field.select({
      label: 'Status',
      defaultValue: 'active',
      options: [
        { label: 'Active', value: 'active', default: true },
        { label: 'Expired', value: 'expired' },
      ],
    }),
  },
  enable: {
    apiEnabled: true,
    searchable: true,
    trackHistory: true,
  },
});

Then export it:

// src/objects/index.ts
export { Warranty } from './warranty.object.js';

Add lifecycle logic

Hooks live beside objects and are collected by src/hooks/index.ts.

// src/objects/warranty.hook.ts
import type { Hook } from '@objectstack/spec/data';

const warrantyHook: Hook = {
  name: 'crm_warranty_hook',
  object: 'crm_warranty',
  events: ['beforeInsert', 'beforeUpdate'],
  handler: async (ctx) => {
    const doc = ctx.input.doc as Record<string, unknown>;
    if (doc.status === 'expired' && !doc.end_date) {
      throw new Error('Expired warranties need an end date.');
    }
  },
};

export default warrantyHook;

Add automation

Use src/flows/ for multi-step automation and export new flows from src/flows/index.ts.

import type * as Automation from '@objectstack/spec/automation';

type Flow = Automation.Flow;

export const WarrantyAlertFlow: Flow = {
  name: 'warranty_alert',
  label: 'Warranty Alert',
  type: 'record_change',
  status: 'active',
  variables: [],
  nodes: [
    { id: 'start', type: 'start', label: 'Warranty updated', config: { objectName: 'crm_warranty', triggerType: 'record-after-update' } },
    { id: 'end', type: 'end', label: 'End' },
  ],
  edges: [{ id: 'e1', source: 'start', target: 'end', type: 'default' }],
};

Verify

pnpm validate
pnpm typecheck
pnpm test

On this page