Products

Maintain the shared catalog of products and services, categories, units of measure, bundles, and pricing rules.

For POS and deal flows that use the catalog, see Sales and POS Client.

Module layout

backend/core-api/src/modules/products/
  graphql/schemas/{product,category,uom,package,rule,similarity,config,index}.ts
  graphql/resolvers/{queries,mutations,customResolvers}/
  db/definitions/{products,categories,uoms,packages,rules,similarities,configs}.ts
  db/models/{Products,Categories,Uoms,Packages,Rules,Similarities,Configs}.ts
  trpc/{product,category,uom,similarity,config,index}.ts
  meta/automations.ts            # AI knowledge provider for automations
  meta/document/productReplacer.ts
  meta/import-export/{import,export}/
  meta/template/                 # category templates
  utils/productRule.ts

Frontend: frontend/core-ui/src/modules/products/ (record table, categories, UoM, packages) with shared product pickers in frontend/libs/ui-modules/src/modules/products/.

Data model

Product (products.ts):

FieldNotes
name, shortName, codecode is unique; the stable external key
categoryIdLinks to ProductCategory (tree via parentId, order)
typeproduct, service, unique, subscription
statusactive or deleted (soft delete)
unitPrice, currencyBase price; discounts[] and inventories hold extras
barcodes, variants, barcodeDescription, sameMasks, sameDefaultBarcode/variant matching for POS
uom, subUomsUnit of measure and convertible sub-units
vendorId, scopeBrandIds, tagIds, mergedIdsOwnership and dedup
customFieldsData, propertiesDataCustom properties
attachment, attachmentMore, videos, pdfAttachmentMedia
duration, durationTypeFor service-type items
similarityIdLinks to Similarity groups

Uom (db/definitions/uoms.ts): name, code (unique), isForSubscription, subscriptionConfig (period, rule, specificDay, subsRenewable). ProductRule prices products by conditions; ProductPackage bundles items; Similarity groups mask-matched products; ProductsConfig stores module-wide settings (productsConfigsUpdate).

GraphQL API

Products (graphql/schemas/product.ts)

OperationKey arguments
productsMaintype, status, categoryId, categoryIds, searchValue, vendorId, brandIds, tag, tagIds, excludeTagIds, tagWithRelated, ids, excludeIds, pipelineId, boardId, segment, segmentIds, propertiesData, groupedSimilarity, similarity, image, brand, branchId, departmentId, minRemainder/maxRemainder, minPrice/maxPrice, discount range args, discountConditions, sortField, sortDirection, cursor params; returns ProductsListResponse
productssame filters with page/perPage; returns a plain [Product]
productsTotalCountsame filters
productDetail_id
productSimilarities_id!, groupedSimilarity
productCountByTagsnone
productsAdd / productsEditname, shortName, categoryId, type, description, barcodes, variants, barcodeDescription, unitPrice, code, propertiesData, attachment, attachmentMore, videos, vendorId, scopeBrandIds, uom, subUoms, currency, duration, durationType, pdfAttachment
productsMergeproductIds, productFields
productsDuplicate / productsRemove_id! / productIds: [String!]

Categories, units, packages, rules

OperationKey arguments
productCategories / productCategoriesTotalCount / productCategoryDetailparentId, searchValue, status, ids / _id
categoriesWithChildsids: [String!]!
productCategoriesAdd / productCategoriesEdit / productCategoriesRemovename, code, parentId, description, meta, attachment, status / _id!
uoms / uomsTotalCountnone
uomsAdd / uomsEdit / uomsRemovename, code, isForSubscription, subscriptionConfig, timely / uomIds: [String!]
productPackages / productPackageDetailcursor params / _id!
productPackagesAdd / productPackagesEdit / productPackagesChangeStatus / productPackagesRemovepackage fields / _ids, status!
productRules / productRulesWithCountnone
productRulesAdd / productRulesEdit / productRulesRemoverule fields / _ids
productBulkSimilarity / productBulkSimilarities_id! / page, perPage, searchValue
productsConfigs / productsConfigsUpdatenone / configsMap: JSON!

Example

query ProductList($categoryId: String) {
  productsMain(categoryId: $categoryId, status: "active", limit: 20) {
    list {
      _id
      name
      code
      unitPrice
      uom
      cursor
    }
    totalCount
    pageInfo { hasNextPage endCursor }
  }
}

mutation AddProduct {
  productsAdd(
    name: "Espresso blend 1kg"
    code: "COF-ESP-1KG"
    type: "product"
    unitPrice: 18.5
    currency: "USD"
    uom: "bag"
    barcodes: ["4006381333931"]
  ) { _id code }
}

How plugins use products

Services do not read the Products collection directly. They call the tRPC router in trpc/product.ts through sendTRPCMessage({ pluginName: 'core', module: 'products', action: ... }). Procedures: find (with automatic categoryId expansion to child categories), findOne, createProduct, updateProduct, updateProducts, removeProducts, count. Example from the POS order route in sales_api:

// backend/plugins/sales_api/src/modules/pos/routes.ts
const products = await sendTRPCMessage({
  subdomain,
  pluginName: 'core',
  module: 'products',
  action: 'find',
  input: { query: { status: { $ne: 'deleted' }, categoryId: { $in: categoryIds } } },
  defaultValue: [],
});

The same pattern appears in the sales pipeline product picker and ecommerce module. Products also show up through meta/document/productReplacer.ts (document templates), meta/automations.ts (AI knowledge provider), and the segment content type core:products.products.

Permissions

Core permission module products (meta/permissions.ts): productsRead (always: true), productsCreate, productsUpdate, productsDelete, productsMerge, productCategoriesManage, productsConfigsManage, uomsManage, productRulesManage, productsImportManage, productsExportManage. Scope is all only; there is no per-owner product scope.

Was this helpful?