Toggle menu
Toggle preferences menu
Toggle personal menu
Not logged in
Your IP address will be publicly visible if you make any edits.

References/Product

From OHC Network Wiki
Revision as of 04:15, 5 July 2026 by Admin (talk | contribs) (OHC identity seed)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
referencesupplyCARE 3.0+

A Product is a concrete batch of stock at a facility — a single lot of a medication, nutritional product, or consumable, with its own expiry, lot number, and purchase price. It instantiates a catalogue entry: everything generic about the item (name, codes, dosage form, product_type) lives on the linked ProductKnowledge, and a Product adds only what's true of one batch.

The Django model is storage; the shape you send and receive is defined by the Pydantic resource specs, which set the enums, narrow the model's opaque JSONFields, enforce validation, and split read from write. The fields below note where the spec diverges from the column; Resource specs (API schema) covers the full read/write split.

Source:

Models

Model Purpose
Product A concrete, batch-level instantiation of a ProductKnowledge definition at a facility (medication, nutritional product, or consumable)

Product extends EMRBaseModel, which supplies external_id, audit fields, and soft-delete semantics.

Product fields

Relationships

All three foreign keys are PROTECT, so a referenced row can't be deleted while a product points at it.

Field Type Notes
facility FK → facility.Facility (PROTECT) Required at storage, but not a spec field — the viewset sets it from the URL, never the request body.

{{Field|model=Product|section=Relationships|name=product_knowledge|type=FK → emr.ProductKnowledge (PROTECT)|notes=The catalogue definition this batch instantiates. Write takes a slug string and resolves it; read returns the nested [[References/Product Knowledge}} {{Field|model=Product|section=Relationships|name=charge_item_definition|type=FK → emr.ChargeItemDefinition (PROTECT, nullable)|notes=Drives charge-item creation when the product is billed. Write takes a slug string; read returns the nested [[References/Charge Item Definition}}

Classification & status

Field Type Required Notes
status CharField(255) yes (spec)
product_type CharField(255)

Batch & pricing

Field Type Required Default Notes
batch JSONField (nullable) no
expiration_date DateTimeField (nullable) no
standard_pack_size IntegerField (nullable) no
purchase_price DecimalField(max_digits=20, decimal_places=6) (nullable) no
extensions JSONField yes (spec)

Enums

ProductStatusOptions values

Defined in [https://github.com/ohcnetwork/care/blob/develop/care/emr/resources/inventory/product/spec.py spec.py] and bound to the model status field.

Value Meaning
active Product is in use
inactive Product is no longer in use but retained
entered_in_error Record created in error

ProductTypeOptions values

Not a field on the Product spec. It's defined in [https://github.com/ohcnetwork/care/blob/develop/care/emr/resources/inventory/product_knowledge/spec.py product_knowledge/spec.py] and carried on the linked ProductKnowledge; it appears here only because the model keeps a product_type storage column.

Value
medication
nutritional_product
consumable

Nested JSON shapes

ProductBatch shape

Structure of the batch JSONField (care/emr/resources/inventory/product/spec.py).

Field Type Required Default Notes
lot_number str no

Resource specs (API schema)

Every spec derives from EMRResource ([https://github.com/ohcnetwork/care/blob/develop/care/emr/resources/base.py care/emr/resources/base.py]), which supplies serialize (DB → pydantic, via the perform_extra_serialization hook) and de_serialize (pydantic → DB, via the perform_extra_deserialization hook).

Spec class Role Fields beyond the base Behaviour
BaseProductSpec shared id, status, batch, expiration_date, extensions, standard_pack_size, purchase_price __model__ = Product. __exclude__ = ["product_knowledge", "charge_item_definition"] — these FKs are resolved by the hooks, not by automatic field mapping. ___extension_resource_type__ = ExtensionResource.product.
ProductWriteSpec write · create None (slug) Mixes in ExtensionValidator. perform_extra_deserialization resolves product_knowledge via get_object_or_404(ProductKnowledge, slug=...) and, when present, charge_item_definition via get_object_or_404(ChargeItemDefinition, slug=...).
ProductUpdateSpec write · update None (slug) Mixes in ExtensionValidator. perform_extra_deserialization resolves charge_item_definition via ChargeItemDefinition.objects.get(slug=...) when supplied. product_knowledge is immutable after create, so it is not re-bound.
ProductReadSpec read · list & detail None perform_extra_serialization sets id = external_id and inlines product_knowledge (via ProductKnowledgeReadSpec) and, when set, charge_item_definition (via ChargeItemDefinitionReadSpec). One spec serves both list and detail.

Consequences for an integrator:

  • product_type and facility never appear in the request or response. facility comes from the route; product_type comes from ProductKnowledge.
  • product_knowledge and charge_item_definition go out as slug strings and come back as fully serialized nested objects.
  • extensions is validated on write against the JSON schemas registered for ExtensionResource.product. Keys with no registered handler are silently dropped — current behaviour, with a TODO to make it an error.
  • There is no server-side status_history; Product does not track status changes.
  • The base de_serialize dumps with exclude_defaults=True, so unset optional fields never reach the model.

Methods & save behaviour

Product adds no methods of its own. Persistence, external_id, audit fields, and soft delete all come from EMRBaseModel.

  • Write: request body → ProductWriteSpec / ProductUpdateSpecde_serializeperform_extra_deserialization (FK slug resolution) → obj.save().
  • Read: Product row → ProductReadSpec.serializeperform_extra_serialization (inline product_knowledge and charge_item_definition) → JSON.
  • PROTECT on every FK blocks deletion of a referenced Facility, ProductKnowledge, or ChargeItemDefinition while products still reference it.

API integration notes

  • A Product carries batch data only. Pull name, codes, dosage form, and product_type from the linked ProductKnowledge — they aren't duplicated here.
  • Send product_knowledge and charge_item_definition as slugs on write; both return as full nested objects on read.
  • Set charge_item_definition to wire the product into billing — charge items are created automatically when it's billed.
  • batch is a structured { lot_number }, not a free-form dict, even though the column is a JSONField.
  • Use extensions for deployment-specific key-value data without a schema migration. Values are checked against the schemas registered for the product extension resource.

Pages that link here: