///|
/// A lot represents one physical stock batch at one facility. Quantities use
/// integer grams, avoiding rounding during the recall traversal.
pub(all) struct Lot {
  id : String
  product : String
  facility : String
  quantity_g : Int64
  produced_at : Int64
} derive(Eq, Debug, ToJson, FromJson)

///|
/// One quantity of a lot consumed or produced in a processing event.
pub(all) struct Portion {
  lot_id : String
  quantity_g : Int64
} derive(Eq, Debug, ToJson, FromJson)

///|
/// A transformation links ingredient lots to output lots. Every output is
/// conservatively considered exposed when any input lot is recalled.
pub(all) struct Transformation {
  id : String
  facility : String
  occurred_at : Int64
  inputs : Array[Portion]
  outputs : Array[Portion]
} derive(Eq, Debug, ToJson, FromJson)

///|
/// A shipment sends some of a finished lot to an external consignee.
pub(all) struct Shipment {
  id : String
  lot_id : String
  quantity_g : Int64
  consignee : String
  shipped_at : Int64
  reference : String
} derive(Eq, Debug, ToJson, FromJson)

///|
/// The document contains only operational facts. Recall decisions live in a
/// separate request so the same ledger can be queried repeatedly.
pub(all) struct Ledger {
  schema_version : Int32
  lots : Array[Lot]
  transformations : Array[Transformation]
  shipments : Array[Shipment]
} derive(Eq, Debug, ToJson, FromJson)

///|
/// A recall request starts from known suspect lots, at an explicit cutoff.
pub(all) struct RecallRequest {
  id : String
  reason : String
  seed_lots : Array[String]
  as_of : Int64
} derive(Eq, Debug, ToJson, FromJson)