HaLim commited on
Commit
26ebf77
Β·
1 Parent(s): b1b13fe

Update csv for unit testing. The optimization most up to date. Streamlit bit fixed

Browse files
Home.py CHANGED
@@ -99,6 +99,9 @@ with col2:
99
 
100
  if st.button("🎯 Run Optimization", key="nav_optimization", help="Configure and run optimization"):
101
  st.switch_page("pages/2_🎯_Optimization.py")
 
 
 
102
 
103
  # Global settings section
104
  st.markdown("---")
@@ -183,13 +186,13 @@ with col_info2:
183
  with col_info3:
184
  st.markdown("""
185
  <div class="feature-card">
186
- <h3>πŸ“ˆ Analytics & Reports</h3>
187
- <p>Rich visualization and reporting:</p>
188
  <ul>
189
- <li>Interactive dashboards</li>
190
- <li>Cost analysis charts</li>
191
- <li>Performance metrics</li>
192
- <li>Export capabilities</li>
193
  </ul>
194
  </div>
195
  """, unsafe_allow_html=True)
 
99
 
100
  if st.button("🎯 Run Optimization", key="nav_optimization", help="Configure and run optimization"):
101
  st.switch_page("pages/2_🎯_Optimization.py")
102
+
103
+ if st.button("πŸ“ˆ Enhanced Reports", key="nav_reports", help="View comprehensive reports and analytics"):
104
+ st.switch_page("pages/3_πŸ“ˆ_Enhanced_Reports.py")
105
 
106
  # Global settings section
107
  st.markdown("---")
 
186
  with col_info3:
187
  st.markdown("""
188
  <div class="feature-card">
189
+ <h3>πŸ“ˆ Enhanced Reports</h3>
190
+ <p>Comprehensive visualization and reporting:</p>
191
  <ul>
192
+ <li>Employee costs per hour analysis</li>
193
+ <li>Production plans & orders tracking</li>
194
+ <li>Line allocation by day visualization</li>
195
+ <li>Total cost breakdowns & scenarios</li>
196
  </ul>
197
  </div>
198
  """, unsafe_allow_html=True)
