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 +9 -6
- data/hierarchy_exports/kit_hierarchy.json +2110 -0
- data/real_data_excel/converted_csv/COOIS_Planned_and_Released.csv +3 -0
- data/real_data_excel/converted_csv/COOIS_Released_Prod_Orders.csv +8 -0
- data/real_data_excel/converted_csv/Kit_Composition_and_relation_cleaned_with_line_type.csv +2 -0
- data/real_data_excel/converted_csv/Kit_Composition_and_relation_cleaned_with_line_type_and_id.csv +676 -0
- data/real_data_excel/converted_csv/Kits__Calculation.csv +5 -1
- data/real_data_excel/converted_csv/Work_Centre_Capacity_processed.csv +1 -1
- pages/1_π_Dataset_Metadata.py +302 -2
- pages/2_π―_Optimization.py +48 -19
- pages/3_π_Enhanced_Reports.py +873 -0
- src/config/optimization_config.py +176 -46
- src/etl/extract.py +69 -1
- src/etl/hierarchy_parser.py +112 -0
- src/models/optimizer_new_aug14.py +170 -33
- streamlit_app.py +0 -517
- streamlit_app_old.py +0 -517
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>π
|
| 187 |
-
<p>
|
| 188 |
<ul>
|
| 189 |
-
<li>
|
| 190 |
-
<li>
|
| 191 |
-
<li>
|
| 192 |
-
<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 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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,
|
|
|
|
| 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.
|
| 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 |
-
#
|
| 206 |
-
|
| 207 |
|
| 208 |
-
|
| 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 |
-
|
| 217 |
-
st.session_state.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 264 |
with col_s2:
|
| 265 |
-
|
| 266 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 267 |
with col_s3:
|
| 268 |
-
|
|
|
|
| 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 |
-
|
| 301 |
-
|
|
|
|
|
|
|
|
|
|
| 302 |
# Create production summary table
|
| 303 |
prod_data = []
|
| 304 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 305 |
prod_data.append({
|
| 306 |
'Product': product,
|
| 307 |
-
'Demand':
|
| 308 |
-
'Produced':
|
| 309 |
-
'Fulfillment %': f"{
|
| 310 |
-
'Status': 'β
Met' if
|
| 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 |
-
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
|
| 20 |
-
|
| 21 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
-
|
|
|
|
|
|
|
| 28 |
PRODUCT_LIST = transformed_data.get_released_product_list(start_date, end_date)
|
| 29 |
-
print("
|
| 30 |
|
| 31 |
|
| 32 |
def get_employee_type_list():
|
|
|
|
| 33 |
try:
|
| 34 |
-
|
| 35 |
-
|
| 36 |
-
|
| 37 |
-
|
| 38 |
-
|
| 39 |
-
|
| 40 |
-
|
| 41 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
-
|
| 49 |
-
|
|
|
|
|
|
|
|
|
|
| 50 |
except Exception as e:
|
| 51 |
-
print(f"
|
| 52 |
-
|
| 53 |
-
|
| 54 |
-
|
| 55 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 56 |
# print("shift list",SHIFT_LIST)
|
| 57 |
|
| 58 |
|
| 59 |
def get_line_list():
|
|
|
|
| 60 |
try:
|
| 61 |
-
|
| 62 |
-
|
|
|
|
|
|
|
|
|
|
| 63 |
except Exception as e:
|
| 64 |
-
print(f"
|
| 65 |
-
|
| 66 |
-
|
| 67 |
-
|
|
|
|
|
|
|
|
|
|
| 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 |
-
|
| 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 |
-
|
| 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 |
-
|
| 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.
|
| 192 |
return per_product_speed
|
| 193 |
|
| 194 |
|
| 195 |
-
|
| 196 |
-
|
| 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:
|
| 203 |
-
7:
|
| 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)
|
| 42 |
-
|
| 43 |
-
|
| 44 |
-
# TODO: μλλ μμλ‘ μ λΆ long(6)μΌλ‘ λ . μ νλ³λ‘ 6/7μ μ§μ ν΄μΌ ν¨.
|
| 45 |
-
p: LINE_LIST[0] for p in PRODUCT_LIST
|
| 46 |
-
}
|
| 47 |
|
| 48 |
-
# 3)
|
| 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 |
-
"""
|
| 55 |
-
L
|
| 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 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
-
|
|
|
|
| 84 |
for p in P:
|
| 85 |
req_total = sum(TEAM_REQ_PER_PRODUCT[e][p] for e in E)
|
| 86 |
-
lt =
|
|
|
|
|
|
|
| 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)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 92 |
if DAILY_WEEKLY_SCHEDULE.lower() == "daily":
|
| 93 |
-
print("[INFO] DAILY_WEEKLY_SCHEDULE='daily'
|
| 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}:
|
| 104 |
-
Z, T, U = {}, {}, {} # T:
|
| 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 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 =
|
| 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 β€
|
| 150 |
for p in P:
|
| 151 |
for ell in L:
|
| 152 |
for s in S:
|
| 153 |
for t in D:
|
| 154 |
-
|
| 155 |
-
|
| 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 |
-
# (
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|