Spaces:
Build error
Build error
Upload 2 files
Browse files- app.py +372 -0
- requirements.txt +3 -0
app.py
ADDED
|
@@ -0,0 +1,372 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import streamlit as st
|
| 2 |
+
import pandas as pd
|
| 3 |
+
|
| 4 |
+
st.set_page_config(
|
| 5 |
+
page_title="Key Innovations Inc.",
|
| 6 |
+
page_icon="https://tscstatic.keyinnovations.ca/logo/logo_1T895ONEIG.png"
|
| 7 |
+
)
|
| 8 |
+
st.image("https://tscstatic.keyinnovations.ca/logo/logo_1T895ONEIG.png", width=150)
|
| 9 |
+
st.title('Quoting System')
|
| 10 |
+
st.subheader('Key Innovation Inc.')
|
| 11 |
+
|
| 12 |
+
|
| 13 |
+
#Option to select a clothing method.
|
| 14 |
+
|
| 15 |
+
option = st.segmented_control(
|
| 16 |
+
"Select an option:",
|
| 17 |
+
["Embroidery", "Screen Print", "Embroidery & Screen Print"]
|
| 18 |
+
)
|
| 19 |
+
|
| 20 |
+
#Embroidory Pricing Dataset
|
| 21 |
+
data = {
|
| 22 |
+
"Quantity": ["0-11", "12-17", "18-23", "24-35", "36-59", "60-119", "120-239", "240-600"],
|
| 23 |
+
"1-1,000": [5.00, 4.00, 3.25, 2.75, 2.25, 2.00, 1.75, 1.50],
|
| 24 |
+
"1,000-2,000": [5.50, 4.40, 3.60, 3.05, 2.53, 2.27, 2.01, 1.75],
|
| 25 |
+
"2,001-3,000": [6.00, 4.80, 3.95, 3.35, 2.81, 2.54, 2.27, 2.00],
|
| 26 |
+
"3,001-4,000": [6.50, 5.20, 4.30, 3.65, 3.09, 2.81, 2.53, 2.25],
|
| 27 |
+
"4,001-5,000": [7.00, 5.60, 4.65, 3.95, 3.37, 3.08, 2.79, 2.50],
|
| 28 |
+
"5,001-6,000": [7.50, 6.00, 5.00, 4.25, 3.65, 3.35, 3.05, 2.75],
|
| 29 |
+
"6,001-7,000": [8.00, 6.40, 5.35, 4.55, 3.93, 3.62, 3.31, 3.00],
|
| 30 |
+
"7,001-8,000": [8.50, 6.80, 5.70, 4.85, 4.21, 3.89, 3.57, 3.25],
|
| 31 |
+
"8,001-9,000": [9.00, 7.20, 6.05, 5.15, 4.49, 4.16, 3.83, 3.50],
|
| 32 |
+
"9,001-10,000": [9.50, 7.60, 6.40, 5.45, 4.77, 4.43, 4.09, 3.75],
|
| 33 |
+
"10,001-11,000": [10.00, 8.00, 6.75, 5.75, 5.05, 4.70, 4.35, 4.00],
|
| 34 |
+
"11,001-12,000": [10.50, 8.40, 7.10, 6.05, 5.33, 4.97, 4.61, 4.25],
|
| 35 |
+
"12,001-13,000": [11.00, 8.80, 7.45, 6.35, 5.61, 5.24, 4.87, 4.50],
|
| 36 |
+
"13,001-14,000": [11.50, 9.20, 7.80, 6.65, 5.89, 5.51, 5.13, 4.75],
|
| 37 |
+
"14,001-15,000": [12.00, 9.60, 8.15, 6.95, 6.17, 5.78, 5.39, 5.00],
|
| 38 |
+
"15,001-16,000": [12.50, 10.00, 8.50, 7.25, 6.45, 6.05, 5.65, 5.25],
|
| 39 |
+
"16,001-17,000": [13.00, 10.40, 8.85, 7.55, 6.73, 6.32, 5.91, 5.50],
|
| 40 |
+
"17,001-18,000": [13.50, 10.80, 9.20, 7.85, 7.01, 6.59, 6.17, 5.75],
|
| 41 |
+
"18,001-19,000": [14.00, 11.20, 9.55, 8.15, 7.29, 6.86, 6.43, 6.00],
|
| 42 |
+
"19,001-20,000": [14.50, 11.60, 9.90, 8.45, 7.57, 7.13, 6.69, 6.25]
|
| 43 |
+
}
|
| 44 |
+
|
| 45 |
+
df = pd.DataFrame(data)
|
| 46 |
+
|
| 47 |
+
net_value = 0
|
| 48 |
+
|
| 49 |
+
if option == 'Embroidery':
|
| 50 |
+
if "entries" not in st.session_state:
|
| 51 |
+
st.session_state.entries = []
|
| 52 |
+
|
| 53 |
+
st.subheader("Enter Item Details")
|
| 54 |
+
|
| 55 |
+
col1, col2, col3 = st.columns(3)
|
| 56 |
+
|
| 57 |
+
with col1:
|
| 58 |
+
quantity = st.number_input("Enter Quantity", min_value=1, step=1, key="quantity")
|
| 59 |
+
|
| 60 |
+
with col2:
|
| 61 |
+
stitch_count = st.selectbox(
|
| 62 |
+
"Select Stitch Count",
|
| 63 |
+
options=list(df.columns[1:]),
|
| 64 |
+
key="stitch_count"
|
| 65 |
+
)
|
| 66 |
+
|
| 67 |
+
with col3:
|
| 68 |
+
item_price = st.number_input('Item Cost (Fixed)', min_value=0.0, step=0.01, key="item_price")
|
| 69 |
+
|
| 70 |
+
use_manual_margin = st.checkbox("Use Manual Margin")
|
| 71 |
+
|
| 72 |
+
# Ensure selected_range & stitch_price are always calculated
|
| 73 |
+
selected_range = next((key for key, val in {
|
| 74 |
+
"0-11": range(0, 12), "12-17": range(12, 18), "18-23": range(18, 24),
|
| 75 |
+
"24-35": range(24, 36), "36-59": range(36, 60), "60-119": range(60, 120),
|
| 76 |
+
"120-239": range(120, 240), "240-600": range(240, 601)
|
| 77 |
+
}.items() if quantity in val), "240-600")
|
| 78 |
+
|
| 79 |
+
stitch_price = df[df["Quantity"] == selected_range][stitch_count].values[0]
|
| 80 |
+
|
| 81 |
+
# Calculate Net Value Before Margin Selection
|
| 82 |
+
net_value = quantity * (item_price + stitch_price)
|
| 83 |
+
|
| 84 |
+
if use_manual_margin:
|
| 85 |
+
margin_percentage = st.slider("Select Margin (%)", min_value=1.0, max_value=99.0, step=0.5, value=60.0, format="%.2f%%")
|
| 86 |
+
margin = margin_percentage / 100 # Manual margin stored as decimal
|
| 87 |
+
margin_display = f"{margin_percentage:.2f}%" # Display the exact selected percentage
|
| 88 |
+
else:
|
| 89 |
+
# Automatic Margin Selection
|
| 90 |
+
margin_data = [
|
| 91 |
+
(0, 0.4), (150, 0.5), (210, 0.525), (275, 0.55), (345, 0.575),
|
| 92 |
+
(840, 0.6), (1563, 0.625), (2600, 0.65), (4290, 0.66), (6030, 0.67)
|
| 93 |
+
]
|
| 94 |
+
margin = next((rate for threshold, rate in reversed(margin_data) if net_value >= threshold), 0.67)
|
| 95 |
+
margin_display = f"{margin * 100:.2f}%" # Convert automatic margin
|
| 96 |
+
|
| 97 |
+
|
| 98 |
+
if st.button("β Add Entry"):
|
| 99 |
+
if quantity and stitch_count and item_price:
|
| 100 |
+
total_selling_price = net_value / (1 - margin)
|
| 101 |
+
unit_selling_price = total_selling_price / quantity
|
| 102 |
+
entry = {
|
| 103 |
+
"Quantity": quantity,
|
| 104 |
+
"Stitch Count": stitch_count,
|
| 105 |
+
"Stitch Price": stitch_price,
|
| 106 |
+
"Total Net Cost": f"${net_value:,.2f}",
|
| 107 |
+
"Margin": margin_display,
|
| 108 |
+
"Unit Selling Price": f"${unit_selling_price:.2f}",
|
| 109 |
+
"Total Selling Price": f"${total_selling_price:.2f}"
|
| 110 |
+
}
|
| 111 |
+
st.session_state.entries.append(entry)
|
| 112 |
+
|
| 113 |
+
|
| 114 |
+
if st.session_state.entries:
|
| 115 |
+
st.subheader("Pricing Breakdown")
|
| 116 |
+
for i, entry in enumerate(st.session_state.entries):
|
| 117 |
+
st.text("==========================================================================")
|
| 118 |
+
st.write(f"### Entry {i+1}")
|
| 119 |
+
col1, col2, col3 = st.columns(3)
|
| 120 |
+
with col1:
|
| 121 |
+
st.metric(label="π Quantity", value=entry["Quantity"])
|
| 122 |
+
with col2:
|
| 123 |
+
#st.metric(label="πͺ‘ Stitch Count", value=entry["Stitch Count"])
|
| 124 |
+
st.metric(label="πͺ‘ Stitch Price (per unit)", value=entry["Stitch Price"])
|
| 125 |
+
with col3:
|
| 126 |
+
st.metric(label="π° Total Net Cost", value=entry["Total Net Cost"])
|
| 127 |
+
|
| 128 |
+
col1, col2, col3 = st.columns(3)
|
| 129 |
+
with col1:
|
| 130 |
+
st.metric(label="π Margin", value=entry["Margin"])
|
| 131 |
+
#st.metric(label="π Margin", value=f"{(entry['Margin']) * 100:.2f}%")
|
| 132 |
+
|
| 133 |
+
with col2:
|
| 134 |
+
st.metric(label=f"π Unit Price", value=entry["Unit Selling Price"])
|
| 135 |
+
with col3:
|
| 136 |
+
st.metric(label="π° Total Selling Price", value=entry["Total Selling Price"])
|
| 137 |
+
|
| 138 |
+
col1, col2 = st.columns(2)
|
| 139 |
+
with col1:
|
| 140 |
+
if st.button(f"β Delete {i+1}", key=f"delete_{i}"):
|
| 141 |
+
del st.session_state.entries[i]
|
| 142 |
+
st.rerun()
|
| 143 |
+
|
| 144 |
+
st.text("===========================================================================")
|
| 145 |
+
|
| 146 |
+
if st.button("π Reset Entries"):
|
| 147 |
+
st.session_state.entries = []
|
| 148 |
+
st.rerun()
|
| 149 |
+
|
| 150 |
+
elif option == 'Screen Print':
|
| 151 |
+
|
| 152 |
+
#Pricing data for screenprint.
|
| 153 |
+
pricing_data = {
|
| 154 |
+
"1-Color ScreenPrint": pd.DataFrame({
|
| 155 |
+
"Qty": ["below 6", "6 to 12", "13 - 18", "19 - 24", "25 - 36", "37 - 48","49 - 72", "73 - 96", "97 - 144", "145 - 288", "289 - 500", "Above 500"],
|
| 156 |
+
"4x4": [6.91, 4.26, 3.80, 3.23, 2.62, 2.26, 1.92, 1.60, 1.34, 1.05, 0.79, 0.63],
|
| 157 |
+
"12x12": [9.74, 5.70, 5.01, 4.12, 3.23, 2.68, 2.24, 1.75, 1.41, 1.19, 0.92, 0.75],
|
| 158 |
+
"14x16": [11.05, 6.73, 5.83, 4.75, 3.76, 3.19, 2.72, 2.21, 1.85, 1.55, 1.18, 1],
|
| 159 |
+
"Darks": [0.3, 0.3, 0.3, 0.3, 0.3, 0.2, 0.2, 0.2, 0.2, 0.1, 0.1, 0.1],
|
| 160 |
+
"Fleece": [0.55, 0.55, 0.55, 0.55, 0.55, 0.45, 0.45, 0.45, 0.45, 0.40, 0.40, 0.40],
|
| 161 |
+
"90% Poly+": [0.5, 0.5, 0.5, 0.5, 0.5, 0.4, 0.4, 0.4, 0.4, 0.25, 0.25, 0.25],
|
| 162 |
+
"Sleeves & Legs":[0.35,0.35,0.35,0.35,0.35,0.35,0.35,0.35,0.35,0.35,0.35,0.35]
|
| 163 |
+
}),
|
| 164 |
+
"2-Color ScreenPrint": pd.DataFrame({
|
| 165 |
+
"Qty": ["below 6", "6 to 12", "13 - 18", "19 - 24", "25 - 36", "37 - 48","49 - 72", "73 - 96", "97 - 144", "145 - 288", "289 - 500", "Above 500"],
|
| 166 |
+
"4x4": [10.24, 6.63, 5.67, 4.48, 3.58, 3.10, 2.46, 2.06, 1.62, 1.40, 1.05, 0.88],
|
| 167 |
+
"12x12": [15.58, 9.62, 8.06, 6.18, 4.80, 4.20, 3.36, 2.51, 2.05, 1.62, 1.31, 1.08],
|
| 168 |
+
"14x16": [17.95, 11.74, 9.70, 7.25, 5.76, 5.02, 4.09, 2.89, 2.41, 2.03, 1.72, 1.51],
|
| 169 |
+
"Darks": [0.4, 0.4, 0.4, 0.4, 0.4, 0.3, 0.3, 0.3, 0.3, 0.2, 0.2, 0.2],
|
| 170 |
+
"Fleece": [0.55, 0.55, 0.55, 0.55, 0.55, 0.45, 0.45, 0.45, 0.45, 0.40, 0.40, 0.40],
|
| 171 |
+
"90% Poly+": [0.5, 0.5, 0.5, 0.5, 0.5, 0.4, 0.4, 0.4, 0.4, 0.25, 0.25, 0.25],
|
| 172 |
+
"Sleeves & Legs":[0.35,0.35,0.35,0.35,0.35,0.35,0.35,0.35,0.35,0.35,0.35,0.35]
|
| 173 |
+
}),
|
| 174 |
+
"3-Color ScreenPrint": pd.DataFrame({
|
| 175 |
+
"Qty": ["below 6", "6 to 12", "13 - 18", "19 - 24", "25 - 36", "37 - 48","49 - 72", "73 - 96", "97 - 144", "145 - 288", "289 - 500", "Above 500"],
|
| 176 |
+
"4x4": [14.99, 9.38, 7.85, 6.02, 4.77, 3.93, 3.35, 2.72, 2.30, 1.78, 1.51, 1.32],
|
| 177 |
+
"12x12": [24.02, 14.32, 11.85, 8.89, 7.02, 5.71, 4.74, 3.41, 2.81, 2.43, 2.04, 1.75],
|
| 178 |
+
"14x16": [27.50, 16.54, 13.72, 10.32, 8.26, 6.91, 5.64, 4.27, 3.60, 3.01, 2.63, 2.24],
|
| 179 |
+
"Darks": [0.5, 0.5, 0.5, 0.5, 0.5, 0.4, 0.4, 0.4, 0.4, 0.3, 0.3, 0.3],
|
| 180 |
+
"Fleece": [0.55, 0.55, 0.55, 0.55, 0.55, 0.45, 0.45, 0.45, 0.45, 0.40, 0.40, 0.40],
|
| 181 |
+
"90% Poly+": [0.5, 0.5, 0.5, 0.5, 0.5, 0.4, 0.4, 0.4, 0.4, 0.25, 0.25, 0.25],
|
| 182 |
+
"Sleeves & Legs":[0.35,0.35,0.35,0.35,0.35,0.35,0.35,0.35,0.35,0.35,0.35,0.35]
|
| 183 |
+
}),
|
| 184 |
+
"4-Color ScreenPrint": pd.DataFrame({
|
| 185 |
+
"Qty": ["below 6", "6 to 12", "13 - 18", "19 - 24", "25 - 36", "37 - 48","49 - 72", "73 - 96", "97 - 144", "145 - 288", "289 - 500", "Above 500"],
|
| 186 |
+
"4x4": [17.58, 10.86, 9.15, 7.07, 5.52, 4.69, 3.92, 3.32, 2.85, 2.15, 2.01, 1.80],
|
| 187 |
+
"12x12": [28.67, 17.00, 14.13, 10.69, 8.36, 7.08, 5.64, 4.35, 3.62, 2.77, 2.42, 2.29],
|
| 188 |
+
"14x16": [33.32, 19.67, 17.09, 13.87, 9.82, 8.45, 6.85, 5.36, 4.62, 3.47, 3.12, 2.91],
|
| 189 |
+
"Darks": [0.6, 0.6, 0.6, 0.6, 0.6, 0.5, 0.5, 0.5, 0.5, 0.4, 0.4, 0.4],
|
| 190 |
+
"Fleece": [0.55, 0.55, 0.55, 0.55, 0.55, 0.45, 0.45, 0.45, 0.45, 0.40, 0.40, 0.40],
|
| 191 |
+
"90% Poly+": [0.5, 0.5, 0.5, 0.5, 0.5, 0.4, 0.4, 0.4, 0.4, 0.25, 0.25, 0.25],
|
| 192 |
+
"Sleeves & Legs":[0.35,0.35,0.35,0.35,0.35,0.35,0.35,0.35,0.35,0.35,0.35,0.35]
|
| 193 |
+
}),
|
| 194 |
+
"5-Color ScreenPrint": pd.DataFrame({
|
| 195 |
+
"Qty": ["below 6", "6 to 12", "13 - 18", "19 - 24", "25 - 36", "37 - 48","49 - 72", "73 - 96", "97 - 144", "145 - 288", "289 - 500", "Above 500"],
|
| 196 |
+
"4x4": [22.22, 12.98, 10.85, 8.26, 6.45, 5.47, 4.58, 3.82, 3.35, 2.51, 2.36, 2.22],
|
| 197 |
+
"12x12": [36.63, 20.84, 17.16, 12.74, 9.90, 8.45, 6.53, 5.25, 4.34, 3.14, 2.77, 2.71],
|
| 198 |
+
"14x16": [43.00, 24.07, 20.28, 15.68, 11.71, 10.00, 8.09, 6.45, 5.36, 3.98, 3.53, 3.26],
|
| 199 |
+
"Darks": [0.75, 0.75, 0.75, 0.75, 0.75, 0.6, 0.6, 0.6, 0.6, 0.5, 0.5, 0.5],
|
| 200 |
+
"Fleece": [0.55, 0.55, 0.55, 0.55, 0.55, 0.45, 0.45, 0.45, 0.45, 0.40, 0.40, 0.40],
|
| 201 |
+
"90% Poly+": [0.5, 0.5, 0.5, 0.5, 0.5, 0.4, 0.4, 0.4, 0.4, 0.25, 0.25, 0.25],
|
| 202 |
+
"Sleeves & Legs":[0.35,0.35,0.35,0.35,0.35,0.35,0.35,0.35,0.35,0.35,0.35,0.35]
|
| 203 |
+
}),
|
| 204 |
+
"6-Color ScreenPrint": pd.DataFrame({
|
| 205 |
+
"Qty": ["below 6", "6 to 12", "13 - 18", "19 - 24", "25 - 36", "37 - 48","49 - 72", "73 - 96", "97 - 144", "145 - 288", "289 - 500", "Above 500"],
|
| 206 |
+
"4x4": [26.13, 15.18, 12.59, 9.47, 7.41, 6.23, 4.90, 4.43, 3.85, 2.96, 2.70, 2.57],
|
| 207 |
+
"12x12": [43.39, 24.70, 20.53, 13.01, 11.46, 9.81, 7.51, 6.19, 5.10, 3.60, 3.05, 2.91],
|
| 208 |
+
"14x16": [51.21, 28.57, 23.48, 17.40, 13.62, 11.52, 9.30, 7.63, 6.19, 4.58, 3.88, 3.68],
|
| 209 |
+
"Darks": [0.85, 0.85, 0.85, 0.85, 0.85, 0.7, 0.7, 0.7, 0.7, 0.6, 0.6, 0.6],
|
| 210 |
+
"Fleece": [0.55, 0.55, 0.55, 0.55, 0.55, 0.45, 0.45, 0.45, 0.45, 0.40, 0.40, 0.40],
|
| 211 |
+
"90% Poly+": [0.5, 0.5, 0.5, 0.5, 0.5, 0.4, 0.4, 0.4, 0.4, 0.25, 0.25, 0.25],
|
| 212 |
+
"Sleeves & Legs":[0.35,0.35,0.35,0.35,0.35,0.35,0.35,0.35,0.35,0.35,0.35,0.35]
|
| 213 |
+
}),
|
| 214 |
+
}
|
| 215 |
+
|
| 216 |
+
|
| 217 |
+
# --- Margin Matrix ---
|
| 218 |
+
margin_data = [
|
| 219 |
+
(0, 0.4), (150, 0.5), (210, 0.525), (275, 0.55), (345, 0.575),
|
| 220 |
+
(840, 0.6), (1563, 0.625), (2600, 0.65), (4290, 0.66), (6030, 0.67)
|
| 221 |
+
]
|
| 222 |
+
|
| 223 |
+
# --- Session State Initialization ---
|
| 224 |
+
if "entries_screenprint" not in st.session_state:
|
| 225 |
+
st.session_state.entries_screenprint = []
|
| 226 |
+
|
| 227 |
+
# --- User Inputs ---
|
| 228 |
+
col1, col2, col3, col4 = st.columns(4)
|
| 229 |
+
selected_type = col1.selectbox("Select Screen Print Type", pricing_data.keys())
|
| 230 |
+
selected_size = col2.selectbox("Select Print Size", ["4x4", "12x12", "14x16"])
|
| 231 |
+
quantity = col3.number_input("Enter Quantity", min_value=1, step=1)
|
| 232 |
+
selected_options = col4.multiselect("Select Options", ["Darks", "Fleece", "90% Poly+", "Sleeves & Legs"])
|
| 233 |
+
item_cost = st.number_input("Enter Base Item Cost", min_value=0.0, step=0.01)
|
| 234 |
+
|
| 235 |
+
use_manual_margin = st.checkbox("Use Manual Margin")
|
| 236 |
+
|
| 237 |
+
# --- Determine the Price Tier Based on Quantity ---
|
| 238 |
+
df = pricing_data[selected_type]
|
| 239 |
+
selected_range = next((row["Qty"] for _, row in df.iterrows() if quantity <= int(row["Qty"].split()[-1])), "Above 500")
|
| 240 |
+
|
| 241 |
+
# --- Fetch the Price Per Unit ---
|
| 242 |
+
row = df[df["Qty"] == selected_range]
|
| 243 |
+
if not row.empty and item_cost > 0:
|
| 244 |
+
base_price = row[selected_size].values[0]
|
| 245 |
+
unit_price_before_margin = base_price + item_cost
|
| 246 |
+
|
| 247 |
+
# --- Apply Additional Costs ---
|
| 248 |
+
for option in selected_options:
|
| 249 |
+
unit_price_before_margin += row[option].values[0]
|
| 250 |
+
|
| 251 |
+
# --- Calculate Total Cost Before Margin ---
|
| 252 |
+
total_cost = unit_price_before_margin * quantity
|
| 253 |
+
|
| 254 |
+
# --- Apply Manual or Automatic Margin ---
|
| 255 |
+
if use_manual_margin:
|
| 256 |
+
margin_percentage = st.slider(
|
| 257 |
+
"Select Margin (%)",
|
| 258 |
+
min_value=1.0,
|
| 259 |
+
max_value=99.0,
|
| 260 |
+
step=0.5,
|
| 261 |
+
value=60.0,
|
| 262 |
+
format="%.2f%%"
|
| 263 |
+
)
|
| 264 |
+
margin = margin_percentage / 100 # β
correct logic
|
| 265 |
+
margin_display = f"{margin_percentage:.2f}%" # β
matches manual input
|
| 266 |
+
else:
|
| 267 |
+
margin_data = [
|
| 268 |
+
(0, 0.4), (150, 0.5), (210, 0.525), (275, 0.55), (345, 0.575),
|
| 269 |
+
(840, 0.6), (1563, 0.625), (2600, 0.65), (4290, 0.66), (6030, 0.67)
|
| 270 |
+
]
|
| 271 |
+
margin = next((rate for threshold, rate in reversed(margin_data) if total_cost >= threshold), 0.67)
|
| 272 |
+
margin_display = f"{margin * 100:.2f}%" # β
correct display
|
| 273 |
+
|
| 274 |
+
|
| 275 |
+
# --- Calculate Selling Prices ---
|
| 276 |
+
total_selling_price = total_cost / (1-margin)
|
| 277 |
+
unit_price_after_margin = total_selling_price / quantity
|
| 278 |
+
|
| 279 |
+
# --- Add Entry Button ---
|
| 280 |
+
if st.button("β Add Entry"):
|
| 281 |
+
entry = {
|
| 282 |
+
"Quantity": quantity,
|
| 283 |
+
"Print Type": selected_type,
|
| 284 |
+
"Print Size": selected_size,
|
| 285 |
+
"Options": ", ".join(selected_options) if selected_options else "None",
|
| 286 |
+
"Item Cost": item_cost,
|
| 287 |
+
"Base Price Per Unit": base_price,
|
| 288 |
+
"Total Cost": total_cost,
|
| 289 |
+
"Margin": margin,
|
| 290 |
+
"Unit Price After Margin": unit_price_after_margin,
|
| 291 |
+
"Total Selling Price": total_selling_price
|
| 292 |
+
}
|
| 293 |
+
st.session_state.entries_screenprint.append(entry)
|
| 294 |
+
|
| 295 |
+
# --- Display Pricing Breakdown ---
|
| 296 |
+
if st.session_state.entries_screenprint:
|
| 297 |
+
st.subheader("Pricing Breakdown")
|
| 298 |
+
|
| 299 |
+
for i, entry in enumerate(st.session_state.entries_screenprint):
|
| 300 |
+
st.text("===========================================================================")
|
| 301 |
+
|
| 302 |
+
st.write(f"### Entry {i+1}")
|
| 303 |
+
|
| 304 |
+
col1, col2, col3 = st.columns(3)
|
| 305 |
+
|
| 306 |
+
with col1:
|
| 307 |
+
st.metric(label="β
Quantity", value=entry["Quantity"])
|
| 308 |
+
st.metric(label="π° Total Net Cost", value=f"${entry['Total Cost']:.2f}")
|
| 309 |
+
|
| 310 |
+
with col2:
|
| 311 |
+
st.metric(label="π Print Size", value=entry["Print Size"])
|
| 312 |
+
#st.metric(label="π Margin", value=f"{(1 - entry['Margin']) * 100:.2f}%")
|
| 313 |
+
st.metric(label="π Margin", value=f"{entry['Margin'] * 100:.2f}%")
|
| 314 |
+
|
| 315 |
+
|
| 316 |
+
with col3:
|
| 317 |
+
st.metric(label="π¨ Print Option", value=entry["Options"])
|
| 318 |
+
st.metric(label="πΈ Unit Selling Price", value=f"${entry['Unit Price After Margin']:.2f}")
|
| 319 |
+
st.metric(label="π΅ Total Selling Price", value=f"${entry['Total Selling Price']:.2f}")
|
| 320 |
+
|
| 321 |
+
# Delete Button
|
| 322 |
+
if st.button(f"β Delete {i+1}", key=f"delete_{i}"):
|
| 323 |
+
del st.session_state.entries_screenprint[i]
|
| 324 |
+
st.rerun()
|
| 325 |
+
if st.button("π Reset Entries"):
|
| 326 |
+
st.session_state.entries_screenprint = []
|
| 327 |
+
st.rerun()
|
| 328 |
+
|
| 329 |
+
|
| 330 |
+
elif option == 'Embroidery & Screen Print':
|
| 331 |
+
st.text('\nDevelopment in Progress.......')
|
| 332 |
+
|
| 333 |
+
def footer():
|
| 334 |
+
theme_bg = st.get_option("theme.backgroundColor")
|
| 335 |
+
theme_text = st.get_option("theme.textColor")
|
| 336 |
+
theme_primary = st.get_option("theme.primaryColor")
|
| 337 |
+
|
| 338 |
+
footer_html = f"""
|
| 339 |
+
<style>
|
| 340 |
+
@media (max-width: 768px) {{
|
| 341 |
+
.footer {{ display: none; }}
|
| 342 |
+
}}
|
| 343 |
+
|
| 344 |
+
.footer {{
|
| 345 |
+
position: fixed;
|
| 346 |
+
bottom: 0;
|
| 347 |
+
width: 100%;
|
| 348 |
+
background-color: {theme_bg}; /* Uses Streamlit theme background */
|
| 349 |
+
color: {theme_text}; /* Uses Streamlit theme text color */
|
| 350 |
+
text-align: center;
|
| 351 |
+
padding: 10px;
|
| 352 |
+
font-size: 14px;
|
| 353 |
+
border-top: 1px solid rgba(255, 255, 255, 0.2); /* Semi-transparent border */
|
| 354 |
+
}}
|
| 355 |
+
.footer a {{
|
| 356 |
+
color: {theme_primary}; /* Uses Streamlit theme primary color */
|
| 357 |
+
text-decoration: none;
|
| 358 |
+
margin: 0 10px;
|
| 359 |
+
}}
|
| 360 |
+
</style>
|
| 361 |
+
<div class="footer">
|
| 362 |
+
© 2025 Key Innovations Inc. All rights reserved.
|
| 363 |
+
<br>
|
| 364 |
+
Follow us on:
|
| 365 |
+
<a href="https://www.linkedin.com/company/keyinnovations" target="_blank">LinkedIn</a> |
|
| 366 |
+
<a href="https://twitter.com/keyinnovations" target="_blank">Twitter</a> |
|
| 367 |
+
<a href="https://www.instagram.com/key.innovations/" target="_blank">Instagram</a> |
|
| 368 |
+
<a href="https://www.facebook.com/KeyInnovations/" target="_blank">Facebook</a>
|
| 369 |
+
</div>
|
| 370 |
+
"""
|
| 371 |
+
st.markdown(footer_html, unsafe_allow_html=True)
|
| 372 |
+
footer()
|
requirements.txt
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
streamlit==1.42.0
|
| 2 |
+
pandas==2.2.0
|
| 3 |
+
|