data/hierarchy_exports/kit_hierarchy.json ADDED
@@ -0,0 +1,2110 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "S9901040": {
3
+ "name": "IEHK 2024,Basic Medicine&Renewable UNIT",
4
+ "type": "master",
5
+ "subkits": {
6
+ "S9991041": {
7
+ "name": "IEHK 2024,Basic Medicine&Renewabl SUB1/3",
8
+ "type": "subkit",
9
+ "prepacks": [
10
+ "S9991044"
11
+ ],
12
+ "dependencies": [
13
+ "S9991044"
14
+ ]
15
+ },
16
+ "S9991042": {
17
+ "name": "IEHK 2024,Basic Medicine&Renewabl SUB2/3",
18
+ "type": "subkit",
19
+ "prepacks": [
20
+ "S9991045"
21
+ ],
22
+ "dependencies": [
23
+ "S9991045"
24
+ ]
25
+ },
26
+ "S9991043": {
27
+ "name": "IEHK 2024,Basic Medicine&Renewabl SUB3/3",
28
+ "type": "subkit",
29
+ "prepacks": [],
30
+ "dependencies": []
31
+ }
32
+ },
33
+ "dependencies": [
34
+ "S9991041",
35
+ "S9991042",
36
+ "S9991043"
37
+ ]
38
+ },
39
+ "S9901041": {
40
+ "name": "IEHK 2024,Basic Malaria UNIT",
41
+ "type": "master",
42
+ "subkits": {},
43
+ "dependencies": []
44
+ },
45
+ "S9901042": {
46
+ "name": "IEHK 2024,Basic Equipment UNIT",
47
+ "type": "master",
48
+ "subkits": {},
49
+ "dependencies": []
50
+ },
51
+ "S9901043": {
52
+ "name": "IEHK 2024,Suppl. Medicine Oral UNIT",
53
+ "type": "master",
54
+ "subkits": {
55
+ "S9991141": {
56
+ "name": "SUB 1/4 IEHK 2024,Suppl.Medicine Oral",
57
+ "type": "subkit",
58
+ "prepacks": [],
59
+ "dependencies": []
60
+ },
61
+ "S9991142": {
62
+ "name": "SUB 2/4 IEHK 2024,Suppl.Medicine Oral",
63
+ "type": "subkit",
64
+ "prepacks": [],
65
+ "dependencies": []
66
+ },
67
+ "S9991143": {
68
+ "name": "SUB 3/4 IEHK 2024,Suppl.Medicine Oral",
69
+ "type": "subkit",
70
+ "prepacks": [],
71
+ "dependencies": []
72
+ },
73
+ "S9991144": {
74
+ "name": "SUB 4/4 IEHK 2024,Suppl.Medicine Oral",
75
+ "type": "subkit",
76
+ "prepacks": [],
77
+ "dependencies": []
78
+ }
79
+ },
80
+ "dependencies": [
81
+ "S9991141",
82
+ "S9991142",
83
+ "S9991143",
84
+ "S9991144"
85
+ ]
86
+ },
87
+ "S9901045": {
88
+ "name": "IEHK 2024,Suppl. Med Mental Health UNIT",
89
+ "type": "master",
90
+ "subkits": {},
91
+ "dependencies": []
92
+ },
93
+ "S9901047": {
94
+ "name": "IEHK 2024,Suppl Medicine Cold Chain UNIT",
95
+ "type": "master",
96
+ "subkits": {},
97
+ "dependencies": []
98
+ },
99
+ "S9901048": {
100
+ "name": "IEHK 2024,Suppl. Malaria UNIT",
101
+ "type": "master",
102
+ "subkits": {
103
+ "S9991048": {
104
+ "name": "IEHK 2024,Suppl. Malaria UNIT SUB 1/4",
105
+ "type": "subkit",
106
+ "prepacks": [],
107
+ "dependencies": []
108
+ },
109
+ "S9991049": {
110
+ "name": "IEHK 2024,Suppl. Malaria UNIT SUB 2/4",
111
+ "type": "subkit",
112
+ "prepacks": [],
113
+ "dependencies": []
114
+ },
115
+ "S9991050": {
116
+ "name": "IEHK 2024,Suppl. Malaria UNIT SUB 3/4",
117
+ "type": "subkit",
118
+ "prepacks": [],
119
+ "dependencies": []
120
+ },
121
+ "S9991051": {
122
+ "name": "IEHK 2024,Suppl. Malaria UNIT SUB 4/4",
123
+ "type": "subkit",
124
+ "prepacks": [],
125
+ "dependencies": []
126
+ }
127
+ },
128
+ "dependencies": [
129
+ "S9991048",
130
+ "S9991049",
131
+ "S9991050",
132
+ "S9991051"
133
+ ]
134
+ },
135
+ "S9901049": {
136
+ "name": "IEHK 2024,Suppl. PEP Unit",
137
+ "type": "master",
138
+ "subkits": {},
139
+ "dependencies": []
140
+ },
141
+ "S9902217": {
142
+ "name": "Midwifery kit,1-drugs",
143
+ "type": "master",
144
+ "subkits": {
145
+ "S9999841": {
146
+ "name": "Subkit 1/4 S9902217 Midwifery kit,1-drug",
147
+ "type": "subkit",
148
+ "prepacks": [],
149
+ "dependencies": []
150
+ },
151
+ "S9999842": {
152
+ "name": "Subkit 2/4 S9902217 Midwifery kit,1-drug",
153
+ "type": "subkit",
154
+ "prepacks": [],
155
+ "dependencies": []
156
+ },
157
+ "S9999843": {
158
+ "name": "Subkit 3/4 S9902217 Midwifery kit,1-drug",
159
+ "type": "subkit",
160
+ "prepacks": [],
161
+ "dependencies": []
162
+ },
163
+ "S9999844": {
164
+ "name": "Subkit 4/4 S9902217 Midwifery kit,1-drug",
165
+ "type": "subkit",
166
+ "prepacks": [],
167
+ "dependencies": []
168
+ }
169
+ },
170
+ "dependencies": [
171
+ "S9999841",
172
+ "S9999842",
173
+ "S9999843",
174
+ "S9999844"
175
+ ]
176
+ },
177
+ "S9902219": {
178
+ "name": "Midwifery kit,3-renewable",
179
+ "type": "master",
180
+ "subkits": {
181
+ "S9999898": {
182
+ "name": "Subkit 1/2 Midwifery ,3-renew S9902219",
183
+ "type": "subkit",
184
+ "prepacks": [
185
+ "S9999941",
186
+ "S9999942",
187
+ "S9999940"
188
+ ],
189
+ "dependencies": [
190
+ "S9999941",
191
+ "S9999942",
192
+ "S9999940"
193
+ ]
194
+ },
195
+ "S9999899": {
196
+ "name": "Subkit 2/2 Midwifery ,3-renew S9902219",
197
+ "type": "subkit",
198
+ "prepacks": [],
199
+ "dependencies": []
200
+ }
201
+ },
202
+ "dependencies": [
203
+ "S9999898",
204
+ "S9999899"
205
+ ]
206
+ },
207
+ "S9902220": {
208
+ "name": "Midwifery kit,suppl.1a-drugs",
209
+ "type": "master",
210
+ "subkits": {},
211
+ "dependencies": []
212
+ },
213
+ "S9902221": {
214
+ "name": "Midwifery kit \u2013 equipment, 2",
215
+ "type": "master",
216
+ "subkits": {},
217
+ "dependencies": []
218
+ },
219
+ "S9902302": {
220
+ "name": "Nigeria Kit 2017",
221
+ "type": "master",
222
+ "subkits": {},
223
+ "dependencies": []
224
+ },
225
+ "S9902410": {
226
+ "name": "NBK,Community,Part B, Module 1,Medicines",
227
+ "type": "master",
228
+ "subkits": {
229
+ "S9992416": {
230
+ "name": "SUB 1/3 NBK,Community,Part B,Module1,Med",
231
+ "type": "subkit",
232
+ "prepacks": [],
233
+ "dependencies": []
234
+ },
235
+ "S9992412": {
236
+ "name": "SUB 2/3 NBK,Community,Part B,Module1,Med",
237
+ "type": "subkit",
238
+ "prepacks": [],
239
+ "dependencies": []
240
+ },
241
+ "S9992413": {
242
+ "name": "SUB 3/3 NBK,Community,Part B,Module1,Med",
243
+ "type": "subkit",
244
+ "prepacks": [],
245
+ "dependencies": []
246
+ }
247
+ },
248
+ "dependencies": [
249
+ "S9992416",
250
+ "S9992412",
251
+ "S9992413"
252
+ ]
253
+ },
254
+ "S9902600": {
255
+ "name": "Clean Delivery Kit",
256
+ "type": "master",
257
+ "subkits": {},
258
+ "dependencies": []
259
+ },
260
+ "S9902420": {
261
+ "name": "NBK,Community,Part B, Module 2,Equipment",
262
+ "type": "master",
263
+ "subkits": {},
264
+ "dependencies": []
265
+ },
266
+ "S9902430": {
267
+ "name": "NBK, Clinic, Module 1, Medicines",
268
+ "type": "master",
269
+ "subkits": {
270
+ "S9992431": {
271
+ "name": "SUB 1/8 NBK, Clinic, Module 1, Medicines",
272
+ "type": "subkit",
273
+ "prepacks": [],
274
+ "dependencies": []
275
+ },
276
+ "S9992432": {
277
+ "name": "SUB 2/8 NBK, Clinic, Module 1, Medicines",
278
+ "type": "subkit",
279
+ "prepacks": [],
280
+ "dependencies": []
281
+ },
282
+ "S9992433": {
283
+ "name": "SUB 3/8 NBK, Clinic, Module 1, Medicines",
284
+ "type": "subkit",
285
+ "prepacks": [],
286
+ "dependencies": []
287
+ },
288
+ "S9992434": {
289
+ "name": "SUB 4/8 NBK, Clinic, Module 1, Medicines",
290
+ "type": "subkit",
291
+ "prepacks": [],
292
+ "dependencies": []
293
+ },
294
+ "S9992435": {
295
+ "name": "SUB 5/8 NBK, Clinic, Module 1, Medicines",
296
+ "type": "subkit",
297
+ "prepacks": [],
298
+ "dependencies": []
299
+ },
300
+ "S9992436": {
301
+ "name": "SUB 6/8 NBK, Clinic, Module 1, Medicines",
302
+ "type": "subkit",
303
+ "prepacks": [],
304
+ "dependencies": []
305
+ },
306
+ "S9992437": {
307
+ "name": "SUB 7/8 NBK, Clinic, Module 1, Medicines",
308
+ "type": "subkit",
309
+ "prepacks": [],
310
+ "dependencies": []
311
+ },
312
+ "S9992438": {
313
+ "name": "S9902430 8/8 NBK,Clinic,Mod part 8-40",
314
+ "type": "subkit",
315
+ "prepacks": [],
316
+ "dependencies": []
317
+ }
318
+ },
319
+ "dependencies": [
320
+ "S9992431",
321
+ "S9992432",
322
+ "S9992433",
323
+ "S9992434",
324
+ "S9992435",
325
+ "S9992436",
326
+ "S9992437",
327
+ "S9992438"
328
+ ]
329
+ },
330
+ "S9902440": {
331
+ "name": "NBK, Clinic, Module 2, Consumables",
332
+ "type": "master",
333
+ "subkits": {
334
+ "S9992441": {
335
+ "name": "SUB 1/3 NBK, Clinic,Module 2,Consumables",
336
+ "type": "subkit",
337
+ "prepacks": [],
338
+ "dependencies": []
339
+ },
340
+ "S9992442": {
341
+ "name": "SUB 2/3 NBK, Clinic,Module 2,Consumables",
342
+ "type": "subkit",
343
+ "prepacks": [],
344
+ "dependencies": []
345
+ },
346
+ "S9992443": {
347
+ "name": "SUB 3/3 NBK, Clinic,Module 2,Consumables",
348
+ "type": "subkit",
349
+ "prepacks": [],
350
+ "dependencies": []
351
+ }
352
+ },
353
+ "dependencies": [
354
+ "S9992441",
355
+ "S9992442",
356
+ "S9992443"
357
+ ]
358
+ },
359
+ "S9902450": {
360
+ "name": "NBK, Clinic, Module 3, Equipment",
361
+ "type": "master",
362
+ "subkits": {
363
+ "S9992451": {
364
+ "name": "SUB 1/6 NBK, Clinic, Module 3, Equipment",
365
+ "type": "subkit",
366
+ "prepacks": [
367
+ "S9992450"
368
+ ],
369
+ "dependencies": [
370
+ "S9992450"
371
+ ]
372
+ },
373
+ "S9992452": {
374
+ "name": "SUB 2/6 NBK, Clinic, Module 3, Equipment",
375
+ "type": "subkit",
376
+ "prepacks": [],
377
+ "dependencies": []
378
+ },
379
+ "S9992453": {
380
+ "name": "SUB 3/6 NBK, Clinic, Module 3, Equipment",
381
+ "type": "subkit",
382
+ "prepacks": [],
383
+ "dependencies": []
384
+ },
385
+ "S9992454": {
386
+ "name": "SUB 4/6 NBK, Clinic, Module 3, Equipment",
387
+ "type": "subkit",
388
+ "prepacks": [],
389
+ "dependencies": []
390
+ },
391
+ "S9992458": {
392
+ "name": "SUB 5/6 NBK, Clinic, Module 3, Equipment",
393
+ "type": "subkit",
394
+ "prepacks": [],
395
+ "dependencies": []
396
+ },
397
+ "S9992457": {
398
+ "name": "SUB 6/6 NBK, Clinic, Module 3, Equipment",
399
+ "type": "subkit",
400
+ "prepacks": [],
401
+ "dependencies": []
402
+ }
403
+ },
404
+ "dependencies": [
405
+ "S9992451",
406
+ "S9992452",
407
+ "S9992453",
408
+ "S9992454",
409
+ "S9992458",
410
+ "S9992457"
411
+ ]
412
+ },
413
+ "S9902460": {
414
+ "name": "NBK, Hospital, Module 1, Medicines",
415
+ "type": "master",
416
+ "subkits": {
417
+ "S9992461": {
418
+ "name": "SUB 1/11 NBK,Hospital, Module 1, Medicin",
419
+ "type": "subkit",
420
+ "prepacks": [],
421
+ "dependencies": []
422
+ },
423
+ "S9992462": {
424
+ "name": "SUB 2/11 NBK,Hospital, Module 1, Medicin",
425
+ "type": "subkit",
426
+ "prepacks": [],
427
+ "dependencies": []
428
+ },
429
+ "S9992463": {
430
+ "name": "SUB 3/11 NBK,Hospital, Module 1, Medicin",
431
+ "type": "subkit",
432
+ "prepacks": [],
433
+ "dependencies": []
434
+ },
435
+ "S9992464": {
436
+ "name": "SUB 4/11 NBK,Hospital, Module 1, Medicin",
437
+ "type": "subkit",
438
+ "prepacks": [],
439
+ "dependencies": []
440
+ },
441
+ "S9992465": {
442
+ "name": "SUB 5/11 NBK,Hospital, Module 1, Medicin",
443
+ "type": "subkit",
444
+ "prepacks": [],
445
+ "dependencies": []
446
+ },
447
+ "S9992466": {
448
+ "name": "SUB 6/11 NBK,Hospital, Module 1, Medicin",
449
+ "type": "subkit",
450
+ "prepacks": [],
451
+ "dependencies": []
452
+ },
453
+ "S9992467": {
454
+ "name": "SUB 7/11 NBK,Hospital, Module 1, Medicin",
455
+ "type": "subkit",
456
+ "prepacks": [],
457
+ "dependencies": []
458
+ },
459
+ "S9992468": {
460
+ "name": "SUB 8/11 NBK,Hospital, Module 1, Medicin",
461
+ "type": "subkit",
462
+ "prepacks": [],
463
+ "dependencies": []
464
+ },
465
+ "S9992469": {
466
+ "name": "SUB 9/11 NBK,Hospital, Module 1, Medicin",
467
+ "type": "subkit",
468
+ "prepacks": [],
469
+ "dependencies": []
470
+ },
471
+ "S9992460": {
472
+ "name": "SUB 10/11 NBK,Hospital, Module 1,Medicin",
473
+ "type": "subkit",
474
+ "prepacks": [],
475
+ "dependencies": []
476
+ },
477
+ "S9992430": {
478
+ "name": "SUB 11/11 NBK,Hospital, Module 1,Medicin",
479
+ "type": "subkit",
480
+ "prepacks": [],
481
+ "dependencies": []
482
+ }
483
+ },
484
+ "dependencies": [
485
+ "S9992461",
486
+ "S9992462",
487
+ "S9992463",
488
+ "S9992464",
489
+ "S9992465",
490
+ "S9992466",
491
+ "S9992467",
492
+ "S9992468",
493
+ "S9992469",
494
+ "S9992460",
495
+ "S9992430"
496
+ ]
497
+ },
498
+ "S9902470": {
499
+ "name": "NBK, Hospital, Module 2, Consumables",
500
+ "type": "master",
501
+ "subkits": {
502
+ "S9992470": {
503
+ "name": "SUB 1/10 NBK, Hospital,Module 2,Consum",
504
+ "type": "subkit",
505
+ "prepacks": [
506
+ "S9992414",
507
+ "S9992415"
508
+ ],
509
+ "dependencies": [
510
+ "S9992414",
511
+ "S9992415"
512
+ ]
513
+ },
514
+ "S9992471": {
515
+ "name": "SUB 2/10 NBK, Hospital,Module 2,Consum",
516
+ "type": "subkit",
517
+ "prepacks": [],
518
+ "dependencies": []
519
+ },
520
+ "S9992472": {
521
+ "name": "SUB 3/10 NBK, Hospital,Module 2,Consum",
522
+ "type": "subkit",
523
+ "prepacks": [],
524
+ "dependencies": []
525
+ },
526
+ "S9992473": {
527
+ "name": "SUB 4/10 NBK, Hospital,Module 2,Consum",
528
+ "type": "subkit",
529
+ "prepacks": [],
530
+ "dependencies": []
531
+ },
532
+ "S9992474": {
533
+ "name": "SUB 5/10 NBK, Hospital,Module 2,Consum",
534
+ "type": "subkit",
535
+ "prepacks": [],
536
+ "dependencies": []
537
+ },
538
+ "S9992475": {
539
+ "name": "SUB 6/10 NBK, Hospital,Module 2,Consum",
540
+ "type": "subkit",
541
+ "prepacks": [],
542
+ "dependencies": []
543
+ },
544
+ "S9992476": {
545
+ "name": "SUB 7/10 NBK, Hospital,Module 2,Consum",
546
+ "type": "subkit",
547
+ "prepacks": [],
548
+ "dependencies": []
549
+ },
550
+ "S9992477": {
551
+ "name": "SUB 8/10 NBK, Hospital,Module 2,Consum",
552
+ "type": "subkit",
553
+ "prepacks": [],
554
+ "dependencies": []
555
+ },
556
+ "S9992478": {
557
+ "name": "SUB 9/10 NBK, Hospital,Module 2,Consum",
558
+ "type": "subkit",
559
+ "prepacks": [],
560
+ "dependencies": []
561
+ },
562
+ "S9992479": {
563
+ "name": "SUB 10/10 NBK, Hospital,Module 2,Consum",
564
+ "type": "subkit",
565
+ "prepacks": [],
566
+ "dependencies": []
567
+ }
568
+ },
569
+ "dependencies": [
570
+ "S9992470",
571
+ "S9992471",
572
+ "S9992472",
573
+ "S9992473",
574
+ "S9992474",
575
+ "S9992475",
576
+ "S9992476",
577
+ "S9992477",
578
+ "S9992478",
579
+ "S9992479"
580
+ ]
581
+ },
582
+ "S9902480": {
583
+ "name": "NBK, Hospital, Module 3, Equipment",
584
+ "type": "master",
585
+ "subkits": {
586
+ "S9992481": {
587
+ "name": "SUB 1/17 NBK, Hospital, Module 3, Equ",
588
+ "type": "subkit",
589
+ "prepacks": [],
590
+ "dependencies": []
591
+ },
592
+ "S9992586": {
593
+ "name": "SUB 2/17 NBK, Hospital, Module 3, Equ",
594
+ "type": "subkit",
595
+ "prepacks": [],
596
+ "dependencies": []
597
+ },
598
+ "S9992483": {
599
+ "name": "SUB 3/17 NBK, Hospital, Module 3, Equ",
600
+ "type": "subkit",
601
+ "prepacks": [
602
+ "S9992480"
603
+ ],
604
+ "dependencies": [
605
+ "S9992480"
606
+ ]
607
+ },
608
+ "S9992484": {
609
+ "name": "SUB 4/17 NBK, Hospital, Module 3, Equ",
610
+ "type": "subkit",
611
+ "prepacks": [],
612
+ "dependencies": []
613
+ },
614
+ "S9992589": {
615
+ "name": "SUB 5/17 NBK, Hospital, Module 3, Equ",
616
+ "type": "subkit",
617
+ "prepacks": [],
618
+ "dependencies": []
619
+ },
620
+ "S9992583": {
621
+ "name": "SUB 6/17 NBK, Hospital, Module 3, Equ",
622
+ "type": "subkit",
623
+ "prepacks": [],
624
+ "dependencies": []
625
+ },
626
+ "S9992588": {
627
+ "name": "SUB 7/17 NBK, Hospital, Module 3, Equ",
628
+ "type": "subkit",
629
+ "prepacks": [],
630
+ "dependencies": []
631
+ },
632
+ "S9992488": {
633
+ "name": "SUB 8/17 NBK, Hospital, Module 3, Equ",
634
+ "type": "subkit",
635
+ "prepacks": [],
636
+ "dependencies": []
637
+ },
638
+ "S9992489": {
639
+ "name": "SUB 9/17 NBK, Hospital, Module 3, Equ",
640
+ "type": "subkit",
641
+ "prepacks": [],
642
+ "dependencies": []
643
+ },
644
+ "S9992459": {
645
+ "name": "SUB 11/17 NBK, Hospital, Module 3, Equ",
646
+ "type": "subkit",
647
+ "prepacks": [],
648
+ "dependencies": []
649
+ },
650
+ "S9992587": {
651
+ "name": "SUB 12/17 NBK, Hospital, Module 3, Equ",
652
+ "type": "subkit",
653
+ "prepacks": [],
654
+ "dependencies": []
655
+ },
656
+ "S9992493": {
657
+ "name": "SUB 13/17 NBK, Hospital, Module 3, Equ",
658
+ "type": "subkit",
659
+ "prepacks": [],
660
+ "dependencies": []
661
+ },
662
+ "S9992494": {
663
+ "name": "SUB 14/17 NBK, Hospital, Module 3, Equ",
664
+ "type": "subkit",
665
+ "prepacks": [],
666
+ "dependencies": []
667
+ },
668
+ "S9992495": {
669
+ "name": "SUB 15/17 NBK, Hospital, Module 3, Equ",
670
+ "type": "subkit",
671
+ "prepacks": [],
672
+ "dependencies": []
673
+ },
674
+ "S9992496": {
675
+ "name": "SUB 16/17 NBK, Hospital, Module 3, Equ",
676
+ "type": "subkit",
677
+ "prepacks": [],
678
+ "dependencies": []
679
+ },
680
+ "S9992497": {
681
+ "name": "SUB 17/17 NBK, Hospital, Module 3, Equ",
682
+ "type": "subkit",
683
+ "prepacks": [],
684
+ "dependencies": []
685
+ },
686
+ "S9992490": {
687
+ "name": "SUB 10/17 NBK, Hospital, Module 3, Equ",
688
+ "type": "subkit",
689
+ "prepacks": [],
690
+ "dependencies": []
691
+ }
692
+ },
693
+ "dependencies": [
694
+ "S9992481",
695
+ "S9992586",
696
+ "S9992483",
697
+ "S9992484",
698
+ "S9992589",
699
+ "S9992583",
700
+ "S9992588",
701
+ "S9992488",
702
+ "S9992489",
703
+ "S9992459",
704
+ "S9992587",
705
+ "S9992493",
706
+ "S9992494",
707
+ "S9992495",
708
+ "S9992496",
709
+ "S9992497",
710
+ "S9992490"
711
+ ]
712
+ },
713
+ "S9903000": {
714
+ "name": "AWD, Community kit, Community Care",
715
+ "type": "master",
716
+ "subkits": {},
717
+ "dependencies": []
718
+ },
719
+ "S9903001": {
720
+ "name": "AWD Kit, Periphery kit, Logistics Part",
721
+ "type": "master",
722
+ "subkits": {
723
+ "S9999131": {
724
+ "name": "Sub 1/6 AWD Kit, Periphery kit,Logistics",
725
+ "type": "subkit",
726
+ "prepacks": [],
727
+ "dependencies": []
728
+ },
729
+ "S9999132": {
730
+ "name": "Sub 2/6 AWD Kit, Periphery kit,Logistics",
731
+ "type": "subkit",
732
+ "prepacks": [],
733
+ "dependencies": []
734
+ },
735
+ "S9999133": {
736
+ "name": "Sub 3/6 AWD Kit, Periphery kit,Logistics",
737
+ "type": "subkit",
738
+ "prepacks": [],
739
+ "dependencies": []
740
+ },
741
+ "S9999134": {
742
+ "name": "Sub 4/6 AWD Kit, Periphery kit,Logistics",
743
+ "type": "subkit",
744
+ "prepacks": [],
745
+ "dependencies": []
746
+ },
747
+ "S9999135": {
748
+ "name": "Sub 5/6 AWD Kit, Periphery kit,Logistics",
749
+ "type": "subkit",
750
+ "prepacks": [],
751
+ "dependencies": []
752
+ },
753
+ "S9999136": {
754
+ "name": "Sub 6/6 AWD Kit, Periphery kit,Logistics",
755
+ "type": "subkit",
756
+ "prepacks": [],
757
+ "dependencies": []
758
+ }
759
+ },
760
+ "dependencies": [
761
+ "S9999131",
762
+ "S9999132",
763
+ "S9999133",
764
+ "S9999134",
765
+ "S9999135",
766
+ "S9999136"
767
+ ]
768
+ },
769
+ "S9903002": {
770
+ "name": "AWD, Periphery kit, Equipment",
771
+ "type": "master",
772
+ "subkits": {},
773
+ "dependencies": []
774
+ },
775
+ "S9903003": {
776
+ "name": "AWD, Periphery kit, Renewable",
777
+ "type": "master",
778
+ "subkits": {
779
+ "S9999144": {
780
+ "name": "AWD SUB 1/3 Periphery,Renewable",
781
+ "type": "subkit",
782
+ "prepacks": [
783
+ "S9999149"
784
+ ],
785
+ "dependencies": [
786
+ "S9999149"
787
+ ]
788
+ },
789
+ "S9999145": {
790
+ "name": "AWD SUB 2/3 Periphery,Renewable",
791
+ "type": "subkit",
792
+ "prepacks": [
793
+ "S9999143",
794
+ "S9999138",
795
+ "S9999137"
796
+ ],
797
+ "dependencies": [
798
+ "S9999143",
799
+ "S9999138",
800
+ "S9999137"
801
+ ]
802
+ },
803
+ "S9999146": {
804
+ "name": "AWD SUB 3/3 Periphery,Renewable",
805
+ "type": "subkit",
806
+ "prepacks": [],
807
+ "dependencies": []
808
+ }
809
+ },
810
+ "dependencies": [
811
+ "S9999144",
812
+ "S9999145",
813
+ "S9999146"
814
+ ]
815
+ },
816
+ "S9903004": {
817
+ "name": "AWD Periphery kit Drug",
818
+ "type": "master",
819
+ "subkits": {
820
+ "S9999124": {
821
+ "name": "AWD Periphery kit Drug S9903004",
822
+ "type": "subkit",
823
+ "prepacks": [],
824
+ "dependencies": []
825
+ },
826
+ "S9999125": {
827
+ "name": "AWD Periphery kit Drug S9903004 sub 2/4",
828
+ "type": "subkit",
829
+ "prepacks": [],
830
+ "dependencies": []
831
+ },
832
+ "S9999126": {
833
+ "name": "AWD Periphery kit Drug S9903004 sub 3/4",
834
+ "type": "subkit",
835
+ "prepacks": [],
836
+ "dependencies": []
837
+ },
838
+ "S9999127": {
839
+ "name": "AWD Periphery kit Drug S9903004 sub 4/4",
840
+ "type": "subkit",
841
+ "prepacks": [],
842
+ "dependencies": []
843
+ }
844
+ },
845
+ "dependencies": [
846
+ "S9999124",
847
+ "S9999125",
848
+ "S9999126",
849
+ "S9999127"
850
+ ]
851
+ },
852
+ "S9903005": {
853
+ "name": "AWD Community Kit Drug",
854
+ "type": "master",
855
+ "subkits": {},
856
+ "dependencies": []
857
+ },
858
+ "S9906706": {
859
+ "name": "Ethiopia Midwifery kit 3-renew",
860
+ "type": "master",
861
+ "subkits": {
862
+ "S9999701": {
863
+ "name": "Subkit 1/2 Ethiopia Midwifery,S9906706",
864
+ "type": "subkit",
865
+ "prepacks": [
866
+ "S9999703",
867
+ "S9999704",
868
+ "S9999705"
869
+ ],
870
+ "dependencies": [
871
+ "S9999703",
872
+ "S9999704",
873
+ "S9999705"
874
+ ]
875
+ },
876
+ "S9999702": {
877
+ "name": "Subkit 2/2 Ethiopia Midwifery,S9906706",
878
+ "type": "subkit",
879
+ "prepacks": [
880
+ "S9999299"
881
+ ],
882
+ "dependencies": [
883
+ "S9999299"
884
+ ]
885
+ }
886
+ },
887
+ "dependencies": [
888
+ "S9999701",
889
+ "S9999702"
890
+ ]
891
+ },
892
+ "S9906708": {
893
+ "name": "Ethiopia IEHK2011,kit,suppl.3-renew",
894
+ "type": "master",
895
+ "subkits": {
896
+ "S9999721": {
897
+ "name": "Subkit 1/6 Ethiopia IEHK renew S9906708",
898
+ "type": "subkit",
899
+ "prepacks": [
900
+ "S9999728"
901
+ ],
902
+ "dependencies": [
903
+ "S9999728"
904
+ ]
905
+ },
906
+ "S9999722": {
907
+ "name": "Subkit 2/6 Ethiopia IEHK renew S9906708",
908
+ "type": "subkit",
909
+ "prepacks": [],
910
+ "dependencies": []
911
+ },
912
+ "S9999723": {
913
+ "name": "Subkit 3/6 Ethiopia IEHK renew S9906708",
914
+ "type": "subkit",
915
+ "prepacks": [],
916
+ "dependencies": []
917
+ },
918
+ "S9999724": {
919
+ "name": "Subkit 4/6 Ethiopia IEHK renew S9906708",
920
+ "type": "subkit",
921
+ "prepacks": [],
922
+ "dependencies": []
923
+ },
924
+ "S9999725": {
925
+ "name": "Subkit 5/6 Ethiopia IEHK renew S9906708",
926
+ "type": "subkit",
927
+ "prepacks": [],
928
+ "dependencies": []
929
+ },
930
+ "S9999726": {
931
+ "name": "Subkit 6/6 Ethiopia IEHK renew S9906708",
932
+ "type": "subkit",
933
+ "prepacks": [
934
+ "S9999729"
935
+ ],
936
+ "dependencies": [
937
+ "S9999729"
938
+ ]
939
+ }
940
+ },
941
+ "dependencies": [
942
+ "S9999721",
943
+ "S9999722",
944
+ "S9999723",
945
+ "S9999724",
946
+ "S9999725",
947
+ "S9999726"
948
+ ]
949
+ },
950
+ "S9906710": {
951
+ "name": "Ethiopia Emergency Drug kit 2019",
952
+ "type": "master",
953
+ "subkits": {
954
+ "S9996710": {
955
+ "name": "SUB 1/7 Ethiopia Emerg. kit2019 S9906710",
956
+ "type": "subkit",
957
+ "prepacks": [
958
+ "S9986712",
959
+ "S9986711"
960
+ ],
961
+ "dependencies": [
962
+ "S9986712",
963
+ "S9986711"
964
+ ]
965
+ },
966
+ "S9996711": {
967
+ "name": "SUB 2/7 Ethiopia Emerg. kit2019 S9906710",
968
+ "type": "subkit",
969
+ "prepacks": [
970
+ "S9986711",
971
+ "S9986713",
972
+ "S9986714",
973
+ "S9986715"
974
+ ],
975
+ "dependencies": [
976
+ "S9986711",
977
+ "S9986713",
978
+ "S9986714",
979
+ "S9986715"
980
+ ]
981
+ },
982
+ "S9996712": {
983
+ "name": "SUB 3/7 Ethiopia Emerg. kit2019 S9906710",
984
+ "type": "subkit",
985
+ "prepacks": [],
986
+ "dependencies": []
987
+ },
988
+ "S9996713": {
989
+ "name": "SUB 4/7 Ethiopia Emerg. kit2019 S9906710",
990
+ "type": "subkit",
991
+ "prepacks": [],
992
+ "dependencies": []
993
+ },
994
+ "S9996714": {
995
+ "name": "SUB 5/7 Ethiopia Emerg. kit2019 S9906710",
996
+ "type": "subkit",
997
+ "prepacks": [],
998
+ "dependencies": []
999
+ },
1000
+ "S9996715": {
1001
+ "name": "SUB 6/7 Ethiopia Emerg. kit2019 S9906710",
1002
+ "type": "subkit",
1003
+ "prepacks": [],
1004
+ "dependencies": []
1005
+ },
1006
+ "S9996716": {
1007
+ "name": "SUB 7/7 Ethiopia Emerg. kit2019 S9906710",
1008
+ "type": "subkit",
1009
+ "prepacks": [],
1010
+ "dependencies": []
1011
+ }
1012
+ },
1013
+ "dependencies": [
1014
+ "S9996710",
1015
+ "S9996711",
1016
+ "S9996712",
1017
+ "S9996713",
1018
+ "S9996714",
1019
+ "S9996715",
1020
+ "S9996716"
1021
+ ]
1022
+ },
1023
+ "S9906712": {
1024
+ "name": "Sudan, Primary Healthcare kit, 2016",
1025
+ "type": "master",
1026
+ "subkits": {
1027
+ "S9999491": {
1028
+ "name": "Sub 1/4, SD SD,Primary Healthcare2016",
1029
+ "type": "subkit",
1030
+ "prepacks": [
1031
+ "S9999494",
1032
+ "S9999495",
1033
+ "S9999496"
1034
+ ],
1035
+ "dependencies": [
1036
+ "S9999494",
1037
+ "S9999495",
1038
+ "S9999496"
1039
+ ]
1040
+ },
1041
+ "S9999492": {
1042
+ "name": "Sub 2/4 , SD Primary Healthcare2016",
1043
+ "type": "subkit",
1044
+ "prepacks": [],
1045
+ "dependencies": []
1046
+ },
1047
+ "S9999493": {
1048
+ "name": "Sub 3/4 , SD Primary Healthcare2016",
1049
+ "type": "subkit",
1050
+ "prepacks": [],
1051
+ "dependencies": []
1052
+ }
1053
+ },
1054
+ "dependencies": [
1055
+ "S9999491",
1056
+ "S9999492",
1057
+ "S9999493"
1058
+ ]
1059
+ },
1060
+ "S9906713": {
1061
+ "name": "Sudan, IMCI Medicine kit, 2016",
1062
+ "type": "master",
1063
+ "subkits": {
1064
+ "S9999505": {
1065
+ "name": "Sub 1/7 SD IMCI Med. kit, 2016",
1066
+ "type": "subkit",
1067
+ "prepacks": [],
1068
+ "dependencies": []
1069
+ },
1070
+ "S9999506": {
1071
+ "name": "Sub 2/7 SD IMCI Med. kit, 2016",
1072
+ "type": "subkit",
1073
+ "prepacks": [
1074
+ "S9999503"
1075
+ ],
1076
+ "dependencies": [
1077
+ "S9999503"
1078
+ ]
1079
+ },
1080
+ "S9999507": {
1081
+ "name": "Sub 3/7 SD IMCI Med. kit, 2016",
1082
+ "type": "subkit",
1083
+ "prepacks": [
1084
+ "S9999504"
1085
+ ],
1086
+ "dependencies": [
1087
+ "S9999504"
1088
+ ]
1089
+ },
1090
+ "S9998505": {
1091
+ "name": "Sub 4/7 SD IMCI Med. kit, 2016",
1092
+ "type": "subkit",
1093
+ "prepacks": [],
1094
+ "dependencies": []
1095
+ },
1096
+ "S9998506": {
1097
+ "name": "Sub 5/7 SD IMCI Med. kit, 2016",
1098
+ "type": "subkit",
1099
+ "prepacks": [],
1100
+ "dependencies": []
1101
+ },
1102
+ "S9998507": {
1103
+ "name": "Sub 6/7 SD IMCI Med. kit, 2016",
1104
+ "type": "subkit",
1105
+ "prepacks": [],
1106
+ "dependencies": []
1107
+ },
1108
+ "S9998508": {
1109
+ "name": "Sub 7/7 SD IMCI Med. kit, 2016",
1110
+ "type": "subkit",
1111
+ "prepacks": [],
1112
+ "dependencies": []
1113
+ }
1114
+ },
1115
+ "dependencies": [
1116
+ "S9999505",
1117
+ "S9999506",
1118
+ "S9999507",
1119
+ "S9998505",
1120
+ "S9998506",
1121
+ "S9998507",
1122
+ "S9998508"
1123
+ ]
1124
+ },
1125
+ "S9906714": {
1126
+ "name": "Sudan Community Midwife kit, 2016",
1127
+ "type": "master",
1128
+ "subkits": {},
1129
+ "dependencies": []
1130
+ },
1131
+ "S9906719": {
1132
+ "name": "DPRK EM Kit 2018",
1133
+ "type": "master",
1134
+ "subkits": {},
1135
+ "dependencies": []
1136
+ },
1137
+ "S9906728": {
1138
+ "name": "DRC Health Centre kit 2016",
1139
+ "type": "master",
1140
+ "subkits": {},
1141
+ "dependencies": []
1142
+ },
1143
+ "S9910001": {
1144
+ "name": "Surg.inst.,basic surgery /SET",
1145
+ "type": "master",
1146
+ "subkits": {},
1147
+ "dependencies": []
1148
+ },
1149
+ "S9910003": {
1150
+ "name": "Surg.inst.,delivery /SET",
1151
+ "type": "master",
1152
+ "subkits": {},
1153
+ "dependencies": []
1154
+ },
1155
+ "S9910005": {
1156
+ "name": "Surg.inst.,dressing /SET",
1157
+ "type": "master",
1158
+ "subkits": {},
1159
+ "dependencies": []
1160
+ },
1161
+ "S9906729": {
1162
+ "name": "DRC Measles Kit 2021",
1163
+ "type": "master",
1164
+ "subkits": {
1165
+ "S9996721": {
1166
+ "name": "DRC Measles Kit 2021 SUB 1/5",
1167
+ "type": "subkit",
1168
+ "prepacks": [],
1169
+ "dependencies": []
1170
+ },
1171
+ "S9996722": {
1172
+ "name": "DRC Measles Kit 2021 SUB 2/5",
1173
+ "type": "subkit",
1174
+ "prepacks": [
1175
+ "S9996726"
1176
+ ],
1177
+ "dependencies": [
1178
+ "S9996726"
1179
+ ]
1180
+ },
1181
+ "S9996723": {
1182
+ "name": "DRC Measles Kit 2021 SUB 3/5",
1183
+ "type": "subkit",
1184
+ "prepacks": [],
1185
+ "dependencies": []
1186
+ },
1187
+ "S9996724": {
1188
+ "name": "DRC Measles Kit 2021 SUB 4/5",
1189
+ "type": "subkit",
1190
+ "prepacks": [],
1191
+ "dependencies": []
1192
+ },
1193
+ "S9996725": {
1194
+ "name": "DRC Measles Kit 2021 SUB 5/5",
1195
+ "type": "subkit",
1196
+ "prepacks": [],
1197
+ "dependencies": []
1198
+ }
1199
+ },
1200
+ "dependencies": [
1201
+ "S9996721",
1202
+ "S9996722",
1203
+ "S9996723",
1204
+ "S9996724",
1205
+ "S9996725"
1206
+ ]
1207
+ },
1208
+ "S9906731": {
1209
+ "name": "DPRK HHD Kit Refill 2018",
1210
+ "type": "master",
1211
+ "subkits": {},
1212
+ "dependencies": []
1213
+ },
1214
+ "S9906732": {
1215
+ "name": "DPRK Household Doctors Kit 2018",
1216
+ "type": "master",
1217
+ "subkits": {},
1218
+ "dependencies": []
1219
+ },
1220
+ "S9906733": {
1221
+ "name": "South Sudan PHCC Kit 2018 Basic Medicin",
1222
+ "type": "master",
1223
+ "subkits": {
1224
+ "S9993731": {
1225
+ "name": "SUB 1/9 S.Sudan PHCC18 B.Med S9906733",
1226
+ "type": "subkit",
1227
+ "prepacks": [
1228
+ "S9993736",
1229
+ "S9993737"
1230
+ ],
1231
+ "dependencies": [
1232
+ "S9993736",
1233
+ "S9993737"
1234
+ ]
1235
+ },
1236
+ "S9993732": {
1237
+ "name": "SUB 2/9 S.Sudan PHCC18 B.Med S9906733",
1238
+ "type": "subkit",
1239
+ "prepacks": [
1240
+ "S9993739"
1241
+ ],
1242
+ "dependencies": [
1243
+ "S9993739"
1244
+ ]
1245
+ },
1246
+ "S9993733": {
1247
+ "name": "SUB 3/9 S.Sudan PHCC18 B.Med S9906733",
1248
+ "type": "subkit",
1249
+ "prepacks": [
1250
+ "S9993738"
1251
+ ],
1252
+ "dependencies": [
1253
+ "S9993738"
1254
+ ]
1255
+ },
1256
+ "S9993734": {
1257
+ "name": "SUB 4/9 S.Sudan PHCC18 B.Med S9906733",
1258
+ "type": "subkit",
1259
+ "prepacks": [
1260
+ "S9993730"
1261
+ ],
1262
+ "dependencies": [
1263
+ "S9993730"
1264
+ ]
1265
+ },
1266
+ "S9993735": {
1267
+ "name": "SUB 5/9 S.Sudan PHCC18 B.Med S9906733",
1268
+ "type": "subkit",
1269
+ "prepacks": [],
1270
+ "dependencies": []
1271
+ },
1272
+ "S9993840": {
1273
+ "name": "SUB 6/9 S.Sudan PHCC18 B.Med S9906733",
1274
+ "type": "subkit",
1275
+ "prepacks": [],
1276
+ "dependencies": []
1277
+ },
1278
+ "S9993841": {
1279
+ "name": "SUB 7/9 S.Sudan PHCC18 B.Med S9906733",
1280
+ "type": "subkit",
1281
+ "prepacks": [],
1282
+ "dependencies": []
1283
+ },
1284
+ "S9993842": {
1285
+ "name": "SUB 8/9 S.Sudan PHCC18 B.Med S9906733",
1286
+ "type": "subkit",
1287
+ "prepacks": [],
1288
+ "dependencies": []
1289
+ },
1290
+ "S9993843": {
1291
+ "name": "SUB 9/9 S.Sudan PHCC18 B.Med S9906733",
1292
+ "type": "subkit",
1293
+ "prepacks": [],
1294
+ "dependencies": []
1295
+ }
1296
+ },
1297
+ "dependencies": [
1298
+ "S9993731",
1299
+ "S9993732",
1300
+ "S9993733",
1301
+ "S9993734",
1302
+ "S9993735",
1303
+ "S9993840",
1304
+ "S9993841",
1305
+ "S9993842",
1306
+ "S9993843"
1307
+ ]
1308
+ },
1309
+ "S9906734": {
1310
+ "name": "South Sudan, PHCU Kit 2018",
1311
+ "type": "master",
1312
+ "subkits": {
1313
+ "S9994731": {
1314
+ "name": "Sub1/8 South Sudan,PHCU Kit2018 S9906734",
1315
+ "type": "subkit",
1316
+ "prepacks": [],
1317
+ "dependencies": []
1318
+ },
1319
+ "S9994732": {
1320
+ "name": "Sub2/8 South Sudan,PHCU Kit2018 S9906734",
1321
+ "type": "subkit",
1322
+ "prepacks": [
1323
+ "S9994735",
1324
+ "S9994736"
1325
+ ],
1326
+ "dependencies": [
1327
+ "S9994735",
1328
+ "S9994736"
1329
+ ]
1330
+ },
1331
+ "S9994733": {
1332
+ "name": "Sub3/8 South Sudan,PHCU Kit2018 S9906734",
1333
+ "type": "subkit",
1334
+ "prepacks": [
1335
+ "S9994737"
1336
+ ],
1337
+ "dependencies": [
1338
+ "S9994737"
1339
+ ]
1340
+ },
1341
+ "S9994734": {
1342
+ "name": "Sub4/8 South Sudan,PHCU Kit2018 S9906734",
1343
+ "type": "subkit",
1344
+ "prepacks": [],
1345
+ "dependencies": []
1346
+ },
1347
+ "S9994730": {
1348
+ "name": "Sub5/8 South Sudan,PHCU Kit2018 S9906734",
1349
+ "type": "subkit",
1350
+ "prepacks": [],
1351
+ "dependencies": []
1352
+ },
1353
+ "S9994739": {
1354
+ "name": "Sub6/8 South Sudan,PHCU Kit2018 S9906734",
1355
+ "type": "subkit",
1356
+ "prepacks": [],
1357
+ "dependencies": []
1358
+ },
1359
+ "S9994831": {
1360
+ "name": "Sub7/8 South Sudan,PHCU Kit2018 S9906734",
1361
+ "type": "subkit",
1362
+ "prepacks": [],
1363
+ "dependencies": []
1364
+ },
1365
+ "S9994832": {
1366
+ "name": "Sub8/8 South Sudan,PHCU Kit2018 S9906734",
1367
+ "type": "subkit",
1368
+ "prepacks": [],
1369
+ "dependencies": []
1370
+ },
1371
+ "S9994738": {
1372
+ "name": "PP4/4 f Sub4/4 S.Sudan,PHCU Kit S9906734",
1373
+ "type": "subkit",
1374
+ "prepacks": [
1375
+ "S9994738"
1376
+ ],
1377
+ "dependencies": [
1378
+ "S9994738"
1379
+ ]
1380
+ }
1381
+ },
1382
+ "dependencies": [
1383
+ "S9994731",
1384
+ "S9994732",
1385
+ "S9994733",
1386
+ "S9994734",
1387
+ "S9994730",
1388
+ "S9994739",
1389
+ "S9994831",
1390
+ "S9994832",
1391
+ "S9994738"
1392
+ ]
1393
+ },
1394
+ "S9906735": {
1395
+ "name": "South Sudan PHCC Kit 2018-Injectables",
1396
+ "type": "master",
1397
+ "subkits": {
1398
+ "S9995731": {
1399
+ "name": "SUB 1/9 SSPHCC Kit 2018-Injecta S9906735",
1400
+ "type": "subkit",
1401
+ "prepacks": [],
1402
+ "dependencies": []
1403
+ },
1404
+ "S9995732": {
1405
+ "name": "SUB 2/9 SSPHCC Kit 2018-Injecta S9906735",
1406
+ "type": "subkit",
1407
+ "prepacks": [],
1408
+ "dependencies": []
1409
+ },
1410
+ "S9995733": {
1411
+ "name": "SUB 3/9 SSPHCC Kit 2018-Injecta S9906735",
1412
+ "type": "subkit",
1413
+ "prepacks": [],
1414
+ "dependencies": []
1415
+ },
1416
+ "S9995734": {
1417
+ "name": "SUB 4/9 SSPHCC Kit 2018-Injecta S9906735",
1418
+ "type": "subkit",
1419
+ "prepacks": [],
1420
+ "dependencies": []
1421
+ },
1422
+ "S9995735": {
1423
+ "name": "SUB 5/9 SSPHCC Kit 2018-Injecta S9906735",
1424
+ "type": "subkit",
1425
+ "prepacks": [],
1426
+ "dependencies": []
1427
+ },
1428
+ "S9995736": {
1429
+ "name": "SUB 6/9 SSPHCC Kit 2018-Injecta S9906735",
1430
+ "type": "subkit",
1431
+ "prepacks": [],
1432
+ "dependencies": []
1433
+ },
1434
+ "S9995737": {
1435
+ "name": "SUB 7/9 SSPHCC Kit 2018-Injecta S9906735",
1436
+ "type": "subkit",
1437
+ "prepacks": [],
1438
+ "dependencies": []
1439
+ },
1440
+ "S9995738": {
1441
+ "name": "SUB 8/9 SSPHCC Kit 2018-Injecta S9906735",
1442
+ "type": "subkit",
1443
+ "prepacks": [],
1444
+ "dependencies": []
1445
+ },
1446
+ "S9995739": {
1447
+ "name": "SUB 9/9 SSPHCC Kit 2018-Injecta S9906735",
1448
+ "type": "subkit",
1449
+ "prepacks": [],
1450
+ "dependencies": []
1451
+ }
1452
+ },
1453
+ "dependencies": [
1454
+ "S9995731",
1455
+ "S9995732",
1456
+ "S9995733",
1457
+ "S9995734",
1458
+ "S9995735",
1459
+ "S9995736",
1460
+ "S9995737",
1461
+ "S9995738",
1462
+ "S9995739"
1463
+ ]
1464
+ },
1465
+ "S9906736": {
1466
+ "name": "South Sudan PHCC Kit 2018- IV infusions",
1467
+ "type": "master",
1468
+ "subkits": {
1469
+ "S9996731": {
1470
+ "name": "S. Sudan PHCC 2018 I sub 1/8 f S9906736",
1471
+ "type": "subkit",
1472
+ "prepacks": [],
1473
+ "dependencies": []
1474
+ },
1475
+ "S9996732": {
1476
+ "name": "S. Sudan PHCC 2018 I sub 2/8 f S9906736",
1477
+ "type": "subkit",
1478
+ "prepacks": [],
1479
+ "dependencies": []
1480
+ },
1481
+ "S9996733": {
1482
+ "name": "S. Sudan PHCC 2018 I sub 3/8 f S9906736",
1483
+ "type": "subkit",
1484
+ "prepacks": [],
1485
+ "dependencies": []
1486
+ },
1487
+ "S9996734": {
1488
+ "name": "S. Sudan PHCC 2018 I sub 4/8 f S9906736",
1489
+ "type": "subkit",
1490
+ "prepacks": [],
1491
+ "dependencies": []
1492
+ },
1493
+ "S9996735": {
1494
+ "name": "S. Sudan PHCC 2018 I sub 5/8 f S9906736",
1495
+ "type": "subkit",
1496
+ "prepacks": [],
1497
+ "dependencies": []
1498
+ },
1499
+ "S9996736": {
1500
+ "name": "S. Sudan PHCC 2018 I sub 6/8 f S9906736",
1501
+ "type": "subkit",
1502
+ "prepacks": [],
1503
+ "dependencies": []
1504
+ },
1505
+ "S9996737": {
1506
+ "name": "S. Sudan PHCC 2018 I sub 7/8 f S9906736",
1507
+ "type": "subkit",
1508
+ "prepacks": [],
1509
+ "dependencies": []
1510
+ },
1511
+ "S9996738": {
1512
+ "name": "S. Sudan PHCC 2018 I sub 8/8 f S9906736",
1513
+ "type": "subkit",
1514
+ "prepacks": [],
1515
+ "dependencies": []
1516
+ }
1517
+ },
1518
+ "dependencies": [
1519
+ "S9996731",
1520
+ "S9996732",
1521
+ "S9996733",
1522
+ "S9996734",
1523
+ "S9996735",
1524
+ "S9996736",
1525
+ "S9996737",
1526
+ "S9996738"
1527
+ ]
1528
+ },
1529
+ "S9906737": {
1530
+ "name": "South Sudan PHCC Kit 2018-Med Dev & Diag",
1531
+ "type": "master",
1532
+ "subkits": {
1533
+ "S9997731": {
1534
+ "name": "SUB 1/8 SSPHCC2018-Med Dev&Diag S9906737",
1535
+ "type": "subkit",
1536
+ "prepacks": [
1537
+ "S9997739",
1538
+ "S9997730"
1539
+ ],
1540
+ "dependencies": [
1541
+ "S9997739",
1542
+ "S9997730"
1543
+ ]
1544
+ },
1545
+ "S9997732": {
1546
+ "name": "SUB 2/8 SSPHCC2018-Med Dev&Diag S9906737",
1547
+ "type": "subkit",
1548
+ "prepacks": [],
1549
+ "dependencies": []
1550
+ },
1551
+ "S9997733": {
1552
+ "name": "SUB 3/8 SSPHCC2018-Med Dev&Diag S9906737",
1553
+ "type": "subkit",
1554
+ "prepacks": [],
1555
+ "dependencies": []
1556
+ },
1557
+ "S9997734": {
1558
+ "name": "SUB 4/8 SSPHCC2018-Med Dev&Diag S9906737",
1559
+ "type": "subkit",
1560
+ "prepacks": [],
1561
+ "dependencies": []
1562
+ },
1563
+ "S9997735": {
1564
+ "name": "SUB 5/8 SSPHCC2018-Med Dev&Diag S9906737",
1565
+ "type": "subkit",
1566
+ "prepacks": [],
1567
+ "dependencies": []
1568
+ },
1569
+ "S9997736": {
1570
+ "name": "SUB 6/8 SSPHCC2018-Med Dev&Diag S9906737",
1571
+ "type": "subkit",
1572
+ "prepacks": [],
1573
+ "dependencies": []
1574
+ },
1575
+ "S9997737": {
1576
+ "name": "SUB 7/8 SSPHCC2018-Med Dev&Diag S9906737",
1577
+ "type": "subkit",
1578
+ "prepacks": [],
1579
+ "dependencies": []
1580
+ },
1581
+ "S9997738": {
1582
+ "name": "SUB 8/8 SSPHCC2018-Med Dev&Diag S9906737",
1583
+ "type": "subkit",
1584
+ "prepacks": [],
1585
+ "dependencies": []
1586
+ }
1587
+ },
1588
+ "dependencies": [
1589
+ "S9997731",
1590
+ "S9997732",
1591
+ "S9997733",
1592
+ "S9997734",
1593
+ "S9997735",
1594
+ "S9997736",
1595
+ "S9997737",
1596
+ "S9997738"
1597
+ ]
1598
+ },
1599
+ "S9906742": {
1600
+ "name": "Mozambique Community Health Worker Kit",
1601
+ "type": "master",
1602
+ "subkits": {},
1603
+ "dependencies": []
1604
+ },
1605
+ "S9906753": {
1606
+ "name": "Nut kit,Inpatient, Ethiop.Mod. Med. Supp",
1607
+ "type": "master",
1608
+ "subkits": {
1609
+ "S9996752": {
1610
+ "name": "SUB1/2 Nut kit,I Ethiop.Mod F S9906753",
1611
+ "type": "subkit",
1612
+ "prepacks": [
1613
+ "S9996751"
1614
+ ],
1615
+ "dependencies": [
1616
+ "S9996751"
1617
+ ]
1618
+ },
1619
+ "S9996753": {
1620
+ "name": "SUB2/2 Nut kit,I Ethiop.Mod F S9906753",
1621
+ "type": "subkit",
1622
+ "prepacks": [],
1623
+ "dependencies": []
1624
+ }
1625
+ },
1626
+ "dependencies": [
1627
+ "S9996752",
1628
+ "S9996753"
1629
+ ]
1630
+ },
1631
+ "S9906754": {
1632
+ "name": "Nutrition Kit, Anthropometric. ETH",
1633
+ "type": "master",
1634
+ "subkits": {
1635
+ "S9996755": {
1636
+ "name": "SUB 2/2 S9906754 Nut,Anthropometric. ETH",
1637
+ "type": "subkit",
1638
+ "prepacks": [],
1639
+ "dependencies": []
1640
+ },
1641
+ "S9996754": {
1642
+ "name": "SUB 1/2 S9906754 Nut,Anthropometric. ETH",
1643
+ "type": "subkit",
1644
+ "prepacks": [],
1645
+ "dependencies": []
1646
+ }
1647
+ },
1648
+ "dependencies": [
1649
+ "S9996755",
1650
+ "S9996754"
1651
+ ]
1652
+ },
1653
+ "S9906791": {
1654
+ "name": "Mali, in-patient CMAM kit, version 2014",
1655
+ "type": "master",
1656
+ "subkits": {
1657
+ "S9999751": {
1658
+ "name": "Subkit 1/6 Mali, in-patient CMAM kit",
1659
+ "type": "subkit",
1660
+ "prepacks": [
1661
+ "S9999691",
1662
+ "S9999692"
1663
+ ],
1664
+ "dependencies": [
1665
+ "S9999691",
1666
+ "S9999692"
1667
+ ]
1668
+ },
1669
+ "S9999752": {
1670
+ "name": "Subkit 2/6 Mali, in-patient CMAM kit",
1671
+ "type": "subkit",
1672
+ "prepacks": [
1673
+ "S9999693"
1674
+ ],
1675
+ "dependencies": [
1676
+ "S9999693"
1677
+ ]
1678
+ },
1679
+ "S9999753": {
1680
+ "name": "Subkit 3/6 Mali, in-patient CMAM kit",
1681
+ "type": "subkit",
1682
+ "prepacks": [
1683
+ "S9999694"
1684
+ ],
1685
+ "dependencies": [
1686
+ "S9999694"
1687
+ ]
1688
+ },
1689
+ "S9999754": {
1690
+ "name": "Subkit 4/6 Mali, in-patient CMAM kit",
1691
+ "type": "subkit",
1692
+ "prepacks": [],
1693
+ "dependencies": []
1694
+ },
1695
+ "S9999755": {
1696
+ "name": "Subkit 5/6 Mali, in-patient CMAM kit",
1697
+ "type": "subkit",
1698
+ "prepacks": [],
1699
+ "dependencies": []
1700
+ },
1701
+ "S9999756": {
1702
+ "name": "Subkit 6/6 Mali, in-patient CMAM kit",
1703
+ "type": "subkit",
1704
+ "prepacks": [],
1705
+ "dependencies": []
1706
+ }
1707
+ },
1708
+ "dependencies": [
1709
+ "S9999751",
1710
+ "S9999752",
1711
+ "S9999753",
1712
+ "S9999754",
1713
+ "S9999755",
1714
+ "S9999756"
1715
+ ]
1716
+ },
1717
+ "S9906797": {
1718
+ "name": "Ethiopia Midwifery kit,2-equipment V2",
1719
+ "type": "master",
1720
+ "subkits": {},
1721
+ "dependencies": []
1722
+ },
1723
+ "S9999604": {
1724
+ "name": "Ethiopia Surg.inst.,suture /SET",
1725
+ "type": "master",
1726
+ "subkits": {},
1727
+ "dependencies": []
1728
+ },
1729
+ "S9999603": {
1730
+ "name": "Ethiopia Surg.inst.,delivery /SET",
1731
+ "type": "master",
1732
+ "subkits": {},
1733
+ "dependencies": []
1734
+ },
1735
+ "S9906800": {
1736
+ "name": "Yemen Primary Health Kit 2021-Basic meds",
1737
+ "type": "master",
1738
+ "subkits": {
1739
+ "S9986721": {
1740
+ "name": "SUB 1/3 Yemen PHK 2021-Basic S9906800",
1741
+ "type": "subkit",
1742
+ "prepacks": [
1743
+ "S9986716"
1744
+ ],
1745
+ "dependencies": [
1746
+ "S9986716"
1747
+ ]
1748
+ },
1749
+ "S9986722": {
1750
+ "name": "SUB 2/3 Yemen PHK 2021-Basic S9906800",
1751
+ "type": "subkit",
1752
+ "prepacks": [
1753
+ "S9986716",
1754
+ "S9986717",
1755
+ "S9986710"
1756
+ ],
1757
+ "dependencies": [
1758
+ "S9986716",
1759
+ "S9986717",
1760
+ "S9986710"
1761
+ ]
1762
+ },
1763
+ "S9986723": {
1764
+ "name": "SUB 3/3 Yemen PHK 2021-Basic S9906800",
1765
+ "type": "subkit",
1766
+ "prepacks": [],
1767
+ "dependencies": []
1768
+ }
1769
+ },
1770
+ "dependencies": [
1771
+ "S9986721",
1772
+ "S9986722",
1773
+ "S9986723"
1774
+ ]
1775
+ },
1776
+ "S9906801": {
1777
+ "name": "Yemen Primary Health Kit2021-consumables",
1778
+ "type": "master",
1779
+ "subkits": {},
1780
+ "dependencies": []
1781
+ },
1782
+ "S9906802": {
1783
+ "name": "Yemen Primary Health Kit 2021-Suppl meds",
1784
+ "type": "master",
1785
+ "subkits": {},
1786
+ "dependencies": []
1787
+ },
1788
+ "S9908200": {
1789
+ "name": "Sterilization, kit C",
1790
+ "type": "master",
1791
+ "subkits": {
1792
+ "S9999923": {
1793
+ "name": "Subkit 1/2 Sterilization, kit C S9908200",
1794
+ "type": "subkit",
1795
+ "prepacks": [],
1796
+ "dependencies": []
1797
+ },
1798
+ "S9999924": {
1799
+ "name": "Subkit 2/2 Sterilization, kit C S9908200",
1800
+ "type": "subkit",
1801
+ "prepacks": [],
1802
+ "dependencies": []
1803
+ }
1804
+ },
1805
+ "dependencies": [
1806
+ "S9999923",
1807
+ "S9999924"
1808
+ ]
1809
+ },
1810
+ "S9908300": {
1811
+ "name": "Obstetric,surgical kit,suppl.1-drugs",
1812
+ "type": "master",
1813
+ "subkits": {
1814
+ "S9999995": {
1815
+ "name": "Sub 1/10 Obstetric S9908300(i) part 1",
1816
+ "type": "subkit",
1817
+ "prepacks": [
1818
+ "S9999944",
1819
+ "S9999945"
1820
+ ],
1821
+ "dependencies": [
1822
+ "S9999944",
1823
+ "S9999945"
1824
+ ]
1825
+ },
1826
+ "S9999994": {
1827
+ "name": "Sub 2/10 Obstetric S9908300(i) part 2",
1828
+ "type": "subkit",
1829
+ "prepacks": [],
1830
+ "dependencies": []
1831
+ },
1832
+ "S9999993": {
1833
+ "name": "Sub 3/10 Obstetric S9908300(i) part 3",
1834
+ "type": "subkit",
1835
+ "prepacks": [],
1836
+ "dependencies": []
1837
+ },
1838
+ "S9999975": {
1839
+ "name": "Sub 4/10 Obstetric S9908300(i) part 4",
1840
+ "type": "subkit",
1841
+ "prepacks": [],
1842
+ "dependencies": []
1843
+ },
1844
+ "S9999973": {
1845
+ "name": "Sub 5/10 Obstetric S9908300(i) part 5",
1846
+ "type": "subkit",
1847
+ "prepacks": [],
1848
+ "dependencies": []
1849
+ },
1850
+ "S9999972": {
1851
+ "name": "Sub 6/10 Obstetric S9908300(i) part 6",
1852
+ "type": "subkit",
1853
+ "prepacks": [],
1854
+ "dependencies": []
1855
+ },
1856
+ "S9999976": {
1857
+ "name": "Sub 7/10 Obstetric S9908300(i) part 7",
1858
+ "type": "subkit",
1859
+ "prepacks": [],
1860
+ "dependencies": []
1861
+ },
1862
+ "S9999977": {
1863
+ "name": "Sub 8/10 Obstetric S9908300(i) part 8",
1864
+ "type": "subkit",
1865
+ "prepacks": [],
1866
+ "dependencies": []
1867
+ },
1868
+ "S9999971": {
1869
+ "name": "Sub 9/10 Obstetric S9908300(i) part 9",
1870
+ "type": "subkit",
1871
+ "prepacks": [],
1872
+ "dependencies": []
1873
+ },
1874
+ "S9999974": {
1875
+ "name": "Sub 10/10 Obstetric S9908300 part 10-42",
1876
+ "type": "subkit",
1877
+ "prepacks": [],
1878
+ "dependencies": []
1879
+ }
1880
+ },
1881
+ "dependencies": [
1882
+ "S9999995",
1883
+ "S9999994",
1884
+ "S9999993",
1885
+ "S9999975",
1886
+ "S9999973",
1887
+ "S9999972",
1888
+ "S9999976",
1889
+ "S9999977",
1890
+ "S9999971",
1891
+ "S9999974"
1892
+ ]
1893
+ },
1894
+ "S9908302": {
1895
+ "name": "Obstetric,surgical kit,suppl.3-renewable",
1896
+ "type": "master",
1897
+ "subkits": {
1898
+ "S9999881": {
1899
+ "name": "Subkit 1/6 Obstetric 3-renew.S9908302",
1900
+ "type": "subkit",
1901
+ "prepacks": [],
1902
+ "dependencies": []
1903
+ },
1904
+ "S9999882": {
1905
+ "name": "Subkit 2/6 Obstetric 3-renew.S9908302",
1906
+ "type": "subkit",
1907
+ "prepacks": [],
1908
+ "dependencies": []
1909
+ },
1910
+ "S9999883": {
1911
+ "name": "Subkit 3/6 Obstetric 3-renew.S9908302",
1912
+ "type": "subkit",
1913
+ "prepacks": [
1914
+ "S9999938"
1915
+ ],
1916
+ "dependencies": [
1917
+ "S9999938"
1918
+ ]
1919
+ },
1920
+ "S9999884": {
1921
+ "name": "Subkit 4/6 Obstetric 3-renew.S9908302",
1922
+ "type": "subkit",
1923
+ "prepacks": [
1924
+ "S9999939",
1925
+ "S9999299"
1926
+ ],
1927
+ "dependencies": [
1928
+ "S9999939",
1929
+ "S9999299"
1930
+ ]
1931
+ },
1932
+ "S9999885": {
1933
+ "name": "Subkit 5/6 Obstetric 3-renew.S9908302",
1934
+ "type": "subkit",
1935
+ "prepacks": [],
1936
+ "dependencies": []
1937
+ },
1938
+ "S9999886": {
1939
+ "name": "Subkit 6/6 Obstetric 3-renew.S9908302",
1940
+ "type": "subkit",
1941
+ "prepacks": [],
1942
+ "dependencies": []
1943
+ }
1944
+ },
1945
+ "dependencies": [
1946
+ "S9999881",
1947
+ "S9999882",
1948
+ "S9999883",
1949
+ "S9999884",
1950
+ "S9999885",
1951
+ "S9999886"
1952
+ ]
1953
+ },
1954
+ "S9908303": {
1955
+ "name": "Obstetric,surgical kit,suppl.1a-drugs",
1956
+ "type": "master",
1957
+ "subkits": {},
1958
+ "dependencies": []
1959
+ },
1960
+ "S9908305": {
1961
+ "name": "Obstetric,surgical kit,suppl.2-equipment",
1962
+ "type": "master",
1963
+ "subkits": {},
1964
+ "dependencies": []
1965
+ },
1966
+ "S9908402": {
1967
+ "name": "Resuscitation kit,basic",
1968
+ "type": "master",
1969
+ "subkits": {},
1970
+ "dependencies": []
1971
+ },
1972
+ "S9910000": {
1973
+ "name": "Surg.inst.,abdominal /SET",
1974
+ "type": "master",
1975
+ "subkits": {},
1976
+ "dependencies": []
1977
+ },
1978
+ "S9910002": {
1979
+ "name": "Surg.inst.,curettage /SET",
1980
+ "type": "master",
1981
+ "subkits": {},
1982
+ "dependencies": []
1983
+ },
1984
+ "S9910004": {
1985
+ "name": "Surg.inst.,suture /SET",
1986
+ "type": "master",
1987
+ "subkits": {},
1988
+ "dependencies": []
1989
+ },
1990
+ "S9910006": {
1991
+ "name": "Surg.inst.,exam/sut,vaginal/cervical/SET",
1992
+ "type": "master",
1993
+ "subkits": {},
1994
+ "dependencies": []
1995
+ },
1996
+ "S9935011": {
1997
+ "name": "School-in-a-carton,40 students, 2016",
1998
+ "type": "master",
1999
+ "subkits": {},
2000
+ "dependencies": []
2001
+ },
2002
+ "S9935019": {
2003
+ "name": "School-in-a-bag kit,40 student,1 teacher",
2004
+ "type": "master",
2005
+ "subkits": {},
2006
+ "dependencies": []
2007
+ },
2008
+ "S9935024": {
2009
+ "name": "Recreation kit, 2016",
2010
+ "type": "master",
2011
+ "subkits": {},
2012
+ "dependencies": []
2013
+ },
2014
+ "S9935030": {
2015
+ "name": "Recreation kit mini version",
2016
+ "type": "master",
2017
+ "subkits": {},
2018
+ "dependencies": []
2019
+ },
2020
+ "S9935034": {
2021
+ "name": "Recreation kit-in-a-carton, 2016",
2022
+ "type": "master",
2023
+ "subkits": {},
2024
+ "dependencies": []
2025
+ },
2026
+ "S9935051": {
2027
+ "name": "School-in-a-carton, Yemen",
2028
+ "type": "master",
2029
+ "subkits": {},
2030
+ "dependencies": []
2031
+ },
2032
+ "S9935064": {
2033
+ "name": "Early Childhood Dvt (ECD) kit, 2016",
2034
+ "type": "master",
2035
+ "subkits": {},
2036
+ "dependencies": []
2037
+ },
2038
+ "S9935065": {
2039
+ "name": "ECD kit-in-a-carton, 2016",
2040
+ "type": "master",
2041
+ "subkits": {},
2042
+ "dependencies": []
2043
+ },
2044
+ "S9935080": {
2045
+ "name": "Arabic Student Kit Grade 1-4",
2046
+ "type": "master",
2047
+ "subkits": {},
2048
+ "dependencies": []
2049
+ },
2050
+ "S9935081": {
2051
+ "name": "Arabic Student Kit Grade 5-8",
2052
+ "type": "master",
2053
+ "subkits": {},
2054
+ "dependencies": []
2055
+ },
2056
+ "S9935082": {
2057
+ "name": "Arabic Teacher's Kit",
2058
+ "type": "master",
2059
+ "subkits": {},
2060
+ "dependencies": []
2061
+ },
2062
+ "S9935084": {
2063
+ "name": "Syria Emergency School in a Carton Kit",
2064
+ "type": "master",
2065
+ "subkits": {},
2066
+ "dependencies": []
2067
+ },
2068
+ "S9935090": {
2069
+ "name": "Syria Paediatric Kit, 2019",
2070
+ "type": "master",
2071
+ "subkits": {},
2072
+ "dependencies": []
2073
+ },
2074
+ "S9935095": {
2075
+ "name": "Replenishment kit for School-in-a-box",
2076
+ "type": "master",
2077
+ "subkits": {},
2078
+ "dependencies": []
2079
+ },
2080
+ "S9935097": {
2081
+ "name": "School-in-a-box,40 students, 2016",
2082
+ "type": "master",
2083
+ "subkits": {},
2084
+ "dependencies": []
2085
+ },
2086
+ "S9935099": {
2087
+ "name": "Extra materials for School-in-a-box",
2088
+ "type": "master",
2089
+ "subkits": {},
2090
+ "dependencies": []
2091
+ },
2092
+ "S9935100": {
2093
+ "name": "School-in-a-carton, Ukraine",
2094
+ "type": "master",
2095
+ "subkits": {},
2096
+ "dependencies": []
2097
+ },
2098
+ "S9935101": {
2099
+ "name": "School-in-a-box, Ukraine",
2100
+ "type": "master",
2101
+ "subkits": {},
2102
+ "dependencies": []
2103
+ },
2104
+ "S9975020": {
2105
+ "name": "First aid kit class A",
2106
+ "type": "master",
2107
+ "subkits": {},
2108
+ "dependencies": []
2109
+ }
2110
+ }
data/real_data_excel/converted_csv/COOIS_Planned_and_Released.csv CHANGED
@@ -1,5 +1,8 @@
1
  Order,Material Number,Material description,Order quantity (GMEIN),Basic start date,Basic finish date,GOS Text display for CPH production orde
2
  100035188,S9910000,"Surg.inst.,abdominal /SET",150,2025-07-07,2025-07-11,
 
 
 
3
  100035218,S9999126,AWD Periphery kit Drug S9903004 sub 3/4,287,2025-07-07,2025-07-11,
4
  100035228,S9935065,"ECD kit-in-a-carton, 2016",800,2025-07-07,2025-07-11,
5
  100035234,S9975020,First aid kit class A,780,2025-07-07,2025-07-11,
 
1
  Order,Material Number,Material description,Order quantity (GMEIN),Basic start date,Basic finish date,GOS Text display for CPH production orde
2
  100035188,S9910000,"Surg.inst.,abdominal /SET",150,2025-07-07,2025-07-11,
3
+ 100035188,S9991041,"fake",150,2025-07-07,2025-07-11,
4
+ 100035188,S9991044,"fake",150,2025-07-07,2025-07-11,
5
+ 100035188,S9901041,"fake",150,2025-07-07,2025-07-11,
6
  100035218,S9999126,AWD Periphery kit Drug S9903004 sub 3/4,287,2025-07-07,2025-07-11,
