270 lines
10 KiB
Python
270 lines
10 KiB
Python
import requests
|
|
import streamlit as st
|
|
|
|
from functions import (
|
|
API_BASE,
|
|
_auth_headers,
|
|
build_order_payload,
|
|
create_client,
|
|
fetch_clients,
|
|
fetch_presets,
|
|
is_water_inci,
|
|
make_empty_ingredient_df,
|
|
validate_order,
|
|
)
|
|
from functions_ui import display_orderData
|
|
|
|
st.set_page_config(page_title="Creazione Ordine", layout="wide")
|
|
st.title("Creazione Ordine")
|
|
|
|
# --- Session state init ---
|
|
if "order_presets" not in st.session_state:
|
|
st.session_state.order_presets = None
|
|
if "order_clients" not in st.session_state:
|
|
st.session_state.order_clients = None
|
|
if "order_submitted" not in st.session_state:
|
|
st.session_state.order_submitted = False
|
|
if "order_result" not in st.session_state:
|
|
st.session_state.order_result = None
|
|
if "ingredient_df" not in st.session_state:
|
|
st.session_state.ingredient_df = make_empty_ingredient_df()
|
|
if "new_client_mode" not in st.session_state:
|
|
st.session_state.new_client_mode = False
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# 1. Istruzioni
|
|
# ---------------------------------------------------------------------------
|
|
with st.expander("Istruzioni per la compilazione", expanded=False):
|
|
st.markdown("""
|
|
### Come compilare l'ordine
|
|
|
|
**Campi obbligatori:**
|
|
- **Nome del cliente**: Selezionare un cliente esistente dal menu a tendina, oppure
|
|
scegliere *"+ Nuovo cliente..."* per inserirne uno nuovo.
|
|
- **Nome del prodotto**: Il nome commerciale del prodotto cosmetico.
|
|
- **Preset di esposizione**: Selezionare il preset che descrive le condizioni di utilizzo
|
|
del prodotto (es. crema viso leave-on, shampoo rinse-off, ecc.).
|
|
I preset si creano dalla pagina *Esposizione*.
|
|
|
|
**Tabella ingredienti:**
|
|
- **INCI**: Nome INCI dell'ingrediente (facoltativo, ma consigliato).
|
|
Se si inserisce *AQUA*, *Water*, *Eau* o varianti, la valutazione tossicologica viene
|
|
automaticamente saltata e il CAS puo essere lasciato vuoto.
|
|
- **CAS**: Numero CAS nel formato standard (es. `56-81-5`). Ogni riga deve contenere
|
|
**un solo** CAS number.
|
|
- **Percentuale (%)**: Percentuale in peso dell'ingrediente nella formulazione.
|
|
Sono ammessi fino a 6 decimali (es. `0.000200`).
|
|
- **Colorante / CAS speciale**: Selezionare per coloranti (CI xxxxx) o sostanze con
|
|
codici non-standard. Il formato CAS non verra validato.
|
|
- **Salta tossicologia**: Selezionare per ingredienti che non richiedono valutazione
|
|
tossicologica. Viene impostato automaticamente per AQUA/Water.
|
|
|
|
**Regole:**
|
|
- La somma delle percentuali deve essere esattamente **100%** (tolleranza: +/- 0.01%).
|
|
- E possibile aggiungere o rimuovere righe dalla tabella cliccando i pulsanti in basso.
|
|
- Compilare tutti i campi obbligatori prima di inviare l'ordine.
|
|
""")
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# 2. Carica ordine esistente
|
|
# ---------------------------------------------------------------------------
|
|
st.markdown("---")
|
|
st.subheader("Carica ordine esistente")
|
|
|
|
col_lookup, col_lookup_btn = st.columns([3, 1])
|
|
with col_lookup:
|
|
order_id_input = st.number_input(
|
|
"ID Ordine",
|
|
min_value=1,
|
|
step=1,
|
|
value=None,
|
|
placeholder="es. 42",
|
|
help="Inserire l'ID numerico di un ordine esistente per precompilare il modulo.",
|
|
)
|
|
with col_lookup_btn:
|
|
st.write("")
|
|
st.write("")
|
|
lookup_clicked = st.button("Carica", disabled=order_id_input is None)
|
|
|
|
if lookup_clicked:
|
|
st.info(
|
|
f"Funzionalita in sviluppo. L'ordine con ID {order_id_input} "
|
|
"verra caricato automaticamente quando l'endpoint API sara disponibile."
|
|
)
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# 3. Caricamento dati da API (preset + clienti)
|
|
# ---------------------------------------------------------------------------
|
|
if st.session_state.order_presets is None:
|
|
st.session_state.order_presets = fetch_presets()
|
|
if st.session_state.order_clients is None:
|
|
st.session_state.order_clients = fetch_clients()
|
|
|
|
preset_names = st.session_state.order_presets
|
|
client_list = st.session_state.order_clients
|
|
client_names = [c["nome_cliente"] for c in client_list]
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# 4. Form fields
|
|
# ---------------------------------------------------------------------------
|
|
st.markdown("---")
|
|
st.subheader("Dati ordine")
|
|
|
|
col_left, col_right = st.columns(2)
|
|
|
|
with col_left:
|
|
dropdown_options = client_names + ["+ Nuovo cliente..."]
|
|
selected_client_option = st.selectbox("Nome del cliente *", options=dropdown_options)
|
|
|
|
client_name = ""
|
|
if selected_client_option == "+ Nuovo cliente...":
|
|
new_client_name = st.text_input(
|
|
"Nome nuovo cliente",
|
|
placeholder="es. Cosmetica Italia S.r.l.",
|
|
)
|
|
if new_client_name.strip():
|
|
if st.button("Aggiungi cliente"):
|
|
success = create_client(new_client_name.strip())
|
|
if success:
|
|
st.success(f"Cliente '{new_client_name.strip()}' aggiunto.")
|
|
st.session_state.order_clients = fetch_clients()
|
|
st.rerun()
|
|
else:
|
|
st.error("Errore nella creazione del cliente.")
|
|
client_name = new_client_name.strip()
|
|
else:
|
|
client_name = selected_client_option
|
|
|
|
product_name = st.text_input("Nome del prodotto *", placeholder="es. Crema idratante viso")
|
|
|
|
with col_right:
|
|
if preset_names:
|
|
selected_preset = st.selectbox("Preset di esposizione *", options=preset_names)
|
|
else:
|
|
st.warning("Nessun preset disponibile. Creare un preset nella pagina Esposizione.")
|
|
selected_preset = None
|
|
|
|
col_reload_p, col_reload_c = st.columns(2)
|
|
with col_reload_p:
|
|
if st.button("Ricarica preset", key="reload_presets"):
|
|
st.session_state.order_presets = fetch_presets()
|
|
st.rerun()
|
|
with col_reload_c:
|
|
if st.button("Ricarica clienti", key="reload_clients"):
|
|
st.session_state.order_clients = fetch_clients()
|
|
st.rerun()
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# 5. Tabella ingredienti
|
|
# ---------------------------------------------------------------------------
|
|
st.markdown("---")
|
|
st.subheader("Ingredienti")
|
|
|
|
column_config = {
|
|
"inci": st.column_config.TextColumn(
|
|
"INCI",
|
|
help="Nome INCI (facoltativo). Inserire AQUA/Water per saltare automaticamente la tossicologia.",
|
|
width="medium",
|
|
),
|
|
"cas": st.column_config.TextColumn(
|
|
"CAS",
|
|
help="Numero CAS (es. 56-81-5). Puo essere vuoto per AQUA/Water.",
|
|
width="medium",
|
|
),
|
|
"percentage": st.column_config.NumberColumn(
|
|
"Percentuale (%)",
|
|
help="Percentuale in peso (fino a 6 decimali)",
|
|
min_value=0.0,
|
|
max_value=100.0,
|
|
format="%.6f",
|
|
width="small",
|
|
),
|
|
"is_colorante": st.column_config.CheckboxColumn(
|
|
"Colorante",
|
|
help="Coloranti o CAS speciali: bypassa la validazione del formato CAS",
|
|
default=False,
|
|
width="small",
|
|
),
|
|
"skip_tox": st.column_config.CheckboxColumn(
|
|
"Salta Tox",
|
|
help="Salta la valutazione tossicologica (automatico per AQUA/Water)",
|
|
default=False,
|
|
width="small",
|
|
),
|
|
}
|
|
|
|
edited_df = st.data_editor(
|
|
st.session_state.ingredient_df,
|
|
column_config=column_config,
|
|
num_rows="dynamic",
|
|
width="stretch",
|
|
hide_index=True,
|
|
key="ingredients_editor",
|
|
)
|
|
|
|
_aqua_count = sum(is_water_inci(v) for v in edited_df["inci"].fillna(""))
|
|
if _aqua_count > 0:
|
|
st.info(f"{_aqua_count} ingrediente/i rilevato/i come AQUA/Water: tossicologia saltata automaticamente.")
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# 6. Validazione
|
|
# ---------------------------------------------------------------------------
|
|
errors = validate_order(client_name, product_name, selected_preset, edited_df)
|
|
|
|
if errors:
|
|
st.markdown("---")
|
|
for err in errors:
|
|
st.error(err)
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# 7. Submit
|
|
# ---------------------------------------------------------------------------
|
|
st.markdown("---")
|
|
|
|
can_submit = (len(errors) == 0) and (not st.session_state.order_submitted)
|
|
|
|
if st.button("Invia Ordine", type="primary", disabled=not can_submit):
|
|
payload = build_order_payload(client_name, product_name, selected_preset, edited_df)
|
|
st.session_state.order_result = payload
|
|
|
|
with st.spinner("Invio ordine in corso..."):
|
|
try:
|
|
resp = requests.post(
|
|
f"{API_BASE}/orders/create",
|
|
json=payload,
|
|
headers=_auth_headers(),
|
|
timeout=30,
|
|
)
|
|
result = resp.json()
|
|
|
|
if result.get("success"):
|
|
st.session_state.order_submitted = True
|
|
id_ordine = result.get("id_ordine")
|
|
st.success(f"Ordine #{id_ordine} creato. Elaborazione avviata in background.")
|
|
display_orderData(payload)
|
|
else:
|
|
st.error(result.get("error") or result.get("detail", "Errore nella creazione dell'ordine"))
|
|
|
|
except requests.ConnectionError:
|
|
st.error("Impossibile connettersi all'API. Verifica che il server sia attivo.")
|
|
except Exception as e:
|
|
st.error(f"Errore: {e}")
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# 8. Debug JSON
|
|
# ---------------------------------------------------------------------------
|
|
if st.session_state.order_result is not None:
|
|
with st.expander("Debug - Raw JSON"):
|
|
st.json(st.session_state.order_result)
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# 9. Reset
|
|
# ---------------------------------------------------------------------------
|
|
st.markdown("---")
|
|
if st.button("Nuovo Ordine", key="reset_order"):
|
|
st.session_state.order_submitted = False
|
|
st.session_state.order_result = None
|
|
st.session_state.ingredient_df = make_empty_ingredient_df()
|
|
if "ingredients_editor" in st.session_state:
|
|
del st.session_state["ingredients_editor"]
|
|
st.rerun()
|