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):

Field Notes
name, shortName, code code is unique; the stable external key
categoryId Links to ProductCategory (tree via parentId, order)
type product, service, unique, subscription
status active or deleted (soft delete)
unitPrice, currency Base price; discounts[] and inventories hold extras
barcodes, variants, barcodeDescription, sameMasks, sameDefault Barcode/variant matching for POS
uom, subUoms Unit of measure and convertible sub-units
vendorId, scopeBrandIds, tagIds, mergedIds Ownership and dedup
customFieldsData, propertiesData Custom properties
attachment, attachmentMore, videos, pdfAttachment Media
duration, durationType For service-type items
similarityId Links 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)

Operation Key arguments
productsMain type, 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
products same filters with page/perPage; returns a plain [Product]
productsTotalCount same filters
productDetail _id
productSimilarities _id!, groupedSimilarity
productCountByTags none
productsAdd / productsEdit name, shortName, categoryId, type, description, barcodes, variants, barcodeDescription, unitPrice, code, propertiesData, attachment, attachmentMore, videos, vendorId, scopeBrandIds, uom, subUoms, currency, duration, durationType, pdfAttachment
productsMerge productIds, productFields
productsDuplicate / productsRemove _id! / productIds: [String!]

Categories, units, packages, rules

Operation Key arguments
productCategories / productCategoriesTotalCount / productCategoryDetail parentId, searchValue, status, ids / _id
categoriesWithChilds ids: [String!]!
productCategoriesAdd / productCategoriesEdit / productCategoriesRemove name, code, parentId, description, meta, attachment, status / _id!
uoms / uomsTotalCount none
uomsAdd / uomsEdit / uomsRemove name, code, isForSubscription, subscriptionConfig, timely / uomIds: [String!]
productPackages / productPackageDetail cursor params / _id!
productPackagesAdd / productPackagesEdit / productPackagesChangeStatus / productPackagesRemove package fields / _ids, status!
productRules / productRulesWithCount none
productRulesAdd / productRulesEdit / productRulesRemove rule fields / _ids
productBulkSimilarity / productBulkSimilarities _id! / page, perPage, searchValue
productsConfigs / productsConfigsUpdate none / 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.

Operate on one system.

Talk to our team about consolidating your stack onto erxes. 30-minute walkthrough, custom to your org.