7
  100035228,S9935065,"ECD kit-in-a-carton, 2016",800,2025-07-07,2025-07-11,
8
  100035234,S9975020,First aid kit class A,780,2025-07-07,2025-07-11,
data/real_data_excel/converted_csv/COOIS_Released_Prod_Orders.csv CHANGED
@@ -1,5 +1,9 @@
1
  Order,Material Number,Material description,Order quantity (GMEIN),Basic start date,Basic finish date,System Status
2
  100033364,S9992431,"SUB 1/8 NBK, Clinic, Module 1, Medicines",14,2025-03-03,2025-03-07,REL PRC BCRQ MACM SETC
 
 
 
 
3
  100034881,S9991123,SUB 3/5 f.S9901026 IEHK2017 part 1,58,2025-03-17,2025-03-21,REL PRC BCRQ MACM SETC
4
  100035124,S9992442,"SUB 2/3 NBK, Clinic,Module 2,Consumables",5,2025-03-14,2025-03-21,REL PRC BCRQ MACM SETC
5
  100034003,S9901042,"IEHK 2024,Basic Equipment UNIT",800,2025-03-24,2025-03-28,REL PRC BCRQ MANC SETC
@@ -56,3 +60,7 @@ Order,Material Number,Material description,Order quantity (GMEIN),Basic start da
56
  100034530,S9992413,"SUB 3/3 NBK,Community,Part B,Module1,Med",119,2025-04-07,2025-04-11,REL PRC BCRQ MANC SETC
57
  100034791,S9991041,"IEHK 2024,Basic Medicine&Renewabl SUB1/3",900,2025-04-07,2025-04-11,REL PRC BCRQ MANC SETC
58
  100032406,S9992583,"SUB 6/17 NBK, Hospital, Module 3, Equ",4,2025-04-28,2025-05-02,REL PRC MANC SETC
 
 
 
 
 
1
  Order,Material Number,Material description,Order quantity (GMEIN),Basic start date,Basic finish date,System Status
2
  100033364,S9992431,"SUB 1/8 NBK, Clinic, Module 1, Medicines",14,2025-03-03,2025-03-07,REL PRC BCRQ MACM SETC
3
+ 100035188,S9910000,"Surg.inst.,abdominal /SET",150,2025-07-07,2025-07-11,
4
+ 100035188,S9991041,"fake",150,2025-07-07,2025-07-11,
5
+ 100035188,S9991044,"fake",150,2025-07-07,2025-07-11,
6
+ 100035188,S9901041,"fake",150,2025-07-07,2025-07-11,
7
  100034881,S9991123,SUB 3/5 f.S9901026 IEHK2017 part 1,58,2025-03-17,2025-03-21,REL PRC BCRQ MACM SETC
8
  100035124,S9992442,"SUB 2/3 NBK, Clinic,Module 2,Consumables",5,2025-03-14,2025-03-21,REL PRC BCRQ MACM SETC
9
  100034003,S9901042,"IEHK 2024,Basic Equipment UNIT",800,2025-03-24,2025-03-28,REL PRC BCRQ MANC SETC
 
60
  100034530,S9992413,"SUB 3/3 NBK,Community,Part B,Module1,Med",119,2025-04-07,2025-04-11,REL PRC BCRQ MANC SETC
61
  100034791,S9991041,"IEHK 2024,Basic Medicine&Renewabl SUB1/3",900,2025-04-07,2025-04-11,REL PRC BCRQ MANC SETC
62
  100032406,S9992583,"SUB 6/17 NBK, Hospital, Module 3, Equ",4,2025-04-28,2025-05-02,REL PRC MANC SETC
63
+ 100035188,S9910000,"Surg.inst.,abdominal /SET",150,2025-07-07,2025-07-11,
64
+ 100035188,S9991041,"fake",150,2025-07-07,2025-07-11,
65
+ 100035188,S9991044,"fake",150,2025-07-07,2025-07-11,
66
+ 100035188,S9901041,"fake",150,2025-07-07,2025-07-11,
data/real_data_excel/converted_csv/Kit_Composition_and_relation_cleaned_with_line_type.csv CHANGED
@@ -1,4 +1,6 @@
1
  kit_name,kit_description,kit_type,line_type
 
 
2
  S9901040,"IEHK 2024,Basic Medicine&Renewable UNIT",master,
3
  S9901040,"IEHK 2024,Basic Medicine&Renewable UNIT",master,
4
  S9901040,"IEHK 2024,Basic Medicine&Renewable UNIT",master,
 
1
  kit_name,kit_description,kit_type,line_type
2
+ S9992431,"This is a fake data point",subkit,long line
3
+ S9901040,"IEHK 2024,Basic Medicine&Renewable UNIT",master,
4
  S9901040,"IEHK 2024,Basic Medicine&Renewable UNIT",master,
5
  S9901040,"IEHK 2024,Basic Medicine&Renewable UNIT",master,
6
  S9901040,"IEHK 2024,Basic Medicine&Renewable UNIT",master,
