///|
fn q(units : Int) -> @mrp.Qty {
@mrp.Qty::int(units)
}
///|
fn qm(milli : Int64) -> @mrp.Qty {
@mrp.Qty::milli(milli)
}
///|
fn item(
sku : String,
stock? : @mrp.Qty = @mrp.Qty::zero(),
safety? : @mrp.SafetyStock = @mrp.SafetyStock::Fixed(@mrp.Qty::zero()),
max? : @mrp.Qty = @mrp.Qty::zero(),
lead? : Int = 0,
yield_rate? : Int = 1000,
lot? : @mrp.LotRule = @mrp.LotForLot,
unit? : String = "",
phantom? : Bool = false,
) -> @mrp.Item {
{
sku,
unit,
on_hand: stock,
safety,
max_stock: max,
phantom,
lead_time: lead,
yield_per_mille: yield_rate,
lot,
}
}
///|
fn demand(
id : String,
item : String,
bucket : Int,
quantity : @mrp.Qty,
) -> @mrp.Movement {
{ id, item, bucket, quantity, }
}
///|
fn show(label : String, p : @mrp.Plan) -> Unit {
println(label)
for o in p.orders {
println(
" " +
o.item +
": quantity=" +
o.quantity.to_string() +
"; release=" +
o.release_bucket.to_string() +
"; receipt=" +
o.receipt_bucket.to_string() +
(if o.late { "; late" } else { "" }),
)
}
}
///|
fn main {
let assembly : @mrp.Request = {
horizon: 6,
items: [
item("KIT", stock=q(1), lead=1, lot=@mrp.Multiple(q(5))),
item("MODULE", stock=q(4), lead=2, lot=@mrp.Multiple(q(4))),
item("RAW", stock=q(5), lead=1),
],
bom: [
{
parent: "KIT",
child: "MODULE",
units_per: q(2),
usage_unit: "",
scrap_per_mille: 0,
},
{
parent: "MODULE",
child: "RAW",
units_per: q(3),
usage_unit: "",
scrap_per_mille: 0,
},
{
parent: "KIT",
child: "RAW",
units_per: q(1),
usage_unit: "",
scrap_per_mille: 0,
},
],
demands: [
demand("sales", "KIT", 5, q(9)),
demand("spares", "MODULE", 4, q(3)),
],
receipts: [demand("open-po", "MODULE", 4, q(5))],
conversions: [],
policy: @mrp.Strict,
}
let plan = @mrp.plan(assembly).unwrap()
show("1 assembly and shared raw material", plan)
let retail : @mrp.Request = {
horizon: 4,
items: [
item(
"LABEL",
stock=q(3),
safety=@mrp.SafetyStock::Fixed(q(2)),
lot=@mrp.MinimumMultiple(q(10), q(4)),
),
],
bom: [],
demands: [
demand("day1", "LABEL", 1, q(9)),
demand("day2", "LABEL", 2, q(3)),
],
receipts: [demand("incoming", "LABEL", 1, q(2))],
conversions: [],
policy: @mrp.Strict,
}
show(
"2 replenishment with minimum order and safety stock",
@mrp.plan(retail).unwrap(),
)
let repair : @mrp.Request = {
horizon: 4,
items: [item("PART", stock=q(2), lead=1)],
bom: [],
demands: [demand("repair", "PART", 3, q(8))],
receipts: [],
conversions: [],
policy: @mrp.Strict,
}
show("3 before supplier confirmation", @mrp.plan(repair).unwrap())
show(
"3 after supplier confirmation",
@mrp.plan({ ..repair, receipts: [demand("confirmed", "PART", 2, q(3))], }).unwrap(),
)
let coating : @mrp.Request = {
horizon: 3,
items: [item("PANEL", yield_rate=950), item("PAINT", unit="KG")],
bom: [
{
parent: "PANEL",
child: "PAINT",
units_per: q(2500),
usage_unit: "G",
scrap_per_mille: 200,
},
],
demands: [demand("frame", "PANEL", 1, q(8))],
receipts: [],
conversions: [{ from: "G", to: "KG", factor: qm(1L), }],
policy: @mrp.Strict,
}
let coated = @mrp.plan(coating).unwrap()
show("4 yield, scrap, fractional usage and unit conversion", coated)
println(
" PAINT gross=" + coated.items[0].buckets[1].gross.to_string() + " KG",
)
let urgent : @mrp.Request = {
horizon: 4,
items: [item("PART", stock=q(2), lead=2)],
bom: [],
demands: [demand("urgent", "PART", 1, q(5))],
receipts: [],
conversions: [],
policy: @mrp.Strict,
}
println(
"5 early release rejected under strict policy=" +
(if @mrp.plan(urgent) is Err(_) { "true" } else { "false" }),
)
let relaxed = { ..urgent, policy: @mrp.AllowLate, }
let late_plan = @mrp.plan(relaxed).unwrap()
show("5 accepted under allow-late policy", late_plan)
for incident in late_plan.incidents {
println(
" incident: " +
incident.item +
" receipt=" +
incident.receipt_bucket.to_string() +
" required_release=" +
incident.required_release.to_string() +
" actual_release=" +
incident.actual_release.to_string(),
)
}
let cycle = {
..repair,
items: [item("A"), item("B")],
demands: [],
bom: [
{
parent: "A",
child: "B",
units_per: q(1),
usage_unit: "",
scrap_per_mille: 0,
},
{
parent: "B",
child: "A",
units_per: q(1),
usage_unit: "",
scrap_per_mille: 0,
},
],
}
println(
"cycle rejected=" +
(if @mrp.plan(cycle) is Err(_) { "true" } else { "false" }),
)
let policy_request : @mrp.Request = {
horizon: 3,
items: [
item("SUB", phantom=true),
item(
"BOLT",
stock=q(100),
max=q(50),
safety=@mrp.SafetyStock::PercentOfDemand(20),
),
item("FRAME", stock=q(10), lot=@mrp.ReorderPoint(q(5), q(20))),
],
bom: [
{
parent: "SUB",
child: "BOLT",
units_per: q(3),
usage_unit: "",
scrap_per_mille: 0,
},
],
demands: [
demand("sub-order", "SUB", 1, q(4)),
demand("frame-order", "FRAME", 1, q(8)),
],
receipts: [],
conversions: [],
policy: @mrp.Strict,
}
let policy_plan = @mrp.plan(policy_request).unwrap()
show("6 phantom BOM, percent safety stock and reorder point", policy_plan)
for exception in policy_plan.exceptions {
println(
" excess: " +
exception.item +
" bucket=" +
exception.bucket.to_string() +
" projected=" +
exception.projected.to_string() +
" max=" +
exception.maximum.to_string(),
)
}
println("assembly JSON")
println(plan.to_json().stringify())
}