← Knowledge library

Inventory

Product Knowledge

ProductKnowledge is the reusable definition of a product — its coding, base presentation unit, ingredients and nutrients, drug characteristics, and storage rules. Define it once and every concrete Product points back to it instead of carrying…

3.1 · versioned documentation · 7 min read

ProductKnowledge is the reusable definition of a product — its coding, base presentation unit, ingredients and nutrients, drug characteristics, and storage rules. Define it once and every concrete Product points back to it instead of carrying duplicate data. A definition is either instance-wide or scoped to a single facility.

The Django model is only the storage layer: code, names, storage_guidelines, definitional, and base_unit are opaque JSONFields whose real structure lives in the Pydantic resource specs. Every enum, nested JSON shape, validation rule, and read/write difference below comes from those specs.

Source:

Models

Model Purpose
ProductKnowledge Foundational, reusable definition of a product (medication, nutritional product, or consumable)

ProductKnowledge extends SlugBaseModel, which extends EMRBaseModel (the shared base providing external_id, created_date/modified_date, soft-delete via deleted, created_by/updated_by, and history/meta JSON). SlugBaseModel sets FACILITY_SCOPED = True and adds the slug helpers calculate_slug and parse_slug, so a record is addressable by an instance slug (i-<slug>) or a facility slug (f-<facility_external_id>-<slug>).

ProductKnowledge fields

Identity & scope

Field Type Notes
facility FK → facility.Facility PROTECT, nullable. Null means an instance-level definition; set means facility-scoped. Excluded from the spec base (__exclude__ = ["facility"]); set on write via a facility UUID, surfaced on read as is_instance_level
slug CharField(255) Slug value; combined with facility to form the addressable slug. Set server-side from slug_value on write
alternate_identifier CharField(255) Nullable; secondary external identifier
status CharField(255) Lifecycle status. Spec-constrained to ProductKnowledgeStatusOptions (required)
product_type CharField(255) Product category. Spec-constrained to ProductTypeOptions (required)
category FK → emr.ResourceCategory CASCADE, nullable; classifies the product into a resource category. Write it as a category slug string, resolved via ResourceCategory.objects.get(slug=...); read returns it as a nested object

Naming

Field Type Notes
name CharField(255) Primary display name (required in spec)
names JSONField (list) Nullable; list of ProductName { name_type: ProductNameTypes, name: str }. name_type is bound to the ProductNameTypes enum, not a FHIR value set
names_cache CharField(2048) Nullable; denormalized space-joined string of name plus every names entry, rebuilt on each save() to power search. Platform-maintained, absent from every spec — never set it from clients

Coding & definition (JSON fields)

The model stores these as bare JSONFields; the spec enforces the shapes below.

Field Model type Spec type / shape Notes
code JSONField (dict) Coding | None Nullable; product code as a Coding, not value-set bound at the spec layer
base_unit JSONField (dict) ValueSetBoundCoding[MANSOQ_UCUM_UNITS] Required. A Coding bound to the UCUM units value set (system-ucum-units)
names JSONField (list) list[ProductName] | None See ProductName
storage_guidelines JSONField (list) list[StorageGuideline] | None See StorageGuideline
definitional JSONField (dict) ProductDefinitionSpec | None Type-specific definition payload. See ProductDefinitionSpec

Enums

ProductTypeOptions (product_type)

Value
medication
nutritional_product
consumable

ProductKnowledgeStatusOptions (status)

Value
draft
active
retired
unknown

ProductNameTypes (names[].name_type)

Value
trade_name
alias
original_name
preferred

DrugCharacteristicCode (definitional.drug_characteristic[].code)

Value
imprint_code
size
shape
color
coating
scoring
logo
image

Nested spec shapes

These are plain Pydantic BaseModels in spec.py, not value-set bound unless noted.

ProductName

names[] — alternate names for the product.

Field Type Required Notes
name_type ProductNameTypes yes Enum, see above
name str yes

StorageGuideline

storage_guidelines[] — a storage note paired with a stability duration.