data/real_data_excel/converted_csv/Kit_Composition_and_relation_cleaned_with_line_type_and_id.csv ADDED
@@ -0,0 +1,676 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ kit_name,kit_description,kit_type,line_type,line_id
2
+ S9901040,"IEHK 2024,Basic Medicine&Renewable UNIT",master,,
3
+ S9901040,"IEHK 2024,Basic Medicine&Renewable UNIT",master,,
4
+ S9901040,"IEHK 2024,Basic Medicine&Renewable UNIT",master,,
5
+ S9901040,"IEHK 2024,Basic Medicine&Renewable UNIT",master,,
6
+ S9901040,"IEHK 2024,Basic Medicine&Renewable UNIT",master,,
7
+ S9901040,"IEHK 2024,Basic Medicine&Renewable UNIT",master,,
8
+ S9902219,"Midwifery kit,3-renewable",master,,
9
+ S9902219,"Midwifery kit,3-renewable",master,,
10
+ S9902219,"Midwifery kit,3-renewable",master,,
11
+ S9902219,"Midwifery kit,3-renewable",master,,
12
+ S9902219,"Midwifery kit,3-renewable",master,,
13
+ S9902219,"Midwifery kit,3-renewable",master,,
14
+ S9902219,"Midwifery kit,3-renewable",master,,
15
+ S9902219,"Midwifery kit,3-renewable",master,,
16
+ S9902219,"Midwifery kit,3-renewable",master,,
17
+ S9902219,"Midwifery kit,3-renewable",master,,
18
+ S9902219,"Midwifery kit,3-renewable",master,,
19
+ S9902219,"Midwifery kit,3-renewable",master,,
20
+ S9902219,"Midwifery kit,3-renewable",master,,
21
+ S9902450,"NBK, Clinic, Module 3, Equipment",master,long line,6.0
22
+ S9902470,"NBK, Hospital, Module 2, Consumables",master,,
23
+ S9902470,"NBK, Hospital, Module 2, Consumables",master,,
24
+ S9902470,"NBK, Hospital, Module 2, Consumables",master,,
25
+ S9902470,"NBK, Hospital, Module 2, Consumables",master,,
26
+ S9902470,"NBK, Hospital, Module 2, Consumables",master,,
27
+ S9902470,"NBK, Hospital, Module 2, Consumables",master,,
28
+ S9902470,"NBK, Hospital, Module 2, Consumables",master,,
29
+ S9902470,"NBK, Hospital, Module 2, Consumables",master,,
30
+ S9902480,"NBK, Hospital, Module 3, Equipment",master,,
31
+ S9902480,"NBK, Hospital, Module 3, Equipment",master,,
32
+ S9902480,"NBK, Hospital, Module 3, Equipment",master,,
33
+ S9902480,"NBK, Hospital, Module 3, Equipment",master,,
34
+ S9902480,"NBK, Hospital, Module 3, Equipment",master,,
35
+ S9902480,"NBK, Hospital, Module 3, Equipment",master,,
36
+ S9903003,"AWD, Periphery kit, Renewable",master,,
37
+ S9903003,"AWD, Periphery kit, Renewable",master,,
38
+ S9903003,"AWD, Periphery kit, Renewable",master,,
39
+ S9903003,"AWD, Periphery kit, Renewable",master,,
40
+ S9903003,"AWD, Periphery kit, Renewable",master,,
41
+ S9903003,"AWD, Periphery kit, Renewable",master,,
42
+ S9903003,"AWD, Periphery kit, Renewable",master,,
43
+ S9903003,"AWD, Periphery kit, Renewable",master,,
44
+ S9903003,"AWD, Periphery kit, Renewable",master,,
45
+ S9903003,"AWD, Periphery kit, Renewable",master,,
46
+ S9903003,"AWD, Periphery kit, Renewable",master,,
47
+ S9903003,"AWD, Periphery kit, Renewable",master,,
48
+ S9903003,"AWD, Periphery kit, Renewable",master,,
49
+ S9903003,"AWD, Periphery kit, Renewable",master,,
50
+ S9903003,"AWD, Periphery kit, Renewable",master,,
51
+ S9903003,"AWD, Periphery kit, Renewable",master,,
52
+ S9903003,"AWD, Periphery kit, Renewable",master,,
53
+ S9903003,"AWD, Periphery kit, Renewable",master,,
54
+ S9903003,"AWD, Periphery kit, Renewable",master,,
55
+ S9906706,Ethiopia Midwifery kit 3-renew,master,,
56
+ S9906706,Ethiopia Midwifery kit 3-renew,master,,
57
+ S9906706,Ethiopia Midwifery kit 3-renew,master,,
58
+ S9906706,Ethiopia Midwifery kit 3-renew,master,,
59
+ S9906706,Ethiopia Midwifery kit 3-renew,master,,
60
+ S9906706,Ethiopia Midwifery kit 3-renew,master,,
61
+ S9906706,Ethiopia Midwifery kit 3-renew,master,,
62
+ S9906706,Ethiopia Midwifery kit 3-renew,master,,
63
+ S9906706,Ethiopia Midwifery kit 3-renew,master,,
64
+ S9906706,Ethiopia Midwifery kit 3-renew,master,,
65
+ S9906706,Ethiopia Midwifery kit 3-renew,master,,
66
+ S9906706,Ethiopia Midwifery kit 3-renew,master,,
67
+ S9906706,Ethiopia Midwifery kit 3-renew,master,,
68
+ S9906706,Ethiopia Midwifery kit 3-renew,master,,
69
+ S9906708,"Ethiopia IEHK2011,kit,suppl.3-renew",master,,
70
+ S9906708,"Ethiopia IEHK2011,kit,suppl.3-renew",master,,
71
+ S9906708,"Ethiopia IEHK2011,kit,suppl.3-renew",master,,
72
+ S9906708,"Ethiopia IEHK2011,kit,suppl.3-renew",master,,
73
+ S9906708,"Ethiopia IEHK2011,kit,suppl.3-renew",master,,
74
+ S9906708,"Ethiopia IEHK2011,kit,suppl.3-renew",master,,
75
+ S9906708,"Ethiopia IEHK2011,kit,suppl.3-renew",master,,
76
+ S9906708,"Ethiopia IEHK2011,kit,suppl.3-renew",master,,
77
+ S9906708,"Ethiopia IEHK2011,kit,suppl.3-renew",master,,
78
+ S9906708,"Ethiopia IEHK2011,kit,suppl.3-renew",master,,
79
+ S9906710,Ethiopia Emergency Drug kit 2019,master,,
80
+ S9906710,Ethiopia Emergency Drug kit 2019,master,,
81
+ S9906710,Ethiopia Emergency Drug kit 2019,master,,
82
+ S9906710,Ethiopia Emergency Drug kit 2019,master,,
83
+ S9906710,Ethiopia Emergency Drug kit 2019,master,,
84
+ S9906710,Ethiopia Emergency Drug kit 2019,master,,
85
+ S9906710,Ethiopia Emergency Drug kit 2019,master,,
86
+ S9906710,Ethiopia Emergency Drug kit 2019,master,,
87
+ S9906710,Ethiopia Emergency Drug kit 2019,master,,
88
+ S9906710,Ethiopia Emergency Drug kit 2019,master,,
89
+ S9906710,Ethiopia Emergency Drug kit 2019,master,,
90
+ S9906710,Ethiopia Emergency Drug kit 2019,master,,
91
+ S9906712,"Sudan, Primary Healthcare kit, 2016",master,,
92
+ S9906712,"Sudan, Primary Healthcare kit, 2016",master,,
93
+ S9906712,"Sudan, Primary Healthcare kit, 2016",master,,
94
+ S9906712,"Sudan, Primary Healthcare kit, 2016",master,,
95
+ S9906712,"Sudan, Primary Healthcare kit, 2016",master,,
96
+ S9906712,"Sudan, Primary Healthcare kit, 2016",master,,
97
+ S9906712,"Sudan, Primary Healthcare kit, 2016",master,,
98
+ S9906712,"Sudan, Primary Healthcare kit, 2016",master,,
99
+ S9906712,"Sudan, Primary Healthcare kit, 2016",master,,
100
+ S9906712,"Sudan, Primary Healthcare kit, 2016",master,,
101
+ S9906712,"Sudan, Primary Healthcare kit, 2016",master,,
102
+ S9906712,"Sudan, Primary Healthcare kit, 2016",master,,
103
+ S9906712,"Sudan, Primary Healthcare kit, 2016",master,,
104
+ S9906712,"Sudan, Primary Healthcare kit, 2016",master,,
105
+ S9906712,"Sudan, Primary Healthcare kit, 2016",master,,
106
+ S9906712,"Sudan, Primary Healthcare kit, 2016",master,,
107
+ S9906712,"Sudan, Primary Healthcare kit, 2016",master,,
108
+ S9906712,"Sudan, Primary Healthcare kit, 2016",master,,
109
+ S9906712,"Sudan, Primary Healthcare kit, 2016",master,,
110
+ S9906712,"Sudan, Primary Healthcare kit, 2016",master,,
111
+ S9906712,"Sudan, Primary Healthcare kit, 2016",master,,
112
+ S9906712,"Sudan, Primary Healthcare kit, 2016",master,,
113
+ S9906713,"Sudan, IMCI Medicine kit, 2016",master,,
114
+ S9906713,"Sudan, IMCI Medicine kit, 2016",master,,
115
+ S9906713,"Sudan, IMCI Medicine kit, 2016",master,,
116
+ S9906713,"Sudan, IMCI Medicine kit, 2016",master,,
117
+ S9906713,"Sudan, IMCI Medicine kit, 2016",master,,
118
+ S9906713,"Sudan, IMCI Medicine kit, 2016",master,,
119
+ S9906713,"Sudan, IMCI Medicine kit, 2016",master,,
120
+ S9906713,"Sudan, IMCI Medicine kit, 2016",master,,
121
+ S9906729,DRC Measles Kit 2021,master,,
122
+ S9906729,DRC Measles Kit 2021,master,,
123
+ S9906729,DRC Measles Kit 2021,master,,
124
+ S9906729,DRC Measles Kit 2021,master,,
125
+ S9906729,DRC Measles Kit 2021,master,,
126
+ S9906729,DRC Measles Kit 2021,master,,
127
+ S9906729,DRC Measles Kit 2021,master,,
128
+ S9906729,DRC Measles Kit 2021,master,,
129
+ S9906729,DRC Measles Kit 2021,master,,
130
+ S9906729,DRC Measles Kit 2021,master,,
131
+ S9906729,DRC Measles Kit 2021,master,,
132
+ S9906733,South Sudan PHCC Kit 2018 Basic Medicin,master,,
133
+ S9906733,South Sudan PHCC Kit 2018 Basic Medicin,master,,
134
+ S9906733,South Sudan PHCC Kit 2018 Basic Medicin,master,,
135
+ S9906733,South Sudan PHCC Kit 2018 Basic Medicin,master,,
136
+ S9906733,South Sudan PHCC Kit 2018 Basic Medicin,master,,
137
+ S9906733,South Sudan PHCC Kit 2018 Basic Medicin,master,,
138
+ S9906733,South Sudan PHCC Kit 2018 Basic Medicin,master,,
139
+ S9906733,South Sudan PHCC Kit 2018 Basic Medicin,master,,
140
+ S9906733,South Sudan PHCC Kit 2018 Basic Medicin,master,,
141
+ S9906733,South Sudan PHCC Kit 2018 Basic Medicin,master,,
142
+ S9906733,South Sudan PHCC Kit 2018 Basic Medicin,master,,
143
+ S9906733,South Sudan PHCC Kit 2018 Basic Medicin,master,,
144
+ S9906733,South Sudan PHCC Kit 2018 Basic Medicin,master,,
145
+ S9906733,South Sudan PHCC Kit 2018 Basic Medicin,master,,
146
+ S9906733,South Sudan PHCC Kit 2018 Basic Medicin,master,,
147
+ S9906733,South Sudan PHCC Kit 2018 Basic Medicin,master,,
148
+ S9906733,South Sudan PHCC Kit 2018 Basic Medicin,master,,
149
+ S9906733,South Sudan PHCC Kit 2018 Basic Medicin,master,,
150
+ S9906733,South Sudan PHCC Kit 2018 Basic Medicin,master,,
151
+ S9906733,South Sudan PHCC Kit 2018 Basic Medicin,master,,
152
+ S9906734,"South Sudan, PHCU Kit 2018",master,,
153
+ S9906734,"South Sudan, PHCU Kit 2018",master,,
154
+ S9906734,"South Sudan, PHCU Kit 2018",master,,
155
+ S9906734,"South Sudan, PHCU Kit 2018",master,,
156
+ S9906734,"South Sudan, PHCU Kit 2018",master,,
157
+ S9906734,"South Sudan, PHCU Kit 2018",master,,
158
+ S9906734,"South Sudan, PHCU Kit 2018",master,,
159
+ S9906734,"South Sudan, PHCU Kit 2018",master,,
160
+ S9906734,"South Sudan, PHCU Kit 2018",master,,
161
+ S9906734,"South Sudan, PHCU Kit 2018",master,,
162
+ S9906734,"South Sudan, PHCU Kit 2018",master,,
163
+ S9906734,"South Sudan, PHCU Kit 2018",master,,
164
+ S9906734,"South Sudan, PHCU Kit 2018",master,,
165
+ S9906734,"South Sudan, PHCU Kit 2018",master,,
166
+ S9906734,"South Sudan, PHCU Kit 2018",master,,
167
+ S9906734,"South Sudan, PHCU Kit 2018",master,,
168
+ S9906734,"South Sudan, PHCU Kit 2018",master,,
169
+ S9906737,South Sudan PHCC Kit 2018-Med Dev & Diag,master,,
170
+ S9906737,South Sudan PHCC Kit 2018-Med Dev & Diag,master,,
171
+ S9906753,"Nut kit,Inpatient, Ethiop.Mod. Med. Supp",master,,
172
+ S9906753,"Nut kit,Inpatient, Ethiop.Mod. Med. Supp",master,,
173
+ S9906753,"Nut kit,Inpatient, Ethiop.Mod. Med. Supp",master,,
174
+ S9906791,"Mali, in-patient CMAM kit, version 2014",master,,
175
+ S9906791,"Mali, in-patient CMAM kit, version 2014",master,,
176
+ S9906791,"Mali, in-patient CMAM kit, version 2014",master,,
177
+ S9906791,"Mali, in-patient CMAM kit, version 2014",master,,
178
+ S9906791,"Mali, in-patient CMAM kit, version 2014",master,,
179
+ S9906791,"Mali, in-patient CMAM kit, version 2014",master,,
180
+ S9906791,"Mali, in-patient CMAM kit, version 2014",master,,
181
+ S9906791,"Mali, in-patient CMAM kit, version 2014",master,,
182
+ S9906791,"Mali, in-patient CMAM kit, version 2014",master,,
183
+ S9906791,"Mali, in-patient CMAM kit, version 2014",master,,
184
+ S9906791,"Mali, in-patient CMAM kit, version 2014",master,,
185
+ S9906791,"Mali, in-patient CMAM kit, version 2014",master,,
186
+ S9906791,"Mali, in-patient CMAM kit, version 2014",master,,
187
+ S9906791,"Mali, in-patient CMAM kit, version 2014",master,,
188
+ S9906791,"Mali, in-patient CMAM kit, version 2014",master,,
189
+ S9906791,"Mali, in-patient CMAM kit, version 2014",master,,
190
+ S9906791,"Mali, in-patient CMAM kit, version 2014",master,,
191
+ S9906791,"Mali, in-patient CMAM kit, version 2014",master,,
192
+ S9906791,"Mali, in-patient CMAM kit, version 2014",master,,
193
+ S9906791,"Mali, in-patient CMAM kit, version 2014",master,,
194
+ S9906791,"Mali, in-patient CMAM kit, version 2014",master,,
195
+ S9906791,"Mali, in-patient CMAM kit, version 2014",master,,
196
+ S9906791,"Mali, in-patient CMAM kit, version 2014",master,,
197
+ S9906791,"Mali, in-patient CMAM kit, version 2014",master,,
198
+ S9906800,Yemen Primary Health Kit 2021-Basic meds,master,,
199
+ S9906800,Yemen Primary Health Kit 2021-Basic meds,master,,
200
+ S9906800,Yemen Primary Health Kit 2021-Basic meds,master,,
201
+ S9906800,Yemen Primary Health Kit 2021-Basic meds,master,,
202
+ S9906800,Yemen Primary Health Kit 2021-Basic meds,master,,
203
+ S9906800,Yemen Primary Health Kit 2021-Basic meds,master,,
204
+ S9906800,Yemen Primary Health Kit 2021-Basic meds,master,,
205
+ S9906800,Yemen Primary Health Kit 2021-Basic meds,master,,
206
+ S9906800,Yemen Primary Health Kit 2021-Basic meds,master,,
207
+ S9906800,Yemen Primary Health Kit 2021-Basic meds,master,,
208
+ S9906800,Yemen Primary Health Kit 2021-Basic meds,master,,
209
+ S9906800,Yemen Primary Health Kit 2021-Basic meds,master,,
210
+ S9906800,Yemen Primary Health Kit 2021-Basic meds,master,,
211
+ S9906800,Yemen Primary Health Kit 2021-Basic meds,master,,
212
+ S9908300,"Obstetric,surgical kit,suppl.1-drugs",master,,
213
+ S9908300,"Obstetric,surgical kit,suppl.1-drugs",master,,
214
+ S9908300,"Obstetric,surgical kit,suppl.1-drugs",master,,
215
+ S9908300,"Obstetric,surgical kit,suppl.1-drugs",master,,
216
+ S9908300,"Obstetric,surgical kit,suppl.1-drugs",master,,
217
+ S9908300,"Obstetric,surgical kit,suppl.1-drugs",master,,
218
+ S9908300,"Obstetric,surgical kit,suppl.1-drugs",master,,
219
+ S9908302,"Obstetric,surgical kit,suppl.3-renewable",master,,
220
+ S9908302,"Obstetric,surgical kit,suppl.3-renewable",master,,
221
+ S9908302,"Obstetric,surgical kit,suppl.3-renewable",master,,
222
+ S9908302,"Obstetric,surgical kit,suppl.3-renewable",master,,
223
+ S9908302,"Obstetric,surgical kit,suppl.3-renewable",master,,
224
+ S9908302,"Obstetric,surgical kit,suppl.3-renewable",master,,
225
+ S9908302,"Obstetric,surgical kit,suppl.3-renewable",master,,
226
+ S9908302,"Obstetric,surgical kit,suppl.3-renewable",master,,
227
+ S9991041,"IEHK 2024,Basic Medicine&Renewabl SUB1/3",subkit,long line,6.0
228
+ S9991041,"IEHK 2024,Basic Medicine&Renewabl SUB1/3",subkit,long line,6.0
229
+ S9991041,"IEHK 2024,Basic Medicine&Renewabl SUB1/3",subkit,long line,6.0
230
+ S9991042,"IEHK 2024,Basic Medicine&Renewabl SUB2/3",subkit,long line,6.0
231
+ S9991042,"IEHK 2024,Basic Medicine&Renewabl SUB2/3",subkit,long line,6.0
232
+ S9991042,"IEHK 2024,Basic Medicine&Renewabl SUB2/3",subkit,long line,6.0
233
+ S9999898,"Subkit 1/2 Midwifery ,3-renew S9902219",subkit,long line,6.0
234
+ S9999898,"Subkit 1/2 Midwifery ,3-renew S9902219",subkit,long line,6.0
235
+ S9999898,"Subkit 1/2 Midwifery ,3-renew S9902219",subkit,long line,6.0
236
+ S9999898,"Subkit 1/2 Midwifery ,3-renew S9902219",subkit,long line,6.0
237
+ S9999898,"Subkit 1/2 Midwifery ,3-renew S9902219",subkit,long line,6.0
238
+ S9999898,"Subkit 1/2 Midwifery ,3-renew S9902219",subkit,long line,6.0
239
+ S9999898,"Subkit 1/2 Midwifery ,3-renew S9902219",subkit,long line,6.0
240
+ S9999898,"Subkit 1/2 Midwifery ,3-renew S9902219",subkit,long line,6.0
241
+ S9999898,"Subkit 1/2 Midwifery ,3-renew S9902219",subkit,long line,6.0
242
+ S9999898,"Subkit 1/2 Midwifery ,3-renew S9902219",subkit,long line,6.0
243
+ S9999898,"Subkit 1/2 Midwifery ,3-renew S9902219",subkit,long line,6.0
244
+ S9999898,"Subkit 1/2 Midwifery ,3-renew S9902219",subkit,long line,6.0
245
+ S9999898,"Subkit 1/2 Midwifery ,3-renew S9902219",subkit,long line,6.0
246
+ S9992451,"SUB 1/6 NBK, Clinic, Module 3, Equipment",subkit,long line,6.0
247
+ S9992470,"SUB 1/10 NBK, Hospital,Module 2,Consum",subkit,long line,6.0
248
+ S9992470,"SUB 1/10 NBK, Hospital,Module 2,Consum",subkit,long line,6.0
249
+ S9992470,"SUB 1/10 NBK, Hospital,Module 2,Consum",subkit,long line,6.0
250
+ S9992470,"SUB 1/10 NBK, Hospital,Module 2,Consum",subkit,long line,6.0
251
+ S9992470,"SUB 1/10 NBK, Hospital,Module 2,Consum",subkit,long line,6.0
252
+ S9992470,"SUB 1/10 NBK, Hospital,Module 2,Consum",subkit,long line,6.0
253
+ S9992470,"SUB 1/10 NBK, Hospital,Module 2,Consum",subkit,long line,6.0
254
+ S9992470,"SUB 1/10 NBK, Hospital,Module 2,Consum",subkit,long line,6.0
255
+ S9992483,"SUB 3/17 NBK, Hospital, Module 3, Equ",subkit,long line,6.0
256
+ S9992483,"SUB 3/17 NBK, Hospital, Module 3, Equ",subkit,long line,6.0
257
+ S9992483,"SUB 3/17 NBK, Hospital, Module 3, Equ",subkit,long line,6.0
258
+ S9992483,"SUB 3/17 NBK, Hospital, Module 3, Equ",subkit,long line,6.0
259
+ S9992483,"SUB 3/17 NBK, Hospital, Module 3, Equ",subkit,long line,6.0
260
+ S9992483,"SUB 3/17 NBK, Hospital, Module 3, Equ",subkit,long line,6.0
261
+ S9999144,"AWD SUB 1/3 Periphery,Renewable",subkit,long line,6.0
262
+ S9999145,"AWD SUB 2/3 Periphery,Renewable",subkit,long line,6.0
263
+ S9999145,"AWD SUB 2/3 Periphery,Renewable",subkit,long line,6.0
264
+ S9999145,"AWD SUB 2/3 Periphery,Renewable",subkit,long line,6.0
265
+ S9999145,"AWD SUB 2/3 Periphery,Renewable",subkit,long line,6.0
266
+ S9999145,"AWD SUB 2/3 Periphery,Renewable",subkit,long line,6.0
267
+ S9999145,"AWD SUB 2/3 Periphery,Renewable",subkit,long line,6.0
268
+ S9999145,"AWD SUB 2/3 Periphery,Renewable",subkit,long line,6.0
269
+ S9999145,"AWD SUB 2/3 Periphery,Renewable",subkit,long line,6.0
270
+ S9999145,"AWD SUB 2/3 Periphery,Renewable",subkit,long line,6.0
271
+ S9999145,"AWD SUB 2/3 Periphery,Renewable",subkit,long line,6.0
272
+ S9999145,"AWD SUB 2/3 Periphery,Renewable",subkit,long line,6.0
273
+ S9999145,"AWD SUB 2/3 Periphery,Renewable",subkit,long line,6.0
274
+ S9999145,"AWD SUB 2/3 Periphery,Renewable",subkit,long line,6.0
275
+ S9999145,"AWD SUB 2/3 Periphery,Renewable",subkit,long line,6.0
276
+ S9999145,"AWD SUB 2/3 Periphery,Renewable",subkit,long line,6.0
277
+ S9999145,"AWD SUB 2/3 Periphery,Renewable",subkit,long line,6.0
278
+ S9999145,"AWD SUB 2/3 Periphery,Renewable",subkit,long line,6.0
279
+ S9999145,"AWD SUB 2/3 Periphery,Renewable",subkit,long line,6.0
280
+ S9999701,"Subkit 1/2 Ethiopia Midwifery,S9906706",subkit,long line,6.0
281
+ S9999701,"Subkit 1/2 Ethiopia Midwifery,S9906706",subkit,long line,6.0
282
+ S9999701,"Subkit 1/2 Ethiopia Midwifery,S9906706",subkit,long line,6.0
283
+ S9999701,"Subkit 1/2 Ethiopia Midwifery,S9906706",subkit,long line,6.0
284
+ S9999701,"Subkit 1/2 Ethiopia Midwifery,S9906706",subkit,long line,6.0
285
+ S9999701,"Subkit 1/2 Ethiopia Midwifery,S9906706",subkit,long line,6.0
286
+ S9999701,"Subkit 1/2 Ethiopia Midwifery,S9906706",subkit,long line,6.0
287
+ S9999701,"Subkit 1/2 Ethiopia Midwifery,S9906706",subkit,long line,6.0
288
+ S9999701,"Subkit 1/2 Ethiopia Midwifery,S9906706",subkit,long line,6.0
289
+ S9999701,"Subkit 1/2 Ethiopia Midwifery,S9906706",subkit,long line,6.0
290
+ S9999701,"Subkit 1/2 Ethiopia Midwifery,S9906706",subkit,long line,6.0
291
+ S9999701,"Subkit 1/2 Ethiopia Midwifery,S9906706",subkit,long line,6.0
292
+ S9999701,"Subkit 1/2 Ethiopia Midwifery,S9906706",subkit,long line,6.0
293
+ S9999702,"Subkit 2/2 Ethiopia Midwifery,S9906706",subkit,long line,6.0
294
+ S9999721,Subkit 1/6 Ethiopia IEHK renew S9906708,subkit,long line,6.0
295
+ S9999721,Subkit 1/6 Ethiopia IEHK renew S9906708,subkit,long line,6.0
296
+ S9999721,Subkit 1/6 Ethiopia IEHK renew S9906708,subkit,long line,6.0
297
+ S9999721,Subkit 1/6 Ethiopia IEHK renew S9906708,subkit,long line,6.0
298
+ S9999721,Subkit 1/6 Ethiopia IEHK renew S9906708,subkit,long line,6.0
299
+ S9999726,Subkit 6/6 Ethiopia IEHK renew S9906708,subkit,long line,6.0
300
+ S9999726,Subkit 6/6 Ethiopia IEHK renew S9906708,subkit,long line,6.0
301
+ S9999726,Subkit 6/6 Ethiopia IEHK renew S9906708,subkit,long line,6.0
302
+ S9999726,Subkit 6/6 Ethiopia IEHK renew S9906708,subkit,long line,6.0
303
+ S9999726,Subkit 6/6 Ethiopia IEHK renew S9906708,subkit,long line,6.0
304
+ S9996710,SUB 1/7 Ethiopia Emerg. kit2019 S9906710,subkit,long line,6.0
305
+ S9996710,SUB 1/7 Ethiopia Emerg. kit2019 S9906710,subkit,long line,6.0
306
+ S9996710,SUB 1/7 Ethiopia Emerg. kit2019 S9906710,subkit,long line,6.0
307
+ S9996710,SUB 1/7 Ethiopia Emerg. kit2019 S9906710,subkit,long line,6.0
308
+ S9996711,SUB 2/7 Ethiopia Emerg. kit2019 S9906710,subkit,long line,6.0
309
+ S9996711,SUB 2/7 Ethiopia Emerg. kit2019 S9906710,subkit,long line,6.0
310
+ S9996711,SUB 2/7 Ethiopia Emerg. kit2019 S9906710,subkit,long line,6.0
311
+ S9996711,SUB 2/7 Ethiopia Emerg. kit2019 S9906710,subkit,long line,6.0
312
+ S9996711,SUB 2/7 Ethiopia Emerg. kit2019 S9906710,subkit,long line,6.0
313
+ S9996711,SUB 2/7 Ethiopia Emerg. kit2019 S9906710,subkit,long line,6.0
314
+ S9996711,SUB 2/7 Ethiopia Emerg. kit2019 S9906710,subkit,long line,6.0
315
+ S9996711,SUB 2/7 Ethiopia Emerg. kit2019 S9906710,subkit,long line,6.0
316
+ S9999491,"Sub 1/4, SD SD,Primary Healthcare2016",subkit,long line,6.0
317
+ S9999491,"Sub 1/4, SD SD,Primary Healthcare2016",subkit,long line,6.0
318
+ S9999491,"Sub 1/4, SD SD,Primary Healthcare2016",subkit,long line,6.0
319
+ S9999491,"Sub 1/4, SD SD,Primary Healthcare2016",subkit,long line,6.0
320
+ S9999491,"Sub 1/4, SD SD,Primary Healthcare2016",subkit,long line,6.0
321
+ S9999491,"Sub 1/4, SD SD,Primary Healthcare2016",subkit,long line,6.0
322
+ S9999491,"Sub 1/4, SD SD,Primary Healthcare2016",subkit,long line,6.0
323
+ S9999491,"Sub 1/4, SD SD,Primary Healthcare2016",subkit,long line,6.0
324
+ S9999491,"Sub 1/4, SD SD,Primary Healthcare2016",subkit,long line,6.0
325
+ S9999491,"Sub 1/4, SD SD,Primary Healthcare2016",subkit,long line,6.0
326
+ S9999491,"Sub 1/4, SD SD,Primary Healthcare2016",subkit,long line,6.0
327
+ S9999491,"Sub 1/4, SD SD,Primary Healthcare2016",subkit,long line,6.0
328
+ S9999491,"Sub 1/4, SD SD,Primary Healthcare2016",subkit,long line,6.0
329
+ S9999491,"Sub 1/4, SD SD,Primary Healthcare2016",subkit,long line,6.0
330
+ S9999491,"Sub 1/4, SD SD,Primary Healthcare2016",subkit,long line,6.0
331
+ S9999491,"Sub 1/4, SD SD,Primary Healthcare2016",subkit,long line,6.0
332
+ S9999491,"Sub 1/4, SD SD,Primary Healthcare2016",subkit,long line,6.0
333
+ S9999491,"Sub 1/4, SD SD,Primary Healthcare2016",subkit,long line,6.0
334
+ S9999491,"Sub 1/4, SD SD,Primary Healthcare2016",subkit,long line,6.0
335
+ S9999491,"Sub 1/4, SD SD,Primary Healthcare2016",subkit,long line,6.0
336
+ S9999491,"Sub 1/4, SD SD,Primary Healthcare2016",subkit,long line,6.0
337
+ S9999491,"Sub 1/4, SD SD,Primary Healthcare2016",subkit,long line,6.0
338
+ S9999506,"Sub 2/7 SD IMCI Med. kit, 2016",subkit,long line,6.0
339
+ S9999506,"Sub 2/7 SD IMCI Med. kit, 2016",subkit,long line,6.0
340
+ S9999507,"Sub 3/7 SD IMCI Med. kit, 2016",subkit,long line,6.0
341
+ S9999507,"Sub 3/7 SD IMCI Med. kit, 2016",subkit,long line,6.0
342
+ S9999507,"Sub 3/7 SD IMCI Med. kit, 2016",subkit,long line,6.0
343
+ S9999507,"Sub 3/7 SD IMCI Med. kit, 2016",subkit,long line,6.0
344
+ S9999507,"Sub 3/7 SD IMCI Med. kit, 2016",subkit,long line,6.0
345
+ S9999507,"Sub 3/7 SD IMCI Med. kit, 2016",subkit,long line,6.0
346
+ S9996722,DRC Measles Kit 2021 SUB 2/5,subkit,long line,6.0
347
+ S9996722,DRC Measles Kit 2021 SUB 2/5,subkit,long line,6.0
348
+ S9996722,DRC Measles Kit 2021 SUB 2/5,subkit,long line,6.0
349
+ S9996722,DRC Measles Kit 2021 SUB 2/5,subkit,long line,6.0
350
+ S9996722,DRC Measles Kit 2021 SUB 2/5,subkit,long line,6.0
351
+ S9996722,DRC Measles Kit 2021 SUB 2/5,subkit,long line,6.0
352
+ S9996722,DRC Measles Kit 2021 SUB 2/5,subkit,long line,6.0
353
+ S9996722,DRC Measles Kit 2021 SUB 2/5,subkit,long line,6.0
354
+ S9996722,DRC Measles Kit 2021 SUB 2/5,subkit,long line,6.0
355
+ S9996722,DRC Measles Kit 2021 SUB 2/5,subkit,long line,6.0
356
+ S9996722,DRC Measles Kit 2021 SUB 2/5,subkit,long line,6.0
357
+ S9993731,SUB 1/9 S.Sudan PHCC18 B.Med S9906733,subkit,long line,6.0
358
+ S9993731,SUB 1/9 S.Sudan PHCC18 B.Med S9906733,subkit,long line,6.0
359
+ S9993731,SUB 1/9 S.Sudan PHCC18 B.Med S9906733,subkit,long line,6.0
360
+ S9993731,SUB 1/9 S.Sudan PHCC18 B.Med S9906733,subkit,long line,6.0
361
+ S9993731,SUB 1/9 S.Sudan PHCC18 B.Med S9906733,subkit,long line,6.0
362
+ S9993731,SUB 1/9 S.Sudan PHCC18 B.Med S9906733,subkit,long line,6.0
363
+ S9993731,SUB 1/9 S.Sudan PHCC18 B.Med S9906733,subkit,long line,6.0
364
+ S9993731,SUB 1/9 S.Sudan PHCC18 B.Med S9906733,subkit,long line,6.0
365
+ S9993731,SUB 1/9 S.Sudan PHCC18 B.Med S9906733,subkit,long line,6.0
366
+ S9993731,SUB 1/9 S.Sudan PHCC18 B.Med S9906733,subkit,long line,6.0
367
+ S9993732,SUB 2/9 S.Sudan PHCC18 B.Med S9906733,subkit,long line,6.0
368
+ S9993733,SUB 3/9 S.Sudan PHCC18 B.Med S9906733,subkit,long line,6.0
369
+ S9993733,SUB 3/9 S.Sudan PHCC18 B.Med S9906733,subkit,long line,6.0
370
+ S9993733,SUB 3/9 S.Sudan PHCC18 B.Med S9906733,subkit,long line,6.0
371
+ S9993733,SUB 3/9 S.Sudan PHCC18 B.Med S9906733,subkit,long line,6.0
372
+ S9993733,SUB 3/9 S.Sudan PHCC18 B.Med S9906733,subkit,long line,6.0
373
+ S9993733,SUB 3/9 S.Sudan PHCC18 B.Med S9906733,subkit,long line,6.0
374
+ S9993733,SUB 3/9 S.Sudan PHCC18 B.Med S9906733,subkit,long line,6.0
375
+ S9993733,SUB 3/9 S.Sudan PHCC18 B.Med S9906733,subkit,long line,6.0
376
+ S9993734,SUB 4/9 S.Sudan PHCC18 B.Med S9906733,subkit,long line,6.0
377
+ S9994732,"Sub2/8 South Sudan,PHCU Kit2018 S9906734",subkit,long line,6.0
378
+ S9994732,"Sub2/8 South Sudan,PHCU Kit2018 S9906734",subkit,long line,6.0
379
+ S9994732,"Sub2/8 South Sudan,PHCU Kit2018 S9906734",subkit,long line,6.0
380
+ S9994732,"Sub2/8 South Sudan,PHCU Kit2018 S9906734",subkit,long line,6.0
381
+ S9994732,"Sub2/8 South Sudan,PHCU Kit2018 S9906734",subkit,long line,6.0
382
+ S9994732,"Sub2/8 South Sudan,PHCU Kit2018 S9906734",subkit,long line,6.0
383
+ S9994732,"Sub2/8 South Sudan,PHCU Kit2018 S9906734",subkit,long line,6.0
384
+ S9994732,"Sub2/8 South Sudan,PHCU Kit2018 S9906734",subkit,long line,6.0
385
+ S9994732,"Sub2/8 South Sudan,PHCU Kit2018 S9906734",subkit,long line,6.0
386
+ S9994732,"Sub2/8 South Sudan,PHCU Kit2018 S9906734",subkit,long line,6.0
387
+ S9994732,"Sub2/8 South Sudan,PHCU Kit2018 S9906734",subkit,long line,6.0
388
+ S9994733,"Sub3/8 South Sudan,PHCU Kit2018 S9906734",subkit,long line,6.0
389
+ S9994733,"Sub3/8 South Sudan,PHCU Kit2018 S9906734",subkit,long line,6.0
390
+ S9994733,"Sub3/8 South Sudan,PHCU Kit2018 S9906734",subkit,long line,6.0
391
+ S9994738,"PP4/4 f Sub4/4 S.Sudan,PHCU Kit S9906734",subkit,long line,6.0
392
+ S9994738,"PP4/4 f Sub4/4 S.Sudan,PHCU Kit S9906734",subkit,long line,6.0
393
+ S9994738,"PP4/4 f Sub4/4 S.Sudan,PHCU Kit S9906734",subkit,long line,6.0
394
+ S9997731,SUB 1/8 SSPHCC2018-Med Dev&Diag S9906737,subkit,long line,6.0
395
+ S9997731,SUB 1/8 SSPHCC2018-Med Dev&Diag S9906737,subkit,long line,6.0
396
+ S9996752,"SUB1/2 Nut kit,I Ethiop.Mod F S9906753",subkit,long line,6.0
397
+ S9996752,"SUB1/2 Nut kit,I Ethiop.Mod F S9906753",subkit,long line,6.0
398
+ S9996752,"SUB1/2 Nut kit,I Ethiop.Mod F S9906753",subkit,long line,6.0
399
+ S9999751,"Subkit 1/6 Mali, in-patient CMAM kit",subkit,long line,6.0
400
+ S9999751,"Subkit 1/6 Mali, in-patient CMAM kit",subkit,long line,6.0
401
+ S9999751,"Subkit 1/6 Mali, in-patient CMAM kit",subkit,long line,6.0
402
+ S9999751,"Subkit 1/6 Mali, in-patient CMAM kit",subkit,long line,6.0
403
+ S9999751,"Subkit 1/6 Mali, in-patient CMAM kit",subkit,long line,6.0
404
+ S9999751,"Subkit 1/6 Mali, in-patient CMAM kit",subkit,long line,6.0
405
+ S9999751,"Subkit 1/6 Mali, in-patient CMAM kit",subkit,long line,6.0
406
+ S9999751,"Subkit 1/6 Mali, in-patient CMAM kit",subkit,long line,6.0
407
+ S9999751,"Subkit 1/6 Mali, in-patient CMAM kit",subkit,long line,6.0
408
+ S9999751,"Subkit 1/6 Mali, in-patient CMAM kit",subkit,long line,6.0
409
+ S9999751,"Subkit 1/6 Mali, in-patient CMAM kit",subkit,long line,6.0
410
+ S9999751,"Subkit 1/6 Mali, in-patient CMAM kit",subkit,long line,6.0
411
+ S9999751,"Subkit 1/6 Mali, in-patient CMAM kit",subkit,long line,6.0
412
+ S9999751,"Subkit 1/6 Mali, in-patient CMAM kit",subkit,long line,6.0
413
+ S9999751,"Subkit 1/6 Mali, in-patient CMAM kit",subkit,long line,6.0
414
+ S9999751,"Subkit 1/6 Mali, in-patient CMAM kit",subkit,long line,6.0
415
+ S9999751,"Subkit 1/6 Mali, in-patient CMAM kit",subkit,long line,6.0
416
+ S9999751,"Subkit 1/6 Mali, in-patient CMAM kit",subkit,long line,6.0
417
+ S9999751,"Subkit 1/6 Mali, in-patient CMAM kit",subkit,long line,6.0
418
+ S9999751,"Subkit 1/6 Mali, in-patient CMAM kit",subkit,long line,6.0
419
+ S9999752,"Subkit 2/6 Mali, in-patient CMAM kit",subkit,long line,6.0
420
+ S9999753,"Subkit 3/6 Mali, in-patient CMAM kit",subkit,long line,6.0
421
+ S9999753,"Subkit 3/6 Mali, in-patient CMAM kit",subkit,long line,6.0
422
+ S9999753,"Subkit 3/6 Mali, in-patient CMAM kit",subkit,long line,6.0
423
+ S9986721,SUB 1/3 Yemen PHK 2021-Basic S9906800,subkit,long line,6.0
424
+ S9986721,SUB 1/3 Yemen PHK 2021-Basic S9906800,subkit,long line,6.0
425
+ S9986721,SUB 1/3 Yemen PHK 2021-Basic S9906800,subkit,long line,6.0
426
+ S9986722,SUB 2/3 Yemen PHK 2021-Basic S9906800,subkit,long line,6.0
427
+ S9986722,SUB 2/3 Yemen PHK 2021-Basic S9906800,subkit,long line,6.0
428
+ S9986722,SUB 2/3 Yemen PHK 2021-Basic S9906800,subkit,long line,6.0
429
+ S9986722,SUB 2/3 Yemen PHK 2021-Basic S9906800,subkit,long line,6.0
430
+ S9986722,SUB 2/3 Yemen PHK 2021-Basic S9906800,subkit,long line,6.0
431
+ S9986722,SUB 2/3 Yemen PHK 2021-Basic S9906800,subkit,long line,6.0
432
+ S9986722,SUB 2/3 Yemen PHK 2021-Basic S9906800,subkit,long line,6.0
433
+ S9986722,SUB 2/3 Yemen PHK 2021-Basic S9906800,subkit,long line,6.0
434
+ S9986722,SUB 2/3 Yemen PHK 2021-Basic S9906800,subkit,long line,6.0
435
+ S9986722,SUB 2/3 Yemen PHK 2021-Basic S9906800,subkit,long line,6.0
436
+ S9986722,SUB 2/3 Yemen PHK 2021-Basic S9906800,subkit,long line,6.0
437
+ S9999995,Sub 1/10 Obstetric S9908300(i) part 1,subkit,long line,6.0
438
+ S9999995,Sub 1/10 Obstetric S9908300(i) part 1,subkit,long line,6.0
439
+ S9999995,Sub 1/10 Obstetric S9908300(i) part 1,subkit,long line,6.0
440
+ S9999995,Sub 1/10 Obstetric S9908300(i) part 1,subkit,long line,6.0
441
+ S9999995,Sub 1/10 Obstetric S9908300(i) part 1,subkit,long line,6.0
442
+ S9999995,Sub 1/10 Obstetric S9908300(i) part 1,subkit,long line,6.0
443
+ S9999995,Sub 1/10 Obstetric S9908300(i) part 1,subkit,long line,6.0
444
+ S9999883,Subkit 3/6 Obstetric 3-renew.S9908302,subkit,long line,6.0
445
+ S9999883,Subkit 3/6 Obstetric 3-renew.S9908302,subkit,long line,6.0
446
+ S9999884,Subkit 4/6 Obstetric 3-renew.S9908302,subkit,long line,6.0
447
+ S9999884,Subkit 4/6 Obstetric 3-renew.S9908302,subkit,long line,6.0
448
+ S9999884,Subkit 4/6 Obstetric 3-renew.S9908302,subkit,long line,6.0
449
+ S9999884,Subkit 4/6 Obstetric 3-renew.S9908302,subkit,long line,6.0
450
+ S9999884,Subkit 4/6 Obstetric 3-renew.S9908302,subkit,long line,6.0
451
+ S9999884,Subkit 4/6 Obstetric 3-renew.S9908302,subkit,long line,6.0
452
+ S9991044,"PP 1/2 IEHK 2024,Basic Medicine&Renewabl",prepack,mini load,7.0
453
+ S9991044,"PP 1/2 IEHK 2024,Basic Medicine&Renewabl",prepack,mini load,7.0
454
+ S9991044,"PP 1/2 IEHK 2024,Basic Medicine&Renewabl",prepack,mini load,7.0
455
+ S9991045,"PP 2/2 IEHK 2024,Basic Medicine&Renewabl",prepack,mini load,7.0
456
+ S9991045,"PP 2/2 IEHK 2024,Basic Medicine&Renewabl",prepack,mini load,7.0
457
+ S9991045,"PP 2/2 IEHK 2024,Basic Medicine&Renewabl",prepack,mini load,7.0
458
+ S9999941,Prepack 1/3Midwifery-renewable S9902219,prepack,mini load,7.0
459
+ S9999941,Prepack 1/3Midwifery-renewable S9902219,prepack,mini load,7.0
460
+ S9999941,Prepack 1/3Midwifery-renewable S9902219,prepack,mini load,7.0
461
+ S9999941,Prepack 1/3Midwifery-renewable S9902219,prepack,mini load,7.0
462
+ S9999941,Prepack 1/3Midwifery-renewable S9902219,prepack,mini load,7.0
463
+ S9999941,Prepack 1/3Midwifery-renewable S9902219,prepack,mini load,7.0
464
+ S9999942,Prepack 2/3Midwifery-renewable S9902219,prepack,mini load,7.0
465
+ S9999942,Prepack 2/3Midwifery-renewable S9902219,prepack,mini load,7.0
466
+ S9999942,Prepack 2/3Midwifery-renewable S9902219,prepack,mini load,7.0
467
+ S9999942,Prepack 2/3Midwifery-renewable S9902219,prepack,mini load,7.0
468
+ S9999942,Prepack 2/3Midwifery-renewable S9902219,prepack,mini load,7.0
469
+ S9999940,Prepack 3/3Midwifery-renewable S9902219,prepack,mini load,7.0
470
+ S9999940,Prepack 3/3Midwifery-renewable S9902219,prepack,mini load,7.0
471
+ S9992450,"PP 1/1 NBK, Clinic, Module 3, Equipment",prepack,mini load,7.0
472
+ S9992414,"PP 1/2 NBK, Hospital,Module 2,Consum",prepack,mini load,7.0
473
+ S9992414,"PP 1/2 NBK, Hospital,Module 2,Consum",prepack,mini load,7.0
474
+ S9992414,"PP 1/2 NBK, Hospital,Module 2,Consum",prepack,mini load,7.0
475
+ S9992414,"PP 1/2 NBK, Hospital,Module 2,Consum",prepack,mini load,7.0
476
+ S9992415,"PP 2/2 NBK, Hospital,Module 2,Consum",prepack,mini load,7.0
477
+ S9992415,"PP 2/2 NBK, Hospital,Module 2,Consum",prepack,mini load,7.0
478
+ S9992415,"PP 2/2 NBK, Hospital,Module 2,Consum",prepack,mini load,7.0
479
+ S9992415,"PP 2/2 NBK, Hospital,Module 2,Consum",prepack,mini load,7.0
480
+ S9992480,"Bag NBK, Hospital, Module 3, Equ",prepack,mini load,7.0
481
+ S9992480,"Bag NBK, Hospital, Module 3, Equ",prepack,mini load,7.0
482
+ S9992480,"Bag NBK, Hospital, Module 3, Equ",prepack,mini load,7.0
483
+ S9992480,"Bag NBK, Hospital, Module 3, Equ",prepack,mini load,7.0
484
+ S9992480,"Bag NBK, Hospital, Module 3, Equ",prepack,mini load,7.0
485
+ S9992480,"Bag NBK, Hospital, Module 3, Equ",prepack,mini load,7.0
486
+ S9999149,"AWD BAG 1/1 Periphery,Renewable S9903003",prepack,mini load,7.0
487
+ S9999143,"PP 3/3 Periphery,Renewable",prepack,mini load,7.0
488
+ S9999143,"PP 3/3 Periphery,Renewable",prepack,mini load,7.0
489
+ S9999143,"PP 3/3 Periphery,Renewable",prepack,mini load,7.0
490
+ S9999143,"PP 3/3 Periphery,Renewable",prepack,mini load,7.0
491
+ S9999143,"PP 3/3 Periphery,Renewable",prepack,mini load,7.0
492
+ S9999143,"PP 3/3 Periphery,Renewable",prepack,mini load,7.0
493
+ S9999138,PP 2/3 (old 2/7)Periphery Renew S9903003,prepack,mini load,7.0
494
+ S9999138,PP 2/3 (old 2/7)Periphery Renew S9903003,prepack,mini load,7.0
495
+ S9999138,PP 2/3 (old 2/7)Periphery Renew S9903003,prepack,mini load,7.0
496
+ S9999138,PP 2/3 (old 2/7)Periphery Renew S9903003,prepack,mini load,7.0
497
+ S9999138,PP 2/3 (old 2/7)Periphery Renew S9903003,prepack,mini load,7.0
498
+ S9999137,PP 1/3 (old 1/7)Periphery Renew S9903003,prepack,mini load,7.0
499
+ S9999137,PP 1/3 (old 1/7)Periphery Renew S9903003,prepack,mini load,7.0
500
+ S9999137,PP 1/3 (old 1/7)Periphery Renew S9903003,prepack,mini load,7.0
501
+ S9999137,PP 1/3 (old 1/7)Periphery Renew S9903003,prepack,mini load,7.0
502
+ S9999137,PP 1/3 (old 1/7)Periphery Renew S9903003,prepack,mini load,7.0
503
+ S9999137,PP 1/3 (old 1/7)Periphery Renew S9903003,prepack,mini load,7.0
504
+ S9999137,PP 1/3 (old 1/7)Periphery Renew S9903003,prepack,mini load,7.0
505
+ S9999703,"Prepack 1/3 Ethiopia Midwifery,S9906706",prepack,mini load,7.0
506
+ S9999703,"Prepack 1/3 Ethiopia Midwifery,S9906706",prepack,mini load,7.0
507
+ S9999703,"Prepack 1/3 Ethiopia Midwifery,S9906706",prepack,mini load,7.0
508
+ S9999703,"Prepack 1/3 Ethiopia Midwifery,S9906706",prepack,mini load,7.0
509
+ S9999703,"Prepack 1/3 Ethiopia Midwifery,S9906706",prepack,mini load,7.0
510
+ S9999703,"Prepack 1/3 Ethiopia Midwifery,S9906706",prepack,mini load,7.0
511
+ S9999704,"Prepack 2/3 Ethiopia Midwifery,S9906706",prepack,mini load,7.0
512
+ S9999704,"Prepack 2/3 Ethiopia Midwifery,S9906706",prepack,mini load,7.0
513
+ S9999704,"Prepack 2/3 Ethiopia Midwifery,S9906706",prepack,mini load,7.0
514
+ S9999704,"Prepack 2/3 Ethiopia Midwifery,S9906706",prepack,mini load,7.0
515
+ S9999704,"Prepack 2/3 Ethiopia Midwifery,S9906706",prepack,mini load,7.0
516
+ S9999705,"Prepack 3/3 Ethiopia Midwifery,S9906706",prepack,mini load,7.0
517
+ S9999705,"Prepack 3/3 Ethiopia Midwifery,S9906706",prepack,mini load,7.0
518
+ S9999299,"BAG for 50 x S0322010 CH12,ster,disp",prepack,mini load,7.0
519
+ S9999728,Prepa. 1/2 Ethiopia IEHK renew S9906708,prepack,mini load,7.0
520
+ S9999728,Prepa. 1/2 Ethiopia IEHK renew S9906708,prepack,mini load,7.0
521
+ S9999728,Prepa. 1/2 Ethiopia IEHK renew S9906708,prepack,mini load,7.0
522
+ S9999728,Prepa. 1/2 Ethiopia IEHK renew S9906708,prepack,mini load,7.0
523
+ S9999728,Prepa. 1/2 Ethiopia IEHK renew S9906708,prepack,mini load,7.0
524
+ S9999729,Prepa. 2/2 Ethiopia IEHK renew S9906708,prepack,mini load,7.0
525
+ S9999729,Prepa. 2/2 Ethiopia IEHK renew S9906708,prepack,mini load,7.0
526
+ S9999729,Prepa. 2/2 Ethiopia IEHK renew S9906708,prepack,mini load,7.0
527
+ S9999729,Prepa. 2/2 Ethiopia IEHK renew S9906708,prepack,mini load,7.0
528
+ S9999729,Prepa. 2/2 Ethiopia IEHK renew S9906708,prepack,mini load,7.0
529
+ S9986712,PP 2/5 F SUB 1/6 Ethiopia Emerg.S9906710,prepack,mini load,7.0
530
+ S9986712,PP 2/5 F SUB 1/6 Ethiopia Emerg.S9906710,prepack,mini load,7.0
531
+ S9986712,PP 2/5 F SUB 1/6 Ethiopia Emerg.S9906710,prepack,mini load,7.0
532
+ S9986711,PP 1/5 F SUB 1/6 Ethiopia Emerg.S9906710,prepack,mini load,7.0
533
+ S9986711,PP 1/5 F SUB 1/6 Ethiopia Emerg.S9906710,prepack,mini load,7.0
534
+ S9986713,PP 3/5 F SUB 2/6 Ethiopia Emerg.S9906710,prepack,mini load,7.0
535
+ S9986713,PP 3/5 F SUB 2/6 Ethiopia Emerg.S9906710,prepack,mini load,7.0
536
+ S9986714,PP 4/5 F SUB 2/6 Ethiopia Emerg.S9906710,prepack,mini load,7.0
537
+ S9986714,PP 4/5 F SUB 2/6 Ethiopia Emerg.S9906710,prepack,mini load,7.0
538
+ S9986714,PP 4/5 F SUB 2/6 Ethiopia Emerg.S9906710,prepack,mini load,7.0
539
+ S9986714,PP 4/5 F SUB 2/6 Ethiopia Emerg.S9906710,prepack,mini load,7.0
540
+ S9986715,PP 5/5 F SUB 2/6 Ethiopia Emerg.S9906710,prepack,mini load,7.0
541
+ S9999494,"Prepack 1/3 SD,Primary Healthcare2016",prepack,mini load,7.0
542
+ S9999494,"Prepack 1/3 SD,Primary Healthcare2016",prepack,mini load,7.0
543
+ S9999494,"Prepack 1/3 SD,Primary Healthcare2016",prepack,mini load,7.0
544
+ S9999494,"Prepack 1/3 SD,Primary Healthcare2016",prepack,mini load,7.0
545
+ S9999494,"Prepack 1/3 SD,Primary Healthcare2016",prepack,mini load,7.0
546
+ S9999494,"Prepack 1/3 SD,Primary Healthcare2016",prepack,mini load,7.0
547
+ S9999494,"Prepack 1/3 SD,Primary Healthcare2016",prepack,mini load,7.0
548
+ S9999494,"Prepack 1/3 SD,Primary Healthcare2016",prepack,mini load,7.0
549
+ S9999495,"Prepack 2/3 SD,Primary Healthcare2016",prepack,mini load,7.0
550
+ S9999495,"Prepack 2/3 SD,Primary Healthcare2016",prepack,mini load,7.0
551
+ S9999495,"Prepack 2/3 SD,Primary Healthcare2016",prepack,mini load,7.0
552
+ S9999495,"Prepack 2/3 SD,Primary Healthcare2016",prepack,mini load,7.0
553
+ S9999496,"Prepack 3/3 SD,Primary Healthcare2016",prepack,mini load,7.0
554
+ S9999496,"Prepack 3/3 SD,Primary Healthcare2016",prepack,mini load,7.0
555
+ S9999496,"Prepack 3/3 SD,Primary Healthcare2016",prepack,mini load,7.0
556
+ S9999496,"Prepack 3/3 SD,Primary Healthcare2016",prepack,mini load,7.0
557
+ S9999496,"Prepack 3/3 SD,Primary Healthcare2016",prepack,mini load,7.0
558
+ S9999496,"Prepack 3/3 SD,Primary Healthcare2016",prepack,mini load,7.0
559
+ S9999496,"Prepack 3/3 SD,Primary Healthcare2016",prepack,mini load,7.0
560
+ S9999496,"Prepack 3/3 SD,Primary Healthcare2016",prepack,mini load,7.0
561
+ S9999496,"Prepack 3/3 SD,Primary Healthcare2016",prepack,mini load,7.0
562
+ S9999496,"Prepack 3/3 SD,Primary Healthcare2016",prepack,mini load,7.0
563
+ S9999503,"Bag 1/1 Sudan, IMCI Med. kit, 2016",prepack,mini load,7.0
564
+ S9999503,"Bag 1/1 Sudan, IMCI Med. kit, 2016",prepack,mini load,7.0
565
+ S9999504,"Prepack 1/1 Sudan IMCI Med. kit, 2016",prepack,mini load,7.0
566
+ S9999504,"Prepack 1/1 Sudan IMCI Med. kit, 2016",prepack,mini load,7.0
567
+ S9999504,"Prepack 1/1 Sudan IMCI Med. kit, 2016",prepack,mini load,7.0
568
+ S9999504,"Prepack 1/1 Sudan IMCI Med. kit, 2016",prepack,mini load,7.0
569
+ S9999504,"Prepack 1/1 Sudan IMCI Med. kit, 2016",prepack,mini load,7.0
570
+ S9999504,"Prepack 1/1 Sudan IMCI Med. kit, 2016",prepack,mini load,7.0
571
+ S9996726,DRC Measles Kit 2021 PP 1/1,prepack,mini load,7.0
572
+ S9996726,DRC Measles Kit 2021 PP 1/1,prepack,mini load,7.0
573
+ S9996726,DRC Measles Kit 2021 PP 1/1,prepack,mini load,7.0
574
+ S9996726,DRC Measles Kit 2021 PP 1/1,prepack,mini load,7.0
575
+ S9996726,DRC Measles Kit 2021 PP 1/1,prepack,mini load,7.0
576
+ S9996726,DRC Measles Kit 2021 PP 1/1,prepack,mini load,7.0
577
+ S9996726,DRC Measles Kit 2021 PP 1/1,prepack,mini load,7.0
578
+ S9996726,DRC Measles Kit 2021 PP 1/1,prepack,mini load,7.0
579
+ S9996726,DRC Measles Kit 2021 PP 1/1,prepack,mini load,7.0
580
+ S9996726,DRC Measles Kit 2021 PP 1/1,prepack,mini load,7.0
581
+ S9996726,DRC Measles Kit 2021 PP 1/1,prepack,mini load,7.0
582
+ S9993736,PP1/5 f SUB 1/5 S.Sudan18 B.Med S9906733,prepack,mini load,7.0
583
+ S9993736,PP1/5 f SUB 1/5 S.Sudan18 B.Med S9906733,prepack,mini load,7.0
584
+ S9993736,PP1/5 f SUB 1/5 S.Sudan18 B.Med S9906733,prepack,mini load,7.0
585
+ S9993736,PP1/5 f SUB 1/5 S.Sudan18 B.Med S9906733,prepack,mini load,7.0
586
+ S9993736,PP1/5 f SUB 1/5 S.Sudan18 B.Med S9906733,prepack,mini load,7.0
587
+ S9993737,PP2/5 f SUB 1/5 S.Sudan18 B.Med S9906733,prepack,mini load,7.0
588
+ S9993737,PP2/5 f SUB 1/5 S.Sudan18 B.Med S9906733,prepack,mini load,7.0
589
+ S9993737,PP2/5 f SUB 1/5 S.Sudan18 B.Med S9906733,prepack,mini load,7.0
590
+ S9993737,PP2/5 f SUB 1/5 S.Sudan18 B.Med S9906733,prepack,mini load,7.0
591
+ S9993737,PP2/5 f SUB 1/5 S.Sudan18 B.Med S9906733,prepack,mini load,7.0
592
+ S9993739,PP4/5 f SUB 3/5 S.Sudan18 B.Med S9906733,prepack,mini load,7.0
593
+ S9993738,PP3/5 f SUB 4/5 S.Sudan18 B.Med S9906733,prepack,mini load,7.0
594
+ S9993738,PP3/5 f SUB 4/5 S.Sudan18 B.Med S9906733,prepack,mini load,7.0
595
+ S9993738,PP3/5 f SUB 4/5 S.Sudan18 B.Med S9906733,prepack,mini load,7.0
596
+ S9993738,PP3/5 f SUB 4/5 S.Sudan18 B.Med S9906733,prepack,mini load,7.0
597
+ S9993738,PP3/5 f SUB 4/5 S.Sudan18 B.Med S9906733,prepack,mini load,7.0
598
+ S9993738,PP3/5 f SUB 4/5 S.Sudan18 B.Med S9906733,prepack,mini load,7.0
599
+ S9993738,PP3/5 f SUB 4/5 S.Sudan18 B.Med S9906733,prepack,mini load,7.0
600
+ S9993738,PP3/5 f SUB 4/5 S.Sudan18 B.Med S9906733,prepack,mini load,7.0
601
+ S9993730,PP5/5 f SUB 4/5 S.Sudan18 B.Med S9906733,prepack,mini load,7.0
602
+ S9994735,"PP1/4 f Sub2/4 S.Sudan,PHCU Kit S9906734",prepack,mini load,7.0
603
+ S9994735,"PP1/4 f Sub2/4 S.Sudan,PHCU Kit S9906734",prepack,mini load,7.0
604
+ S9994735,"PP1/4 f Sub2/4 S.Sudan,PHCU Kit S9906734",prepack,mini load,7.0
605
+ S9994735,"PP1/4 f Sub2/4 S.Sudan,PHCU Kit S9906734",prepack,mini load,7.0
606
+ S9994735,"PP1/4 f Sub2/4 S.Sudan,PHCU Kit S9906734",prepack,mini load,7.0
607
+ S9994735,"PP1/4 f Sub2/4 S.Sudan,PHCU Kit S9906734",prepack,mini load,7.0
608
+ S9994735,"PP1/4 f Sub2/4 S.Sudan,PHCU Kit S9906734",prepack,mini load,7.0
609
+ S9994735,"PP1/4 f Sub2/4 S.Sudan,PHCU Kit S9906734",prepack,mini load,7.0
610
+ S9994736,"PP2/4 f Sub2/4 S.Sudan,PHCU Kit S9906734",prepack,mini load,7.0
611
+ S9994736,"PP2/4 f Sub2/4 S.Sudan,PHCU Kit S9906734",prepack,mini load,7.0
612
+ S9994736,"PP2/4 f Sub2/4 S.Sudan,PHCU Kit S9906734",prepack,mini load,7.0
613
+ S9994737,"PP3/4 f Sub3/4 S.Sudan,PHCU Kit S9906734",prepack,mini load,7.0
614
+ S9994737,"PP3/4 f Sub3/4 S.Sudan,PHCU Kit S9906734",prepack,mini load,7.0
615
+ S9994737,"PP3/4 f Sub3/4 S.Sudan,PHCU Kit S9906734",prepack,mini load,7.0
616
+ S9994738,"PP4/4 f Sub4/4 S.Sudan,PHCU Kit S9906734",prepack,mini load,7.0
617
+ S9994738,"PP4/4 f Sub4/4 S.Sudan,PHCU Kit S9906734",prepack,mini load,7.0
618
+ S9994738,"PP4/4 f Sub4/4 S.Sudan,PHCU Kit S9906734",prepack,mini load,7.0
619
+ S9997739,PP 1/2 F SUB 1/8 SSPHCC2018-Med S9906737,prepack,mini load,7.0
620
+ S9997730,PP 2/2 F SUB 1/8 SSPHCC2018-Med S9906737,prepack,mini load,7.0
621
+ S9996751,"Prepack. Nut kit,I Ethiop.Mod F S9906753",prepack,mini load,7.0
622
+ S9996751,"Prepack. Nut kit,I Ethiop.Mod F S9906753",prepack,mini load,7.0
623
+ S9996751,"Prepack. Nut kit,I Ethiop.Mod F S9906753",prepack,mini load,7.0
624
+ S9999691,"Prepack 1/4 Mali, in-patient CMAM kit",prepack,mini load,7.0
625
+ S9999691,"Prepack 1/4 Mali, in-patient CMAM kit",prepack,mini load,7.0
626
+ S9999691,"Prepack 1/4 Mali, in-patient CMAM kit",prepack,mini load,7.0
627
+ S9999691,"Prepack 1/4 Mali, in-patient CMAM kit",prepack,mini load,7.0
628
+ S9999691,"Prepack 1/4 Mali, in-patient CMAM kit",prepack,mini load,7.0
629
+ S9999691,"Prepack 1/4 Mali, in-patient CMAM kit",prepack,mini load,7.0
630
+ S9999691,"Prepack 1/4 Mali, in-patient CMAM kit",prepack,mini load,7.0
631
+ S9999691,"Prepack 1/4 Mali, in-patient CMAM kit",prepack,mini load,7.0
632
+ S9999691,"Prepack 1/4 Mali, in-patient CMAM kit",prepack,mini load,7.0
633
+ S9999691,"Prepack 1/4 Mali, in-patient CMAM kit",prepack,mini load,7.0
634
+ S9999691,"Prepack 1/4 Mali, in-patient CMAM kit",prepack,mini load,7.0
635
+ S9999692,"Prepack 2/4 Mali, in-patient CMAM kit",prepack,mini load,7.0
636
+ S9999692,"Prepack 2/4 Mali, in-patient CMAM kit",prepack,mini load,7.0
637
+ S9999692,"Prepack 2/4 Mali, in-patient CMAM kit",prepack,mini load,7.0
638
+ S9999692,"Prepack 2/4 Mali, in-patient CMAM kit",prepack,mini load,7.0
639
+ S9999692,"Prepack 2/4 Mali, in-patient CMAM kit",prepack,mini load,7.0
640
+ S9999692,"Prepack 2/4 Mali, in-patient CMAM kit",prepack,mini load,7.0
641
+ S9999692,"Prepack 2/4 Mali, in-patient CMAM kit",prepack,mini load,7.0
642
+ S9999692,"Prepack 2/4 Mali, in-patient CMAM kit",prepack,mini load,7.0
643
+ S9999692,"Prepack 2/4 Mali, in-patient CMAM kit",prepack,mini load,7.0
644
+ S9999693,"Prepack 3/4 Mali, in-patient CMAM kit",prepack,mini load,7.0
645
+ S9999694,"Prepack 4/4 Mali, in-patient CMAM kit",prepack,mini load,7.0
646
+ S9999694,"Prepack 4/4 Mali, in-patient CMAM kit",prepack,mini load,7.0
647
+ S9999694,"Prepack 4/4 Mali, in-patient CMAM kit",prepack,mini load,7.0
648
+ S9986716,PP 1/3 F Sub 1/3 Yemen PHK S9906800,prepack,mini load,7.0
649
+ S9986716,PP 1/3 F Sub 1/3 Yemen PHK S9906800,prepack,mini load,7.0
650
+ S9986716,PP 1/3 F Sub 1/3 Yemen PHK S9906800,prepack,mini load,7.0
651
+ S9986716,PP 1/3 F Sub 1/3 Yemen PHK S9906800,prepack,mini load,7.0
652
+ S9986717,PP 2/3 F Sub 2/3 Yemen PHK S9906800,prepack,mini load,7.0
653
+ S9986717,PP 2/3 F Sub 2/3 Yemen PHK S9906800,prepack,mini load,7.0
654
+ S9986717,PP 2/3 F Sub 2/3 Yemen PHK S9906800,prepack,mini load,7.0
655
+ S9986717,PP 2/3 F Sub 2/3 Yemen PHK S9906800,prepack,mini load,7.0
656
+ S9986717,PP 2/3 F Sub 2/3 Yemen PHK S9906800,prepack,mini load,7.0
657
+ S9986710,PP 3/3 F Sub 2/3 Yemen PHK S9906800,prepack,mini load,7.0
658
+ S9986710,PP 3/3 F Sub 2/3 Yemen PHK S9906800,prepack,mini load,7.0
659
+ S9986710,PP 3/3 F Sub 2/3 Yemen PHK S9906800,prepack,mini load,7.0
660
+ S9986710,PP 3/3 F Sub 2/3 Yemen PHK S9906800,prepack,mini load,7.0
661
+ S9986710,PP 3/3 F Sub 2/3 Yemen PHK S9906800,prepack,mini load,7.0
662
+ S9999944,"Prepack 1/2 Obstetric,surgical kit,suppl",prepack,mini load,7.0
663
+ S9999944,"Prepack 1/2 Obstetric,surgical kit,suppl",prepack,mini load,7.0
664
+ S9999944,"Prepack 1/2 Obstetric,surgical kit,suppl",prepack,mini load,7.0
665
+ S9999944,"Prepack 1/2 Obstetric,surgical kit,suppl",prepack,mini load,7.0
666
+ S9999945,"Prepack 2/2 Obstetric,surgical kit,suppl",prepack,mini load,7.0
667
+ S9999945,"Prepack 2/2 Obstetric,surgical kit,suppl",prepack,mini load,7.0
668
+ S9999945,"Prepack 2/2 Obstetric,surgical kit,suppl",prepack,mini load,7.0
669
+ S9999938,Prepack 1/2 Obstetric 3-renew. S9908302,prepack,mini load,7.0
670
+ S9999938,Prepack 1/2 Obstetric 3-renew. S9908302,prepack,mini load,7.0
671
+ S9999939,Prepack 2/2 Obstetric 3-renew. S9908302,prepack,mini load,7.0
672
+ S9999939,Prepack 2/2 Obstetric 3-renew. S9908302,prepack,mini load,7.0
673
+ S9999939,Prepack 2/2 Obstetric 3-renew. S9908302,prepack,mini load,7.0
674
+ S9999939,Prepack 2/2 Obstetric 3-renew. S9908302,prepack,mini load,7.0
675
+ S9999939,Prepack 2/2 Obstetric 3-renew. S9908302,prepack,mini load,7.0
676
+ S9999299,"BAG for 50 x S0322010 CH12,ster,disp",prepack,mini load,7.0
data/real_data_excel/converted_csv/Kits__Calculation.csv CHANGED
@@ -1,5 +1,9 @@
1
  Kit,Description,Kit per day,Humanizer,UNICEF staff,Paid work hours per day,Humanizer hours,UNICEF hours,Total Hours,Hourly rate humanizer staff in USD,Hourly rate UNICEF staff in USD,Cost for humanizer for 800 kits ,Cost for UNICEF staff for 800 kits,Total cost,Hours per kit,Cost for subkit/prepack/Kits
2
- S9992431,"School-in-a-carton,40 students, 2016",800,11,2,7.5,82.5,15,97.5,25.87,30,2134.275,450,2584.275,0.121875,3.2303437500000003
 
 
 
 
3
  S9935011,"School-in-a-carton,40 students, 2016",800,11,2,7.5,82.5,15,97.5,25.87,30,2134.275,450,2584.275,0.121875,3.2303437500000003
