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.