BEGIN TRANSACTION; CREATE TABLE demand ( part_id INTEGER PRIMARY KEY REFERENCES parts(part_id), quantity INTEGER NOT NULL CHECK (quantity >= 0), -- customer demand due Friday starting_stock INTEGER NOT NULL DEFAULT 0 -- inventory on Monday 05:00 ); INSERT INTO "demand" VALUES(1,2760,552); INSERT INTO "demand" VALUES(2,1620,324); INSERT INTO "demand" VALUES(3,2400,480); INSERT INTO "demand" VALUES(4,1680,336); INSERT INTO "demand" VALUES(5,2310,462); INSERT INTO "demand" VALUES(6,1725,345); INSERT INTO "demand" VALUES(7,2380,476); INSERT INTO "demand" VALUES(8,1250,250); INSERT INTO "demand" VALUES(9,2200,440); INSERT INTO "demand" VALUES(10,1700,340); CREATE TABLE disruptions ( id INTEGER PRIMARY KEY AUTOINCREMENT, press_id INTEGER NOT NULL REFERENCES presses(press_id), start_ts TEXT NOT NULL, -- 'YYYY-MM-DD HH:MM' minutes REAL NOT NULL CHECK (minutes > 0), note TEXT ); CREATE TABLE glossary ( section TEXT, symbol TEXT, meaning TEXT, formula TEXT ); INSERT INTO "glossary" VALUES('Sets','P','Presses','p ∈ {1, 2, 3}'); INSERT INTO "glossary" VALUES('Sets','J','Parts. Part j runs only on mold j','j ∈ {1 … 10}'); INSERT INTO "glossary" VALUES('Sets','S','Shifts in time order: S_reg = Mon–Fri, S_ot = Sat/Sun (overtime)','s = 0 … 14 regular, 15 … 20 overtime'); INSERT INTO "glossary" VALUES('Sets','K(p,s)','Ordered blocks of press p in shift s. A block = optional mold change followed by one run','k = 1 … K'); INSERT INTO "glossary" VALUES('Parameters','r[j]','Production rate of part j','parts / press-hour'); INSERT INTO "glossary" VALUES('Parameters','D[j]','Customer demand of part j, due Friday','parts'); INSERT INTO "glossary" VALUES('Parameters','I0[j]','Starting stock on Monday','parts'); INSERT INTO "glossary" VALUES('Parameters','d[j]','Daily demand','d[j] = D[j] / W (W = working days)'); INSERT INTO "glossary" VALUES('Parameters','T[j]','Stock target = DOH days of demand','T[j] = ⌈ DOH · d[j] ⌉'); INSERT INTO "glossary" VALUES('Parameters','need[j]','Parts that must be built this week to ship demand and end on target','need[j] = max(0, D[j] + T[j] − I0[j])'); INSERT INTO "glossary" VALUES('Parameters','τ[j]','Time to install mold j (mold change duration)','hours (default 0.5 = 30 min)'); INSERT INTO "glossary" VALUES('Parameters','L','Shift length','8 hours'); INSERT INTO "glossary" VALUES('Parameters','t[s]','Start time of shift s, in hours after Monday 05:00','t[s] = 24 · ⌊s / shifts_per_day⌋ + L · (s mod shifts_per_day)'); INSERT INTO "glossary" VALUES('Parameters','A[p,s]','Available hours of press p in shift s after unplanned downtime','A[p,s] = L − overlap(shift s, downtime of press p)'); INSERT INTO "glossary" VALUES('Parameters','μ','Minimum run after a mold change','hours (default 1.0)'); INSERT INTO "glossary" VALUES('Parameters','ρ','Shortest run block (no slivers)','hours (default 0.5)'); INSERT INTO "glossary" VALUES('Parameters','ε','Minimum share of a staffed press-shift that must actually run (no crew for a few hours of work)','default 0.5'); INSERT INTO "glossary" VALUES('Parameters','a0[p]','Mold mounted on press p at Monday 05:00','index of mold'); INSERT INTO "glossary" VALUES('Parameters','[α, β]','Start and end of an unplanned downtime window on a press','hours after Monday 05:00'); INSERT INTO "glossary" VALUES('Parameters','ω','Overtime labor premium','default 1.5'); INSERT INTO "glossary" VALUES('Decision variables','z[p,s]','1 if press p is staffed (runs) in shift s. Production only happens in whole 8-hour shifts','z ∈ {0,1}'); INSERT INTO "glossary" VALUES('Decision variables','a[p,s,k,j]','1 if mold j is mounted on press p during block k of shift s','a ∈ {0,1}'); INSERT INTO "glossary" VALUES('Decision variables','h[p,s,k,j]','Hours press p runs part j in block k of shift s','h ≥ 0'); INSERT INTO "glossary" VALUES('Decision variables','S[p,s,k]','Start time of block k (its mold change starts here)','hours, continuous'); INSERT INTO "glossary" VALUES('Decision variables','E[p,s,k]','End time of block k','hours, continuous'); INSERT INTO "glossary" VALUES('Decision variables','X[j]','Parts produced this week','X[j] = r[j] · Σ h[p,s,k,j]'); INSERT INTO "glossary" VALUES('Assist variables','g[p,s,k,j]','1 if a mold change *into* mold j starts block k. Exact linearisation of a[k,j] · (1 − a[k−1,j])','0 ≤ g ≤ 1, forced to 0/1 by (2)'); INSERT INTO "glossary" VALUES('Assist variables','o[p,s,j]','1 if mold j sits on press p at any moment of shift s (incl. carried in from the previous shift)','0 ≤ o ≤ 1'); INSERT INTO "glossary" VALUES('Assist variables','y[p,s,k]','1 if block k of press p in shift s runs a part','y ∈ {0,1}'); INSERT INTO "glossary" VALUES('Assist variables','w[p,q,s,k,l]','Order of two mold changes: 1 = press p changes before press q','w ∈ {0,1}'); INSERT INTO "glossary" VALUES('Assist variables','θ','1 if a block lies before a downtime window, 0 if after it','θ ∈ {0,1}'); INSERT INTO "glossary" VALUES('Assist variables','b[j]','1 if part j needs overtime to cover demand (demand-only overtime mode)','b ∈ {0,1}'); INSERT INTO "glossary" VALUES('Assist variables','u[j]','Unmet customer demand','u[j] ≥ D[j] − I0[j] − X[j], u ≥ 0'); INSERT INTO "glossary" VALUES('Assist variables','v[j]','Missing stock versus target (parts)','v[j] ≥ need[j] − X[j], v ≥ 0'); INSERT INTO "glossary" VALUES('Constraints','(1) one mold','Exactly one mold is mounted in every block','Σ_j a[p,s,k,j] = 1'); INSERT INTO "glossary" VALUES('Constraints','(2) mold change','A change starts a block only if the mounted mold differs from the previous block (carried across shifts and from a0)','g ≥ a[k,j] − a[k−1,j]; g ≤ a[k,j]; g ≤ 1 − a[k−1,j]; g ≤ z[p,s]'); INSERT INTO "glossary" VALUES('Constraints','(3) run capacity','Run only the mounted mold, only in staffed shifts, only in available time','h[k,j] ≤ A[p,s] · a[k,j]; ε · A[p,s] · z ≤ Σ h ≤ A[p,s] · z'); INSERT INTO "glossary" VALUES('Constraints','(4) minimum run','Every mold change is followed by a real run, and a block runs at least ρ hours or not at all (no pointless changeovers, no slivers)','Σ_j h[k,j] ≥ μ · Σ_j g[k,j]; ρ · y ≤ Σ_j h[k,j] ≤ A[p,s] · y'); INSERT INTO "glossary" VALUES('Constraints','(5) timeline','Blocks are sequential inside the shift; a change of length Σ τ[j]·g precedes the run','E[k] = S[k] + Σ_j τ[j]·g[k,j] + Σ_j h[k,j]; S[1] ≥ t[s]; S[k+1] ≥ E[k]; E[K] ≤ t[s] + L'); INSERT INTO "glossary" VALUES('Constraints','(6) one change at a time','No two mold changes overlap, on any two presses (big-M disjunction)','S_p + Δ_p ≤ S_q + L(1 − w) + L(2 − c_p − c_q); S_q + Δ_q ≤ S_p + L·w + L(2 − c_p − c_q); Δ = Σ τ·g, c = Σ g'); INSERT INTO "glossary" VALUES('Constraints','(7) one mold, one press','A physical mold can only be on one press in a shift','o[p,s,j] ≥ a[p,s,k,j]; o[p,s,j] ≥ a[p,s−1,K,j]; Σ_p o[p,s,j] ≤ 1'); INSERT INTO "glossary" VALUES('Constraints','(8) downtime','A block is entirely before or entirely after each unplanned-downtime window','E ≤ α + M(1 − θ); S ≥ β − M·θ'); INSERT INTO "glossary" VALUES('Constraints','(9) stock balance','Demand shortfall, stock shortfall and overbuild cap','u ≥ D − I0 − X; v ≥ need − X; X ≤ need + extra'); INSERT INTO "glossary" VALUES('Constraints','(10) overtime policy','off: z = 0 on weekend shifts. demand: weekend output of part j only covers demand the weekdays could not','X_ot[j] ≤ M·b[j]; X_ot[j] ≤ D[j] − I0[j] − X_reg[j] + M(1 − b[j])'); INSERT INTO "glossary" VALUES('Objectives (lexicographic)','f_delivery','On-time delivery: parts short of demand at the Friday ship date','min Σ_j u[j]'); INSERT INTO "glossary" VALUES('Objectives (lexicographic)','f_doh','Days on hand by Friday: stock missing versus target, in thousandths of a day of demand','min 1000 · Σ_j v[j] / max(d[j], 1)'); INSERT INTO "glossary" VALUES('Objectives (lexicographic)','f_idle','Machine utilisation: staffed press-hours not spent running parts (includes mold changes and waiting)','min Σ_{p,s} A[p,s]·z[p,s] − Σ h'); INSERT INTO "glossary" VALUES('Objectives (lexicographic)','f_labor','Labor hours: staffed press-shift hours, overtime weighted by ω','min Ops · L · (Σ_reg z + ω · Σ_ot z)'); INSERT INTO "glossary" VALUES('Objectives (lexicographic)','Lexicographic step','Solve goals in the chosen order; after goal i is solved to value f_i*, add f_i ≤ f_i* to the model and solve goal i+1','lexmin (f_π1, f_π2, f_π3, f_π4)'); INSERT INTO "glossary" VALUES('Objectives (lexicographic)','Demand floor','Optional (default on): before the chosen order, Σ u is minimised and frozen, so labor or idle ranked first can never plan below the customer demand','Σ_j u[j] ≤ u* (u* = min Σ_j u[j])'); INSERT INTO "glossary" VALUES('Objectives (lexicographic)','Tie-break','After the four goals, fewest mold changes','min Σ g'); INSERT INTO "glossary" VALUES('Dashboard metrics','Ending stock','Stock after shipping the weekly demand','End[j] = I0[j] + X[j] − D[j]'); INSERT INTO "glossary" VALUES('Dashboard metrics','DOH','Days on hand at Friday','DOH[j] = End[j] / d[j]'); INSERT INTO "glossary" VALUES('Dashboard metrics','Capacity','Press-hours available Monday–Friday','3 presses × shifts/day × 5 days × 8 h (120 h for 1 shift/day would be 3 × 8 × 5)'); INSERT INTO "glossary" VALUES('Dashboard metrics','Utilisation','Share of available press-hours spent running or changing molds','(Σ run h + Σ change h) / capacity'); INSERT INTO "glossary" VALUES('Dashboard metrics','Outcome','Covered: demand + DOH target met. Shortfall: demand met, DOH short. Short: demand not met','u = 0 and v = 0 → Covered; u = 0, v > 0 → Shortfall; u > 0 → Short'); CREATE TABLE molds ( mold_id INTEGER PRIMARY KEY, name TEXT NOT NULL, change_minutes REAL NOT NULL DEFAULT 30 -- time to install this mold on a press ); INSERT INTO "molds" VALUES(1,'Mold 1',30.0); INSERT INTO "molds" VALUES(2,'Mold 2',30.0); INSERT INTO "molds" VALUES(3,'Mold 3',30.0); INSERT INTO "molds" VALUES(4,'Mold 4',30.0); INSERT INTO "molds" VALUES(5,'Mold 5',30.0); INSERT INTO "molds" VALUES(6,'Mold 6',30.0); INSERT INTO "molds" VALUES(7,'Mold 7',30.0); INSERT INTO "molds" VALUES(8,'Mold 8',30.0); INSERT INTO "molds" VALUES(9,'Mold 9',30.0); INSERT INTO "molds" VALUES(10,'Mold 10',30.0); CREATE TABLE parts ( part_id INTEGER PRIMARY KEY, name TEXT NOT NULL, mold_id INTEGER NOT NULL REFERENCES molds(mold_id), rate_per_hour REAL NOT NULL CHECK (rate_per_hour > 0) -- good parts per press-hour ); INSERT INTO "parts" VALUES(1,'Part 1',1,120.0); INSERT INTO "parts" VALUES(2,'Part 2',2,90.0); INSERT INTO "parts" VALUES(3,'Part 3',3,150.0); INSERT INTO "parts" VALUES(4,'Part 4',4,60.0); INSERT INTO "parts" VALUES(5,'Part 5',5,110.0); INSERT INTO "parts" VALUES(6,'Part 6',6,75.0); INSERT INTO "parts" VALUES(7,'Part 7',7,140.0); INSERT INTO "parts" VALUES(8,'Part 8',8,50.0); INSERT INTO "parts" VALUES(9,'Part 9',9,100.0); INSERT INTO "parts" VALUES(10,'Part 10',10,85.0); CREATE TABLE plan_runs ( run_id INTEGER PRIMARY KEY AUTOINCREMENT, created_at TEXT NOT NULL, summary TEXT -- JSON: objective values, solver log ); INSERT INTO "plan_runs" VALUES(1,'2026-09-21 06:54','{"input_hash": "d79abb4516ecc624408bbcac1652254617c6684a", "blocks": [{"kind": "change", "s": 1, "start": 8.0, "end": 8.5, "from_mold": 0, "mold": 7, "press": 0, "overtime": false, "seq": 1, "press_id": 1, "minutes": 30.0, "start_ts": "2026-09-28 13:00", "end_ts": "2026-09-28 13:30", "next": {"kind": "run", "part": 7, "mold": 7, "start_ts": "2026-09-28 13:30", "minutes": 360.0}, "part": 7}, {"kind": "run", "s": 1, "start": 8.5, "end": 14.5, "mold": 7, "part": 7, "qty": 300, "press": 0, "overtime": false, "seq": 2, "press_id": 1, "minutes": 360.0, "start_ts": "2026-09-28 13:30", "end_ts": "2026-09-28 19:30", "next": {"kind": "change", "part": null, "mold": 1, "start_ts": "2026-09-28 19:30", "minutes": 30.0}}, {"kind": "change", "s": 1, "start": 14.5, "end": 15.0, "from_mold": 7, "mold": 1, "press": 0, "overtime": false, "seq": 3, "press_id": 1, "minutes": 30.0, "start_ts": "2026-09-28 19:30", "end_ts": "2026-09-28 20:00", "next": {"kind": "run", "part": 1, "mold": 1, "start_ts": "2026-09-28 20:00", "minutes": 149.4}, "part": 1}, {"kind": "run", "s": 1, "start": 15.0, "end": 17.49, "mold": 1, "part": 1, "qty": 224, "press": 0, "overtime": false, "seq": 4, "press_id": 1, "minutes": 149.4, "start_ts": "2026-09-28 20:00", "end_ts": "2026-09-28 22:29", "next": {"kind": "change", "part": null, "mold": 9, "start_ts": "2026-09-28 22:29", "minutes": 30.0}}, {"kind": "change", "s": 2, "start": 17.49, "end": 17.99, "from_mold": 1, "mold": 9, "press": 0, "overtime": false, "seq": 5, "press_id": 1, "minutes": 30.0, "start_ts": "2026-09-28 22:29", "end_ts": "2026-09-28 22:59", "next": {"kind": "run", "part": 9, "mold": 9, "start_ts": "2026-09-28 22:59", "minutes": 360.6}, "part": 9}, {"kind": "run", "s": 2, "start": 17.99, "end": 24.0, "mold": 9, "part": 9, "qty": 511, "press": 0, "overtime": false, "seq": 6, "press_id": 1, "minutes": 360.6, "start_ts": "2026-09-28 22:59", "end_ts": "2026-09-29 05:00", "next": {"kind": "change", "part": null, "mold": 0, "start_ts": "2026-09-29 05:30", "minutes": 30.0}}, {"kind": "change", "s": 3, "start": 24.5, "end": 25.0, "from_mold": 9, "mold": 0, "press": 0, "overtime": false, "seq": 7, "press_id": 1, "minutes": 30.0, "start_ts": "2026-09-29 05:30", "end_ts": "2026-09-29 06:00", "next": {"kind": "run", "part": 0, "mold": 0, "start_ts": "2026-09-29 06:00", "minutes": 120.0}, "part": 0}, {"kind": "run", "s": 3, "start": 25.0, "end": 27.0, "mold": 0, "part": 0, "qty": 240, "press": 0, "overtime": false, "seq": 8, "press_id": 1, "minutes": 120.0, "start_ts": "2026-09-29 06:00", "end_ts": "2026-09-29 08:00", "next": {"kind": "change", "part": null, "mold": 9, "start_ts": "2026-09-29 08:00", "minutes": 30.0}}, {"kind": "change", "s": 3, "start": 27.0, "end": 27.5, "from_mold": 0, "mold": 9, "press": 0, "overtime": false, "seq": 9, "press_id": 1, "minutes": 30.0, "start_ts": "2026-09-29 08:00", "end_ts": "2026-09-29 08:30", "next": {"kind": "run", "part": 9, "mold": 9, "start_ts": "2026-09-29 08:30", "minutes": 270.0}, "part": 9}, {"kind": "run", "s": 3, "start": 27.5, "end": 32.0, "mold": 9, "part": 9, "qty": 383, "press": 0, "overtime": false, "seq": 10, "press_id": 1, "minutes": 270.0, "start_ts": "2026-09-29 08:30", "end_ts": "2026-09-29 13:00", "next": {"kind": "change", "part": null, "mold": 2, "start_ts": "2026-09-29 13:00", "minutes": 30.0}}, {"kind": "change", "s": 4, "start": 32.0, "end": 32.5, "from_mold": 9, "mold": 2, "press": 0, "overtime": false, "seq": 11, "press_id": 1, "minutes": 30.0, "start_ts": "2026-09-29 13:00", "end_ts": "2026-09-29 13:30", "next": {"kind": "run", "part": 2, "mold": 2, "start_ts": "2026-09-29 13:30", "minutes": 450.0}, "part": 2}, {"kind": "run", "s": 4, "start": 32.5, "end": 40.0, "mold": 2, "part": 2, "qty": 1125, "press": 0, "overtime": false, "seq": 12, "press_id": 1, "minutes": 450.0, "start_ts": "2026-09-29 13:30", "end_ts": "2026-09-29 21:00", "next": {"kind": "run", "part": 2, "mold": 2, "start_ts": "2026-09-30 05:00", "minutes": 30.0}}, {"kind": "run", "s": 6, "start": 48.0, "end": 48.5, "mold": 2, "part": 2, "qty": 75, "press": 0, "overtime": false, "seq": 13, "press_id": 1, "minutes": 30.0, "start_ts": "2026-09-30 05:00", "end_ts": "2026-09-30 05:30", "next": {"kind": "change", "part": null, "mold": 6, "start_ts": "2026-09-30 05:30", "minutes": 30.0}}, {"kind": "change", "s": 6, "start": 48.5, "end": 49.0, "from_mold": 2, "mold": 6, "press": 0, "overtime": false, "seq": 14, "press_id": 1, "minutes": 30.0, "start_ts": "2026-09-30 05:30", "end_ts": "2026-09-30 06:00", "next": {"kind": "run", "part": 6, "mold": 6, "start_ts": "2026-09-30 06:00", "minutes": 420.0}, "part": 6}, {"kind": "run", "s": 6, "start": 49.0, "end": 56.0, "mold": 6, "part": 6, "qty": 980, "press": 0, "overtime": false, "seq": 15, "press_id": 1, "minutes": 420.0, "start_ts": "2026-09-30 06:00", "end_ts": "2026-09-30 13:00", "next": {"kind": "change", "part": null, "mold": 0, "start_ts": "2026-09-30 13:00", "minutes": 30.0}}, {"kind": "change", "s": 7, "start": 56.0, "end": 56.5, "from_mold": 6, "mold": 0, "press": 0, "overtime": false, "seq": 16, "press_id": 1, "minutes": 30.0, "start_ts": "2026-09-30 13:00", "end_ts": "2026-09-30 13:30", "next": {"kind": "run", "part": 0, "mold": 0, "start_ts": "2026-09-30 13:30", "minutes": 450.0}, "part": 0}, {"kind": "run", "s": 7, "start": 56.5, "end": 64.0, "mold": 0, "part": 0, "qty": 900, "press": 0, "overtime": false, "seq": 17, "press_id": 1, "minutes": 450.0, "start_ts": "2026-09-30 13:30", "end_ts": "2026-09-30 21:00", "next": {"kind": "change", "part": null, "mold": 7, "start_ts": "2026-10-01 13:00", "minutes": 30.0}}, {"kind": "change", "s": 10, "start": 80.0, "end": 80.5, "from_mold": 0, "mold": 7, "press": 0, "overtime": false, "seq": 18, "press_id": 1, "minutes": 30.0, "start_ts": "2026-10-01 13:00", "end_ts": "2026-10-01 13:30", "next": {"kind": "run", "part": 7, "mold": 7, "start_ts": "2026-10-01 13:30", "minutes": 300.6}, "part": 7}, {"kind": "run", "s": 10, "start": 80.5, "end": 85.51, "mold": 7, "part": 7, "qty": 251, "press": 0, "overtime": false, "seq": 19, "press_id": 1, "minutes": 300.6, "start_ts": "2026-10-01 13:30", "end_ts": "2026-10-01 18:30", "next": {"kind": "change", "part": null, "mold": 9, "start_ts": "2026-10-01 18:30", "minutes": 30.0}}, {"kind": "change", "s": 10, "start": 85.51, "end": 86.01, "from_mold": 7, "mold": 9, "press": 0, "overtime": false, "seq": 20, "press_id": 1, "minutes": 30.0, "start_ts": "2026-10-01 18:30", "end_ts": "2026-10-01 19:00", "next": {"kind": "run", "part": 9, "mold": 9, "start_ts": "2026-10-01 19:00", "minutes": 119.4}, "part": 9}, {"kind": "run", "s": 10, "start": 86.01, "end": 88.0, "mold": 9, "part": 9, "qty": 169, "press": 0, "overtime": false, "seq": 21, "press_id": 1, "minutes": 119.4, "start_ts": "2026-10-01 19:00", "end_ts": "2026-10-01 21:00", "next": {"kind": "change", "part": null, "mold": 4, "start_ts": "2026-10-02 05:30", "minutes": 30.0}}, {"kind": "change", "s": 12, "start": 96.5, "end": 97.0, "from_mold": 9, "mold": 4, "press": 0, "overtime": false, "seq": 22, "press_id": 1, "minutes": 30.0, "start_ts": "2026-10-02 05:30", "end_ts": "2026-10-02 06:00", "next": {"kind": "run", "part": 4, "mold": 4, "start_ts": "2026-10-02 06:00", "minutes": 330.0}, "part": 4}, {"kind": "run", "s": 12, "start": 97.0, "end": 102.5, "mold": 4, "part": 4, "qty": 605, "press": 0, "overtime": false, "seq": 23, "press_id": 1, "minutes": 330.0, "start_ts": "2026-10-02 06:00", "end_ts": "2026-10-02 11:30", "next": {"kind": "change", "part": null, "mold": 2, "start_ts": "2026-10-02 11:30", "minutes": 30.0}}, {"kind": "change", "s": 12, "start": 102.5, "end": 103.0, "from_mold": 4, "mold": 2, "press": 0, "overtime": false, "seq": 24, "press_id": 1, "minutes": 30.0, "start_ts": "2026-10-02 11:30", "end_ts": "2026-10-02 12:00", "next": {"kind": "run", "part": 2, "mold": 2, "start_ts": "2026-10-02 12:00", "minutes": 60.0}, "part": 2}, {"kind": "run", "s": 12, "start": 103.0, "end": 104.0, "mold": 2, "part": 2, "qty": 150, "press": 0, "overtime": false, "seq": 25, "press_id": 1, "minutes": 60.0, "start_ts": "2026-10-02 12:00", "end_ts": "2026-10-02 13:00", "next": {"kind": "change", "part": null, "mold": 9, "start_ts": "2026-10-02 13:00", "minutes": 30.0}}, {"kind": "change", "s": 13, "start": 104.0, "end": 104.5, "from_mold": 2, "mold": 9, "press": 0, "overtime": false, "seq": 26, "press_id": 1, "minutes": 30.0, "start_ts": "2026-10-02 13:00", "end_ts": "2026-10-02 13:30", "next": {"kind": "run", "part": 9, "mold": 9, "start_ts": "2026-10-02 13:30", "minutes": 450.0}, "part": 9}, {"kind": "run", "s": 13, "start": 104.5, "end": 112.0, "mold": 9, "part": 9, "qty": 637, "press": 0, "overtime": false, "seq": 27, "press_id": 1, "minutes": 450.0, "start_ts": "2026-10-02 13:30", "end_ts": "2026-10-02 21:00", "next": null}, {"kind": "run", "s": 0, "start": 0.0, "end": 0.5, "mold": 1, "part": 1, "qty": 45, "press": 1, "overtime": false, "seq": 1, "press_id": 2, "minutes": 30.0, "start_ts": "2026-09-28 05:00", "end_ts": "2026-09-28 05:30", "next": {"kind": "change", "part": null, "mold": 5, "start_ts": "2026-09-28 05:30", "minutes": 30.0}}, {"kind": "change", "s": 0, "start": 0.5, "end": 1.0, "from_mold": 1, "mold": 5, "press": 1, "overtime": false, "seq": 2, "press_id": 2, "minutes": 30.0, "start_ts": "2026-09-28 05:30", "end_ts": "2026-09-28 06:00", "next": {"kind": "run", "part": 5, "mold": 5, "start_ts": "2026-09-28 06:00", "minutes": 417.6}, "part": 5}, {"kind": "run", "s": 0, "start": 1.0, "end": 7.96, "mold": 5, "part": 5, "qty": 522, "press": 1, "overtime": false, "seq": 3, "press_id": 2, "minutes": 417.6, "start_ts": "2026-09-28 06:00", "end_ts": "2026-09-28 12:57", "next": {"kind": "run", "part": 5, "mold": 5, "start_ts": "2026-09-28 13:00", "minutes": 480.0}}, {"kind": "run", "s": 1, "start": 8.0, "end": 16.0, "mold": 5, "part": 5, "qty": 600, "press": 1, "overtime": false, "seq": 4, "press_id": 2, "minutes": 480.0, "start_ts": "2026-09-28 13:00", "end_ts": "2026-09-28 21:00", "next": {"kind": "change", "part": null, "mold": 7, "start_ts": "2026-09-28 21:00", "minutes": 30.0}}, {"kind": "change", "s": 2, "start": 16.0, "end": 16.5, "from_mold": 5, "mold": 7, "press": 1, "overtime": false, "seq": 5, "press_id": 2, "minutes": 30.0, "start_ts": "2026-09-28 21:00", "end_ts": "2026-09-28 21:30", "next": {"kind": "run", "part": 7, "mold": 7, "start_ts": "2026-09-28 21:30", "minutes": 209.4}, "part": 7}, {"kind": "run", "s": 2, "start": 16.5, "end": 19.990000000000002, "mold": 7, "part": 7, "qty": 174, "press": 1, "overtime": false, "seq": 6, "press_id": 2, "minutes": 209.4, "start_ts": "2026-09-28 21:30", "end_ts": "2026-09-29 00:59", "next": {"kind": "change", "part": null, "mold": 6, "start_ts": "2026-09-29 00:59", "minutes": 30.0}}, {"kind": "change", "s": 2, "start": 19.99, "end": 20.49, "from_mold": 7, "mold": 6, "press": 1, "overtime": false, "seq": 7, "press_id": 2, "minutes": 30.0, "start_ts": "2026-09-29 00:59", "end_ts": "2026-09-29 01:29", "next": {"kind": "run", "part": 6, "mold": 6, "start_ts": "2026-09-29 01:29", "minutes": 600.0}, "part": 6}, {"kind": "run", "s": 2, "start": 20.49, "end": 30.490000000000002, "mold": 6, "part": 6, "qty": 1400, "press": 1, "overtime": false, "seq": 8, "press_id": 2, "minutes": 600.0, "start_ts": "2026-09-29 01:29", "end_ts": "2026-09-29 11:29", "next": {"kind": "change", "part": null, "mold": 1, "start_ts": "2026-09-29 11:29", "minutes": 30.0}}, {"kind": "change", "s": 3, "start": 30.49, "end": 30.99, "from_mold": 6, "mold": 1, "press": 1, "overtime": false, "seq": 9, "press_id": 2, "minutes": 30.0, "start_ts": "2026-09-29 11:29", "end_ts": "2026-09-29 11:59", "next": {"kind": "run", "part": 1, "mold": 1, "start_ts": "2026-09-29 11:59", "minutes": 60.6}, "part": 1}, {"kind": "run", "s": 3, "start": 30.99, "end": 32.0, "mold": 1, "part": 1, "qty": 91, "press": 1, "overtime": false, "seq": 10, "press_id": 2, "minutes": 60.6, "start_ts": "2026-09-29 11:59", "end_ts": "2026-09-29 13:00", "next": {"kind": "run", "part": 1, "mold": 1, "start_ts": "2026-09-29 21:00", "minutes": 480.0}}, {"kind": "run", "s": 5, "start": 40.0, "end": 48.0, "mold": 1, "part": 1, "qty": 720, "press": 1, "overtime": false, "seq": 11, "press_id": 2, "minutes": 480.0, "start_ts": "2026-09-29 21:00", "end_ts": "2026-09-30 05:00", "next": {"kind": "change", "part": null, "mold": 4, "start_ts": "2026-09-30 05:00", "minutes": 30.0}}, {"kind": "change", "s": 6, "start": 48.0, "end": 48.5, "from_mold": 1, "mold": 4, "press": 1, "overtime": false, "seq": 12, "press_id": 2, "minutes": 30.0, "start_ts": "2026-09-30 05:00", "end_ts": "2026-09-30 05:30", "next": {"kind": "run", "part": 4, "mold": 4, "start_ts": "2026-09-30 05:30", "minutes": 450.0}, "part": 4}, {"kind": "run", "s": 6, "start": 48.5, "end": 56.0, "mold": 4, "part": 4, "qty": 825, "press": 1, "overtime": false, "seq": 13, "press_id": 2, "minutes": 450.0, "start_ts": "2026-09-30 05:30", "end_ts": "2026-09-30 13:00", "next": {"kind": "change", "part": null, "mold": 2, "start_ts": "2026-09-30 21:00", "minutes": 30.0}}, {"kind": "change", "s": 8, "start": 64.0, "end": 64.5, "from_mold": 4, "mold": 2, "press": 1, "overtime": false, "seq": 14, "press_id": 2, "minutes": 30.0, "start_ts": "2026-09-30 21:00", "end_ts": "2026-09-30 21:30", "next": {"kind": "run", "part": 2, "mold": 2, "start_ts": "2026-09-30 21:30", "minutes": 360.0}, "part": 2}, {"kind": "run", "s": 8, "start": 64.5, "end": 70.5, "mold": 2, "part": 2, "qty": 900, "press": 1, "overtime": false, "seq": 15, "press_id": 2, "minutes": 360.0, "start_ts": "2026-09-30 21:30", "end_ts": "2026-10-01 03:30", "next": {"kind": "change", "part": null, "mold": 4, "start_ts": "2026-10-01 03:30", "minutes": 30.0}}, {"kind": "change", "s": 8, "start": 70.5, "end": 71.0, "from_mold": 2, "mold": 4, "press": 1, "overtime": false, "seq": 16, "press_id": 2, "minutes": 30.0, "start_ts": "2026-10-01 03:30", "end_ts": "2026-10-01 04:00", "next": {"kind": "run", "part": 4, "mold": 4, "start_ts": "2026-10-01 04:00", "minutes": 60.0}, "part": 4}, {"kind": "run", "s": 8, "start": 71.0, "end": 72.0, "mold": 4, "part": 4, "qty": 110, "press": 1, "overtime": false, "seq": 17, "press_id": 2, "minutes": 60.0, "start_ts": "2026-10-01 04:00", "end_ts": "2026-10-01 05:00", "next": {"kind": "change", "part": null, "mold": 1, "start_ts": "2026-10-01 05:00", "minutes": 30.0}}, {"kind": "change", "s": 9, "start": 72.0, "end": 72.5, "from_mold": 4, "mold": 1, "press": 1, "overtime": false, "seq": 18, "press_id": 2, "minutes": 30.0, "start_ts": "2026-10-01 05:00", "end_ts": "2026-10-01 05:30", "next": {"kind": "run", "part": 1, "mold": 1, "start_ts": "2026-10-01 05:30", "minutes": 360.0}, "part": 1}, {"kind": "run", "s": 9, "start": 72.5, "end": 78.5, "mold": 1, "part": 1, "qty": 540, "press": 1, "overtime": false, "seq": 19, "press_id": 2, "minutes": 360.0, "start_ts": "2026-10-01 05:30", "end_ts": "2026-10-01 11:30", "next": {"kind": "change", "part": null, "mold": 5, "start_ts": "2026-10-01 11:30", "minutes": 30.0}}, {"kind": "change", "s": 9, "start": 78.5, "end": 79.0, "from_mold": 1, "mold": 5, "press": 1, "overtime": false, "seq": 20, "press_id": 2, "minutes": 30.0, "start_ts": "2026-10-01 11:30", "end_ts": "2026-10-01 12:00", "next": {"kind": "run", "part": 5, "mold": 5, "start_ts": "2026-10-01 12:00", "minutes": 60.0}, "part": 5}, {"kind": "run", "s": 9, "start": 79.0, "end": 80.0, "mold": 5, "part": 5, "qty": 75, "press": 1, "overtime": false, "seq": 21, "press_id": 2, "minutes": 60.0, "start_ts": "2026-10-01 12:00", "end_ts": "2026-10-01 13:00", "next": {"kind": "change", "part": null, "mold": 0, "start_ts": "2026-10-01 21:00", "minutes": 30.0}}, {"kind": "change", "s": 11, "start": 88.0, "end": 88.5, "from_mold": 5, "mold": 0, "press": 1, "overtime": false, "seq": 22, "press_id": 2, "minutes": 30.0, "start_ts": "2026-10-01 21:00", "end_ts": "2026-10-01 21:30", "next": {"kind": "run", "part": 0, "mold": 0, "start_ts": "2026-10-01 21:30", "minutes": 450.0}, "part": 0}, {"kind": "run", "s": 11, "start": 88.5, "end": 96.0, "mold": 0, "part": 0, "qty": 900, "press": 1, "overtime": false, "seq": 23, "press_id": 2, "minutes": 450.0, "start_ts": "2026-10-01 21:30", "end_ts": "2026-10-02 05:00", "next": {"kind": "change", "part": null, "mold": 7, "start_ts": "2026-10-02 05:00", "minutes": 30.0}}, {"kind": "change", "s": 12, "start": 96.0, "end": 96.5, "from_mold": 0, "mold": 7, "press": 1, "overtime": false, "seq": 24, "press_id": 2, "minutes": 30.0, "start_ts": "2026-10-02 05:00", "end_ts": "2026-10-02 05:30", "next": {"kind": "run", "part": 7, "mold": 7, "start_ts": "2026-10-02 05:30", "minutes": 630.0}, "part": 7}, {"kind": "run", "s": 12, "start": 96.5, "end": 107.0, "mold": 7, "part": 7, "qty": 525, "press": 1, "overtime": false, "seq": 25, "press_id": 2, "minutes": 630.0, "start_ts": "2026-10-02 05:30", "end_ts": "2026-10-02 16:00", "next": {"kind": "change", "part": null, "mold": 3, "start_ts": "2026-10-02 16:00", "minutes": 30.0}}, {"kind": "change", "s": 13, "start": 107.0, "end": 107.5, "from_mold": 7, "mold": 3, "press": 1, "overtime": false, "seq": 26, "press_id": 2, "minutes": 30.0, "start_ts": "2026-10-02 16:00", "end_ts": "2026-10-02 16:30", "next": {"kind": "run", "part": 3, "mold": 3, "start_ts": "2026-10-02 16:30", "minutes": 750.0}, "part": 3}, {"kind": "run", "s": 13, "start": 107.5, "end": 120.0, "mold": 3, "part": 3, "qty": 750, "press": 1, "overtime": false, "seq": 27, "press_id": 2, "minutes": 750.0, "start_ts": "2026-10-02 16:30", "end_ts": "2026-10-03 05:00", "next": null}, {"kind": "run", "s": 0, "start": 0.0, "end": 1.0, "mold": 2, "part": 2, "qty": 150, "press": 2, "overtime": false, "seq": 1, "press_id": 3, "minutes": 60.0, "start_ts": "2026-09-28 05:00", "end_ts": "2026-09-28 06:00", "next": {"kind": "change", "part": null, "mold": 3, "start_ts": "2026-09-28 06:00", "minutes": 30.0}}, {"kind": "change", "s": 0, "start": 1.0, "end": 1.5, "from_mold": 2, "mold": 3, "press": 2, "overtime": false, "seq": 2, "press_id": 3, "minutes": 30.0, "start_ts": "2026-09-28 06:00", "end_ts": "2026-09-28 06:30", "next": {"kind": "run", "part": 3, "mold": 3, "start_ts": "2026-09-28 06:30", "minutes": 390.0}, "part": 3}, {"kind": "run", "s": 0, "start": 1.5, "end": 8.0, "mold": 3, "part": 3, "qty": 390, "press": 2, "overtime": false, "seq": 3, "press_id": 3, "minutes": 390.0, "start_ts": "2026-09-28 06:30", "end_ts": "2026-09-28 13:00", "next": {"kind": "change", "part": null, "mold": 5, "start_ts": "2026-09-29 05:00", "minutes": 30.0}}, {"kind": "change", "s": 3, "start": 24.0, "end": 24.5, "from_mold": 3, "mold": 5, "press": 2, "overtime": false, "seq": 4, "press_id": 3, "minutes": 30.0, "start_ts": "2026-09-29 05:00", "end_ts": "2026-09-29 05:30", "next": {"kind": "run", "part": 5, "mold": 5, "start_ts": "2026-09-29 05:30", "minutes": 362.4}, "part": 5}, {"kind": "run", "s": 3, "start": 24.5, "end": 30.54, "mold": 5, "part": 5, "qty": 453, "press": 2, "overtime": false, "seq": 5, "press_id": 3, "minutes": 362.4, "start_ts": "2026-09-29 05:30", "end_ts": "2026-09-29 11:32", "next": {"kind": "change", "part": null, "mold": 4, "start_ts": "2026-09-29 13:30", "minutes": 30.0}}, {"kind": "change", "s": 4, "start": 32.5, "end": 33.0, "from_mold": 5, "mold": 4, "press": 2, "overtime": false, "seq": 6, "press_id": 3, "minutes": 30.0, "start_ts": "2026-09-29 13:30", "end_ts": "2026-09-29 14:00", "next": {"kind": "run", "part": 4, "mold": 4, "start_ts": "2026-09-29 14:00", "minutes": 420.0}, "part": 4}, {"kind": "run", "s": 4, "start": 33.0, "end": 40.0, "mold": 4, "part": 4, "qty": 770, "press": 2, "overtime": false, "seq": 7, "press_id": 3, "minutes": 420.0, "start_ts": "2026-09-29 14:00", "end_ts": "2026-09-29 21:00", "next": {"kind": "change", "part": null, "mold": 3, "start_ts": "2026-09-29 21:00", "minutes": 30.0}}, {"kind": "change", "s": 5, "start": 40.0, "end": 40.5, "from_mold": 4, "mold": 3, "press": 2, "overtime": false, "seq": 8, "press_id": 3, "minutes": 30.0, "start_ts": "2026-09-29 21:00", "end_ts": "2026-09-29 21:30", "next": {"kind": "run", "part": 3, "mold": 3, "start_ts": "2026-09-29 21:30", "minutes": 450.0}, "part": 3}, {"kind": "run", "s": 5, "start": 40.5, "end": 48.0, "mold": 3, "part": 3, "qty": 450, "press": 2, "overtime": false, "seq": 9, "press_id": 3, "minutes": 450.0, "start_ts": "2026-09-29 21:30", "end_ts": "2026-09-30 05:00", "next": {"kind": "run", "part": 3, "mold": 3, "start_ts": "2026-09-30 21:00", "minutes": 90.0}}, {"kind": "run", "s": 8, "start": 64.0, "end": 65.5, "mold": 3, "part": 3, "qty": 90, "press": 2, "overtime": false, "seq": 10, "press_id": 3, "minutes": 90.0, "start_ts": "2026-09-30 21:00", "end_ts": "2026-09-30 22:30", "next": {"kind": "change", "part": null, "mold": 8, "start_ts": "2026-09-30 22:30", "minutes": 30.0}}, {"kind": "change", "s": 8, "start": 65.5, "end": 66.0, "from_mold": 3, "mold": 8, "press": 2, "overtime": false, "seq": 11, "press_id": 3, "minutes": 30.0, "start_ts": "2026-09-30 22:30", "end_ts": "2026-09-30 23:00", "next": {"kind": "run", "part": 8, "mold": 8, "start_ts": "2026-09-30 23:00", "minutes": 360.0}, "part": 8}, {"kind": "run", "s": 8, "start": 66.0, "end": 72.0, "mold": 8, "part": 8, "qty": 600, "press": 2, "overtime": false, "seq": 12, "press_id": 3, "minutes": 360.0, "start_ts": "2026-09-30 23:00", "end_ts": "2026-10-01 05:00", "next": {"kind": "run", "part": 8, "mold": 8, "start_ts": "2026-10-01 21:00", "minutes": 960.0}}, {"kind": "run", "s": 11, "start": 88.0, "end": 104.0, "mold": 8, "part": 8, "qty": 1600, "press": 2, "overtime": false, "seq": 13, "press_id": 3, "minutes": 960.0, "start_ts": "2026-10-01 21:00", "end_ts": "2026-10-02 13:00", "next": {"kind": "change", "part": null, "mold": 5, "start_ts": "2026-10-02 21:00", "minutes": 30.0}}, {"kind": "change", "s": 14, "start": 112.0, "end": 112.5, "from_mold": 8, "mold": 5, "press": 2, "overtime": false, "seq": 14, "press_id": 3, "minutes": 30.0, "start_ts": "2026-10-02 21:00", "end_ts": "2026-10-02 21:30", "next": {"kind": "run", "part": 5, "mold": 5, "start_ts": "2026-10-02 21:30", "minutes": 60.0}, "part": 5}, {"kind": "run", "s": 14, "start": 112.5, "end": 113.5, "mold": 5, "part": 5, "qty": 75, "press": 2, "overtime": false, "seq": 15, "press_id": 3, "minutes": 60.0, "start_ts": "2026-10-02 21:30", "end_ts": "2026-10-02 22:30", "next": {"kind": "change", "part": null, "mold": 0, "start_ts": "2026-10-02 22:30", "minutes": 30.0}}, {"kind": "change", "s": 14, "start": 113.5, "end": 114.0, "from_mold": 5, "mold": 0, "press": 2, "overtime": false, "seq": 16, "press_id": 3, "minutes": 30.0, "start_ts": "2026-10-02 22:30", "end_ts": "2026-10-02 23:00", "next": {"kind": "run", "part": 0, "mold": 0, "start_ts": "2026-10-02 23:00", "minutes": 360.0}, "part": 0}, {"kind": "run", "s": 14, "start": 114.0, "end": 120.0, "mold": 0, "part": 0, "qty": 720, "press": 2, "overtime": false, "seq": 17, "press_id": 3, "minutes": 360.0, "start_ts": "2026-10-02 23:00", "end_ts": "2026-10-03 05:00", "next": null}], "summary": [{"part": "Part 1", "mold": "Mold 1", "demand": 2760, "starting_stock": 552, "production": 2760, "ending_stock": 552, "stock_target": 552, "doh": 1.0, "outcome": "Covered", "overtime_units": 0}, {"part": "Part 2", "mold": "Mold 2", "demand": 1620, "starting_stock": 324, "production": 1620, "ending_stock": 324, "stock_target": 324, "doh": 1.0, "outcome": "Covered", "overtime_units": 0}, {"part": "Part 3", "mold": "Mold 3", "demand": 2400, "starting_stock": 480, "production": 2400, "ending_stock": 480, "stock_target": 480, "doh": 1.0, "outcome": "Covered", "overtime_units": 0}, {"part": "Part 4", "mold": "Mold 4", "demand": 1680, "starting_stock": 336, "production": 1680, "ending_stock": 336, "stock_target": 336, "doh": 1.0, "outcome": "Covered", "overtime_units": 0}, {"part": "Part 5", "mold": "Mold 5", "demand": 2310, "starting_stock": 462, "production": 2310, "ending_stock": 462, "stock_target": 462, "doh": 1.0, "outcome": "Covered", "overtime_units": 0}, {"part": "Part 6", "mold": "Mold 6", "demand": 1725, "starting_stock": 345, "production": 1725, "ending_stock": 345, "stock_target": 345, "doh": 1.0, "outcome": "Covered", "overtime_units": 0}, {"part": "Part 7", "mold": "Mold 7", "demand": 2380, "starting_stock": 476, "production": 2380, "ending_stock": 476, "stock_target": 476, "doh": 1.0, "outcome": "Covered", "overtime_units": 0}, {"part": "Part 8", "mold": "Mold 8", "demand": 1250, "starting_stock": 250, "production": 1250, "ending_stock": 250, "stock_target": 250, "doh": 1.0, "outcome": "Covered", "overtime_units": 0}, {"part": "Part 9", "mold": "Mold 9", "demand": 2200, "starting_stock": 440, "production": 2200, "ending_stock": 440, "stock_target": 440, "doh": 1.0, "outcome": "Covered", "overtime_units": 0}, {"part": "Part 10", "mold": "Mold 10", "demand": 1700, "starting_stock": 340, "production": 1700, "ending_stock": 340, "stock_target": 340, "doh": 1.0, "outcome": "Covered", "overtime_units": 0}], "kpi": {"demand_total": 20025, "due": "Fri Oct 02", "planned_total": 20025, "target_total": 20025, "doh_days": 1.0, "opening_stock": 4005, "target_stock": 4005, "capacity_hours": 360.0, "busy_hours": 229.0, "busy_ot_hours": 0, "staffed_hours": 232.0, "downtime_hours": 0, "run_hours": 213.0, "change_hours": 16.0, "changes": 32, "utilization": 63.6, "status": "Covered", "reasons": ["Every part reaches 1 day(s) of stock by Friday and all customer demand is delivered."], "overtime_used": false, "capable_regular": true, "capable_any": true, "short_units": 0}, "off": [{"press": 0, "s": 0, "start": 0.0, "end": 8.0, "overtime": false}, {"press": 0, "s": 5, "start": 40.0, "end": 48.0, "overtime": false}, {"press": 0, "s": 8, "start": 64.0, "end": 72.0, "overtime": false}, {"press": 0, "s": 9, "start": 72.0, "end": 80.0, "overtime": false}, {"press": 0, "s": 11, "start": 88.0, "end": 96.0, "overtime": false}, {"press": 0, "s": 14, "start": 112.0, "end": 120.0, "overtime": false}, {"press": 1, "s": 4, "start": 32.0, "end": 40.0, "overtime": false}, {"press": 1, "s": 7, "start": 56.0, "end": 64.0, "overtime": false}, {"press": 1, "s": 10, "start": 80.0, "end": 88.0, "overtime": false}, {"press": 2, "s": 1, "start": 8.0, "end": 16.0, "overtime": false}, {"press": 2, "s": 2, "start": 16.0, "end": 24.0, "overtime": false}, {"press": 2, "s": 6, "start": 48.0, "end": 56.0, "overtime": false}, {"press": 2, "s": 7, "start": 56.0, "end": 64.0, "overtime": false}, {"press": 2, "s": 9, "start": 72.0, "end": 80.0, "overtime": false}, {"press": 2, "s": 10, "start": 80.0, "end": 88.0, "overtime": false}, {"press": 2, "s": 13, "start": 104.0, "end": 112.0, "overtime": false}], "levels": [{"rank": 1, "goal": "delivery", "value": 0.0, "status": "OPTIMAL", "seconds": 2.91, "floor": false}, {"rank": 2, "goal": "doh", "value": 0.0, "status": "OPTIMAL", "seconds": 2.48, "floor": false}, {"rank": 3, "goal": "idle", "value": 18.96, "status": "FEASIBLE", "seconds": 3.37, "floor": false}, {"rank": 4, "goal": "labor", "value": 232.0, "status": "kept previous (NOT_SOLVED)", "seconds": 1.78, "floor": false}, {"rank": 5, "goal": "tidy", "value": 32.0, "status": "FEASIBLE", "seconds": 2.43, "floor": false}], "ot_notes": {}, "order": ["delivery", "doh", "idle", "labor"], "n_shifts": 15, "n_regular": 15, "shift_hours": 8.0, "shifts_per_day": 3, "origin": "2026-09-28 05:00", "solve_seconds": 13.3, "parts": [{"id": 1, "name": "Part 1", "mold": "Mold 1", "mold_id": 1, "rate": 120.0, "change_min": 30.0}, {"id": 2, "name": "Part 2", "mold": "Mold 2", "mold_id": 2, "rate": 90.0, "change_min": 30.0}, {"id": 3, "name": "Part 3", "mold": "Mold 3", "mold_id": 3, "rate": 150.0, "change_min": 30.0}, {"id": 4, "name": "Part 4", "mold": "Mold 4", "mold_id": 4, "rate": 60.0, "change_min": 30.0}, {"id": 5, "name": "Part 5", "mold": "Mold 5", "mold_id": 5, "rate": 110.0, "change_min": 30.0}, {"id": 6, "name": "Part 6", "mold": "Mold 6", "mold_id": 6, "rate": 75.0, "change_min": 30.0}, {"id": 7, "name": "Part 7", "mold": "Mold 7", "mold_id": 7, "rate": 140.0, "change_min": 30.0}, {"id": 8, "name": "Part 8", "mold": "Mold 8", "mold_id": 8, "rate": 50.0, "change_min": 30.0}, {"id": 9, "name": "Part 9", "mold": "Mold 9", "mold_id": 9, "rate": 100.0, "change_min": 30.0}, {"id": 10, "name": "Part 10", "mold": "Mold 10", "mold_id": 10, "rate": 85.0, "change_min": 30.0}], "presses": [{"id": 1, "name": "Press 1"}, {"id": 2, "name": "Press 2"}, {"id": 3, "name": "Press 3"}], "overtime_stage": false, "warnings": []}'); CREATE TABLE press_mold_compat ( press_id INTEGER NOT NULL REFERENCES presses(press_id), mold_id INTEGER NOT NULL REFERENCES molds(mold_id), allowed INTEGER NOT NULL DEFAULT 1, PRIMARY KEY (press_id, mold_id) ); INSERT INTO "press_mold_compat" VALUES(1,1,1); INSERT INTO "press_mold_compat" VALUES(1,2,1); INSERT INTO "press_mold_compat" VALUES(1,3,1); INSERT INTO "press_mold_compat" VALUES(1,4,1); INSERT INTO "press_mold_compat" VALUES(1,5,1); INSERT INTO "press_mold_compat" VALUES(1,6,1); INSERT INTO "press_mold_compat" VALUES(1,7,1); INSERT INTO "press_mold_compat" VALUES(1,8,1); INSERT INTO "press_mold_compat" VALUES(1,9,1); INSERT INTO "press_mold_compat" VALUES(1,10,1); INSERT INTO "press_mold_compat" VALUES(2,1,1); INSERT INTO "press_mold_compat" VALUES(2,2,1); INSERT INTO "press_mold_compat" VALUES(2,3,1); INSERT INTO "press_mold_compat" VALUES(2,4,1); INSERT INTO "press_mold_compat" VALUES(2,5,1); INSERT INTO "press_mold_compat" VALUES(2,6,1); INSERT INTO "press_mold_compat" VALUES(2,7,1); INSERT INTO "press_mold_compat" VALUES(2,8,1); INSERT INTO "press_mold_compat" VALUES(2,9,1); INSERT INTO "press_mold_compat" VALUES(2,10,1); INSERT INTO "press_mold_compat" VALUES(3,1,1); INSERT INTO "press_mold_compat" VALUES(3,2,1); INSERT INTO "press_mold_compat" VALUES(3,3,1); INSERT INTO "press_mold_compat" VALUES(3,4,1); INSERT INTO "press_mold_compat" VALUES(3,5,1); INSERT INTO "press_mold_compat" VALUES(3,6,1); INSERT INTO "press_mold_compat" VALUES(3,7,1); INSERT INTO "press_mold_compat" VALUES(3,8,1); INSERT INTO "press_mold_compat" VALUES(3,9,1); INSERT INTO "press_mold_compat" VALUES(3,10,1); CREATE TABLE presses ( press_id INTEGER PRIMARY KEY, name TEXT NOT NULL, initial_mold INTEGER NOT NULL REFERENCES molds(mold_id) -- mold mounted at Monday 05:00 ); INSERT INTO "presses" VALUES(1,'Press 1',1); INSERT INTO "presses" VALUES(2,'Press 2',2); INSERT INTO "presses" VALUES(3,'Press 3',3); CREATE TABLE priorities ( rank INTEGER PRIMARY KEY, -- 1 = most important goal TEXT NOT NULL UNIQUE -- delivery | doh | idle | labor ); INSERT INTO "priorities" VALUES(1,'delivery'); INSERT INTO "priorities" VALUES(2,'doh'); INSERT INTO "priorities" VALUES(3,'idle'); INSERT INTO "priorities" VALUES(4,'labor'); CREATE TABLE schedule_blocks ( run_id INTEGER NOT NULL REFERENCES plan_runs(run_id), press_id INTEGER NOT NULL, seq INTEGER NOT NULL, -- order on the press kind TEXT NOT NULL, -- run | change | downtime part_id INTEGER, mold_id INTEGER, from_mold INTEGER, start_ts TEXT NOT NULL, end_ts TEXT NOT NULL, minutes REAL NOT NULL, quantity INTEGER, overtime INTEGER NOT NULL DEFAULT 0, PRIMARY KEY (run_id, press_id, seq) ); INSERT INTO "schedule_blocks" VALUES(1,1,1,'change',8,8,1,'2026-09-28 13:00','2026-09-28 13:30',30.0,NULL,0); INSERT INTO "schedule_blocks" VALUES(1,1,2,'run',8,8,NULL,'2026-09-28 13:30','2026-09-28 19:30',360.0,300,0); INSERT INTO "schedule_blocks" VALUES(1,1,3,'change',2,2,8,'2026-09-28 19:30','2026-09-28 20:00',30.0,NULL,0); INSERT INTO "schedule_blocks" VALUES(1,1,4,'run',2,2,NULL,'2026-09-28 20:00','2026-09-28 22:29',149.4,224,0); INSERT INTO "schedule_blocks" VALUES(1,1,5,'change',10,10,2,'2026-09-28 22:29','2026-09-28 22:59',30.0,NULL,0); INSERT INTO "schedule_blocks" VALUES(1,1,6,'run',10,10,NULL,'2026-09-28 22:59','2026-09-29 05:00',360.6,511,0); INSERT INTO "schedule_blocks" VALUES(1,1,7,'change',1,1,10,'2026-09-29 05:30','2026-09-29 06:00',30.0,NULL,0); INSERT INTO "schedule_blocks" VALUES(1,1,8,'run',1,1,NULL,'2026-09-29 06:00','2026-09-29 08:00',120.0,240,0); INSERT INTO "schedule_blocks" VALUES(1,1,9,'change',10,10,1,'2026-09-29 08:00','2026-09-29 08:30',30.0,NULL,0); INSERT INTO "schedule_blocks" VALUES(1,1,10,'run',10,10,NULL,'2026-09-29 08:30','2026-09-29 13:00',270.0,383,0); INSERT INTO "schedule_blocks" VALUES(1,1,11,'change',3,3,10,'2026-09-29 13:00','2026-09-29 13:30',30.0,NULL,0); INSERT INTO "schedule_blocks" VALUES(1,1,12,'run',3,3,NULL,'2026-09-29 13:30','2026-09-29 21:00',450.0,1125,0); INSERT INTO "schedule_blocks" VALUES(1,1,13,'run',3,3,NULL,'2026-09-30 05:00','2026-09-30 05:30',30.0,75,0); INSERT INTO "schedule_blocks" VALUES(1,1,14,'change',7,7,3,'2026-09-30 05:30','2026-09-30 06:00',30.0,NULL,0); INSERT INTO "schedule_blocks" VALUES(1,1,15,'run',7,7,NULL,'2026-09-30 06:00','2026-09-30 13:00',420.0,980,0); INSERT INTO "schedule_blocks" VALUES(1,1,16,'change',1,1,7,'2026-09-30 13:00','2026-09-30 13:30',30.0,NULL,0); INSERT INTO "schedule_blocks" VALUES(1,1,17,'run',1,1,NULL,'2026-09-30 13:30','2026-09-30 21:00',450.0,900,0); INSERT INTO "schedule_blocks" VALUES(1,1,18,'change',8,8,1,'2026-10-01 13:00','2026-10-01 13:30',30.0,NULL,0); INSERT INTO "schedule_blocks" VALUES(1,1,19,'run',8,8,NULL,'2026-10-01 13:30','2026-10-01 18:30',300.6,251,0); INSERT INTO "schedule_blocks" VALUES(1,1,20,'change',10,10,8,'2026-10-01 18:30','2026-10-01 19:00',30.0,NULL,0); INSERT INTO "schedule_blocks" VALUES(1,1,21,'run',10,10,NULL,'2026-10-01 19:00','2026-10-01 21:00',119.4,169,0); INSERT INTO "schedule_blocks" VALUES(1,1,22,'change',5,5,10,'2026-10-02 05:30','2026-10-02 06:00',30.0,NULL,0); INSERT INTO "schedule_blocks" VALUES(1,1,23,'run',5,5,NULL,'2026-10-02 06:00','2026-10-02 11:30',330.0,605,0); INSERT INTO "schedule_blocks" VALUES(1,1,24,'change',3,3,5,'2026-10-02 11:30','2026-10-02 12:00',30.0,NULL,0); INSERT INTO "schedule_blocks" VALUES(1,1,25,'run',3,3,NULL,'2026-10-02 12:00','2026-10-02 13:00',60.0,150,0); INSERT INTO "schedule_blocks" VALUES(1,1,26,'change',10,10,3,'2026-10-02 13:00','2026-10-02 13:30',30.0,NULL,0); INSERT INTO "schedule_blocks" VALUES(1,1,27,'run',10,10,NULL,'2026-10-02 13:30','2026-10-02 21:00',450.0,637,0); INSERT INTO "schedule_blocks" VALUES(1,2,1,'run',2,2,NULL,'2026-09-28 05:00','2026-09-28 05:30',30.0,45,0); INSERT INTO "schedule_blocks" VALUES(1,2,2,'change',6,6,2,'2026-09-28 05:30','2026-09-28 06:00',30.0,NULL,0); INSERT INTO "schedule_blocks" VALUES(1,2,3,'run',6,6,NULL,'2026-09-28 06:00','2026-09-28 12:57',417.6,522,0); INSERT INTO "schedule_blocks" VALUES(1,2,4,'run',6,6,NULL,'2026-09-28 13:00','2026-09-28 21:00',480.0,600,0); INSERT INTO "schedule_blocks" VALUES(1,2,5,'change',8,8,6,'2026-09-28 21:00','2026-09-28 21:30',30.0,NULL,0); INSERT INTO "schedule_blocks" VALUES(1,2,6,'run',8,8,NULL,'2026-09-28 21:30','2026-09-29 00:59',209.4,174,0); INSERT INTO "schedule_blocks" VALUES(1,2,7,'change',7,7,8,'2026-09-29 00:59','2026-09-29 01:29',30.0,NULL,0); INSERT INTO "schedule_blocks" VALUES(1,2,8,'run',7,7,NULL,'2026-09-29 01:29','2026-09-29 11:29',600.0,1400,0); INSERT INTO "schedule_blocks" VALUES(1,2,9,'change',2,2,7,'2026-09-29 11:29','2026-09-29 11:59',30.0,NULL,0); INSERT INTO "schedule_blocks" VALUES(1,2,10,'run',2,2,NULL,'2026-09-29 11:59','2026-09-29 13:00',60.6,91,0); INSERT INTO "schedule_blocks" VALUES(1,2,11,'run',2,2,NULL,'2026-09-29 21:00','2026-09-30 05:00',480.0,720,0); INSERT INTO "schedule_blocks" VALUES(1,2,12,'change',5,5,2,'2026-09-30 05:00','2026-09-30 05:30',30.0,NULL,0); INSERT INTO "schedule_blocks" VALUES(1,2,13,'run',5,5,NULL,'2026-09-30 05:30','2026-09-30 13:00',450.0,825,0); INSERT INTO "schedule_blocks" VALUES(1,2,14,'change',3,3,5,'2026-09-30 21:00','2026-09-30 21:30',30.0,NULL,0); INSERT INTO "schedule_blocks" VALUES(1,2,15,'run',3,3,NULL,'2026-09-30 21:30','2026-10-01 03:30',360.0,900,0); INSERT INTO "schedule_blocks" VALUES(1,2,16,'change',5,5,3,'2026-10-01 03:30','2026-10-01 04:00',30.0,NULL,0); INSERT INTO "schedule_blocks" VALUES(1,2,17,'run',5,5,NULL,'2026-10-01 04:00','2026-10-01 05:00',60.0,110,0); INSERT INTO "schedule_blocks" VALUES(1,2,18,'change',2,2,5,'2026-10-01 05:00','2026-10-01 05:30',30.0,NULL,0); INSERT INTO "schedule_blocks" VALUES(1,2,19,'run',2,2,NULL,'2026-10-01 05:30','2026-10-01 11:30',360.0,540,0); INSERT INTO "schedule_blocks" VALUES(1,2,20,'change',6,6,2,'2026-10-01 11:30','2026-10-01 12:00',30.0,NULL,0); INSERT INTO "schedule_blocks" VALUES(1,2,21,'run',6,6,NULL,'2026-10-01 12:00','2026-10-01 13:00',60.0,75,0); INSERT INTO "schedule_blocks" VALUES(1,2,22,'change',1,1,6,'2026-10-01 21:00','2026-10-01 21:30',30.0,NULL,0); INSERT INTO "schedule_blocks" VALUES(1,2,23,'run',1,1,NULL,'2026-10-01 21:30','2026-10-02 05:00',450.0,900,0); INSERT INTO "schedule_blocks" VALUES(1,2,24,'change',8,8,1,'2026-10-02 05:00','2026-10-02 05:30',30.0,NULL,0); INSERT INTO "schedule_blocks" VALUES(1,2,25,'run',8,8,NULL,'2026-10-02 05:30','2026-10-02 16:00',630.0,525,0); INSERT INTO "schedule_blocks" VALUES(1,2,26,'change',4,4,8,'2026-10-02 16:00','2026-10-02 16:30',30.0,NULL,0); INSERT INTO "schedule_blocks" VALUES(1,2,27,'run',4,4,NULL,'2026-10-02 16:30','2026-10-03 05:00',750.0,750,0); INSERT INTO "schedule_blocks" VALUES(1,3,1,'run',3,3,NULL,'2026-09-28 05:00','2026-09-28 06:00',60.0,150,0); INSERT INTO "schedule_blocks" VALUES(1,3,2,'change',4,4,3,'2026-09-28 06:00','2026-09-28 06:30',30.0,NULL,0); INSERT INTO "schedule_blocks" VALUES(1,3,3,'run',4,4,NULL,'2026-09-28 06:30','2026-09-28 13:00',390.0,390,0); INSERT INTO "schedule_blocks" VALUES(1,3,4,'change',6,6,4,'2026-09-29 05:00','2026-09-29 05:30',30.0,NULL,0); INSERT INTO "schedule_blocks" VALUES(1,3,5,'run',6,6,NULL,'2026-09-29 05:30','2026-09-29 11:32',362.4,453,0); INSERT INTO "schedule_blocks" VALUES(1,3,6,'change',5,5,6,'2026-09-29 13:30','2026-09-29 14:00',30.0,NULL,0); INSERT INTO "schedule_blocks" VALUES(1,3,7,'run',5,5,NULL,'2026-09-29 14:00','2026-09-29 21:00',420.0,770,0); INSERT INTO "schedule_blocks" VALUES(1,3,8,'change',4,4,5,'2026-09-29 21:00','2026-09-29 21:30',30.0,NULL,0); INSERT INTO "schedule_blocks" VALUES(1,3,9,'run',4,4,NULL,'2026-09-29 21:30','2026-09-30 05:00',450.0,450,0); INSERT INTO "schedule_blocks" VALUES(1,3,10,'run',4,4,NULL,'2026-09-30 21:00','2026-09-30 22:30',90.0,90,0); INSERT INTO "schedule_blocks" VALUES(1,3,11,'change',9,9,4,'2026-09-30 22:30','2026-09-30 23:00',30.0,NULL,0); INSERT INTO "schedule_blocks" VALUES(1,3,12,'run',9,9,NULL,'2026-09-30 23:00','2026-10-01 05:00',360.0,600,0); INSERT INTO "schedule_blocks" VALUES(1,3,13,'run',9,9,NULL,'2026-10-01 21:00','2026-10-02 13:00',960.0,1600,0); INSERT INTO "schedule_blocks" VALUES(1,3,14,'change',6,6,9,'2026-10-02 21:00','2026-10-02 21:30',30.0,NULL,0); INSERT INTO "schedule_blocks" VALUES(1,3,15,'run',6,6,NULL,'2026-10-02 21:30','2026-10-02 22:30',60.0,75,0); INSERT INTO "schedule_blocks" VALUES(1,3,16,'change',1,1,6,'2026-10-02 22:30','2026-10-02 23:00',30.0,NULL,0); INSERT INTO "schedule_blocks" VALUES(1,3,17,'run',1,1,NULL,'2026-10-02 23:00','2026-10-03 05:00',360.0,720,0); CREATE TABLE schedule_summary ( run_id INTEGER NOT NULL REFERENCES plan_runs(run_id), part_id INTEGER NOT NULL, demand INTEGER, starting_stock INTEGER, production INTEGER, ending_stock INTEGER, stock_target INTEGER, doh REAL, outcome TEXT, PRIMARY KEY (run_id, part_id) ); INSERT INTO "schedule_summary" VALUES(1,1,2760,552,2760,552,552,1.0,'Covered'); INSERT INTO "schedule_summary" VALUES(1,2,1620,324,1620,324,324,1.0,'Covered'); INSERT INTO "schedule_summary" VALUES(1,3,2400,480,2400,480,480,1.0,'Covered'); INSERT INTO "schedule_summary" VALUES(1,4,1680,336,1680,336,336,1.0,'Covered'); INSERT INTO "schedule_summary" VALUES(1,5,2310,462,2310,462,462,1.0,'Covered'); INSERT INTO "schedule_summary" VALUES(1,6,1725,345,1725,345,345,1.0,'Covered'); INSERT INTO "schedule_summary" VALUES(1,7,2380,476,2380,476,476,1.0,'Covered'); INSERT INTO "schedule_summary" VALUES(1,8,1250,250,1250,250,250,1.0,'Covered'); INSERT INTO "schedule_summary" VALUES(1,9,2200,440,2200,440,440,1.0,'Covered'); INSERT INTO "schedule_summary" VALUES(1,10,1700,340,1700,340,340,1.0,'Covered'); CREATE TABLE settings ( key TEXT PRIMARY KEY, value TEXT NOT NULL, description TEXT ); INSERT INTO "settings" VALUES('doh_days','1.0','Target days-on-hand inventory at Friday end (try 1.5, 2, ...)'); INSERT INTO "settings" VALUES('working_days','5','Days over which weekly demand is spread (daily demand = weekly / this)'); INSERT INTO "settings" VALUES('shift_hours','8','Length of one shift in hours'); INSERT INTO "settings" VALUES('shifts_per_day','3','Shifts per weekday (Mon-Fri); 3 = 24 h/day. Weekend shifts are overtime'); INSERT INTO "settings" VALUES('start_hour','5','Hour of day when the Monday shift starts (05:00)'); INSERT INTO "settings" VALUES('week_start','','Monday date of the planning week, YYYY-MM-DD (blank = next Monday)'); INSERT INTO "settings" VALUES('default_change_minutes','30','Standard mold change time; new molds/edits start from this'); INSERT INTO "settings" VALUES('min_run_hours','1.0','Shortest run after a mold change (stops pointless changeovers)'); INSERT INTO "settings" VALUES('min_block_hours','0.5','Shortest run block on a press (a run is at least this long, or does not happen)'); INSERT INTO "settings" VALUES('min_shift_utilization','0.5','A staffed press-shift must run at least this share of its available time'); INSERT INTO "settings" VALUES('demand_floor','1','1 = always plan enough to ship customer demand when capacity allows; 0 = pure lexicographic order'); INSERT INTO "settings" VALUES('overtime_mode','demand','off | demand (overtime only to meet demand) | demand_doh (also to build DOH)'); INSERT INTO "settings" VALUES('overtime_premium','1.5','Weight of overtime labor hours in the labor objective'); INSERT INTO "settings" VALUES('operators_per_press','1','Operators needed per staffed press-shift (labor hours = press-hours x this)'); INSERT INTO "settings" VALUES('max_extra_doh_days','0.0','Overbuild allowed above the DOH target (days); 0 = never overbuild'); INSERT INTO "settings" VALUES('solver_seconds','3','Time limit per lexicographic level (higher = better schedules, slower)'); DELETE FROM "sqlite_sequence"; INSERT INTO "sqlite_sequence" VALUES('plan_runs',1); COMMIT;