4
  S9999991,Education prepack 2 for new SIB (I),800,5,1,7.5,37.5,7.5,45,25.87,30,970.125,225,1195.125,0.05625,1.49390625
5
  S9999990,Education prepack 1 for new SIB (I),800,5,1,7.5,37.5,7.5,45,25.87,30,970.125,225,1195.125,0.05625,1.49390625
 
1
  Kit,Description,Kit per day,Humanizer,UNICEF staff,Paid work hours per day,Humanizer hours,UNICEF hours,Total Hours,Hourly rate humanizer staff in USD,Hourly rate UNICEF staff in USD,Cost for humanizer for 800 kits ,Cost for UNICEF staff for 800 kits,Total cost,Hours per kit,Cost for subkit/prepack/Kits
2
+ S9910000,"School-in-a-carton,40 students, 2016",800,11,2,7.5,82.5,15,97.5,25.87,30,2134.275,450,2584.275,0.121875,3.2303437500000003
3
+ S9992431,"School-in-a-carton,40 students, 2016",800,6,2,7.5,82.5,15,97.5,25.87,30,2134.275,450,2584.275,0.121875,3.2303437500000003
4
+ S9991041,"School-in-a-carton,40 students, 2016",800,7,2,7.5,82.5,15,97.5,25.87,30,2134.275,450,2584.275,0.121875,3.2303437500000003
5
+ S9991044,"School-in-a-carton,40 students, 2016",800,9,1,7.5,82.5,15,97.5,25.87,30,2134.275,450,2584.275,0.121875,3.2303437500000003
6
+ S9901041,"School-in-a-carton,40 students, 2016",500,5,1,7.5,82.5,15,97.5,25.87,30,2134.275,450,2584.275,0.121875,3.2303437500000003
7
  S9935011,"School-in-a-carton,40 students, 2016",800,11,2,7.5,82.5,15,97.5,25.87,30,2134.275,450,2584.275,0.121875,3.2303437500000003
8
  S9999991,Education prepack 2 for new SIB (I),800,5,1,7.5,37.5,7.5,45,25.87,30,970.125,225,1195.125,0.05625,1.49390625
9
  S9999990,Education prepack 1 for new SIB (I),800,5,1,7.5,37.5,7.5,45,25.87,30,970.125,225,1195.125,0.05625,1.49390625
data/real_data_excel/converted_csv/Work_Centre_Capacity_processed.csv CHANGED
@@ -5,4 +5,4 @@ id,Work_Area,per_hour_capacity,per_hour_unit,line_for_packaging,line_count
5
  4,Pre-pack_lines,300.0,cartons,False,1
6
  5,Pre-pick_station,54.0,pallets,False,1
7
  6,Long_line,300.0,cartons,True,2
8
- 7,Short_ine,54.0,pallets,True,2
 
5
  4,Pre-pack_lines,300.0,cartons,False,1
6
  5,Pre-pick_station,54.0,pallets,False,1
7
  6,Long_line,300.0,cartons,True,2
8
+ 7,Mini_load,54.0,pallets,True,2
pages/1_πŸ“Š_Dataset_Metadata.py CHANGED
@@ -117,12 +117,14 @@ else:
117
  st.stop()
118
 
119
  # Create tabs for different metadata sections
120
- tab1, tab2, tab3, tab4, tab5 = st.tabs([
121
  "πŸ“‹ Data Overview",
122
  "πŸ“¦ Demand Analysis",
123
  "πŸ‘₯ Workforce Analysis",
124
  "🏭 Production Capacity",
125
- "πŸ’° Cost Analysis"
 
 
126
  ])
127
 
128
  # Tab 1: Data Overview
@@ -660,6 +662,304 @@ with tab5:
660
  except Exception as e:
661
  st.error(f"Error in cost analysis: {e}")
662
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
663
  # Footer
664
  st.markdown("---")
