Demo

ChangeLogService

Record activity on a business reference (plugin + prefix + number) — creates, updates, replies, and reactions. Prefer this over a plugin-local audit feed.

Inject

TSXinject
1import { 2 ChangeLogService, 3 ContainerRegistryManager, 4 Inject, 5 Service, 6} from "@quan-erp/shared-backend-core"; 7 8@Service() 9export class MyPluginService { 10 @Inject(ChangeLogService, ContainerRegistryManager.BUILTIN_PLUGIN) 11 private changeLogService: ChangeLogService; 12}

Reference key

Every entry is scoped by three fields that form the feed for one record.

  • pluginName — usually metadata.name (or builtin for core modules)
  • referencePrefix — entity kind string (e.g. "SO", "currency", "project")
  • referenceNumber — record id / code as string

Write methods

  • addChangeLog(params: CreateServiceProps<ChangeLogDto>) — insert one entry; optional replyToId threads a reply; passive: true may notify the parent author
  • addChangeLogs(params: CreateServiceProps<ChangeLogDto[]>) — bulk insert (same shape per item)
  • ChangeLogDto — message (required), pluginName, referencePrefix, referenceNumber; optional createdBy, metadata, isCreatedBySystem, replyToId, passive
TSXaddChangeLog
1import { 2 ChangeLogService, 3 ContainerRegistryManager, 4 Inject, 5 Service, 6} from "@quan-erp/shared-backend-core"; 7import metadata from "../../module.metadata.json" with { type: "json" }; 8 9@Service() 10export class OrderChangeLogService { 11 @Inject(ChangeLogService, ContainerRegistryManager.BUILTIN_PLUGIN) 12 private changeLogService: ChangeLogService; 13 14 async onCreated(orderId: number, userId?: number) { 15 await this.changeLogService.addChangeLog({ 16 data: { 17 pluginName: metadata.name, 18 referencePrefix: "SO", 19 referenceNumber: String(orderId), 20 message: `Sales order #${orderId} created.`, 21 isCreatedBySystem: !userId, 22 createdBy: userId, 23 metadata: { action: "create" }, 24 }, 25 }); 26 } 27}

Read method

  • getChangeLogs({ pluginName, referencePrefix, referenceNumber, parentId?, skip, limit }) — paginated feed for that reference
  • Omit parentId for root entries (replyTo IS NULL); pass parentId to list replies under one entry
  • Each row includes reactions and replyCount
TSXgetChangeLogs
1import { 2 ChangeLogService, 3 ContainerRegistryManager, 4 Inject, 5 Service, 6} from "@quan-erp/shared-backend-core"; 7import metadata from "../../module.metadata.json" with { type: "json" }; 8 9@Service() 10export class OrderChangeLogService { 11 @Inject(ChangeLogService, ContainerRegistryManager.BUILTIN_PLUGIN) 12 private changeLogService: ChangeLogService; 13 14 async list(orderId: number) { 15 return this.changeLogService.getChangeLogs({ 16 pluginName: metadata.name, 17 referencePrefix: "SO", 18 referenceNumber: String(orderId), 19 skip: 0, 20 limit: 20, 21 }); 22 } 23}

Reaction methods

  • addReaction(params: CreateServiceProps<ChangeLogReactionDto>) — reaction + reactBy (user id) + changeLogId; notifies the entry author when not system-created
  • removeReaction({ id, removeByUserId }) — remove the current user’s reaction for that change-log id

Frontend

Base frontend exposes ChangeLog UI and hooks (ChangeLog, useChangeLogQuery, useInfiniteChangeLog, reaction mutations). Prefer those on detail pages instead of building a custom feed.