Field Type Required Notes
note str yes
stability_duration DurationSpec { value: int, unit: Coding } yes value is an integer count; unit is a free Coding

ProductDefinitionSpec

definitional — type-specific definition payload.

Field Type Default Notes
dosage_form ValueSetBoundCoding[MEDICATION_FORM_CODES] | None required (nullable) Coding bound to medication form codes (system-medication-form-codes, SNOMED is-a 736542009)
intended_routes list[Coding] [] Free Codings, not value-set bound
ingredients list[ProductIngredient] [] See ProductIngredient
nutrients list[ProductNutrient] [] See ProductNutrient
drug_characteristic list[DrugCharacteristic] [] See DrugCharacteristic

ProductIngredient

definitional.ingredients[].

Field Type Required Notes
is_active bool yes Active vs. inactive ingredient
substance ValueSetBoundCoding[MANSOQ_SUBSTANCE_VALUSET] yes Coding bound to the substance value set (system-substance, SNOMED is-a 105590001)
strength ProductStrength yes See ProductStrength

ProductNutrient

definitional.nutrients[].

Field Type Required Notes
item ValueSetBoundCoding[MANSOQ_NUTRIENTS_VALUESET] yes Coding bound to the nutrients value set (system-nutrients, SNOMED is-a 226355009)
amount ProductStrength yes See ProductStrength

ProductStrength

Shared by ProductIngredient.strength and ProductNutrient.amount.

Field Type Required Notes
ratio Ratio { numerator: Quantity, denominator: Quantity } yes
quantity Quantity yes See Quantity

DrugCharacteristic

definitional.drug_characteristic[].

Field Type Required Notes
code DrugCharacteristicCode yes Enum, see above
value str yes

Shared common types

Coding

From care/emr/resources/common/coding.py (extra="forbid").

Field Type Required
system str | None no
version str | None no
code str yes
display str | None no

A ValueSetBoundCoding[<slug>] is a Coding subclass that additionally validates code against the named value set on deserialization.

Quantity

From care/emr/resources/common/quantity.py (extra="forbid").

Field Type Notes
value Decimal | None max 20 digits, 6 decimal places
unit Coding | None human-readable unit
code Coding | None machine-processable unit
meta dict | None

Ratio { numerator: Quantity, denominator: Quantity } — both required.

Resource specs (API schema)

Every spec builds on EMRResource (care/emr/resources/base.py), which provides serialize (DB → Pydantic), de_serialize (Pydantic → DB), and the perform_extra_serialization / perform_extra_deserialization hooks. EMRResource.serialize copies only non-FK database fields that are declared spec fields and not in __exclude__.

Spec class Role Adds / behaviour
BaseProductKnowledgeSpec shared base __exclude__ = ["facility"]. Fields: id, alternate_identifier, status, product_type, code, base_unit (required, UCUM-bound), name, names, storage_guidelines, definitional
ProductKnowledgeUpdateSpec write · update Adds category: str | None (resource-category slug) and slug_value: SlugType (required). On deserialize, resolves category via ResourceCategory.objects.get(slug=...) and sets obj.slug = self.slug_value
ProductKnowledgeWriteSpec write · create Extends the update spec; adds facility: UUID4 | None. On deserialize, runs the update-spec logic, then resolves facility via Facility.objects.get(external_id=...) when supplied
ProductKnowledgeReadSpec read · detail/list Adds is_instance_level: bool, category: dict | None, slug_config: dict, slug: str. On serialize, sets id = external_id, is_instance_level = not facility_id, serializes category via ResourceCategoryReadSpec, and sets slug_config = parse_slug(slug)

Validation & bound value sets

Server-maintained behaviour

Methods & save behaviour

save() side effects

save() rebuilds names_cache before persisting:

  1. names_cache is reset to "<name> ".
  2. For each entry in names, the entry's name (dict key or attribute) is appended, space-separated.
  3. super().save() persists the record, including the slug logic inherited from SlugBaseModel.

names_cache is platform-maintained — write to name and names instead of setting it directly.

API integration notes

Related

Source evidence

References are pinned to the private MANSOQ source repository. Repository access is required to open these links.