665
  st.markdown("""
 
117
  st.stop()
118
 
119
  # Create tabs for different metadata sections
120
+ tab1, tab2, tab3, tab4, tab5, tab6, tab7 = st.tabs([
121
  "πŸ“‹ Data Overview",
122
  "πŸ“¦ Demand Analysis",
123
  "πŸ‘₯ Workforce Analysis",
124
  "🏭 Production Capacity",
125
+ "πŸ’° Cost Analysis",
126
+ "πŸ“ˆ Quick Reports",
127
+ "βš™οΈ Optimization Settings"
128
  ])
129
 
130
  # Tab 1: Data Overview
 
662
  except Exception as e:
663
  st.error(f"Error in cost analysis: {e}")
664
 
665
+ # Tab 6: Quick Reports
666
+ with tab6:
667
+ st.markdown('<h2 class="section-header">πŸ“ˆ Quick Reports</h2>', unsafe_allow_html=True)
668
+
669
+ st.info("πŸ’‘ **Need more detailed analysis?** Check out the **Enhanced Reports** page for comprehensive visualizations!")
670
+
671
+ if st.button("πŸš€ Go to Enhanced Reports", type="primary", use_container_width=True):
672
+ st.switch_page("pages/3_πŸ“ˆ_Enhanced_Reports.py")
673
+
674
+ try:
675
+ # Quick summary metrics
676
+ demand_df = extract.read_released_orders_data(start_date=start_date, end_date=end_date)
677
+ employee_df = extract.read_employee_data()
678
+ cost_data = optimization_config.COST_LIST_PER_EMP_SHIFT
679
+
680
+ st.markdown("### ⚑ Quick Summary")
681
+
682
+ # Key metrics
683
+ total_orders = len(demand_df)
684
+ total_quantity = demand_df["Order quantity (GMEIN)"].sum()
685
+ unique_products = demand_df["Material Number"].nunique()
686
+ duration = (end_date - start_date).days + 1
687
+
688
+ col_q1, col_q2, col_q3, col_q4 = st.columns(4)
689
+
690
+ with col_q1:
691
+ st.metric("πŸ“¦ Total Orders", f"{total_orders:,}")
692
+ with col_q2:
693
+ st.metric("πŸ“Š Total Quantity", f"{total_quantity:,.0f}")
694
+ with col_q3:
695
+ st.metric("🎯 Unique Products", unique_products)
696
+ with col_q4:
697
+ avg_daily_demand = total_quantity / duration
698
+ st.metric("πŸ“… Avg Daily Demand", f"{avg_daily_demand:,.0f}")
699
+
700
+ # Quick cost estimate
701
+ st.markdown("### πŸ’° Cost Estimate")
702
+
703
+ # Simple cost calculation
704
+ total_employees = len(employee_df)
705
+ emp_counts = employee_df['employment_type'].value_counts()
706
+
707
+ # Estimate daily cost
708
+ estimated_daily_cost = 0
709
+ for emp_type, count in emp_counts.items():
710
+ if emp_type in cost_data:
711
+ # Use regular shift rate
712
+ regular_rate = cost_data[emp_type].get(1, 0)
713
+ estimated_daily_cost += count * regular_rate * 7 # Assume 7-hour shifts
714
+
715
+ period_cost = estimated_daily_cost * duration
716
+ cost_per_unit = period_cost / total_quantity if total_quantity > 0 else 0
717
+
718
+ col_cost1, col_cost2, col_cost3 = st.columns(3)
719
+
720
+ with col_cost1:
721
+ st.metric("πŸ’° Est. Daily Cost", f"${estimated_daily_cost:,.2f}")
722
+ with col_cost2:
723
+ st.metric("πŸ’΅ Est. Period Cost", f"${period_cost:,.2f}")
724
+ with col_cost3:
725
+ st.metric("πŸ“¦ Est. Cost/Unit", f"${cost_per_unit:.3f}")
726
+
727
+ # Quick production overview
728
+ st.markdown("### 🏭 Production Overview")
729
+
730
+ # Daily production distribution
731
+ demand_df['Date'] = pd.to_datetime(demand_df['Basic finish date'])
732
+ daily_production = demand_df.groupby('Date')['Order quantity (GMEIN)'].sum().reset_index()
733
+
734
+ if len(daily_production) > 0:
735
+ fig_quick = px.bar(
736
+ daily_production,
737
+ x='Date',
738
+ y='Order quantity (GMEIN)',
739
+ title='Daily Production Requirements',
740
+ color='Order quantity (GMEIN)',
741
+ color_continuous_scale='Blues'
742
+ )
743
+ st.plotly_chart(fig_quick, use_container_width=True)
744
+
745
+ # Top products quick view
746
+ top_products = demand_df.groupby('Material Number')['Order quantity (GMEIN)'].sum().sort_values(ascending=False).head(5)
747
+
748
+ st.markdown("**Top 5 Products by Quantity:**")
749
+ for i, (product, quantity) in enumerate(top_products.items(), 1):
750
+ st.write(f"{i}. {product}: {quantity:,.0f} units")
751
+
752
+ except Exception as e:
753
+ st.error(f"Error generating quick reports: {e}")
754
+
755
+ # Tab 7: Optimization Settings
756
+ with tab7:
757
+ st.markdown('<h2 class="section-header">βš™οΈ Optimization Settings</h2>', unsafe_allow_html=True)
758
+
759
+ st.info("πŸ’‘ **Configure optimization parameters here!** These settings will be used by the optimization engine when you run optimizations.")
760
+
761
+ try:
762
+ # Load available options from data
763
+ employee_df = extract.read_employee_data()
764
+ line_df = extract.read_packaging_line_data()
765
+ shift_df = extract.get_shift_info()
766
+
767
+ # Employee Types Selection
768
+ st.markdown("### πŸ‘₯ Employee Types")
769
+ available_emp_types = employee_df["employment_type"].unique().tolist()
770
+
771
+ # Initialize session state if not exists
772
+ if 'selected_employee_types' not in st.session_state:
773
+ st.session_state.selected_employee_types = available_emp_types
774
+
775
+ selected_emp_types = st.multiselect(
776
+ "Select employee types to include in optimization:",
777
+ options=available_emp_types,
778
+ default=st.session_state.selected_employee_types,
779
+ key="emp_types_selector",
780
+ help="Choose which employee types should be available for optimization"
781
+ )
782
+
783
+ # Update session state
784
+ if selected_emp_types != st.session_state.selected_employee_types:
785
+ st.session_state.selected_employee_types = selected_emp_types
786
+ st.success(f"βœ… Employee types updated: {', '.join(selected_emp_types)}")
787
+
788
+ # Display current employee type counts
789
+ col_emp1, col_emp2 = st.columns(2)
790
+ with col_emp1:
791
+ st.markdown("**Current Employee Availability:**")
792
+ emp_counts = employee_df['employment_type'].value_counts()
793
+ for emp_type in selected_emp_types:
794
+ count = emp_counts.get(emp_type, 0)
795
+ st.write(f"β€’ {emp_type}: {count} employees")
796
+
797
+ with col_emp2:
798
+ # Show cost information for selected types
799
+ st.markdown("**Hourly Rates (Regular Shift):**")
800
+ cost_data = optimization_config.COST_LIST_PER_EMP_SHIFT
801
+ for emp_type in selected_emp_types:
802
+ if emp_type in cost_data:
803
+ regular_rate = cost_data[emp_type].get(1, "N/A")
804
+ st.write(f"β€’ {emp_type}: ${regular_rate}/hour")
805
+
806
+ st.markdown("---")
807
+
808
+ # Shifts Selection
809
+ st.markdown("### πŸ• Shifts")
810
+ available_shifts = shift_df["id"].unique().tolist()
811
+
812
+ # Initialize session state if not exists
813
+ if 'selected_shifts' not in st.session_state:
814
+ st.session_state.selected_shifts = available_shifts
815
+
816
+ shift_names = {1: 'Regular (Day)', 2: 'Overtime', 3: 'Evening'}
817
+ shift_options = [f"{shift_id}: {shift_names.get(shift_id, f'Shift {shift_id}')}" for shift_id in available_shifts]
818
+
819
+ selected_shift_options = st.multiselect(
820
+ "Select shifts to include in optimization:",
821
+ options=shift_options,
822
+ default=[f"{shift_id}: {shift_names.get(shift_id, f'Shift {shift_id}')}" for shift_id in st.session_state.selected_shifts],
823
+ key="shifts_selector",
824
+ help="Choose which shifts should be available for optimization"
825
+ )
826
+
827
+ # Extract shift IDs from selected options
828
+ selected_shifts = [int(option.split(':')[0]) for option in selected_shift_options]
829
+
830
+ # Update session state
831
+ if selected_shifts != st.session_state.selected_shifts:
832
+ st.session_state.selected_shifts = selected_shifts
833
+ st.success(f"βœ… Shifts updated: {', '.join([shift_names.get(s, f'Shift {s}') for s in selected_shifts])}")
834
+
835
+ # Display shift information
836
+ st.markdown("**Shift Details:**")
837
+ shift_hours = optimization_config.MAX_HOUR_PER_SHIFT_PER_PERSON
838
+ for shift_id in selected_shifts:
839
+ shift_name = shift_names.get(shift_id, f'Shift {shift_id}')
840
+ hours = shift_hours.get(shift_id, "N/A")
841
+ st.write(f"β€’ {shift_name}: {hours} hours")
842
+
843
+ st.markdown("---")
844
+
845
+ # Production Lines Selection
846
+ st.markdown("### 🏭 Production Lines")
847
+ available_lines = line_df["id"].unique().tolist()
848
+
849
+ # Initialize session state if not exists
850
+ if 'selected_lines' not in st.session_state:
851
+ st.session_state.selected_lines = available_lines
852
+
853
+ selected_lines = st.multiselect(
854
+ "Select production lines to include in optimization:",
855
+ options=available_lines,
856
+ default=st.session_state.selected_lines,
857
+ key="lines_selector",
858
+ help="Choose which production lines should be available for optimization"
859
+ )
860
+
861
+ # Update session state
862
+ if selected_lines != st.session_state.selected_lines:
863
+ st.session_state.selected_lines = selected_lines
864
+ st.success(f"βœ… Production lines updated: {', '.join([f'Line {line}' for line in selected_lines])}")
865
+
866
+ # Display line information
867
+ col_line1, col_line2 = st.columns(2)
868
+ with col_line1:
869
+ st.markdown("**Line Capacities:**")
870
+ for line_id in selected_lines:
871
+ line_info = line_df[line_df['id'] == line_id]
872
+ if not line_info.empty:
873
+ line_count = line_info.iloc[0]['line_count']
874
+ st.write(f"β€’ Line {line_id}: {line_count} units available")
875
+
876
+ with col_line2:
877
+ st.markdown("**Processing Speed:**")
878
+ line_speeds = optimization_config.PER_PRODUCT_SPEED
879
+ for line_id in selected_lines:
880
+ speed = line_speeds.get(line_id, "N/A")
881
+ st.write(f"β€’ Line {line_id}: {speed} units/hour")
882
+
883
+ st.markdown("---")
884
+
885
+ # Additional Settings
886
+ st.markdown("### πŸ”§ Additional Settings")
887
+
888
+ col_settings1, col_settings2 = st.columns(2)
889
+
890
+ with col_settings1:
891
+ # Constraint mode
892
+ constraint_mode = st.selectbox(
893
+ "Fixed Staff Constraint Mode:",
894
+ options=["priority", "mandatory", "none"],
895
+ index=0,
896
+ key="constraint_mode_selector",
897
+ help="priority=Use fixed staff first, mandatory=Force all fixed hours, none=Demand-driven"
898
+ )
899
+
900
+ if 'selected_constraint_mode' not in st.session_state:
901
+ st.session_state.selected_constraint_mode = constraint_mode
902
+
903
+ if constraint_mode != st.session_state.selected_constraint_mode:
904
+ st.session_state.selected_constraint_mode = constraint_mode
905
+ st.success(f"βœ… Constraint mode updated: {constraint_mode}")
906
+
907
+ with col_settings2:
908
+ # Evening shift mode
909
+ evening_mode = st.selectbox(
910
+ "Evening Shift Mode:",
911
+ options=["normal", "activate_evening", "always_available"],
912
+ index=0,
913
+ key="evening_mode_selector",
914
+ help="normal=Regular+Overtime only, activate_evening=Auto-activate when needed, always_available=Always include evening shift"
915
+ )
916
+
917
+ if 'selected_evening_mode' not in st.session_state:
918
+ st.session_state.selected_evening_mode = evening_mode
919
+
920
+ if evening_mode != st.session_state.selected_evening_mode:
921
+ st.session_state.selected_evening_mode = evening_mode
922
+ st.success(f"βœ… Evening shift mode updated: {evening_mode}")
923
+
924
+ # Summary of current settings
925
+ st.markdown("### πŸ“‹ Current Optimization Configuration")
926
+ st.markdown('<div class="cost-highlight">', unsafe_allow_html=True)
927
+
928
+ col_summary1, col_summary2 = st.columns(2)
929
+
930
+ with col_summary1:
931
+ st.markdown("**Selected Configuration:**")
932
+ st.write(f"β€’ **Employee Types:** {len(st.session_state.get('selected_employee_types', []))} types")
933
+ st.write(f"β€’ **Shifts:** {len(st.session_state.get('selected_shifts', []))} shifts")
934
+ st.write(f"β€’ **Production Lines:** {len(st.session_state.get('selected_lines', []))} lines")
935
+ st.write(f"β€’ **Constraint Mode:** {st.session_state.get('selected_constraint_mode', 'priority')}")
936
+
937
+ with col_summary2:
938
+ st.markdown("**Ready for Optimization:**")
939
+ if (st.session_state.get('selected_employee_types') and
940
+ st.session_state.get('selected_shifts') and
941
+ st.session_state.get('selected_lines')):
942
+ st.success("βœ… All required settings configured!")
943
+ if st.button("πŸš€ Go to Optimization Page", type="primary", use_container_width=True):
944
+ st.switch_page("pages/2_🎯_Optimization.py")
945
+ else:
946
+ st.warning("⚠️ Please configure all settings above")
947
+
948
+ st.markdown('</div>', unsafe_allow_html=True)
949
+
950
+ # Reset button
951
+ if st.button("πŸ”„ Reset to Defaults", type="secondary"):
952
+ st.session_state.selected_employee_types = available_emp_types
953
+ st.session_state.selected_shifts = available_shifts
954
+ st.session_state.selected_lines = available_lines
955
+ st.session_state.selected_constraint_mode = "priority"
956
+ st.session_state.selected_evening_mode = "normal"
957
+ st.success("βœ… Settings reset to defaults!")
958
+ st.rerun()
959
+
960
+ except Exception as e:
961
+ st.error(f"Error loading optimization settings: {e}")
962
+
963
  # Footer
964
  st.markdown("---")
965
  st.markdown("""
pages/2_🎯_Optimization.py CHANGED
@@ -21,7 +21,7 @@ import numpy as np
21
  sys.path.append(os.path.join(os.path.dirname(os.path.dirname(__file__)), 'src'))
22
 
23
  try:
24
- from src.models.optimizer_real import OptimizerReal
25
  from src.config import optimization_config
26
  import src.etl.extract as extract
27
  import src.etl.transform as transform
@@ -202,19 +202,23 @@ else:
202
  if run_optimization:
203
  with st.spinner("πŸ”„ Running optimization... This may take a few moments."):
204
  try:
205
- # Create optimizer instance
206
- optimizer = OptimizerReal()
207
 
208
- # Run optimization and get structured results
209
- results = optimizer.solve_option_A_multi_day_generalized()
210
 
211
  if results is None:
212
  st.error("❌ Optimization returned no results")
213
- elif results.get('status') == 'failed':
214
  st.error(f"❌ Optimization failed: {results.get('message', 'Unknown error')}")
215
  else:
216
- st.session_state.optimization_results = results
217
- st.session_state.optimizer = optimizer
 
 
 
 
 
218
  st.success("βœ… Optimization completed successfully!")
219
 
220
  except Exception as e:
@@ -256,16 +260,31 @@ if st.session_state.optimization_results:
256
  with tab1:
257
  st.markdown("### πŸ“Š Optimization Summary")
258
 
 
 
 
 
 
 
259
  # Additional summary metrics
260
  col_s1, col_s2, col_s3 = st.columns(3)
261
 
262
  with col_s1:
263
- st.metric("🎯 Total Demand", f"{params.get('total_demand', 0):,.0f}")
 
 
 
 
264
  with col_s2:
265
- cost_per_unit = total_cost / params.get('total_demand', 1) if params.get('total_demand', 0) > 0 else 0
266
- st.metric("πŸ’΅ Cost per Unit", f"${cost_per_unit:.3f}")
 
 
 
 
267
  with col_s3:
268
- st.metric("βš™οΈ Constraint Mode", params.get('constraint_mode', 'N/A'))
 
269
 
270
  # Show optimization parameters used
271
  st.markdown("#### πŸ“‹ Configuration Used")
@@ -297,17 +316,27 @@ if st.session_state.optimization_results:
297
  with tab2:
298
  st.markdown("### πŸ“ˆ Production Results")
299
 
300
- production_results = results.get('production_results', {})
301
- if production_results:
 
 
 
302
  # Create production summary table
303
  prod_data = []
304
- for product, data in production_results.items():
 
 
 
 
 
 
 
305
  prod_data.append({
306
  'Product': product,
307
- 'Demand': data['demand'],
308
- 'Produced': data['produced'],
309
- 'Fulfillment %': f"{data['fulfillment_rate']:.1f}%",
310
- 'Status': 'βœ… Met' if data['fulfillment_rate'] >= 100 else '⚠️ Partial'
311
  })
312
 
313
  if prod_data:
 
21
  sys.path.append(os.path.join(os.path.dirname(os.path.dirname(__file__)), 'src'))
22
 
23
  try:
24
+ from src.models.optimizer_new_aug14 import solve_fixed_team_weekly
25
  from src.config import optimization_config
26
  import src.etl.extract as extract
27
  import src.etl.transform as transform
 
202
  if run_optimization:
203
  with st.spinner("πŸ”„ Running optimization... This may take a few moments."):
204
  try:
205
+ # Run optimization using the new optimizer
206
+ st.info("πŸ”§ Using optimizer_new_aug14 with fixed team weekly scheduling")
207
 
208
+ results = solve_fixed_team_weekly()
 
209
 
210
  if results is None:
211
  st.error("❌ Optimization returned no results")
212
+ elif isinstance(results, dict) and results.get('status') == 'failed':
213
  st.error(f"❌ Optimization failed: {results.get('message', 'Unknown error')}")
214
  else:
215
+ # Convert results to expected format for display
216
+ st.session_state.optimization_results = {
217
+ 'status': 'success',
218
+ 'total_cost': results.get('objective', 0),
219
+ 'raw_results': results,
220
+ 'solver_type': 'optimizer_new_aug14'
221
+ }
222
  st.success("βœ… Optimization completed successfully!")
223
 
224
  except Exception as e:
 
260
  with tab1:
261
  st.markdown("### πŸ“Š Optimization Summary")
262
 
263
+ # Check if we have raw results from new optimizer
264
+ raw_results = results.get('raw_results', {})
265
+ solver_type = results.get('solver_type', 'Unknown')
266
+
267
+ st.info(f"πŸ”§ Solver Used: {solver_type}")
268
+
269
  # Additional summary metrics
270
  col_s1, col_s2, col_s3 = st.columns(3)
271
 
272
  with col_s1:
273
+ if 'weekly_production' in raw_results:
274
+ total_produced = sum(raw_results['weekly_production'].values())
275
+ st.metric("🏭 Total Produced", f"{total_produced:,.0f}")
276
+ else:
277
+ st.metric("🏭 Total Produced", "N/A")
278
  with col_s2:
279
+ if 'weekly_production' in raw_results:
280
+ total_produced = sum(raw_results['weekly_production'].values())
281
+ cost_per_unit = total_cost / total_produced if total_produced > 0 else 0
282
+ st.metric("πŸ’΅ Cost per Unit", f"${cost_per_unit:.3f}")
283
+ else:
284
+ st.metric("πŸ’΅ Cost per Unit", "N/A")
285
  with col_s3:
286
+ products_count = len(raw_results.get('weekly_production', {}))
287
+ st.metric("πŸ“¦ Products Optimized", products_count)
288
 
289
  # Show optimization parameters used
290
  st.markdown("#### πŸ“‹ Configuration Used")
 
316
  with tab2:
317
  st.markdown("### πŸ“ˆ Production Results")
318
 
319
+ # Use new optimizer results format
320
+ weekly_production = raw_results.get('weekly_production', {})
321
+ run_schedule = raw_results.get('run_schedule', [])
322
+
323
+ if weekly_production:
324
  # Create production summary table
325
  prod_data = []
326
+
327
+ # Get demand data for comparison
328
+ demand_data = optimization_config.DEMAND_DICTIONARY
329
+
330
+ for product, produced in weekly_production.items():
331
+ demand = demand_data.get(product, 0)
332
+ fulfillment_rate = (produced / demand * 100) if demand > 0 else 0
333
+
334
  prod_data.append({
335
  'Product': product,
336
+ 'Demand': demand,
337
+ 'Produced': produced,
338
+ 'Fulfillment %': f"{fulfillment_rate:.1f}%",
339
+ 'Status': 'βœ… Met' if fulfillment_rate >= 100 else '⚠️ Partial'
340
  })
341
 
342
  if prod_data:
pages/3_πŸ“ˆ_Enhanced_Reports.py ADDED
@@ -0,0 +1,873 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+
3
+ # Page configuration
4
+ st.set_page_config(
5
+ page_title="Enhanced Reports",
6
+ page_icon="πŸ“ˆ",
7
+ layout="wide"
8
+ )
9
+
10
+ # Import libraries
11
+ import pandas as pd
12
+ import plotly.express as px
13
+ import plotly.graph_objects as go
14
+ from plotly.subplots import make_subplots
15
+ import sys
16
+ import os
17
+ from datetime import datetime, timedelta
18
+ import numpy as np
19
+
20
+ # Add src to path for imports
21
+ sys.path.append(os.path.join(os.path.dirname(os.path.dirname(__file__)), 'src'))
22
+
23
+ try:
24
+ import src.etl.extract as extract
25
+ import src.etl.transform as transform
26
+ from src.config import optimization_config
27
+ except ImportError as e:
28
+ st.error(f"Error importing modules: {e}")
29
+ st.stop()
30
+
31
+ # Custom CSS
32
+ st.markdown("""
33
+ <style>
34
+ .main-header {
35
+ font-size: 2.5rem;
36
+ font-weight: bold;
37
+ color: #1f77b4;
38
+ margin-bottom: 1rem;
39
+ }
40
+ .section-header {
41
+ font-size: 1.5rem;
42
+ font-weight: bold;
43
+ color: #2c3e50;
44
+ margin: 1rem 0;
45
+ }
46
+ .metric-card {
47
+ background-color: #f8f9fa;
48
+ padding: 1rem;
49
+ border-radius: 0.5rem;
50
+ border-left: 4px solid #1f77b4;
51
+ margin-bottom: 1rem;
52
+ }
53
+ .cost-highlight {
54
+ background-color: #e8f5e8;
55
+ padding: 1rem;
56
+ border-radius: 0.5rem;
57
+ border-left: 4px solid #28a745;
58
+ margin: 1rem 0;
59
+ }
60
+ .production-highlight {
61
+ background-color: #fff3cd;
62
+ padding: 1rem;
63
+ border-radius: 0.5rem;
64
+ border-left: 4px solid #ffc107;
65
+ margin: 1rem 0;
66
+ }
67
+ </style>
68
+ """, unsafe_allow_html=True)
69
+
70
+ # Initialize session state
71
+ if 'date_range' not in st.session_state:
72
+ st.session_state.date_range = None
73
+
74
+ # Title
75
+ st.markdown('<h1 class="main-header">πŸ“ˆ Enhanced Visualization Reports</h1>', unsafe_allow_html=True)
76
+
77
+ # Sidebar for date selection
78
+ with st.sidebar:
79
+ st.markdown("## πŸ“… Date Selection")
80
+
81
+ try:
82
+ date_ranges = transform.get_date_ranges()
83
+ if date_ranges:
84
+ date_range_options = [f"{start.strftime('%Y-%m-%d')} to {end.strftime('%Y-%m-%d')}" for start, end in date_ranges]
85
+ selected_range_str = st.selectbox(
86
+ "Select date range:",
87
+ options=date_range_options,
88
+ help="Available date ranges from released orders"
89
+ )
90
+
91
+ selected_index = date_range_options.index(selected_range_str)
92
+ start_date, end_date = date_ranges[selected_index]
93
+ st.session_state.date_range = (start_date, end_date)
94
+
95
+ duration = (end_date - start_date).days + 1
96
+ st.info(f"Duration: {duration} days")
97
+
98
+ else:
99
+ st.warning("No date ranges found")
100
+ start_date = datetime(2025, 3, 24).date()
101
+ end_date = datetime(2025, 3, 28).date()
102
+ st.session_state.date_range = (start_date, end_date)
103
+
104
+ except Exception as e:
105
+ st.error(f"Error loading dates: {e}")
106
+ start_date = datetime(2025, 3, 24).date()
107
+ end_date = datetime(2025, 3, 28).date()
108
+ st.session_state.date_range = (start_date, end_date)
109
+
110
+ st.markdown("---")
111
+ st.markdown("## πŸ”„ Refresh Data")
112
+ if st.button("πŸ”„ Reload All Data"):
113
+ st.rerun()
114
+
115
+ # Main content
116
+ if st.session_state.date_range:
117
+ start_date, end_date = st.session_state.date_range
118
+ st.markdown(f"**Analysis Period:** {start_date} to {end_date}")
119
+ else:
120
+ st.warning("No date range selected")
121
+ st.stop()
122
+
123
+ # Create main tabs for the enhanced reports
124
+ tab1, tab2, tab3, tab4 = st.tabs([
125
+ "πŸ’° Employee Costs per Hour",
126
+ "πŸ“‹ Production Plans & Orders",
127
+ "🏭 Line Allocation by Day",
128
+ "πŸ’΅ Total Costs & Analysis"
129
+ ])
130
+
131
+ # Tab 1: Employee Costs per Hour
132
+ with tab1:
133
+ st.markdown('<h2 class="section-header">πŸ’° Employee Costs per Hour Analysis</h2>', unsafe_allow_html=True)
134
+
135
+ try:
136
+ # Load employee cost data
137
+ employee_df = extract.read_employee_data()
138
+ cost_data = optimization_config.COST_LIST_PER_EMP_SHIFT
139
+ shift_hours = optimization_config.MAX_HOUR_PER_SHIFT_PER_PERSON
140
+
141
+ # Create comprehensive cost analysis
142
+ st.markdown("### πŸ“Š Hourly Rate Breakdown")
143
+
144
+ # Detailed cost table with all combinations
145
+ cost_breakdown = []
146
+ employee_counts = employee_df['employment_type'].value_counts()
147
+
148
+ for emp_type, shifts in cost_data.items():
149
+ emp_count = employee_counts.get(emp_type, 0)
150
+
151
+ for shift_id, hourly_rate in shifts.items():
152
+ shift_name = {1: 'Regular', 2: 'Overtime', 3: 'Evening'}.get(shift_id, f'Shift {shift_id}')
153
+ shift_duration = shift_hours.get(shift_id, 0)
154
+
155
+ cost_breakdown.append({
156
+ 'Employee Type': emp_type,
157
+ 'Shift': shift_name,
158
+ 'Shift ID': shift_id,
159
+ 'Available Staff': emp_count,
160
+ 'Hourly Rate ($)': hourly_rate,
161
+ 'Shift Duration (hrs)': shift_duration,
162
+ 'Daily Cost per Employee ($)': hourly_rate * shift_duration,
163
+ 'Total Daily Cost Potential ($)': hourly_rate * shift_duration * emp_count
164
+ })
165
+
166
+ cost_df = pd.DataFrame(cost_breakdown)
167
+
168
+ # Display detailed cost table
169
+ st.dataframe(cost_df, use_container_width=True)
170
+
171
+ # Cost analysis visualizations
172
+ col_cost1, col_cost2 = st.columns(2)
173
+
174
+ with col_cost1:
175
+ # Hourly rates comparison
176
+ fig_hourly = px.bar(
177
+ cost_df,
178
+ x='Employee Type',
179
+ y='Hourly Rate ($)',
180
+ color='Shift',
181
+ title='Hourly Rates by Employee Type and Shift',
182
+ barmode='group',
183
+ text='Hourly Rate ($)'
184
+ )
185
+ fig_hourly.update_traces(texttemplate='$%{text:.0f}', textposition='outside')
186
+ st.plotly_chart(fig_hourly, use_container_width=True)
187
+
188
+ with col_cost2:
189
+ # Daily cost per employee
190
+ fig_daily = px.bar(
191
+ cost_df,
192
+ x='Employee Type',
193
+ y='Daily Cost per Employee ($)',
194
+ color='Shift',
195
+ title='Daily Cost per Employee by Type and Shift',
196
+ barmode='group',
197
+ text='Daily Cost per Employee ($)'
198
+ )
199
+ fig_daily.update_traces(texttemplate='$%{text:.0f}', textposition='outside')
200
+ st.plotly_chart(fig_daily, use_container_width=True)
201
+
202
+ # Cost efficiency analysis
203
+ st.markdown("### πŸ“ˆ Cost Efficiency Analysis")
204
+
205
+ col_eff1, col_eff2 = st.columns(2)
206
+
207
+ with col_eff1:
208
+ # Cost per hour vs productivity visualization
209
+ fig_efficiency = px.scatter(
210
+ cost_df,
211
+ x='Shift Duration (hrs)',
212
+ y='Hourly Rate ($)',
213
+ size='Available Staff',
214
+ color='Employee Type',
215
+ title='Cost Efficiency: Hourly Rate vs Shift Duration',
216
+ hover_data=['Shift', 'Total Daily Cost Potential ($)']
217
+ )
218
+ st.plotly_chart(fig_efficiency, use_container_width=True)
219
+
220
+ with col_eff2:
221
+ # Cost distribution pie chart
222
+ emp_cost_totals = cost_df.groupby('Employee Type')['Total Daily Cost Potential ($)'].sum()
223
+ fig_pie = px.pie(
224
+ values=emp_cost_totals.values,
225
+ names=emp_cost_totals.index,
226
+ title='Total Daily Cost Potential Distribution'
227
+ )
228
+ st.plotly_chart(fig_pie, use_container_width=True)
229
+
230
+ # Summary metrics
231
+ st.markdown("### πŸ“‹ Cost Summary Metrics")
232
+
233
+ col_summary1, col_summary2, col_summary3, col_summary4 = st.columns(4)
234
+
235
+ total_potential_cost = cost_df['Total Daily Cost Potential ($)'].sum()
236
+ min_hourly_rate = cost_df['Hourly Rate ($)'].min()
237
+ max_hourly_rate = cost_df['Hourly Rate ($)'].max()
238
+ avg_hourly_rate = cost_df['Hourly Rate ($)'].mean()
239
+
240
+ with col_summary1:
241
+ st.metric("πŸ’° Total Daily Potential", f"${total_potential_cost:,.2f}")
242
+ with col_summary2:
243
+ st.metric("πŸ“‰ Min Hourly Rate", f"${min_hourly_rate:.2f}")
244
+ with col_summary3:
245
+ st.metric("πŸ“ˆ Max Hourly Rate", f"${max_hourly_rate:.2f}")
246
+ with col_summary4:
247
+ st.metric("πŸ“Š Avg Hourly Rate", f"${avg_hourly_rate:.2f}")
248
+
249
+ # Cost projections
250
+ st.markdown('<div class="cost-highlight">', unsafe_allow_html=True)
251
+ st.markdown("#### πŸ“… Cost Projections")
252
+
253
+ duration = (end_date - start_date).days + 1
254
+ period_cost = total_potential_cost * duration
255
+ weekly_cost = total_potential_cost * 7
256
+ monthly_cost = total_potential_cost * 30
257
+
258
+ col_proj1, col_proj2, col_proj3 = st.columns(3)
259
+
260
+ with col_proj1:
261
+ st.metric("πŸ“… Current Period", f"${period_cost:,.2f}", f"{duration} days")
262
+ with col_proj2:
263
+ st.metric("πŸ“… Weekly Projection", f"${weekly_cost:,.2f}")
264
+ with col_proj3:
265
+ st.metric("πŸ“… Monthly Projection", f"${monthly_cost:,.2f}")
266
+
267
+ st.markdown('</div>', unsafe_allow_html=True)
268
+
269
+ except Exception as e:
270
+ st.error(f"Error in employee cost analysis: {e}")
271
+
272
+ # Tab 2: Production Plans & Orders
273
+ with tab2:
274
+ st.markdown('<h2 class="section-header">πŸ“‹ Production Plans & Orders Analysis</h2>', unsafe_allow_html=True)
275
+
276
+ try:
277
+ # Load production data
278
+ demand_df = extract.read_released_orders_data(start_date=start_date, end_date=end_date)
279
+ planned_df = extract.read_planned_orders_data(start_date=start_date, end_date=end_date)
280
+
281
+ # Production overview
282
+ st.markdown("### πŸ“Š Production Overview")
283
+
284
+ col_prod1, col_prod2, col_prod3, col_prod4 = st.columns(4)
285
+
286
+ total_released_orders = len(demand_df)
287
+ total_planned_orders = len(planned_df) if planned_df is not None else 0
288
+ total_released_quantity = demand_df["Order quantity (GMEIN)"].sum()
289
+ unique_materials = demand_df["Material Number"].nunique()
290
+
291
+ with col_prod1:
292
+ st.metric("πŸ“¦ Released Orders", f"{total_released_orders:,}")
293
+ with col_prod2:
294
+ st.metric("πŸ“‹ Planned Orders", f"{total_planned_orders:,}")
295
+ with col_prod3:
296
+ st.metric("πŸ“Š Total Quantity", f"{total_released_quantity:,.0f}")
297
+ with col_prod4:
298
+ st.metric("🎯 Unique Materials", f"{unique_materials:,}")
299
+
300
+ # Daily production schedule
301
+ st.markdown("### πŸ“… Daily Production Schedule")
302
+
303
+ # Convert dates and create daily view
304
+ demand_df['Date'] = pd.to_datetime(demand_df['Basic finish date'])
305
+ daily_production = demand_df.groupby(['Date', 'Material Number']).agg({
306
+ 'Order quantity (GMEIN)': 'sum',
307
+ 'Order': 'count'
308
+ }).reset_index()
309
+
310
+ # Daily summary
311
+ daily_summary = demand_df.groupby('Date').agg({
312
+ 'Order quantity (GMEIN)': 'sum',
313
+ 'Order': 'count',
314
+ 'Material Number': 'nunique'
315
+ }).reset_index()
316
+ daily_summary.columns = ['Date', 'Total Quantity', 'Total Orders', 'Unique Materials']
317
+
318
+ col_daily1, col_daily2 = st.columns(2)
319
+
320
+ with col_daily1:
321
+ # Daily quantity trend
322
+ fig_daily_qty = px.line(
323
+ daily_summary,
324
+ x='Date',
325
+ y='Total Quantity',
326
+ title='Daily Production Quantity Trend',
327
+ markers=True
328
+ )
329
+ fig_daily_qty.update_layout(xaxis_title="Date", yaxis_title="Total Quantity")
330
+ st.plotly_chart(fig_daily_qty, use_container_width=True)
331
+
332
+ with col_daily2:
333
+ # Daily orders count
334
+ fig_daily_orders = px.bar(
335
+ daily_summary,
336
+ x='Date',
337
+ y='Total Orders',
338
+ title='Daily Orders Count',
339
+ color='Total Orders',
340
+ color_continuous_scale='Blues'
341
+ )
342
+ st.plotly_chart(fig_daily_orders, use_container_width=True)
343
+
344
+ # Material analysis
345
+ st.markdown("### 🎯 Material Analysis")
346
+
347
+ # Top materials by quantity
348
+ material_summary = demand_df.groupby('Material Number').agg({
349
+ 'Order quantity (GMEIN)': 'sum',
350
+ 'Order': 'count'
351
+ }).reset_index()
352
+ material_summary.columns = ['Material', 'Total Quantity', 'Order Count']
353
+ material_summary = material_summary.sort_values('Total Quantity', ascending=False)
354
+
355
+ col_mat1, col_mat2 = st.columns(2)
356
+
357
+ with col_mat1:
358
+ # Top 10 materials by quantity
359
+ top_materials = material_summary.head(10)
360
+ fig_top_mat = px.bar(
361
+ top_materials,
362
+ x='Material',
363
+ y='Total Quantity',
364
+ title='Top 10 Materials by Total Quantity',
365
+ color='Total Quantity',
366
+ color_continuous_scale='Viridis'
367
+ )
368
+ fig_top_mat.update_layout(xaxis_tickangle=-45)
369
+ st.plotly_chart(fig_top_mat, use_container_width=True)
370
+
371
+ with col_mat2:
372
+ # Order frequency vs quantity scatter
373
+ fig_scatter = px.scatter(
374
+ material_summary,
375
+ x='Order Count',
376
+ y='Total Quantity',
377
+ title='Order Frequency vs Total Quantity',
378
+ hover_data=['Material'],
379
+ size='Total Quantity',
380
+ color='Order Count',
381
+ color_continuous_scale='Plasma'
382
+ )
383
+ st.plotly_chart(fig_scatter, use_container_width=True)
384
+
385
+ # Detailed production schedule table
386
+ st.markdown("### πŸ“‹ Detailed Production Schedule")
387
+
388
+ # Show daily breakdown with materials
389
+ expanded_schedule = demand_df[['Date', 'Order', 'Material Number', 'Material description',
390
+ 'Order quantity (GMEIN)', 'Basic start date', 'Basic finish date']].copy()
391
+ expanded_schedule['Date'] = expanded_schedule['Date'].dt.strftime('%Y-%m-%d')
392
+ expanded_schedule['Basic start date'] = pd.to_datetime(expanded_schedule['Basic start date']).dt.strftime('%Y-%m-%d')
393
+ expanded_schedule['Basic finish date'] = pd.to_datetime(expanded_schedule['Basic finish date']).dt.strftime('%Y-%m-%d')
394
+
395
+ st.dataframe(expanded_schedule, use_container_width=True)
396
+
397
+ # Production capacity analysis
398
+ st.markdown('<div class="production-highlight">', unsafe_allow_html=True)
399
+ st.markdown("#### 🏭 Production Capacity Requirements")
400
+
401
+ # Calculate required line hours per day
402
+ line_capacity = optimization_config.PER_PRODUCT_SPEED
403
+ material_line_req = {}
404
+
405
+ for _, row in demand_df.iterrows():
406
+ material = row['Material Number']
407
+ quantity = row['Order quantity (GMEIN)']
408
+ date = row['Date'].strftime('%Y-%m-%d')
409
+
410
+ # Get line assignment (simplified - using line 6 as default)
411
+ line_id = optimization_config.KIT_LINE_MATCH_DICT.get(material, 6)
412
+ line_speed = line_capacity.get(line_id, 300) # Default 300 units/hour
413
+
414
+ required_hours = quantity / line_speed if line_speed > 0 else 0
415
+
416
+ if date not in material_line_req:
417
+ material_line_req[date] = {}
418
+ if line_id not in material_line_req[date]:
419
+ material_line_req[date][line_id] = 0
420
+
421
+ material_line_req[date][line_id] += required_hours
422
+
423
+ # Display capacity requirements
424
+ capacity_data = []
425
+ for date, lines in material_line_req.items():
426
+ for line_id, hours in lines.items():
427
+ capacity_data.append({
428
+ 'Date': date,
429
+ 'Line': f'Line {line_id}',
430
+ 'Required Hours': hours,
431
+ 'Line Capacity (units/hr)': line_capacity.get(line_id, 300)
432
+ })
433
+
434
+ if capacity_data:
435
+ capacity_df = pd.DataFrame(capacity_data)
436
+
437
+ # Capacity visualization
438
+ fig_capacity = px.bar(
439
+ capacity_df,
440
+ x='Date',
441
+ y='Required Hours',
442
+ color='Line',
443
+ title='Daily Line Capacity Requirements',
444
+ barmode='stack'
445
+ )
446
+ st.plotly_chart(fig_capacity, use_container_width=True)
447
+
448
+ # Capacity summary table
449
+ st.dataframe(capacity_df, use_container_width=True)
450
+
451
+ st.markdown('</div>', unsafe_allow_html=True)
452
+
453
+ except Exception as e:
454
+ st.error(f"Error in production analysis: {e}")
455
+
456
+ # Tab 3: Line Allocation by Day
457
+ with tab3:
458
+ st.markdown('<h2 class="section-header">🏭 Line Allocation by Day Analysis</h2>', unsafe_allow_html=True)
459
+
460
+ try:
461
+ # Load line and production data
462
+ line_df = extract.read_packaging_line_data()
463
+ demand_df = extract.read_released_orders_data(start_date=start_date, end_date=end_date)
464
+
465
+ # Line capacity configuration
466
+ line_capacity = optimization_config.PER_PRODUCT_SPEED
467
+ shift_hours = optimization_config.MAX_HOUR_PER_SHIFT_PER_PERSON
468
+
469
+ st.markdown("### 🏭 Production Line Overview")
470
+
471
+ # Line metrics
472
+ col_line1, col_line2, col_line3, col_line4 = st.columns(4)
473
+
474
+ total_lines = line_df['line_count'].sum()
475
+ line_types = len(line_df)
476
+ max_capacity_line = line_df.loc[line_df['line_count'].idxmax()]
477
+
478
+ with col_line1:
479
+ st.metric("🏭 Total Lines", total_lines)
480
+ with col_line2:
481
+ st.metric("πŸ“‹ Line Types", line_types)
482
+ with col_line3:
483
+ st.metric("πŸ”Ί Max Capacity Line", f"Line {max_capacity_line['id']}")
484
+ with col_line4:
485
+ st.metric("πŸ“Š Max Line Count", max_capacity_line['line_count'])
486
+
487
+ # Daily line allocation analysis
488
+ st.markdown("### πŸ“… Daily Line Allocation Requirements")
489
+
490
+ # Calculate daily requirements per line
491
+ demand_df['Date'] = pd.to_datetime(demand_df['Basic finish date'])
492
+ daily_line_allocation = {}
493
+
494
+ # Group by date and calculate line requirements
495
+ for date, date_group in demand_df.groupby('Date'):
496
+ date_str = date.strftime('%Y-%m-%d')
497
+ daily_line_allocation[date_str] = {}
498
+
499
+ for _, row in date_group.iterrows():
500
+ material = row['Material Number']
501
+ quantity = row['Order quantity (GMEIN)']
502
+
503
+ # Get line assignment
504
+ line_id = optimization_config.KIT_LINE_MATCH_DICT.get(material, 6)
505
+ line_speed = line_capacity.get(line_id, 300)
506
+
507
+ # Calculate required hours
508
+ required_hours = quantity / line_speed if line_speed > 0 else 0
509
+
510
+ if line_id not in daily_line_allocation[date_str]:
511
+ daily_line_allocation[date_str][line_id] = {
512
+ 'required_hours': 0,
513
+ 'materials': [],
514
+ 'total_quantity': 0
515
+ }
516
+
517
+ daily_line_allocation[date_str][line_id]['required_hours'] += required_hours
518
+ daily_line_allocation[date_str][line_id]['materials'].append(material)
519
+ daily_line_allocation[date_str][line_id]['total_quantity'] += quantity
520
+
521
+ # Create allocation visualization data
522
+ allocation_data = []
523
+ for date, lines in daily_line_allocation.items():
524
+ for line_id, data in lines.items():
525
+ line_info = line_df[line_df['id'] == line_id].iloc[0] if len(line_df[line_df['id'] == line_id]) > 0 else None
526
+ available_lines = line_info['line_count'] if line_info is not None else 1
527
+
528
+ # Calculate utilization per line
529
+ utilization_per_line = data['required_hours'] / available_lines if available_lines > 0 else 0
530
+ max_hours_per_line = sum(shift_hours.values()) # Total available hours per day
531
+ utilization_percentage = (utilization_per_line / max_hours_per_line) * 100 if max_hours_per_line > 0 else 0
532
+
533
+ allocation_data.append({
534
+ 'Date': date,
535
+ 'Line': f'Line {line_id}',
536
+ 'Line ID': line_id,
537
+ 'Required Hours': data['required_hours'],
538
+ 'Available Lines': available_lines,
539
+ 'Hours per Line': utilization_per_line,
540
+ 'Utilization %': min(utilization_percentage, 100), # Cap at 100%
541
+ 'Materials Count': len(set(data['materials'])),
542
+ 'Total Quantity': data['total_quantity'],
543
+ 'Max Hours Available': max_hours_per_line * available_lines
544
+ })
545
+
546
+ allocation_df = pd.DataFrame(allocation_data)
547
+
548
+ if not allocation_df.empty:
549
+ # Line utilization visualization
550
+ col_util1, col_util2 = st.columns(2)
551
+
552
+ with col_util1:
553
+ # Daily utilization by line
554
+ fig_util = px.bar(
555
+ allocation_df,
556
+ x='Date',
557
+ y='Utilization %',
558
+ color='Line',
559
+ title='Daily Line Utilization Percentage',
560
+ barmode='group'
561
+ )
562
+ fig_util.add_hline(y=100, line_dash="dash", line_color="red", annotation_text="100% Capacity")
563
+ fig_util.update_layout(yaxis_title="Utilization %", xaxis_title="Date")
564
+ st.plotly_chart(fig_util, use_container_width=True)
565
+
566
+ with col_util2:
567
+ # Required hours by line and date
568
+ fig_hours = px.bar(
569
+ allocation_df,
570
+ x='Date',
571
+ y='Required Hours',
572
+ color='Line',
573
+ title='Required Hours by Line and Date',
574
+ barmode='stack'
575
+ )
576
+ st.plotly_chart(fig_hours, use_container_width=True)
577
+
578
+ # Detailed allocation table
579
+ st.markdown("### πŸ“‹ Detailed Line Allocation")
580
+ st.dataframe(allocation_df, use_container_width=True)
581
+
582
+ # Line capacity analysis
583
+ st.markdown("### πŸ“Š Line Capacity Analysis")
584
+
585
+ # Calculate daily totals
586
+ daily_totals = allocation_df.groupby('Date').agg({
587
+ 'Required Hours': 'sum',
588
+ 'Max Hours Available': 'sum',
589
+ 'Materials Count': 'sum',
590
+ 'Total Quantity': 'sum'
591
+ }).reset_index()
592
+
593
+ daily_totals['Overall Utilization %'] = (daily_totals['Required Hours'] / daily_totals['Max Hours Available']) * 100
594
+ daily_totals['Capacity Status'] = daily_totals['Overall Utilization %'].apply(
595
+ lambda x: '🟒 Normal' if x <= 80 else ('🟑 High' if x <= 100 else 'πŸ”΄ Overload')
596
+ )
597
+
598
+ col_cap1, col_cap2 = st.columns(2)
599
+
600
+ with col_cap1:
601
+ # Overall daily utilization
602
+ fig_overall = px.line(
603
+ daily_totals,
604
+ x='Date',
605
+ y='Overall Utilization %',
606
+ title='Overall Daily Capacity Utilization',
607
+ markers=True
608
+ )
609
+ fig_overall.add_hline(y=80, line_dash="dash", line_color="orange", annotation_text="High Utilization (80%)")
610
+ fig_overall.add_hline(y=100, line_dash="dash", line_color="red", annotation_text="Full Capacity (100%)")
611
+ st.plotly_chart(fig_overall, use_container_width=True)
612
+
613
+ with col_cap2:
614
+ # Capacity status summary
615
+ status_counts = daily_totals['Capacity Status'].value_counts()
616
+ fig_status = px.pie(
617
+ values=status_counts.values,
618
+ names=status_counts.index,
619
+ title='Daily Capacity Status Distribution'
620
+ )
621
+ st.plotly_chart(fig_status, use_container_width=True)
622
+
623
+ # Summary metrics
624
+ col_summary1, col_summary2, col_summary3, col_summary4 = st.columns(4)
625
+
626
+ avg_utilization = daily_totals['Overall Utilization %'].mean()
627
+ max_utilization = daily_totals['Overall Utilization %'].max()
628
+ overload_days = len(daily_totals[daily_totals['Overall Utilization %'] > 100])
629
+
630
+ with col_summary1:
631
+ st.metric("πŸ“Š Avg Utilization", f"{avg_utilization:.1f}%")
632
+ with col_summary2:
633
+ st.metric("πŸ”Ί Peak Utilization", f"{max_utilization:.1f}%")
634
+ with col_summary3:
635
+ st.metric("πŸ”΄ Overload Days", overload_days)
636
+ with col_summary4:
637
+ total_capacity_hours = daily_totals['Max Hours Available'].iloc[0] if len(daily_totals) > 0 else 0
638
+ st.metric("⚑ Daily Capacity", f"{total_capacity_hours:.0f} hrs")
639
+
640
+ except Exception as e:
641
+ st.error(f"Error in line allocation analysis: {e}")
642
+
643
+ # Tab 4: Total Costs & Analysis
644
+ with tab4:
645
+ st.markdown('<h2 class="section-header">πŸ’΅ Total Costs & Analysis</h2>', unsafe_allow_html=True)
646
+
647
+ try:
648
+ # Load all necessary data for comprehensive cost analysis
649
+ employee_df = extract.read_employee_data()
650
+ demand_df = extract.read_released_orders_data(start_date=start_date, end_date=end_date)
651
+ cost_data = optimization_config.COST_LIST_PER_EMP_SHIFT
652
+ shift_hours = optimization_config.MAX_HOUR_PER_SHIFT_PER_PERSON
653
+ line_capacity = optimization_config.PER_PRODUCT_SPEED
654
+
655
+ st.markdown("### πŸ’° Comprehensive Cost Analysis")
656
+
657
+ # Calculate production requirements and costs
658
+ duration = (end_date - start_date).days + 1
659
+ total_demand = demand_df["Order quantity (GMEIN)"].sum()
660
+
661
+ # Estimate labor requirements
662
+ total_production_hours = 0
663
+ daily_requirements = {}
664
+
665
+ demand_df['Date'] = pd.to_datetime(demand_df['Basic finish date'])
666
+
667
+ for date, date_group in demand_df.groupby('Date'):
668
+ date_str = date.strftime('%Y-%m-%d')
669
+ daily_hours = 0
670
+
671
+ for _, row in date_group.iterrows():
672
+ material = row['Material Number']
673
+ quantity = row['Order quantity (GMEIN)']
674
+
675
+ # Get line speed
676
+ line_id = optimization_config.KIT_LINE_MATCH_DICT.get(material, 6)
677
+ line_speed = line_capacity.get(line_id, 300)
678
+
679
+ # Calculate production hours needed
680
+ hours_needed = quantity / line_speed if line_speed > 0 else 0
681
+ daily_hours += hours_needed
682
+
683
+ daily_requirements[date_str] = daily_hours
684
+ total_production_hours += daily_hours
685
+
686
+ # Cost scenario analysis
687
+ st.markdown("### πŸ“Š Cost Scenario Analysis")
688
+
689
+ # Calculate different staffing scenarios
690
+ scenarios = {
691
+ 'Minimum Cost': {'UNICEF Fixed term': 0.5, 'Humanizer': 0.5}, # 50% of each type
692
+ 'Balanced': {'UNICEF Fixed term': 0.6, 'Humanizer': 0.4},
693
+ 'Quality Focus': {'UNICEF Fixed term': 0.8, 'Humanizer': 0.2},
694
+ 'Maximum Capacity': {'UNICEF Fixed term': 1.0, 'Humanizer': 1.0}
695
+ }
696
+
697
+ scenario_results = []
698
+ employee_counts = employee_df['employment_type'].value_counts()
699
+
700
+ for scenario_name, ratios in scenarios.items():
701
+ scenario_cost = 0
702
+ scenario_hours = 0
703
+
704
+ for emp_type, ratio in ratios.items():
705
+ available_staff = employee_counts.get(emp_type, 0)
706
+ used_staff = int(available_staff * ratio)
707
+
708
+ # Calculate cost for each shift type
709
+ if emp_type in cost_data:
710
+ for shift_id, hourly_rate in cost_data[emp_type].items():
711
+ shift_duration = shift_hours.get(shift_id, 0)
712
+ shift_cost = used_staff * hourly_rate * shift_duration
713
+ scenario_cost += shift_cost
714
+ scenario_hours += used_staff * shift_duration
715
+
716
+ # Project for the entire period
717
+ period_cost = scenario_cost * duration
718
+
719
+ scenario_results.append({
720
+ 'Scenario': scenario_name,
721
+ 'Daily Cost ($)': scenario_cost,
722
+ 'Period Cost ($)': period_cost,
723
+ 'Daily Hours': scenario_hours,
724
+ 'Cost per Hour ($)': scenario_cost / scenario_hours if scenario_hours > 0 else 0,
725
+ 'Cost per Unit ($)': period_cost / total_demand if total_demand > 0 else 0
726
+ })
727
+
728
+ scenario_df = pd.DataFrame(scenario_results)
729
+
730
+ # Scenario comparison visualization
731
+ col_scenario1, col_scenario2 = st.columns(2)
732
+
733
+ with col_scenario1:
734
+ fig_scenario_cost = px.bar(
735
+ scenario_df,
736
+ x='Scenario',
737
+ y='Period Cost ($)',
738
+ title='Total Period Cost by Scenario',
739
+ color='Period Cost ($)',
740
+ color_continuous_scale='Reds'
741
+ )
742
+ fig_scenario_cost.update_layout(xaxis_tickangle=-45)
743
+ st.plotly_chart(fig_scenario_cost, use_container_width=True)
744
+
745
+ with col_scenario2:
746
+ fig_scenario_unit = px.bar(
747
+ scenario_df,
748
+ x='Scenario',
749
+ y='Cost per Unit ($)',
750
+ title='Cost per Unit by Scenario',
751
+ color='Cost per Unit ($)',
752
+ color_continuous_scale='Blues'
753
+ )
754
+ fig_scenario_unit.update_layout(xaxis_tickangle=-45)
755
+ st.plotly_chart(fig_scenario_unit, use_container_width=True)
756
+
757
+ # Scenario details table
758
+ st.dataframe(scenario_df, use_container_width=True)
759
+
760
+ # Daily cost breakdown
761
+ st.markdown("### πŸ“… Daily Cost Breakdown")
762
+
763
+ # Calculate daily costs based on requirements
764
+ daily_cost_data = []
765
+ for date_str, required_hours in daily_requirements.items():
766
+
767
+ # Simple allocation: distribute hours across available staff
768
+ total_available_hours = 0
769
+ for emp_type in cost_data.keys():
770
+ available_staff = employee_counts.get(emp_type, 0)
771
+ for shift_id in cost_data[emp_type].keys():
772
+ total_available_hours += available_staff * shift_hours.get(shift_id, 0)
773
+
774
+ if total_available_hours > 0:
775
+ utilization_rate = min(required_hours / total_available_hours, 1.0)
776
+
777
+ daily_cost = 0
778
+ for emp_type in cost_data.keys():
779
+ available_staff = employee_counts.get(emp_type, 0)
780
+ for shift_id, hourly_rate in cost_data[emp_type].items():
781
+ shift_duration = shift_hours.get(shift_id, 0)
782
+ # Allocate proportionally
783
+ used_hours = available_staff * shift_duration * utilization_rate
784
+ daily_cost += used_hours * hourly_rate
785
+
786
+ daily_cost_data.append({
787
+ 'Date': date_str,
788
+ 'Required Hours': required_hours,
789
+ 'Utilization Rate': utilization_rate * 100,
790
+ 'Daily Cost ($)': daily_cost,
791
+ 'Cost per Hour ($)': daily_cost / required_hours if required_hours > 0 else 0
792
+ })
793
+
794
+ if daily_cost_data:
795
+ daily_cost_df = pd.DataFrame(daily_cost_data)
796
+
797
+ col_daily1, col_daily2 = st.columns(2)
798
+
799
+ with col_daily1:
800
+ fig_daily_cost = px.line(
801
+ daily_cost_df,
802
+ x='Date',
803
+ y='Daily Cost ($)',
804
+ title='Daily Labor Cost Trend',
805
+ markers=True
806
+ )
807
+ st.plotly_chart(fig_daily_cost, use_container_width=True)
808
+
809
+ with col_daily2:
810
+ fig_daily_util = px.bar(
811
+ daily_cost_df,
812
+ x='Date',
813
+ y='Utilization Rate',
814
+ title='Daily Labor Utilization Rate (%)',
815
+ color='Utilization Rate',
816
+ color_continuous_scale='RdYlGn'
817
+ )
818
+ st.plotly_chart(fig_daily_util, use_container_width=True)
819
+
820
+ # Daily breakdown table
821
+ st.dataframe(daily_cost_df, use_container_width=True)
822
+
823
+ # Summary metrics
824
+ st.markdown("### πŸ“‹ Cost Summary")
825
+
826
+ col_total1, col_total2, col_total3, col_total4 = st.columns(4)
827
+
828
+ if daily_cost_data:
829
+ total_period_cost = sum(item['Daily Cost ($)'] for item in daily_cost_data)
830
+ avg_daily_cost = total_period_cost / len(daily_cost_data)
831
+ total_required_hours = sum(item['Required Hours'] for item in daily_cost_data)
832
+ avg_cost_per_hour = total_period_cost / total_required_hours if total_required_hours > 0 else 0
833
+ else:
834
+ total_period_cost = avg_daily_cost = total_required_hours = avg_cost_per_hour = 0
835
+
836
+ with col_total1:
837
+ st.metric("πŸ’° Total Period Cost", f"${total_period_cost:,.2f}")
838
+ with col_total2:
839
+ st.metric("πŸ“… Avg Daily Cost", f"${avg_daily_cost:,.2f}")
840
+ with col_total3:
841
+ st.metric("⏰ Total Labor Hours", f"{total_required_hours:,.0f}")
842
+ with col_total4:
843
+ st.metric("πŸ’΅ Avg Cost/Hour", f"${avg_cost_per_hour:.2f}")
844
+
845
+ # ROI and efficiency metrics
846
+ st.markdown('<div class="cost-highlight">', unsafe_allow_html=True)
847
+ st.markdown("#### πŸ“ˆ Efficiency Metrics")
848
+
849
+ col_eff1, col_eff2, col_eff3 = st.columns(3)
850
+
851
+ cost_per_unit = total_period_cost / total_demand if total_demand > 0 else 0
852
+ cost_per_day = total_period_cost / duration if duration > 0 else 0
853
+
854
+ with col_eff1:
855
+ st.metric("πŸ’΅ Cost per Unit", f"${cost_per_unit:.3f}")
856
+ with col_eff2:
857
+ st.metric("πŸ“… Cost per Day", f"${cost_per_day:,.2f}")
858
+ with col_eff3:
859
+ hours_per_unit = total_required_hours / total_demand if total_demand > 0 else 0
860
+ st.metric("⏰ Hours per Unit", f"{hours_per_unit:.3f}")
861
+
862
+ st.markdown('</div>', unsafe_allow_html=True)
863
+
864
+ except Exception as e:
865
+ st.error(f"Error in total cost analysis: {e}")
866
+
867
+ # Footer
868
+ st.markdown("---")
869
+ st.markdown("""
870
+ <div style='text-align: center; color: gray; padding: 1rem;'>
871
+ <small>Enhanced Visualization Reports | Real-time data analysis | Updated: {}</small>
872
+ </div>
873
+ """.format(datetime.now().strftime('%Y-%m-%d %H:%M')), unsafe_allow_html=True)
src/config/optimization_config.py CHANGED
@@ -1,74 +1,187 @@
1
  import pandas as pd
2
  import src.etl.transform as transformed_data
3
- import streamlit_page.page1 as dashboard
4
  import datetime
5
  from datetime import timedelta
6
  import src.etl.extract as extract
7
 
 
 
 
 
 
 
 
8
 
9
  def get_date_span():
10
- try:
11
- start_date = dashboard.start_date
12
- end_date = dashboard.end_date
13
- date_span = list(range(1, (end_date - start_date).days + 2))
14
- print(f"date from user input")
15
- print("date span",date_span)
16
- print("start date",start_date)
17
- print("end date",end_date)
18
- return date_span, start_date, end_date
19
- except Exception as e:
20
- print(f"using default value for date span")
21
- return list(range(1, 6)), datetime(2025, 3, 24), datetime(2025, 3, 29) # Default 7 days
 
 
 
 
 
 
 
 
22
 
23
 
24
  #fetch date from streamlit or default value. The streamlit and default references the demand data (COOIS_Planned_and_Released.csv)
25
  DATE_SPAN, start_date, end_date = get_date_span()
26
 
27
- # COOIS_Released_Prod_Orders.csv
 
 
28
  PRODUCT_LIST = transformed_data.get_released_product_list(start_date, end_date)
29
- print("product list",PRODUCT_LIST)
30
 
31
 
32
  def get_employee_type_list():
 
33
  try:
34
- streamlit_employee_type_list = dashboard.employee_type_list
35
- return streamlit_employee_type_list
36
- except (AttributeError, Exception) as e:
37
- print(f"using default value for employee type list: {e}")
38
- employee_type_list = extract.read_employee_data()
39
- # print("employee type df",employee_type_list)
40
- emp_type_list = employee_type_list["employment_type"].unique()
41
- return emp_type_list
 
 
 
 
 
42
 
43
  EMPLOYEE_TYPE_LIST = get_employee_type_list()
44
  # print("employee type list",EMPLOYEE_TYPE_LIST)
45
 
46
  def get_shift_list():
 
47
  try:
48
- streamlit_shift_list = dashboard.shift_list
49
- return streamlit_shift_list
 
 
 
50
  except Exception as e:
51
- print(f"using default value for shift list")
52
- shift_list = extract.get_shift_info()
53
- shift_list = shift_list["id"].unique()
54
- return shift_list
55
- SHIFT_LIST = get_shift_list()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
56
  # print("shift list",SHIFT_LIST)
57
 
58
 
59
  def get_line_list():
 
60
  try:
61
- streamlit_line_list = dashboard.line_list
62
- return streamlit_line_list
 
 
 
63
  except Exception as e:
64
- print(f"using default value for line list")
65
- line_df = extract.read_packaging_line_data()
66
- line_list = line_df["id"].unique().tolist()
67
- return line_list
 
 
 
68
 
69
  LINE_LIST = get_line_list()
70
  # print("line list",LINE_LIST)
71
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
72
  def get_line_cnt_per_type():
73
  try:
74
  streamlit_line_cnt_per_type = dashboard.line_cnt_per_type
@@ -89,14 +202,15 @@ def get_demand_dictionary():
89
  streamlit_demand_dictionary = dashboard.demand_dictionary
90
  return streamlit_demand_dictionary
91
  except Exception as e:
92
- print(f"using default value for demand dictionary")
93
  # Use released orders instead of planned orders for demand
94
  demand_df = extract.read_released_orders_data(start_date=start_date, end_date=end_date)
95
  demand_dictionary = demand_df.groupby('Material Number')["Order quantity (GMEIN)"].sum().to_dict()
 
96
  return demand_dictionary
97
 
98
  DEMAND_DICTIONARY = get_demand_dictionary()
99
- # print("demand dictionary",DEMAND_DICTIONARY)
100
 
101
  def get_cost_list_per_emp_shift():
102
  try:
@@ -163,8 +277,7 @@ def get_team_requirements(PRODUCT_LIST):
163
  team_req_dict["UNICEF Fixed term"][product] = int(unicef_req) if pd.notna(unicef_req) else 0
164
  else:
165
  print(f"Warning: Product {product} not found in Kits Calculation data, setting requirements to 0")
166
- team_req_dict["Humanizer"][product] = 0
167
- team_req_dict["UNICEF Fixed term"][product] = 0
168
 
169
  return team_req_dict
170
 
@@ -188,19 +301,36 @@ def get_per_product_speed():
188
  return streamlit_per_product_speed
189
  except Exception as e:
190
  print(f"using default value for per product speed")
191
- per_product_speed = extract.read_productivity_data()
192
  return per_product_speed
193
 
194
 
195
- PER_PRODUCT_SPEED = {
196
- 6: {'A': 2200, 'B': 2100, 'C': 2000}, # long
197
- 7: {'A': 1600, 'B': 1550, 'C': 1500}} # mini
198
  # number of products that can be produced per hour per line
199
  #This information is critical and it should not rely on the productivity information
200
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
201
  MAX_PARALLEL_WORKERS = {
202
- 6: 6, # long line can have max 6 workers simultaneously
203
- 7: 4, # short line can have max 4 workers simultaneously
204
  }
205
  # maximum number of workers that can work on a line at the same time
206
 
 
1
  import pandas as pd
2
  import src.etl.transform as transformed_data
 
3
  import datetime
4
  from datetime import timedelta
5
  import src.etl.extract as extract
6
 
7
+ # Re-import all the packages
8
+ import importlib
9
+
10
+ # Reload modules to get latest changes
11
+ importlib.reload(extract)
12
+ importlib.reload(transformed_data) # Uncomment if needed
13
+
14
 
15
  def get_date_span():
16
+ print(f"πŸ”§ FORCING NEW DATE RANGE FOR TESTING")
17
+ # Force use new date range to match user's data
18
+ from datetime import datetime
19
+ return list(range(1, 6)), datetime(2025, 7, 7), datetime(2025, 7, 11) # Force 5 days for user's data
20
+
21
+ # Original logic (commented out for testing)
22
+ # try:
23
+ # start_date = dashboard.start_date
24
+ # end_date = dashboard.end_date
25
+ # date_span = list(range(1, (end_date - start_date).days + 2))
26
+ # print(f"date from user input")
27
+ # print("date span",date_span)
28
+ # print("start date",start_date)
29
+ # print("end date",end_date)
30
+ # return date_span, start_date, end_date
31
+ # except Exception as e:
32
+ # print(f"using default value for date span")
33
+ # # Updated to match the user's data in COOIS_Released_Prod_Orders.csv
34
+ # from datetime import datetime
35
+ # return list(range(1, 6)), datetime(2025, 7, 7), datetime(2025, 7, 11) # Default 5 days
36
 
37
 
38
  #fetch date from streamlit or default value. The streamlit and default references the demand data (COOIS_Planned_and_Released.csv)
39
  DATE_SPAN, start_date, end_date = get_date_span()
40
 
41
+
42
+ print(f"\nπŸ“… DATE RANGE: {start_date} to {end_date}")
43
+ print(f"πŸ“ PRODUCT SOURCE: COOIS_Released_Prod_Orders.csv")
44
  PRODUCT_LIST = transformed_data.get_released_product_list(start_date, end_date)
45
+ print(f"πŸ“¦ PRODUCTS FOUND: {len(PRODUCT_LIST)} products -> {PRODUCT_LIST}")
46
 
47
 
48
  def get_employee_type_list():
49
+ """Get employee type list - try from streamlit session state first, then from data files"""
50
  try:
51
+ # Try to get from streamlit session state (from Dataset Metadata page)
52
+ import streamlit as st
53
+ if hasattr(st, 'session_state') and 'selected_employee_types' in st.session_state:
54
+ print(f"Using employee types from Dataset Metadata page: {st.session_state.selected_employee_types}")
55
+ return st.session_state.selected_employee_types
56
+ except Exception as e:
57
+ print(f"Could not get employee types from streamlit session: {e}")
58
+
59
+ # Default: load from data files
60
+ print(f"Loading employee type list from data files")
61
+ employee_type_list = extract.read_employee_data()
62
+ emp_type_list = employee_type_list["employment_type"].unique()
63
+ return emp_type_list
64
 
65
  EMPLOYEE_TYPE_LIST = get_employee_type_list()
66
  # print("employee type list",EMPLOYEE_TYPE_LIST)
67
 
68
  def get_shift_list():
69
+ """Get shift list - try from streamlit session state first, then from data files"""
70
  try:
71
+ # Try to get from streamlit session state (from Dataset Metadata page)
72
+ import streamlit as st
73
+ if hasattr(st, 'session_state') and 'selected_shifts' in st.session_state:
74
+ print(f"Using shifts from Dataset Metadata page: {st.session_state.selected_shifts}")
75
+ return st.session_state.selected_shifts
76
  except Exception as e:
77
+ print(f"Could not get shifts from streamlit session: {e}")
78
+
79
+ # Default: load from data files
80
+ print(f"Loading shift list from data files")
81
+ shift_list = extract.get_shift_info()
82
+ shift_list = shift_list["id"].unique()
83
+ return shift_list
84
+
85
+ # Evening shift activation mode - define early to avoid circular dependency
86
+ # Options:
87
+ # "normal" - Only use regular shift (1) and overtime shift (2)
88
+ # "activate_evening" - Allow evening shift (3) when demand is too high or cost-effective
89
+ # "always_available" - Evening shift always available as option
90
+ EVENING_SHIFT_MODE = "normal" # Default: only regular + overtime
91
+
92
+ # Evening shift activation threshold
93
+ # If demand cannot be met with regular + overtime, suggest evening shift activation
94
+ EVENING_SHIFT_DEMAND_THRESHOLD = 0.9 # Activate if regular+overtime capacity < 90% of demand
95
+
96
+ def get_active_shift_list():
97
+ """
98
+ Get the list of active shifts based on EVENING_SHIFT_MODE setting.
99
+ """
100
+ all_shifts = get_shift_list()
101
+
102
+ if EVENING_SHIFT_MODE == "normal":
103
+ # Only regular (1) and overtime (2) shifts
104
+ active_shifts = [s for s in all_shifts if s in [1, 2]]
105
+ print(f"[SHIFT MODE] Normal mode: Using shifts {active_shifts} (Regular + Overtime only)")
106
+
107
+ elif EVENING_SHIFT_MODE == "activate_evening":
108
+ # All shifts including evening (3)
109
+ active_shifts = list(all_shifts)
110
+ print(f"[SHIFT MODE] Evening activated: Using all shifts {active_shifts}")
111
+
112
+ elif EVENING_SHIFT_MODE == "always_available":
113
+ # All shifts always available
114
+ active_shifts = list(all_shifts)
115
+ print(f"[SHIFT MODE] Always available: Using all shifts {active_shifts}")
116
+
117
+ else:
118
+ # Default to normal mode
119
+ active_shifts = [s for s in all_shifts if s in [1, 2]]
120
+ print(f"[SHIFT MODE] Unknown mode '{EVENING_SHIFT_MODE}', defaulting to normal: {active_shifts}")
121
+
122
+ return active_shifts
123
+
124
+ SHIFT_LIST = get_active_shift_list()
125
  # print("shift list",SHIFT_LIST)
126
 
127
 
128
  def get_line_list():
129
+ """Get line list - try from streamlit session state first, then from data files"""
130
  try:
131
+ # Try to get from streamlit session state (from Dataset Metadata page)
132
+ import streamlit as st
133
+ if hasattr(st, 'session_state') and 'selected_lines' in st.session_state:
134
+ print(f"Using lines from Dataset Metadata page: {st.session_state.selected_lines}")
135
+ return st.session_state.selected_lines
136
  except Exception as e:
137
+ print(f"Could not get lines from streamlit session: {e}")
138
+
139
+ # Default: load from data files
140
+ print(f"Loading line list from data files")
141
+ line_df = extract.read_packaging_line_data()
142
+ line_list = line_df["id"].unique().tolist()
143
+ return line_list
144
 
145
  LINE_LIST = get_line_list()
146
  # print("line list",LINE_LIST)
147
 
148
+
149
+ def get_kit_line_match():
150
+ kit_line_match = extract.read_kit_line_match_data()
151
+ kit_line_match_dict = kit_line_match.set_index("kit_name")["line_type"].to_dict()
152
+
153
+ # Create line name to ID mapping
154
+ line_name_to_id = {
155
+ "long line": 6,
156
+ "mini load": 7,
157
+ "Long_line": 6, # Alternative naming
158
+ "Mini_load": 7, # Alternative naming
159
+ }
160
+
161
+ # Convert string line names to numeric IDs
162
+ converted_dict = {}
163
+ for kit, line_name in kit_line_match_dict.items():
164
+ if isinstance(line_name, str) and line_name.strip():
165
+ # Convert string names to numeric IDs
166
+ line_id = line_name_to_id.get(line_name.strip(), None)
167
+ if line_id is not None:
168
+ converted_dict[kit] = line_id
169
+ else:
170
+ print(f"Warning: Unknown line type '{line_name}' for kit {kit}")
171
+ # Default to long line if unknown
172
+ converted_dict[kit] = 6
173
+ elif isinstance(line_name, (int, float)) and not pd.isna(line_name):
174
+ # Already numeric
175
+ converted_dict[kit] = int(line_name)
176
+ else:
177
+ # Missing or empty line type - default to long line
178
+ converted_dict[kit] = 6
179
+
180
+ return converted_dict
181
+
182
+ KIT_LINE_MATCH_DICT = get_kit_line_match()
183
+
184
+
185
  def get_line_cnt_per_type():
186
  try:
187
  streamlit_line_cnt_per_type = dashboard.line_cnt_per_type
 
202
  streamlit_demand_dictionary = dashboard.demand_dictionary
203
  return streamlit_demand_dictionary
204
  except Exception as e:
205
+
206
  # Use released orders instead of planned orders for demand
207
  demand_df = extract.read_released_orders_data(start_date=start_date, end_date=end_date)
208
  demand_dictionary = demand_df.groupby('Material Number')["Order quantity (GMEIN)"].sum().to_dict()
209
+ print(f"πŸ“ˆ DEMAND DATA: {len(demand_dictionary)} products with total demand {sum(demand_dictionary.values())}")
210
  return demand_dictionary
211
 
212
  DEMAND_DICTIONARY = get_demand_dictionary()
213
+ print(f"🎯 FINAL DEMAND: {DEMAND_DICTIONARY}")
214
 
215
  def get_cost_list_per_emp_shift():
216
  try:
 
277
  team_req_dict["UNICEF Fixed term"][product] = int(unicef_req) if pd.notna(unicef_req) else 0
278
  else:
279
  print(f"Warning: Product {product} not found in Kits Calculation data, setting requirements to 0")
280
+
 
281
 
282
  return team_req_dict
283
 
 
301
  return streamlit_per_product_speed
302
  except Exception as e:
303
  print(f"using default value for per product speed")
304
+ per_product_speed = extract.read_package_speed_data()
305
  return per_product_speed
306
 
307
 
308
+ # Get per product speed - will use actual product names from PRODUCT_LIST
309
+ PER_PRODUCT_SPEED = get_per_product_speed()
 
310
  # number of products that can be produced per hour per line
311
  #This information is critical and it should not rely on the productivity information
312
 
313
+ # ---- Kit Hierarchy for Production Ordering ----
314
+ def get_kit_hierarchy_data():
315
+ try:
316
+ # Try to get from streamlit first (future extension)
317
+ # streamlit_hierarchy = dashboard.kit_hierarchy_data
318
+ # return streamlit_hierarchy
319
+ pass
320
+ except Exception as e:
321
+ print(f"Using default hierarchy data from extract: {e}")
322
+
323
+ # Get hierarchy data from extract functions
324
+ kit_levels, dependencies, priority_order = extract.get_production_order_data()
325
+
326
+ return kit_levels, dependencies, priority_order
327
+
328
+ KIT_LEVELS, KIT_DEPENDENCIES, PRODUCTION_PRIORITY_ORDER = get_kit_hierarchy_data()
329
+ print(f"Kit Hierarchy loaded: {len(KIT_LEVELS)} kits, Priority order: {len(PRODUCTION_PRIORITY_ORDER)} items")
330
+
331
  MAX_PARALLEL_WORKERS = {
332
+ 6: 15, # long line can have max 15 workers simultaneously
333
+ 7: 15, # mini load can have max 15 workers simultaneously
334
  }
335
  # maximum number of workers that can work on a line at the same time
336
 
src/etl/extract.py CHANGED
@@ -1,7 +1,8 @@
1
  import pandas as pd
2
  import datetime
3
  from datetime import date, timedelta
4
-
 
5
  START_DATE = pd.Timestamp(2025, 7, 7)
6
  END_DATE = pd.Timestamp(2025, 7, 11)
7
 
@@ -22,6 +23,11 @@ def read_demand_data(
22
 
23
  return df
24
 
 
 
 
 
 
25
 
26
  def read_employee_data(
27
  path="data/real_data_excel/converted_csv/WH_Workforce_Hourly_Pay_Scale_processed.csv",
@@ -94,8 +100,70 @@ def read_released_orders_data(
94
  return df
95
 
96
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
97
  if __name__ == "__main__":
98
  employee_data = read_employee_data()
99
  print("employee data")
100
  print(employee_data)
 
101
 
 
1
  import pandas as pd
2
  import datetime
3
  from datetime import date, timedelta
4
+ import json
5
+ import os
6
  START_DATE = pd.Timestamp(2025, 7, 7)
7
  END_DATE = pd.Timestamp(2025, 7, 11)
8
 
 
23
 
24
  return df
25
 
26
+ def read_kit_line_match_data(
27
+ path="data/real_data_excel/converted_csv/Kit_Composition_and_relation_cleaned_with_line_type.csv",
28
+ ) -> pd.DataFrame:
29
+ return pd.read_csv(path)
30
+
31
 
32
  def read_employee_data(
33
  path="data/real_data_excel/converted_csv/WH_Workforce_Hourly_Pay_Scale_processed.csv",
 
100
  return df
101
 
102
 
103
+ def read_package_speed_data(
104
+ path="data/real_data_excel/converted_csv/Kits__Calculation.csv",
105
+ ):
106
+ df = pd.read_csv(path, usecols=["Kit", "Kit per day","Paid work hours per day"])
107
+ df['kits_per_hour'] = df['Kit per day']/df['Paid work hours per day']
108
+ speeds_per_hour = dict(zip(df["Kit"], df["kits_per_hour"]))
109
+ return speeds_per_hour
110
+
111
+
112
+
113
+ def get_production_order_data():
114
+ """
115
+ Extract production order information from hierarchy.
116
+ Returns:
117
+ - kit_levels: {kit_id: level} where level 0=prepack, 1=subkit, 2=master
118
+ - dependencies: {kit_id: [dependency_list]}
119
+ - priority_order: [kit_ids] sorted by production priority
120
+ """
121
+ path = "data/hierarchy_exports/kit_hierarchy.json"
122
+ with open(path, 'r', encoding='utf-8') as f:
123
+ hierarchy = json.load(f)
124
+
125
+ kit_levels = {}
126
+ dependencies = {}
127
+
128
+ # Process hierarchy to extract levels and dependencies
129
+ for master_id, master_data in hierarchy.items():
130
+ # Master kits are level 2
131
+ kit_levels[master_id] = 2
132
+ dependencies[master_id] = master_data.get('dependencies', [])
133
+
134
+ # Process subkits (level 1)
135
+ for subkit_id, subkit_data in master_data.get('subkits', {}).items():
136
+ kit_levels[subkit_id] = 1
137
+ dependencies[subkit_id] = subkit_data.get('dependencies', [])
138
+
139
+ # Process prepacks (level 0)
140
+ for prepack_id in subkit_data.get('prepacks', []):
141
+ if prepack_id not in kit_levels: # Avoid overwriting if already exists
142
+ kit_levels[prepack_id] = 0
143
+ dependencies[prepack_id] = []
144
+
145
+ # Create priority order: prepacks first, then subkits, then masters
146
+ priority_order = []
147
+
148
+ # Level 0: Prepacks (highest priority)
149
+ prepacks = [kit for kit, level in kit_levels.items() if level == 0]
150
+ priority_order.extend(sorted(prepacks))
151
+
152
+ # Level 1: Subkits (medium priority)
153
+ subkits = [kit for kit, level in kit_levels.items() if level == 1]
154
+ priority_order.extend(sorted(subkits))
155
+
156
+ # Level 2: Masters (lowest priority)
157
+ masters = [kit for kit, level in kit_levels.items() if level == 2]
158
+ priority_order.extend(sorted(masters))
159
+
160
+ return kit_levels, dependencies, priority_order
161
+
162
+
163
+
164
  if __name__ == "__main__":
165
  employee_data = read_employee_data()
166
  print("employee data")
167
  print(employee_data)
168
+ print("line speed data",read_package_speed_data())
169
 
src/etl/hierarchy_parser.py ADDED
@@ -0,0 +1,112 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """
3
+ Kit Hierarchy Parser - Converts CSV hierarchy data to optimized formats
4
+
5
+ This module provides functions to:
6
+ 1. Parse Kit_Composition_and_relation.csv
7
+ 2. Generate JSON hierarchy structure
8
+ 3. Create production order CSV
9
+ 4. Build DAG for optimization constraints
10
+ """
11
+
12
+ import pandas as pd
13
+ import json
14
+ from typing import Dict, List, Tuple, Set
15
+ from collections import defaultdict, deque
16
+
17
+
18
+ class KitHierarchyParser:
19
+ """
20
+ Parses kit composition data and creates hierarchy structures
21
+ for production order optimization.
22
+ """
23
+
24
+ def __init__(self, csv_path: str = "data/real_data_excel/converted_csv/Kit_Composition_and_relation.csv"):
25
+ self.csv_path = csv_path
26
+ self.df = None
27
+ self.hierarchy_json = {}
28
+ self.production_order_csv = []
29
+ self.dependency_graph = {'nodes': set(), 'edges': set()}
30
+
31
+ def load_data(self):
32
+ """Load and clean the CSV data"""
33
+ self.df = pd.read_csv(self.csv_path)
34
+ print(f"Loaded {len(self.df)} rows from {self.csv_path}")
35
+
36
+ def parse_hierarchy(self) -> Dict:
37
+ """
38
+ Parse the hierarchy from CSV into JSON structure
39
+ Returns: Nested dictionary representing the hierarchy
40
+ """
41
+ if self.df is None:
42
+ self.load_data()
43
+
44
+ # Get unique relationships
45
+ relationships = self.df[['Master Kit', 'Master Kit Description',
46
+ 'Sub kit', 'Sub kit description',
47
+ 'Prepack', 'Prepack Description']].drop_duplicates()
48
+
49
+ hierarchy = defaultdict(lambda: {
50
+ 'name': '',
51
+ 'type': 'master',
52
+ 'subkits': defaultdict(lambda: {
53
+ 'name': '',
54
+ 'type': 'subkit',
55
+ 'prepacks': [],
56
+ 'dependencies': []
57
+ }),
58
+ 'dependencies': []
59
+ })
60
+
61
+ for _, row in relationships.iterrows():
62
+ master_id = row['Master Kit']
63
+ master_desc = row['Master Kit Description']
64
+ subkit_id = row['Sub kit']
65
+ subkit_desc = row['Sub kit description']
66
+ prepack_id = row['Prepack']
67
+ prepack_desc = row['Prepack Description']
68
+
69
+ if pd.notna(master_id):
70
+ # Set master info
71
+ hierarchy[master_id]['name'] = master_desc if pd.notna(master_desc) else ''
72
+
73
+ if pd.notna(subkit_id):
74
+ # Set subkit info
75
+ hierarchy[master_id]['subkits'][subkit_id]['name'] = subkit_desc if pd.notna(subkit_desc) else ''
76
+
77
+ # Add subkit to master dependencies
78
+ if subkit_id not in hierarchy[master_id]['dependencies']:
79
+ hierarchy[master_id]['dependencies'].append(subkit_id)
80
+
81
+ if pd.notna(prepack_id):
82
+ # Set prepack info
83
+ if prepack_id not in hierarchy[master_id]['subkits'][subkit_id]['prepacks']:
84
+ hierarchy[master_id]['subkits'][subkit_id]['prepacks'].append(prepack_id)
85
+
86
+ # Add prepack to subkit dependencies
87
+ if prepack_id not in hierarchy[master_id]['subkits'][subkit_id]['dependencies']:
88
+ hierarchy[master_id]['subkits'][subkit_id]['dependencies'].append(prepack_id)
89
+
90
+ # Convert defaultdict to regular dict for JSON serialization
91
+ self.hierarchy_json = json.loads(json.dumps(hierarchy, default=dict))
92
+ return self.hierarchy_json
93
+
94
+
95
+
96
+ def main():
97
+ """Demo the hierarchy parser"""
98
+ parser = KitHierarchyParser()
99
+
100
+ print("πŸ”„ Parsing kit hierarchy...")
101
+ hierarchy = parser.parse_hierarchy()
102
+
103
+ #export to json
104
+ with open('data/hierarchy_exports/kit_hierarchy.json', 'w') as f:
105
+ json.dump(hierarchy, f,indent=4)
106
+
107
+ print(f"πŸ“Š Found {len(hierarchy)} master kits")
108
+
109
+
110
+
111
+ if __name__ == "__main__":
112
+ main()
src/models/optimizer_new_aug14.py CHANGED
@@ -28,31 +28,32 @@ from src.config.optimization_config import (
28
  DAILY_WEEKLY_SCHEDULE, # 'daily' or 'weekly' (μ—¬κΈ°μ„  weekly둜 λͺ¨λΈλ§)
29
  FIXED_STAFF_CONSTRAINT_MODE, # not used in fixed-team model (λ™μ‹œ νˆ¬μž…μ΄λΌ 무의미)
30
  TEAM_REQ_PER_PRODUCT, # {emp_type: {product: team_size}} from Kits_Calculation.csv
 
 
 
 
 
 
 
31
  )
32
 
33
  # -----------------------------------------
34
  # μΆ”κ°€ νŒŒλΌλ―Έν„° μ„€μ • (config에 μ—†λ˜ 것듀) - TODO: μ‹€μ œ κ°’ μ±„μš°μ„Έμš”
35
  # -----------------------------------------
36
 
37
- # 1) μ œν’ˆλ³„ νŒ€ ꡬ성(λ™μ‹œ νˆ¬μž… 인원) β€” 이제 Kits_Calculation.csvμ—μ„œ μ‹€μ œ 데이터 μ‚¬μš©
38
- # TEAM_REQ_PER_PRODUCTλŠ” config νŒŒμΌμ—μ„œ import됨
39
- # ν˜•νƒœ: {'UNICEF Fixed term': {'product':count,...}, 'Humanizer': {'product':count,...}}
40
 
41
- # 2) μ œν’ˆβ†’λΌμΈνƒ€μž…(숫자 id) λ§€ν•‘ β€” λ°˜λ“œμ‹œ 6(long) λ˜λŠ” 7(short)둜 μ§€μ •
42
- # 예: {'A':6,'B':7,...}
43
- PRODUCT_LINE_TYPE_ID = {
44
- # TODO: μ•„λž˜λŠ” μž„μ‹œλ‘œ μ „λΆ€ long(6)으둜 λ‘ . μ œν’ˆλ³„λ‘œ 6/7을 μ§€μ •ν•΄μ•Ό 함.
45
- p: LINE_LIST[0] for p in PRODUCT_LIST
46
- }
47
 
48
- # 3) (선택) νŠΉμ • μ œν’ˆμ΄ νŠΉμ • λ‚ μ§œμ— 생산 λΆˆκ°€ν•˜λ©΄ 0으둜 κΊΌμ£Όμ„Έμš”.
49
  ACTIVE = {t: {p: 1 for p in PRODUCT_LIST} for t in DATE_SPAN}
50
  # 예: ACTIVE[2]['C'] = 0
51
 
52
 
53
  def build_lines():
54
- """LINE_CNT_PER_TYPEλ₯Ό 기반으둜 물리적 라인 μΈμŠ€ν„΄μŠ€ λͺ©λ‘ L 생성.
55
- L μš”μ†ŒλŠ” (line_type_id, idx) νŠœν”Œ. 예: (6,1), (6,2), (7,1), ...
56
  """
57
  L = []
58
  for lt in LINE_LIST: # lt: 6 or 7
@@ -61,37 +62,123 @@ def build_lines():
61
  L.append((lt, i))
62
  return L
63
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
64
 
65
  def solve_fixed_team_weekly():
66
  # --- Sets ---
67
  D = list(DATE_SPAN)
68
  S = sorted(list(SHIFT_LIST))
69
  E = list(EMPLOYEE_TYPE_LIST) # e.g., ['UNICEF Fixed term','Humanizer']
70
- P = list(PRODUCT_LIST)
 
 
 
 
 
 
 
71
  L = build_lines()
72
 
73
  # --- Short aliases for parameters ---
74
  Hmax_s = dict(MAX_HOUR_PER_SHIFT_PER_PERSON) # per-shift hours
75
- Hmax_daily = MAX_HOUR_PER_PERSON_PER_DAY
76
- cap_line = dict(PER_PRODUCT_SPEED) # {6:cap, 7:cap}
77
  max_workers_line = dict(MAX_PARALLEL_WORKERS) # per line type
78
  N_day = MAX_EMPLOYEE_PER_TYPE_ON_DAY # {emp_type:{t:headcount}}
79
  cost = COST_LIST_PER_EMP_SHIFT # {emp_type:{shift:cost}}
80
  d_week = DEMAND_DICTIONARY # {product: demand over period}
81
 
82
  # --- Feasibility quick checks ---
83
- # 1) νŒ€ ꡬ성 총원이 라인 λ™μ‹œμž‘μ—… ν•œκ³„λ³΄λ‹€ 큰 μ œν’ˆ-λΌμΈνƒ€μž… μ‘°ν•© 사전 차단
 
84
  for p in P:
85
  req_total = sum(TEAM_REQ_PER_PRODUCT[e][p] for e in E)
86
- lt = PRODUCT_LINE_TYPE_ID[p]
 
 
87
  if req_total > max_workers_line.get(lt, 1e9):
88
  print(f"[WARN] Product {p}: team size {req_total} > MAX_PARALLEL_WORKERS[{lt}] "
89
- f"= {max_workers_line.get(lt)}. ν•΄λ‹Ή λΌμΈνƒ€μž…μ— 배치 λΆˆκ°€ β†’ μžλ™ 차단.")
90
-
91
- # 2) DAILY_WEEKLY_SCHEDULE κ²½κ³  (ν˜„μž¬ λͺ¨λΈμ€ μ£Όκ°„ μˆ˜μš”λ§Œ κ°•μ œ)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
92
  if DAILY_WEEKLY_SCHEDULE.lower() == "daily":
93
- print("[INFO] DAILY_WEEKLY_SCHEDULE='daily' μ΄μ§€λ§Œ, λ³Έ λͺ¨λΈμ€ μ£Όκ°„ μˆ˜μš”(weekly)만 κ°•μ œν•©λ‹ˆλ‹€. "
94
- "일/일 μˆ˜μš” λΆ„ν•΄κ°€ ν•„μš”ν•˜λ©΄ μ•Œλ €μ£Όμ„Έμš”.")
95
 
96
  # --- Solver ---
97
  solver = pywraplp.Solver.CreateSolver('CBC')
@@ -100,8 +187,8 @@ def solve_fixed_team_weekly():
100
  INF = solver.infinity()
101
 
102
  # --- Variables ---
103
- # Z[p,ell,s,t] ∈ {0,1}: ν•΄λ‹Ή (라인,κ΅λŒ€,λ‚ μ§œ)에 μ œν’ˆ pλ₯Ό 돌리면 1
104
- Z, T, U = {}, {}, {} # T: κ°€λ™μ‹œκ°„(hours), U: μƒμ‚°μˆ˜λŸ‰(units)
105
  for p in P:
106
  for ell in L: # ell = (line_type_id, idx)
107
  for s in S:
@@ -110,12 +197,22 @@ def solve_fixed_team_weekly():
110
  T[p, ell, s, t] = solver.NumVar(0, Hmax_s[s], f"T_{p}_{ell[0]}_{ell[1]}_s{s}_d{t}")
111
  U[p, ell, s, t] = solver.NumVar(0, INF, f"U_{p}_{ell[0]}_{ell[1]}_s{s}_d{t}")
112
 
113
- # --- Objective: total labor cost ---
 
114
  total_cost = solver.Sum(
115
  cost[e][s] * TEAM_REQ_PER_PRODUCT[e][p] * T[p, ell, s, t]
116
  for e in E for s in S for p in P for ell in L for t in D
117
  )
118
- solver.Minimize(total_cost)
 
 
 
 
 
 
 
 
 
119
 
120
  # --- Constraints ---
121
 
@@ -135,7 +232,7 @@ def solve_fixed_team_weekly():
135
 
136
  # 3) Product-line type compatibility + (optional) activity by day
137
  for p in P:
138
- req_lt = PRODUCT_LINE_TYPE_ID[p] # must be 6 or 7
139
  req_total = sum(TEAM_REQ_PER_PRODUCT[e][p] for e in E)
140
  for ell in L:
141
  allowed = (ell[0] == req_lt) and (req_total <= max_workers_line.get(ell[0], 1e9))
@@ -146,14 +243,25 @@ def solve_fixed_team_weekly():
146
  solver.Add(T[p, ell, s, t] == 0)
147
  solver.Add(U[p, ell, s, t] == 0)
148
 
149
- # 4) Line throughput: U ≀ cap(line_type) * T
150
  for p in P:
151
  for ell in L:
152
  for s in S:
153
  for t in D:
154
- solver.Add(
155
- U[p, ell, s, t] <= PER_PRODUCT_SPEED[ell[0]] * T[p, ell, s, t]
156
- )
 
 
 
 
 
 
 
 
 
 
 
157
 
158
  # 5) Per-shift staffing capacity by type: sum(req*hours) ≀ shift_hours * headcount
159
  for e in E:
@@ -180,13 +288,42 @@ def solve_fixed_team_weekly():
180
  <=
181
  solver.Sum(TEAM_REQ_PER_PRODUCT[e][p] * T[p, ell, 1, t] for p in P for ell in L)
182
  )
183
- # (ν•„μš” μ‹œ evening(3)도 usual(1) λ’€λ‘œ: sum(...)_s=3 ≀ sum(...)_s=1)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
184
 
185
  # --- Solve ---
186
  status = solver.Solve()
187
  if status != pywraplp.Solver.OPTIMAL:
188
  print(f"No optimal solution. Status: {status} (2=INFEASIBLE)")
189
- # 디버깅 힌트:
190
  # solver.EnableOutput()
191
  # solver.ExportModelAsLpFile("model.lp")
192
  return None
 
28
  DAILY_WEEKLY_SCHEDULE, # 'daily' or 'weekly' (μ—¬κΈ°μ„  weekly둜 λͺ¨λΈλ§)
29
  FIXED_STAFF_CONSTRAINT_MODE, # not used in fixed-team model (λ™μ‹œ νˆ¬μž…μ΄λΌ 무의미)
30
  TEAM_REQ_PER_PRODUCT, # {emp_type: {product: team_size}} from Kits_Calculation.csv
31
+ KIT_LINE_MATCH_DICT,
32
+ EVENING_SHIFT_MODE,
33
+ EVENING_SHIFT_DEMAND_THRESHOLD,
34
+ # Hierarchy variables for production ordering
35
+ KIT_LEVELS, # {kit_id: level} where 0=prepack, 1=subkit, 2=master
36
+ KIT_DEPENDENCIES, # {kit_id: [dependency_list]}
37
+ PRODUCTION_PRIORITY_ORDER, # [kit_ids] sorted by production priority
38
  )
39
 
40
  # -----------------------------------------
41
  # μΆ”κ°€ νŒŒλΌλ―Έν„° μ„€μ • (config에 μ—†λ˜ 것듀) - TODO: μ‹€μ œ κ°’ μ±„μš°μ„Έμš”
42
  # -----------------------------------------
43
 
 
 
 
44
 
45
+ # 2) kit_line_match
46
+ KIT_LINE_MATCH_DICT
47
+ print("KIT_LINE_MATCH_DICT",KIT_LINE_MATCH_DICT)
 
 
 
48
 
49
+ # 3) If specific product is not produced on specific date, set it to 0
50
  ACTIVE = {t: {p: 1 for p in PRODUCT_LIST} for t in DATE_SPAN}
51
  # 예: ACTIVE[2]['C'] = 0
52
 
53
 
54
  def build_lines():
55
+ """List of line instances.
56
+ L elements are (line_type_id, idx) tuples. e.g., (6,1), (6,2), (7,1), ...
57
  """
58
  L = []
59
  for lt in LINE_LIST: # lt: 6 or 7
 
62
  L.append((lt, i))
63
  return L
64
 
65
+ L=build_lines()
66
+ print("L",L)
67
+ print("PER_PRODUCT_SPEED",PER_PRODUCT_SPEED)
68
+
69
+ def sort_products_by_hierarchy(product_list):
70
+ """
71
+ Sort products by hierarchy levels and dependencies.
72
+ Returns products in optimal production order: prepacks β†’ subkits β†’ masters
73
+ """
74
+ # Filter products that are in our production list and have hierarchy data
75
+ products_with_hierarchy = [p for p in product_list if p in KIT_LEVELS]
76
+ products_without_hierarchy = [p for p in product_list if p not in KIT_LEVELS]
77
+
78
+ if products_without_hierarchy:
79
+ print(f"[HIERARCHY] Products without hierarchy data: {products_without_hierarchy}")
80
+
81
+ # Sort by hierarchy level (0=prepack, 1=subkit, 2=master) then by product ID
82
+ sorted_products = sorted(products_with_hierarchy,
83
+ key=lambda p: (KIT_LEVELS.get(p, 999), p))
84
+
85
+ # Add products without hierarchy at the end
86
+ sorted_products.extend(sorted(products_without_hierarchy))
87
+
88
+ print(f"[HIERARCHY] Production order: {len(sorted_products)} products")
89
+ for i, p in enumerate(sorted_products[:10]): # Show first 10
90
+ level = KIT_LEVELS.get(p, "unknown")
91
+ level_name = {0: "prepack", 1: "subkit", 2: "master"}.get(level, "unknown")
92
+ deps = KIT_DEPENDENCIES.get(p, [])
93
+ print(f" {i+1}. {p} (level {level}={level_name}, deps: {len(deps)})")
94
+
95
+ if len(sorted_products) > 10:
96
+ print(f" ... and {len(sorted_products) - 10} more products")
97
+
98
+ return sorted_products
99
+
100
+ def get_dependency_timing_weight(product):
101
+ """
102
+ Calculate timing weight based on hierarchy level.
103
+ Lower levels (prepacks) should be produced earlier.
104
+ """
105
+ level = KIT_LEVELS.get(product, 2) # Default to master level
106
+ # Weight: prepack=0.1, subkit=0.5, master=1.0
107
+ weights = {0: 0.1, 1: 0.5, 2: 1.0}
108
+ return weights.get(level, 1.0)
109
 
110
  def solve_fixed_team_weekly():
111
  # --- Sets ---
112
  D = list(DATE_SPAN)
113
  S = sorted(list(SHIFT_LIST))
114
  E = list(EMPLOYEE_TYPE_LIST) # e.g., ['UNICEF Fixed term','Humanizer']
115
+
116
+ # *** HIERARCHY SORTING: Sort products by production priority ***
117
+ print("\n" + "="*60)
118
+ print("πŸ”— APPLYING HIERARCHY-BASED PRODUCTION ORDERING")
119
+ print("="*60)
120
+ P_sorted = sort_products_by_hierarchy(list(PRODUCT_LIST))
121
+ P = P_sorted # Use sorted product list
122
+
123
  L = build_lines()
124
 
125
  # --- Short aliases for parameters ---
126
  Hmax_s = dict(MAX_HOUR_PER_SHIFT_PER_PERSON) # per-shift hours
127
+ Hmax_daily = MAX_HOUR_PER_PERSON_PER_DAY # {6:cap, 7:cap}
 
128
  max_workers_line = dict(MAX_PARALLEL_WORKERS) # per line type
129
  N_day = MAX_EMPLOYEE_PER_TYPE_ON_DAY # {emp_type:{t:headcount}}
130
  cost = COST_LIST_PER_EMP_SHIFT # {emp_type:{shift:cost}}
131
  d_week = DEMAND_DICTIONARY # {product: demand over period}
132
 
133
  # --- Feasibility quick checks ---
134
+
135
+ # 1) If team size is greater than max_workers_line, block the product-line type combination
136
  for p in P:
137
  req_total = sum(TEAM_REQ_PER_PRODUCT[e][p] for e in E)
138
+ lt = KIT_LINE_MATCH_DICT.get(p, 6) # Default to long line (6) if not found
139
+ if p not in KIT_LINE_MATCH_DICT:
140
+ print(f"[WARN] Product {p}: No line type mapping found, defaulting to long line (6)")
141
  if req_total > max_workers_line.get(lt, 1e9):
142
  print(f"[WARN] Product {p}: team size {req_total} > MAX_PARALLEL_WORKERS[{lt}] "
143
+ f"= {max_workers_line.get(lt)}. Blocked.")
144
+
145
+ # 2) Check if demand can be met without evening shift (only if in normal mode)
146
+ if EVENING_SHIFT_MODE == "normal":
147
+ total_demand = sum(DEMAND_DICTIONARY.get(p, 0) for p in P)
148
+
149
+ # Calculate maximum capacity with regular + overtime shifts only
150
+ regular_overtime_shifts = [s for s in S if s in [1, 2]] # Only shifts 1, 2
151
+ max_capacity = 0
152
+
153
+ for p in P:
154
+ if p in PER_PRODUCT_SPEED:
155
+ product_speed = PER_PRODUCT_SPEED[p] # units per hour
156
+ # Calculate max hours available for this product across all lines and shifts
157
+ max_hours_per_product = 0
158
+ for ell in L:
159
+ for s in regular_overtime_shifts:
160
+ for t in D:
161
+ max_hours_per_product += Hmax_s[s]
162
+
163
+ max_capacity += product_speed * max_hours_per_product
164
+
165
+ capacity_ratio = max_capacity / total_demand if total_demand > 0 else float('inf')
166
+
167
+ print(f"[CAPACITY CHECK] Total demand: {total_demand}")
168
+ print(f"[CAPACITY CHECK] Max capacity (Regular + Overtime): {max_capacity:.1f}")
169
+ print(f"[CAPACITY CHECK] Capacity ratio: {capacity_ratio:.2f}")
170
+
171
+ if capacity_ratio < EVENING_SHIFT_DEMAND_THRESHOLD:
172
+ print(f"\n🚨 [ALERT] DEMAND TOO HIGH!")
173
+ print(f" Current capacity can only meet {capacity_ratio*100:.1f}% of demand")
174
+ print(f" Threshold: {EVENING_SHIFT_DEMAND_THRESHOLD*100:.1f}%")
175
+ print(f" RECOMMENDATION: Change EVENING_SHIFT_MODE to 'activate_evening' to enable evening shift")
176
+ print(f" This will add shift 3 to increase capacity\n")
177
+
178
+ # 3) DAILY_WEEKLY_SCHEDULE warning (current model only enforces weekly demand)
179
  if DAILY_WEEKLY_SCHEDULE.lower() == "daily":
180
+ print("[INFO] DAILY_WEEKLY_SCHEDULE='daily' but this model only enforces weekly demand. "
181
+ "If daily demand decomposition is needed, please let us know.")
182
 
183
  # --- Solver ---
184
  solver = pywraplp.Solver.CreateSolver('CBC')
 
187
  INF = solver.infinity()
188
 
189
  # --- Variables ---
190
+ # Z[p,ell,s,t] ∈ {0,1}: 1 if product p runs on (line,shift,day)
191
+ Z, T, U = {}, {}, {} # T: run hours, U: production units
192
  for p in P:
193
  for ell in L: # ell = (line_type_id, idx)
194
  for s in S:
 
197
  T[p, ell, s, t] = solver.NumVar(0, Hmax_s[s], f"T_{p}_{ell[0]}_{ell[1]}_s{s}_d{t}")
198
  U[p, ell, s, t] = solver.NumVar(0, INF, f"U_{p}_{ell[0]}_{ell[1]}_s{s}_d{t}")
199
 
200
+ # --- Objective: total labor cost + hierarchy timing penalty ---
201
+ # Primary objective: minimize labor cost
202
  total_cost = solver.Sum(
203
  cost[e][s] * TEAM_REQ_PER_PRODUCT[e][p] * T[p, ell, s, t]
204
  for e in E for s in S for p in P for ell in L for t in D
205
  )
206
+
207
+ # Secondary objective: encourage earlier production of dependencies (soft constraint)
208
+ # Small weight (0.01) to prioritize hierarchy without overwhelming cost optimization
209
+ hierarchy_penalty = solver.Sum(
210
+ 0.01 * get_dependency_timing_weight(p) * t * T[p, ell, s, t]
211
+ for p in P for ell in L for s in S for t in D
212
+ )
213
+
214
+ total_objective = total_cost + hierarchy_penalty
215
+ solver.Minimize(total_objective)
216
 
217
  # --- Constraints ---
218
 
 
232
 
233
  # 3) Product-line type compatibility + (optional) activity by day
234
  for p in P:
235
+ req_lt = KIT_LINE_MATCH_DICT.get(p, 6) # Default to long line (6) if not found
236
  req_total = sum(TEAM_REQ_PER_PRODUCT[e][p] for e in E)
237
  for ell in L:
238
  allowed = (ell[0] == req_lt) and (req_total <= max_workers_line.get(ell[0], 1e9))
 
243
  solver.Add(T[p, ell, s, t] == 0)
244
  solver.Add(U[p, ell, s, t] == 0)
245
 
246
+ # 4) Line throughput: U ≀ product_speed * T
247
  for p in P:
248
  for ell in L:
249
  for s in S:
250
  for t in D:
251
+ # Get product speed (same speed regardless of line type)
252
+ if p in PER_PRODUCT_SPEED:
253
+ # Convert kit per day to kit per hour (assuming 7.5 hour workday)
254
+ speed = PER_PRODUCT_SPEED[p]
255
+ solver.Add(
256
+ U[p, ell, s, t] <= speed * T[p, ell, s, t]
257
+ )
258
+ else:
259
+ # Default speed if not found
260
+ default_speed = 800 / 7.5 # units per hour
261
+ print(f"Warning: No speed data for product {p}, using default {default_speed:.1f} per hour")
262
+ solver.Add(
263
+ U[p, ell, s, t] <= default_speed * T[p, ell, s, t]
264
+ )
265
 
266
  # 5) Per-shift staffing capacity by type: sum(req*hours) ≀ shift_hours * headcount
267
  for e in E:
 
288
  <=
289
  solver.Sum(TEAM_REQ_PER_PRODUCT[e][p] * T[p, ell, 1, t] for p in P for ell in L)
290
  )
291
+ # (if needed, evening(3) after usual(1): sum(...)_s=3 ≀ sum(...)_s=1)
292
+
293
+ # 8) *** HIERARCHY DEPENDENCY CONSTRAINTS ***
294
+ # For subkits with prepack dependencies: dependencies should be produced before or same time
295
+ print("\n[HIERARCHY] Adding dependency constraints...")
296
+ dependency_constraints_added = 0
297
+
298
+ for p in P:
299
+ dependencies = KIT_DEPENDENCIES.get(p, [])
300
+ if dependencies:
301
+ # Get the level of the current product
302
+ p_level = KIT_LEVELS.get(p, 2)
303
+
304
+ for dep in dependencies:
305
+ if dep in P: # Only if dependency is also in production list
306
+ # Calculate "completion time" for each product (sum of all production times)
307
+ p_completion = solver.Sum(
308
+ t * T[p, ell, s, t] for ell in L for s in S for t in D
309
+ )
310
+ dep_completion = solver.Sum(
311
+ t * T[dep, ell, s, t] for ell in L for s in S for t in D
312
+ )
313
+
314
+ # Dependency should complete before or at the same time
315
+ solver.Add(dep_completion <= p_completion)
316
+ dependency_constraints_added += 1
317
+
318
+ print(f" Added constraint: {dep} (dependency) <= {p} (level {p_level})")
319
+
320
+ print(f"[HIERARCHY] Added {dependency_constraints_added} dependency constraints")
321
 
322
  # --- Solve ---
323
  status = solver.Solve()
324
  if status != pywraplp.Solver.OPTIMAL:
325
  print(f"No optimal solution. Status: {status} (2=INFEASIBLE)")
326
+ # Debug hint:
327
  # solver.EnableOutput()
328
  # solver.ExportModelAsLpFile("model.lp")
329
  return None
streamlit_app.py DELETED
@@ -1,517 +0,0 @@
1
- import streamlit as st
2
-
3
- # Page configuration - MUST be first Streamlit command
4
- st.set_page_config(
5
- page_title="SD Roster Optimization Tool",
6
- page_icon="πŸ“Š",
7
- layout="wide",
8
- initial_sidebar_state="expanded"
9
- )
10
-
11
- # Now import everything else
12
- import pandas as pd
13
- import plotly.express as px
14
- import plotly.graph_objects as go
15
- from plotly.subplots import make_subplots
16
- import sys
17
- import os
18
- from datetime import datetime, timedelta
19
- import numpy as np
20
-
21
- # Add src to path for imports
22
- sys.path.append(os.path.join(os.path.dirname(__file__), 'src'))
23
-
24
- from src.models.optimizer_real import OptimizerReal
25
- from src.config import optimization_config
26
- import src.etl.extract as extract
27
- import src.etl.transform as transform
28
-
29
- # Custom CSS for better styling
30
- st.markdown("""
31
- <style>
32
- .main-header {
33
- font-size: 2.5rem;
34
- font-weight: bold;
35
- color: #1f77b4;
36
- margin-bottom: 1rem;
37
- }
38
- .section-header {
39
- font-size: 1.5rem;
40
- font-weight: bold;
41
- color: #2c3e50;
42
- margin: 1rem 0;
43
- }
44
- .metric-card {
45
- background-color: #f8f9fa;
46
- padding: 1rem;
47
- border-radius: 0.5rem;
48
- border-left: 4px solid #1f77b4;
49
- margin-bottom: 1rem;
50
- }
51
- .stTabs [data-baseweb="tab-list"] {
52
- gap: 2rem;
53
- }
54
- </style>
55
- """, unsafe_allow_html=True)
56
-
57
- # Initialize session state
58
- if 'optimization_results' not in st.session_state:
59
- st.session_state.optimization_results = None
60
- if 'optimizer' not in st.session_state:
61
- st.session_state.optimizer = None
62
- if 'date_range' not in st.session_state:
63
- st.session_state.date_range = None
64
-
65
- # Title
66
- st.markdown('<h1 class="main-header">πŸ“Š SD Roster Optimization Tool</h1>', unsafe_allow_html=True)
67
-
68
- # Create layout: Left sidebar + Main content
69
- with st.sidebar:
70
- st.markdown("## πŸŽ›οΈ Control Panel")
71
-
72
- # Date Selection Section
73
- st.markdown("### πŸ“… Date Range Selection")
74
- try:
75
- # Get available date ranges from the data
76
- date_ranges = transform.get_date_ranges()
77
- if date_ranges:
78
- date_range_options = [f"{start.strftime('%Y-%m-%d')} to {end.strftime('%Y-%m-%d')}" for start, end in date_ranges]
79
- selected_range_str = st.selectbox(
80
- "Select date range:",
81
- options=date_range_options,
82
- help="Available date ranges from released orders"
83
- )
84
-
85
- # Extract selected dates
86
- selected_index = date_range_options.index(selected_range_str)
87
- start_date, end_date = date_ranges[selected_index]
88
- st.session_state.date_range = (start_date, end_date)
89
-
90
- # Display duration
91
- duration = (end_date - start_date).days + 1
92
- st.info(f"Duration: {duration} days")
93
-
94
- else:
95
- st.warning("No date ranges found in data")
96
- start_date = datetime(2025, 3, 24).date()
97
- end_date = datetime(2025, 3, 28).date()
98
- st.session_state.date_range = (start_date, end_date)
99
-
100
- except Exception as e:
101
- st.error(f"Error loading dates: {e}")
102
- start_date = datetime(2025, 3, 24).date()
103
- end_date = datetime(2025, 3, 28).date()
104
- st.session_state.date_range = (start_date, end_date)
105
-
106
- st.markdown("---")
107
-
108
- # Optimization Parameters Section
109
- st.markdown("### βš™οΈ Optimization Parameters")
110
-
111
- # Employee Type Selection
112
- try:
113
- employee_df = extract.read_employee_data()
114
- available_emp_types = employee_df["employment_type"].unique().tolist()
115
- except:
116
- available_emp_types = ["UNICEF Fixed term", "Humanizer"]
117
-
118
- selected_emp_types = st.multiselect(
119
- "Employee Types:",
120
- available_emp_types,
121
- default=available_emp_types,
122
- help="Select employee types to include in optimization"
123
- )
124
-
125
- # Shift Selection
126
- try:
127
- shift_df = extract.get_shift_info()
128
- available_shifts = shift_df["id"].unique().tolist()
129
- except:
130
- available_shifts = [1, 2, 3]
131
-
132
- selected_shifts = st.multiselect(
133
- "Shifts:",
134
- available_shifts,
135
- default=available_shifts,
136
- help="1=Regular, 2=Overtime, 3=Evening"
137
- )
138
-
139
- # Line Selection
140
- try:
141
- line_df = extract.read_packaging_line_data()
142
- available_lines = line_df["id"].unique().tolist()
143
- except:
144
- available_lines = [6, 7]
145
-
146
- selected_lines = st.multiselect(
147
- "Production Lines:",
148
- available_lines,
149
- default=available_lines,
150
- help="Select production lines to include"
151
- )
152
-
153
- # Advanced Parameters
154
- with st.expander("πŸ”§ Advanced Parameters"):
155
- constraint_mode = st.selectbox(
156
- "Fixed Staff Constraint Mode:",
157
- ["priority", "mandatory", "none"],
158
- index=0,
159
- help="priority=Use fixed staff first, mandatory=Force all fixed hours, none=Demand-driven"
160
- )
161
-
162
- max_hours_per_person = st.number_input(
163
- "Max hours per person per day:",
164
- min_value=8,
165
- max_value=24,
166
- value=14,
167
- help="Legal daily limit"
168
- )
169
-
170
- # Employee availability override
171
- st.markdown("**Employee Availability Override:**")
172
- col1, col2 = st.columns(2)
173
- with col1:
174
- unicef_count = st.number_input("UNICEF Fixed term:", min_value=0, value=8)
175
- with col2:
176
- humanizer_count = st.number_input("Humanizer:", min_value=0, value=6)
177
-
178
- st.markdown("---")
179
-
180
- # Run Optimization Button
181
- run_optimization = st.button("πŸš€ Run Optimization", type="primary", use_container_width=True)
182
-
183
- # Main content area
184
- col1, col2 = st.columns([1, 2])
185
-
186
- # Left column - Metadata
187
- with col1:
188
- st.markdown('<h2 class="section-header">πŸ“‹ Metadata & Overview</h2>', unsafe_allow_html=True)
189
-
190
- # Show current date range
191
- if st.session_state.date_range:
192
- start_date, end_date = st.session_state.date_range
193
- st.markdown(f"**Selected Period:** {start_date} to {end_date}")
194
-
195
- # Demand Information
196
- with st.expander("πŸ“Š Demand Overview", expanded=True):
197
- try:
198
- if st.session_state.date_range:
199
- start_date, end_date = st.session_state.date_range
200
- demand_df = extract.read_released_orders_data(start_date=start_date, end_date=end_date)
201
-
202
- # Total demand metrics
203
- total_orders = len(demand_df)
204
- total_quantity = demand_df["Order quantity (GMEIN)"].sum()
205
- unique_products = demand_df["Material Number"].nunique()
206
-
207
- col_d1, col_d2, col_d3 = st.columns(3)
208
- with col_d1:
209
- st.metric("Total Orders", total_orders)
210
- with col_d2:
211
- st.metric("Total Quantity", f"{total_quantity:,.0f}")
212
- with col_d3:
213
- st.metric("Unique Products", unique_products)
214
-
215
- # Top products by demand
216
- top_products = demand_df.groupby('Material Number')["Order quantity (GMEIN)"].sum().sort_values(ascending=False).head(5)
217
-
218
- if not top_products.empty:
219
- st.markdown("**Top 5 Products by Demand:**")
220
- for product, quantity in top_products.items():
221
- st.markdown(f"β€’ {product}: {quantity:,.0f}")
222
-
223
- except Exception as e:
224
- st.error(f"Error loading demand data: {e}")
225
-
226
- # Employee Information
227
- with st.expander("πŸ‘₯ Employee Overview", expanded=True):
228
- try:
229
- employee_df = extract.read_employee_data()
230
-
231
- # Employee metrics
232
- total_employees = len(employee_df)
233
- emp_by_type = employee_df.groupby("employment_type").size()
234
-
235
- st.metric("Total Employees", total_employees)
236
-
237
- st.markdown("**By Employment Type:**")
238
- for emp_type, count in emp_by_type.items():
239
- st.markdown(f"β€’ {emp_type}: {count}")
240
-
241
- except Exception as e:
242
- st.error(f"Error loading employee data: {e}")
243
-
244
- # Production Lines
245
- with st.expander("🏭 Production Lines", expanded=True):
246
- try:
247
- line_df = extract.read_packaging_line_data()
248
-
249
- st.markdown("**Available Lines:**")
250
- for _, row in line_df.iterrows():
251
- st.markdown(f"β€’ Line {row['id']}: {row['line_count']} units")
252
-
253
- except Exception as e:
254
- st.error(f"Error loading line data: {e}")
255
-
256
- # Right column - Optimization Results
257
- with col2:
258
- st.markdown('<h2 class="section-header">🎯 Optimization Results</h2>', unsafe_allow_html=True)
259
-
260
- if run_optimization:
261
- with st.spinner("Running optimization..."):
262
- try:
263
- # Create optimizer instance
264
- optimizer = OptimizerReal()
265
-
266
- # Run optimization and get structured results
267
- results = optimizer.solve_option_A_multi_day_generalized()
268
-
269
- if results is None:
270
- st.error("❌ Optimization returned no results")
271
- elif results.get('status') == 'failed':
272
- st.error(f"❌ Optimization failed: {results.get('message', 'Unknown error')}")
273
- else:
274
- st.session_state.optimization_results = results
275
- st.session_state.optimizer = optimizer
276
- st.success("βœ… Optimization completed successfully!")
277
-
278
- except Exception as e:
279
- st.error(f"❌ Optimization failed: {e}")
280
- st.exception(e)
281
-
282
- # Display results if available
283
- if st.session_state.optimization_results:
284
- results = st.session_state.optimization_results
285
-
286
- # Create tabs for different result views
287
- tab1, tab2, tab3, tab4 = st.tabs(["πŸ“Š Summary", "πŸ“ˆ Production", "πŸ‘· Labor", "πŸ’° Costs"])
288
-
289
- with tab1:
290
- st.markdown("### Optimization Summary")
291
-
292
- # Display key metrics
293
- total_cost = results.get('total_cost', 0)
294
- st.metric("πŸ’° Total Optimization Cost", f"${total_cost:,.2f}")
295
-
296
- # Additional summary metrics
297
- params = results.get('parameters', {})
298
- col_s1, col_s2, col_s3 = st.columns(3)
299
-
300
- with col_s1:
301
- st.metric("Total Products", len(params.get('product_list', [])))
302
- with col_s2:
303
- st.metric("Employee Types", len(params.get('employee_types', [])))
304
- with col_s3:
305
- st.metric("Total Demand", f"{params.get('total_demand', 0):,.0f}")
306
-
307
- # Show optimization parameters used
308
- st.markdown("**Optimization Parameters:**")
309
- if st.session_state.date_range:
310
- start_date, end_date = st.session_state.date_range
311
- duration = (end_date - start_date).days + 1
312
- st.markdown(f"β€’ Date Range: {start_date} to {end_date} ({duration} days)")
313
- st.markdown(f"β€’ Employee Types: {', '.join(selected_emp_types)}")
314
- st.markdown(f"β€’ Shifts: {', '.join(map(str, selected_shifts))}")
315
- st.markdown(f"β€’ Production Lines: {', '.join(map(str, selected_lines))}")
316
- st.markdown(f"β€’ Constraint Mode: {params.get('constraint_mode', 'N/A')}")
317
-
318
- # Cost efficiency metrics
319
- if st.session_state.date_range:
320
- start_date, end_date = st.session_state.date_range
321
- duration = (end_date - start_date).days + 1
322
- cost_per_day = total_cost / duration if duration > 0 else 0
323
- cost_per_unit = total_cost / params.get('total_demand', 1) if params.get('total_demand', 0) > 0 else 0
324
-
325
- col_e1, col_e2 = st.columns(2)
326
- with col_e1:
327
- st.metric("Cost per Day", f"${cost_per_day:,.2f}")
328
- with col_e2:
329
- st.metric("Cost per Unit", f"${cost_per_unit:.3f}")
330
-
331
- with tab2:
332
- st.markdown("### Production Results")
333
-
334
- production_results = results.get('production_results', {})
335
- if production_results:
336
- # Create production summary table
337
- prod_data = []
338
- for product, data in production_results.items():
339
- prod_data.append({
340
- 'Product': product,
341
- 'Demand': data['demand'],
342
- 'Produced': data['produced'],
343
- 'Fulfillment %': f"{data['fulfillment_rate']:.1f}%"
344
- })
345
-
346
- if prod_data:
347
- prod_df = pd.DataFrame(prod_data)
348
- st.dataframe(prod_df, use_container_width=True)
349
-
350
- # Production fulfillment chart
351
- fig_prod = px.bar(
352
- prod_df,
353
- x='Product',
354
- y=['Demand', 'Produced'],
355
- title='Production vs Demand by Product',
356
- barmode='group'
357
- )
358
- st.plotly_chart(fig_prod, use_container_width=True)
359
-
360
- # Fulfillment rate chart
361
- fulfillment_data = [(row['Product'], float(row['Fulfillment %'].rstrip('%'))) for row in prod_data]
362
- fulfill_df = pd.DataFrame(fulfillment_data, columns=['Product', 'Fulfillment_Rate'])
363
-
364
- fig_fulfill = px.bar(
365
- fulfill_df,
366
- x='Product',
367
- y='Fulfillment_Rate',
368
- title='Fulfillment Rate by Product (%)',
369
- color='Fulfillment_Rate',
370
- color_continuous_scale='RdYlGn'
371
- )
372
- fig_fulfill.update_layout(yaxis_title="Fulfillment Rate (%)")
373
- st.plotly_chart(fig_fulfill, use_container_width=True)
374
- else:
375
- st.info("No production data available")
376
-
377
- with tab3:
378
- st.markdown("### Labor Allocation")
379
-
380
- employee_hours = results.get('employee_hours', {})
381
- headcount_req = results.get('headcount_requirements', {})
382
-
383
- if employee_hours:
384
- # Create labor hours visualization
385
- labor_data = []
386
- for emp_type, shifts in employee_hours.items():
387
- for shift, daily_hours in shifts.items():
388
- total_hours = sum(daily_hours)
389
- if total_hours > 0:
390
- labor_data.append({
391
- 'Employee Type': emp_type,
392
- 'Shift': f"Shift {shift}",
393
- 'Total Hours': total_hours,
394
- 'Avg Daily Hours': total_hours / len(daily_hours) if daily_hours else 0
395
- })
396
-
397
- if labor_data:
398
- labor_df = pd.DataFrame(labor_data)
399
- st.dataframe(labor_df, use_container_width=True)
400
-
401
- # Labor hours chart
402
- fig_labor = px.bar(
403
- labor_df,
404
- x='Employee Type',
405
- y='Total Hours',
406
- color='Shift',
407
- title='Total Labor Hours by Employee Type and Shift',
408
- barmode='group'
409
- )
410
- st.plotly_chart(fig_labor, use_container_width=True)
411
-
412
- # Headcount requirements
413
- if headcount_req:
414
- st.markdown("#### Required Headcount")
415
- headcount_data = []
416
- for emp_type, shifts in headcount_req.items():
417
- for shift, daily_count in shifts.items():
418
- max_count = max(daily_count) if daily_count else 0
419
- avg_count = sum(daily_count) / len(daily_count) if daily_count else 0
420
- if max_count > 0:
421
- headcount_data.append({
422
- 'Employee Type': emp_type,
423
- 'Shift': f"Shift {shift}",
424
- 'Max Daily': max_count,
425
- 'Avg Daily': f"{avg_count:.1f}"
426
- })
427
-
428
- if headcount_data:
429
- headcount_df = pd.DataFrame(headcount_data)
430
- st.dataframe(headcount_df, use_container_width=True)
431
-
432
- with tab4:
433
- st.markdown("### Cost Analysis")
434
-
435
- total_cost = results.get('total_cost', 0)
436
- st.metric("Total Optimization Cost", f"${total_cost:,.2f}")
437
-
438
- # Cost breakdown by employee type (estimated)
439
- employee_hours = results.get('employee_hours', {})
440
- if employee_hours:
441
- cost_data = []
442
- # Use wage data from config
443
- wage_types = optimization_config.COST_LIST_PER_EMP_SHIFT
444
-
445
- for emp_type, shifts in employee_hours.items():
446
- emp_total_cost = 0
447
- for shift, daily_hours in shifts.items():
448
- total_hours = sum(daily_hours)
449
- if total_hours > 0 and emp_type in wage_types and shift in wage_types[emp_type]:
450
- shift_cost = total_hours * wage_types[emp_type][shift]
451
- emp_total_cost += shift_cost
452
- cost_data.append({
453
- 'Employee Type': emp_type,
454
- 'Shift': f"Shift {shift}",
455
- 'Hours': total_hours,
456
- 'Rate': wage_types[emp_type][shift],
457
- 'Cost': shift_cost
458
- })
459
-
460
- if cost_data:
461
- cost_df = pd.DataFrame(cost_data)
462
- st.dataframe(cost_df, use_container_width=True)
463
-
464
- # Cost breakdown chart
465
- fig_cost = px.pie(
466
- cost_df,
467
- values='Cost',
468
- names='Employee Type',
469
- title='Cost Distribution by Employee Type'
470
- )
471
- st.plotly_chart(fig_cost, use_container_width=True)
472
-
473
- # Cost by shift chart
474
- fig_shift_cost = px.bar(
475
- cost_df,
476
- x='Employee Type',
477
- y='Cost',
478
- color='Shift',
479
- title='Cost Breakdown by Employee Type and Shift',
480
- barmode='stack'
481
- )
482
- st.plotly_chart(fig_shift_cost, use_container_width=True)
483
-
484
- # Priority mode results
485
- priority_results = results.get('priority_results')
486
- if priority_results and priority_results.get('summary'):
487
- st.markdown("#### Priority Mode Analysis")
488
- summary = priority_results['summary']
489
- if summary['unicef_sufficient']:
490
- st.success("βœ… UNICEF Fixed term staff sufficient for all demand")
491
- st.info("β†’ Humanizer staff not needed")
492
- else:
493
- st.warning(f"⚠️ {summary['total_capacity_flags']} cases where UNICEF at capacity")
494
- st.info("β†’ Humanizer staff utilized")
495
-
496
- else:
497
- st.info("πŸ‘ˆ Click 'Run Optimization' in the sidebar to see results")
498
-
499
- # Show placeholder content
500
- st.markdown("""
501
- ### What you'll see here:
502
-
503
- - **πŸ“Š Summary**: Overall optimization results and key metrics
504
- - **πŸ“ˆ Production**: Production schedule by product and day
505
- - **πŸ‘· Labor**: Employee allocation and shift assignments
506
- - **πŸ’° Costs**: Detailed cost breakdown and analysis
507
-
508
- Configure your parameters in the sidebar and click 'Run Optimization' to get started!
509
- """)
510
-
511
- # Footer
512
- st.markdown("---")
513
- st.markdown("""
514
- <div style='text-align: center; color: gray; padding: 2rem;'>
515
- <small>SD Roster Optimization Tool | Built with Streamlit & OR-Tools</small>
516
- </div>
517
- """, unsafe_allow_html=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
streamlit_app_old.py DELETED
@@ -1,517 +0,0 @@
1
- import streamlit as st
2
-
3
- # Page configuration - MUST be first Streamlit command
4
- st.set_page_config(
5
- page_title="SD Roster Optimization Tool",
6
- page_icon="πŸ“Š",
7
- layout="wide",
8
- initial_sidebar_state="expanded"
9
- )
10
-
11
- # Now import everything else
12
- import pandas as pd
13
- import plotly.express as px
14
- import plotly.graph_objects as go
15
- from plotly.subplots import make_subplots
16
- import sys
17
- import os
18
- from datetime import datetime, timedelta
19
- import numpy as np
20
-
21
- # Add src to path for imports
22
- sys.path.append(os.path.join(os.path.dirname(__file__), 'src'))
23
-
24
- from src.models.optimizer_real import OptimizerReal
25
- from src.config import optimization_config
26
- import src.etl.extract as extract
27
- import src.etl.transform as transform
28
-
29
- # Custom CSS for better styling
30
- st.markdown("""
31
- <style>
32
- .main-header {
33
- font-size: 2.5rem;
34
- font-weight: bold;
35
- color: #1f77b4;
36
- margin-bottom: 1rem;
37
- }
38
- .section-header {
39
- font-size: 1.5rem;
40
- font-weight: bold;
41
- color: #2c3e50;
42
- margin: 1rem 0;
43
- }
44
- .metric-card {
45
- background-color: #f8f9fa;
46
- padding: 1rem;
47
- border-radius: 0.5rem;
48
- border-left: 4px solid #1f77b4;
49
- margin-bottom: 1rem;
50
- }
51
- .stTabs [data-baseweb="tab-list"] {
52
- gap: 2rem;
53
- }
54
- </style>
55
- """, unsafe_allow_html=True)
56
-
57
- # Initialize session state
58
- if 'optimization_results' not in st.session_state:
59
- st.session_state.optimization_results = None
60
- if 'optimizer' not in st.session_state:
61
- st.session_state.optimizer = None
62
- if 'date_range' not in st.session_state:
63
- st.session_state.date_range = None
64
-
65
- # Title
66
- st.markdown('<h1 class="main-header">πŸ“Š SD Roster Optimization Tool</h1>', unsafe_allow_html=True)
67
-
68
- # Create layout: Left sidebar + Main content
69
- with st.sidebar:
70
- st.markdown("## πŸŽ›οΈ Control Panel")
71
-
72
- # Date Selection Section
73
- st.markdown("### πŸ“… Date Range Selection")
74
- try:
75
- # Get available date ranges from the data
76
- date_ranges = transform.get_date_ranges()
77
- if date_ranges:
78
- date_range_options = [f"{start.strftime('%Y-%m-%d')} to {end.strftime('%Y-%m-%d')}" for start, end in date_ranges]
79
- selected_range_str = st.selectbox(
80
- "Select date range:",
81
- options=date_range_options,
82
- help="Available date ranges from released orders"
83
- )
84
-
85
- # Extract selected dates
86
- selected_index = date_range_options.index(selected_range_str)
87
- start_date, end_date = date_ranges[selected_index]
88
- st.session_state.date_range = (start_date, end_date)
89
-
90
- # Display duration
91
- duration = (end_date - start_date).days + 1
92
- st.info(f"Duration: {duration} days")
93
-
94
- else:
95
- st.warning("No date ranges found in data")
96
- start_date = datetime(2025, 3, 24).date()
97
- end_date = datetime(2025, 3, 28).date()
98
- st.session_state.date_range = (start_date, end_date)
99
-
100
- except Exception as e:
101
- st.error(f"Error loading dates: {e}")
102
- start_date = datetime(2025, 3, 24).date()
103
- end_date = datetime(2025, 3, 28).date()
104
- st.session_state.date_range = (start_date, end_date)
105
-
106
- st.markdown("---")
107
-
108
- # Optimization Parameters Section
109
- st.markdown("### βš™οΈ Optimization Parameters")
110
-
111
- # Employee Type Selection
112
- try:
113
- employee_df = extract.read_employee_data()
114
- available_emp_types = employee_df["employment_type"].unique().tolist()
115
- except:
116
- available_emp_types = ["UNICEF Fixed term", "Humanizer"]
117
-
118
- selected_emp_types = st.multiselect(
119
- "Employee Types:",
120
- available_emp_types,
121
- default=available_emp_types,
122
- help="Select employee types to include in optimization"
123
- )
124
-
125
- # Shift Selection
126
- try:
127
- shift_df = extract.get_shift_info()
128
- available_shifts = shift_df["id"].unique().tolist()
129
- except:
130
- available_shifts = [1, 2, 3]
131
-
132
- selected_shifts = st.multiselect(
133
- "Shifts:",
134
- available_shifts,
135
- default=available_shifts,
136
- help="1=Regular, 2=Overtime, 3=Evening"
137
- )
138
-
139
- # Line Selection
140
- try:
141
- line_df = extract.read_packaging_line_data()
142
- available_lines = line_df["id"].unique().tolist()
143
- except:
144
- available_lines = [6, 7]
145
-
146
- selected_lines = st.multiselect(
147
- "Production Lines:",
148
- available_lines,
149
- default=available_lines,
150
- help="Select production lines to include"
151
- )
152
-
153
- # Advanced Parameters
154
- with st.expander("πŸ”§ Advanced Parameters"):
155
- constraint_mode = st.selectbox(
156
- "Fixed Staff Constraint Mode:",
157
- ["priority", "mandatory", "none"],
158
- index=0,
159
- help="priority=Use fixed staff first, mandatory=Force all fixed hours, none=Demand-driven"
160
- )
161
-
162
- max_hours_per_person = st.number_input(
163
- "Max hours per person per day:",
164
- min_value=8,
165
- max_value=24,
166
- value=14,
167
- help="Legal daily limit"
168
- )
169
-
170
- # Employee availability override
171
- st.markdown("**Employee Availability Override:**")
172
- col1, col2 = st.columns(2)
173
- with col1:
174
- unicef_count = st.number_input("UNICEF Fixed term:", min_value=0, value=8)
175
- with col2:
176
- humanizer_count = st.number_input("Humanizer:", min_value=0, value=6)
177
-
178
- st.markdown("---")
179
-
180
- # Run Optimization Button
181
- run_optimization = st.button("πŸš€ Run Optimization", type="primary", use_container_width=True)
182
-
183
- # Main content area
184
- col1, col2 = st.columns([1, 2])
185
-
186
- # Left column - Metadata
187
- with col1:
188
- st.markdown('<h2 class="section-header">πŸ“‹ Metadata & Overview</h2>', unsafe_allow_html=True)
189
-
190
- # Show current date range
191
- if st.session_state.date_range:
192
- start_date, end_date = st.session_state.date_range
193
- st.markdown(f"**Selected Period:** {start_date} to {end_date}")
194
-
195
- # Demand Information
196
- with st.expander("πŸ“Š Demand Overview", expanded=True):
197
- try:
198
- if st.session_state.date_range:
199
- start_date, end_date = st.session_state.date_range
200
- demand_df = extract.read_released_orders_data(start_date=start_date, end_date=end_date)
201
-
202
- # Total demand metrics
203
- total_orders = len(demand_df)
204
- total_quantity = demand_df["Order quantity (GMEIN)"].sum()
205
- unique_products = demand_df["Material Number"].nunique()
206
-
207
- col_d1, col_d2, col_d3 = st.columns(3)
208
- with col_d1:
209
- st.metric("Total Orders", total_orders)
210
- with col_d2:
211
- st.metric("Total Quantity", f"{total_quantity:,.0f}")
212
- with col_d3:
213
- st.metric("Unique Products", unique_products)
214
-
215
- # Top products by demand
216
- top_products = demand_df.groupby('Material Number')["Order quantity (GMEIN)"].sum().sort_values(ascending=False).head(5)
217
-
218
- if not top_products.empty:
219
- st.markdown("**Top 5 Products by Demand:**")
220
- for product, quantity in top_products.items():
221
- st.markdown(f"β€’ {product}: {quantity:,.0f}")
222
-
223
- except Exception as e:
224
- st.error(f"Error loading demand data: {e}")
225
-
226
- # Employee Information
227
- with st.expander("πŸ‘₯ Employee Overview", expanded=True):
228
- try:
229
- employee_df = extract.read_employee_data()
230
-
231
- # Employee metrics
232
- total_employees = len(employee_df)
233
- emp_by_type = employee_df.groupby("employment_type").size()
234
-
235
- st.metric("Total Employees", total_employees)
236
-
237
- st.markdown("**By Employment Type:**")
238
- for emp_type, count in emp_by_type.items():
239
- st.markdown(f"β€’ {emp_type}: {count}")
240
-
241
- except Exception as e:
242
- st.error(f"Error loading employee data: {e}")
243
-
244
- # Production Lines
245
- with st.expander("🏭 Production Lines", expanded=True):
246
- try:
247
- line_df = extract.read_packaging_line_data()
248
-
249
- st.markdown("**Available Lines:**")
250
- for _, row in line_df.iterrows():
251
- st.markdown(f"β€’ Line {row['id']}: {row['line_count']} units")
252
-
253
- except Exception as e:
254
- st.error(f"Error loading line data: {e}")
255
-
256
- # Right column - Optimization Results
257
- with col2:
258
- st.markdown('<h2 class="section-header">🎯 Optimization Results</h2>', unsafe_allow_html=True)
259
-
260
- if run_optimization:
261
- with st.spinner("Running optimization..."):
262
- try:
263
- # Create optimizer instance
264
- optimizer = OptimizerReal()
265
-
266
- # Run optimization and get structured results
267
- results = optimizer.solve_option_A_multi_day_generalized()
268
-
269
- if results is None:
270
- st.error("❌ Optimization returned no results")
271
- elif results.get('status') == 'failed':
272
- st.error(f"❌ Optimization failed: {results.get('message', 'Unknown error')}")
273
- else:
274
- st.session_state.optimization_results = results
275
- st.session_state.optimizer = optimizer
276
- st.success("βœ… Optimization completed successfully!")
277
-
278
- except Exception as e:
279
- st.error(f"❌ Optimization failed: {e}")
280
- st.exception(e)
281
-
282
- # Display results if available
283
- if st.session_state.optimization_results:
284
- results = st.session_state.optimization_results
285
-
286
- # Create tabs for different result views
287
- tab1, tab2, tab3, tab4 = st.tabs(["πŸ“Š Summary", "πŸ“ˆ Production", "πŸ‘· Labor", "πŸ’° Costs"])
288
-
289
- with tab1:
290
- st.markdown("### Optimization Summary")
291
-
292
- # Display key metrics
293
- total_cost = results.get('total_cost', 0)
294
- st.metric("πŸ’° Total Optimization Cost", f"${total_cost:,.2f}")
295
-
296
- # Additional summary metrics
297
- params = results.get('parameters', {})
298
- col_s1, col_s2, col_s3 = st.columns(3)
299
-
300
- with col_s1:
301
- st.metric("Total Products", len(params.get('product_list', [])))
302
- with col_s2:
303
- st.metric("Employee Types", len(params.get('employee_types', [])))
304
- with col_s3:
305
- st.metric("Total Demand", f"{params.get('total_demand', 0):,.0f}")
306
-
307
- # Show optimization parameters used
308
- st.markdown("**Optimization Parameters:**")
309
- if st.session_state.date_range:
310
- start_date, end_date = st.session_state.date_range
311
- duration = (end_date - start_date).days + 1
312
- st.markdown(f"β€’ Date Range: {start_date} to {end_date} ({duration} days)")
313
- st.markdown(f"β€’ Employee Types: {', '.join(selected_emp_types)}")
314
- st.markdown(f"β€’ Shifts: {', '.join(map(str, selected_shifts))}")
315
- st.markdown(f"β€’ Production Lines: {', '.join(map(str, selected_lines))}")
316
- st.markdown(f"β€’ Constraint Mode: {params.get('constraint_mode', 'N/A')}")
317
-
318
- # Cost efficiency metrics
319
- if st.session_state.date_range:
320
- start_date, end_date = st.session_state.date_range
321
- duration = (end_date - start_date).days + 1
322
- cost_per_day = total_cost / duration if duration > 0 else 0
323
- cost_per_unit = total_cost / params.get('total_demand', 1) if params.get('total_demand', 0) > 0 else 0
324
-
325
- col_e1, col_e2 = st.columns(2)
326
- with col_e1:
327
- st.metric("Cost per Day", f"${cost_per_day:,.2f}")
328
- with col_e2:
329
- st.metric("Cost per Unit", f"${cost_per_unit:.3f}")
330
-
331
- with tab2:
332
- st.markdown("### Production Results")
333
-
334
- production_results = results.get('production_results', {})
335
- if production_results:
336
- # Create production summary table
337
- prod_data = []
338
- for product, data in production_results.items():
339
- prod_data.append({
340
- 'Product': product,
341
- 'Demand': data['demand'],
342
- 'Produced': data['produced'],
343
- 'Fulfillment %': f"{data['fulfillment_rate']:.1f}%"
344
- })
345
-
346
- if prod_data:
347
- prod_df = pd.DataFrame(prod_data)
348
- st.dataframe(prod_df, use_container_width=True)
349
-
350
- # Production fulfillment chart
351
- fig_prod = px.bar(
352
- prod_df,
353
- x='Product',
354
- y=['Demand', 'Produced'],
355
- title='Production vs Demand by Product',
356
- barmode='group'
357
- )
358
- st.plotly_chart(fig_prod, use_container_width=True)
359
-
360
- # Fulfillment rate chart
361
- fulfillment_data = [(row['Product'], float(row['Fulfillment %'].rstrip('%'))) for row in prod_data]
362
- fulfill_df = pd.DataFrame(fulfillment_data, columns=['Product', 'Fulfillment_Rate'])
363
-
364
- fig_fulfill = px.bar(
365
- fulfill_df,
366
- x='Product',
367
- y='Fulfillment_Rate',
368
- title='Fulfillment Rate by Product (%)',
369
- color='Fulfillment_Rate',
370
- color_continuous_scale='RdYlGn'
371
- )
372
- fig_fulfill.update_layout(yaxis_title="Fulfillment Rate (%)")
373
- st.plotly_chart(fig_fulfill, use_container_width=True)
374
- else:
375
- st.info("No production data available")
376
-
377
- with tab3:
378
- st.markdown("### Labor Allocation")
379
-
380
- employee_hours = results.get('employee_hours', {})
381
- headcount_req = results.get('headcount_requirements', {})
382
-
383
- if employee_hours:
384
- # Create labor hours visualization
385
- labor_data = []
386
- for emp_type, shifts in employee_hours.items():
387
- for shift, daily_hours in shifts.items():
388
- total_hours = sum(daily_hours)
389
- if total_hours > 0:
390
- labor_data.append({
391
- 'Employee Type': emp_type,
392
- 'Shift': f"Shift {shift}",
393
- 'Total Hours': total_hours,
394
- 'Avg Daily Hours': total_hours / len(daily_hours) if daily_hours else 0
395
- })
396
-
397
- if labor_data:
398
- labor_df = pd.DataFrame(labor_data)
399
- st.dataframe(labor_df, use_container_width=True)
400
-
401
- # Labor hours chart
402
- fig_labor = px.bar(
403
- labor_df,
404
- x='Employee Type',
405
- y='Total Hours',
406
- color='Shift',
407
- title='Total Labor Hours by Employee Type and Shift',
408
- barmode='group'
409
- )
410
- st.plotly_chart(fig_labor, use_container_width=True)
411
-
412
- # Headcount requirements
413
- if headcount_req:
414
- st.markdown("#### Required Headcount")
415
- headcount_data = []
416
- for emp_type, shifts in headcount_req.items():
417
- for shift, daily_count in shifts.items():
418
- max_count = max(daily_count) if daily_count else 0
419
- avg_count = sum(daily_count) / len(daily_count) if daily_count else 0
420
- if max_count > 0:
421
- headcount_data.append({
422
- 'Employee Type': emp_type,
423
- 'Shift': f"Shift {shift}",
424
- 'Max Daily': max_count,
425
- 'Avg Daily': f"{avg_count:.1f}"
426
- })
427
-
428
- if headcount_data:
429
- headcount_df = pd.DataFrame(headcount_data)
430
- st.dataframe(headcount_df, use_container_width=True)
431
-
432
- with tab4:
433
- st.markdown("### Cost Analysis")
434
-
435
- total_cost = results.get('total_cost', 0)
436
- st.metric("Total Optimization Cost", f"${total_cost:,.2f}")
437
-
438
- # Cost breakdown by employee type (estimated)
439
- employee_hours = results.get('employee_hours', {})
440
- if employee_hours:
441
- cost_data = []
442
- # Use wage data from config
443
- wage_types = optimization_config.COST_LIST_PER_EMP_SHIFT
444
-
445
- for emp_type, shifts in employee_hours.items():
446
- emp_total_cost = 0
447
- for shift, daily_hours in shifts.items():
448
- total_hours = sum(daily_hours)
449
- if total_hours > 0 and emp_type in wage_types and shift in wage_types[emp_type]:
450
- shift_cost = total_hours * wage_types[emp_type][shift]
451
- emp_total_cost += shift_cost
452
- cost_data.append({
453
- 'Employee Type': emp_type,
454
- 'Shift': f"Shift {shift}",
455
- 'Hours': total_hours,
456
- 'Rate': wage_types[emp_type][shift],
457
- 'Cost': shift_cost
458
- })
459
-
460
- if cost_data:
461
- cost_df = pd.DataFrame(cost_data)
462
- st.dataframe(cost_df, use_container_width=True)
463
-
464
- # Cost breakdown chart
465
- fig_cost = px.pie(
466
- cost_df,
467
- values='Cost',
468
- names='Employee Type',
469
- title='Cost Distribution by Employee Type'
470
- )
471
- st.plotly_chart(fig_cost, use_container_width=True)
472
-
473
- # Cost by shift chart
474
- fig_shift_cost = px.bar(
475
- cost_df,
476
- x='Employee Type',
477
- y='Cost',
478
- color='Shift',
479
- title='Cost Breakdown by Employee Type and Shift',
480
- barmode='stack'
481
- )
482
- st.plotly_chart(fig_shift_cost, use_container_width=True)
483
-
484
- # Priority mode results
485
- priority_results = results.get('priority_results')
486
- if priority_results and priority_results.get('summary'):
487
- st.markdown("#### Priority Mode Analysis")
488
- summary = priority_results['summary']
489
- if summary['unicef_sufficient']:
490
- st.success("βœ… UNICEF Fixed term staff sufficient for all demand")
491
- st.info("β†’ Humanizer staff not needed")
492
- else:
493
- st.warning(f"⚠️ {summary['total_capacity_flags']} cases where UNICEF at capacity")
494
- st.info("β†’ Humanizer staff utilized")
495
-
496
- else:
497
- st.info("πŸ‘ˆ Click 'Run Optimization' in the sidebar to see results")
498
-
499
- # Show placeholder content
500
- st.markdown("""
501
- ### What you'll see here:
502
-
503
- - **πŸ“Š Summary**: Overall optimization results and key metrics
504
- - **πŸ“ˆ Production**: Production schedule by product and day
505
- - **πŸ‘· Labor**: Employee allocation and shift assignments
506
- - **πŸ’° Costs**: Detailed cost breakdown and analysis
507
-
508
- Configure your parameters in the sidebar and click 'Run Optimization' to get started!
509
- """)
510
-
511
- # Footer
512
- st.markdown("---")
513
- st.markdown("""
514
- <div style='text-align: center; color: gray; padding: 2rem;'>
515
- <small>SD Roster Optimization Tool | Built with Streamlit & OR-Tools</small>
516
- </div>
517
- """, unsafe_allow_html=True)