Spaces:
Sleeping
Sleeping
Update src/streamlit_app.py
Browse files- src/streamlit_app.py +104 -109
src/streamlit_app.py
CHANGED
|
@@ -10,9 +10,9 @@ def generate_poste_dat_file(df_shipping_mark_input, df_shipment_order_input):
|
|
| 10 |
|
| 11 |
Args:
|
| 12 |
df_shipping_mark_input (pd.DataFrame): DataFrame contenente le shipping mark e i riferimenti d'ordine.
|
| 13 |
-
Deve contenere le colonne 'shippingMark'
|
| 14 |
df_shipment_order_input (pd.DataFrame): DataFrame contenente i dettagli dell'ordine.
|
| 15 |
-
Deve contenere 'orderRef' e i campi relativi al destinatario.
|
| 16 |
|
| 17 |
Returns:
|
| 18 |
tuple: (str, str) contenente i percorsi dei file .dat e .dis generati, o (None, None) in caso di errore.
|
|
@@ -23,12 +23,12 @@ def generate_poste_dat_file(df_shipping_mark_input, df_shipment_order_input):
|
|
| 23 |
df_shipment_order = df_shipment_order_input.copy()
|
| 24 |
|
| 25 |
# Assicurati che i nomi delle colonne siano in minuscolo per una maggiore robustezza
|
| 26 |
-
# Nota: per nomi con punti come 'consignee.partner.address.address', .lower() li mantiene così.
|
| 27 |
df_shipping_mark.columns = df_shipping_mark.columns.str.lower()
|
| 28 |
df_shipment_order.columns = df_shipment_order.columns.str.lower()
|
| 29 |
|
| 30 |
# Verifica la presenza delle colonne necessarie nel file shipping mark
|
| 31 |
-
|
|
|
|
| 32 |
if not all(col in df_shipping_mark.columns for col in required_shipping_mark_cols):
|
| 33 |
st.error(f"Il file delle shipping mark deve contenere le colonne: {required_shipping_mark_cols}")
|
| 34 |
return None, None
|
|
@@ -36,54 +36,32 @@ def generate_poste_dat_file(df_shipping_mark_input, df_shipment_order_input):
|
|
| 36 |
# Mapping dei nomi delle colonne dal file ShipmentOrder_ ai nomi interni/standardizzati.
|
| 37 |
# **IMPORTANTE**: Le chiavi di questo dizionario devono corrispondere esattamente
|
| 38 |
# ai nomi delle colonne (in minuscolo) presenti nel tuo file ShipmentOrder_.xlsx.
|
|
|
|
|
|
|
| 39 |
column_mapping = {
|
| 40 |
-
# Campi
|
| 41 |
-
|
| 42 |
-
'
|
| 43 |
-
'
|
| 44 |
-
'
|
| 45 |
-
'
|
| 46 |
-
'
|
| 47 |
-
'
|
| 48 |
-
'
|
| 49 |
-
'
|
| 50 |
-
'
|
| 51 |
-
|
| 52 |
-
'peso colli': 'package_weight',
|
| 53 |
-
# 'codice servizio' è ora un valore fisso STD (Campo 34)
|
| 54 |
-
# 'identificativo collo' (shippingMark) viene dal primo file (Campo 42)
|
| 55 |
-
# 'filiale' è ora un valore fisso FIL05 (Campo 43)
|
| 56 |
-
# 'tipo cliente' è ora un valore fisso MGCS (Campo 49)
|
| 57 |
}
|
| 58 |
|
| 59 |
-
# Aggiorna il mapping basandosi sui nomi di colonna con punti che hai fornito
|
| 60 |
-
# Se i tuoi nomi di colonna sono letteralmente "consignee.partner.address.address" etc.
|
| 61 |
-
# allora le chiavi del mapping DEVONO essere esattamente così, in minuscolo.
|
| 62 |
-
# Potrebbe essere necessario verificare i nomi esatti delle colonne nel tuo Excel.
|
| 63 |
-
# Ad esempio, se la colonna è "consignee.partner.address.address" in Excel, allora
|
| 64 |
-
# df_shipment_order.columns.str.lower() darà "consignee.partner.address.address".
|
| 65 |
-
# Qui aggiorno per includere i nomi con i punti come chiavi.
|
| 66 |
-
column_mapping.update({
|
| 67 |
-
'consignee.partner.contact.name': 'consignee_contact',
|
| 68 |
-
'consignee.partner.address.streettype': 'consignee_street_type',
|
| 69 |
-
'consignee.partner.address.address': 'consignee_address',
|
| 70 |
-
'consignee.partner.address.housenumber': 'consignee_house_number',
|
| 71 |
-
'consignee.partner.phone.number': 'consignee_phone',
|
| 72 |
-
'consignee.partner.address.zipcode': 'consignee_zip_code',
|
| 73 |
-
'consignee.partner.address.city': 'consignee_city',
|
| 74 |
-
'consignee.partner.address.province': 'consignee_province',
|
| 75 |
-
})
|
| 76 |
-
|
| 77 |
-
|
| 78 |
# Applica il mapping delle colonne a df_shipment_order.
|
| 79 |
-
# La funzione lambda `x
|
| 80 |
# e poi cerca nel `column_mapping`.
|
| 81 |
df_shipment_order = df_shipment_order.rename(columns=lambda x: column_mapping.get(x, x))
|
| 82 |
|
| 83 |
-
|
| 84 |
-
|
| 85 |
-
if
|
| 86 |
-
st.error(f"Il file degli ordini deve contenere
|
| 87 |
return None, None
|
| 88 |
|
| 89 |
# Pulisci le colonne di join per una corrispondenza robusta (rimuovi spazi e assicurati che siano stringhe)
|
|
@@ -104,16 +82,16 @@ def generate_poste_dat_file(df_shipping_mark_input, df_shipment_order_input):
|
|
| 104 |
# Basato sul NUOVO tracciato fornito.
|
| 105 |
fixed_and_default_values = {
|
| 106 |
# Campi fissi Mittente (Campi 1-18) - Valori dalla tua ultima tabella
|
| 107 |
-
'
|
| 108 |
'id_ldv_fixed': 'FIL01', # Campo 2
|
| 109 |
'tipo_spedizione_fixed': 'N', # Campo 3
|
| 110 |
-
'
|
| 111 |
-
'
|
| 112 |
# Campo 6 (orderref) dal file
|
| 113 |
-
|
| 114 |
'ritiro_mp_fixed': 'P', # Campo 8
|
| 115 |
'codice_indirizzo_mittente_fixed': 'IND001', # Campo 9
|
| 116 |
-
'ragione_sociale_mittente_fixed': '
|
| 117 |
'contatto_mittente_fixed': 'postelogistics@poste.it', # Campo 11
|
| 118 |
'tipo_via_mittente_fixed': 'Via', # Campo 12
|
| 119 |
'via_mittente_fixed': 'Via da Inserire', # Campo 13
|
|
@@ -136,43 +114,44 @@ def generate_poste_dat_file(df_shipping_mark_input, df_shipment_order_input):
|
|
| 136 |
# 'derived' (calcolato, es. data_spedizione_derived)
|
| 137 |
dat_format_spec = [
|
| 138 |
# BLOCCO MITTENTE FISSO (Campi 1-18 dal NUOVO tracciato)
|
| 139 |
-
{'name': '
|
| 140 |
-
{'name': 'id_ldv_fixed', 'length': 30, 'numeric_pad_zero': False, 'decimals': 0, 'mandatory': True
|
| 141 |
-
{'name': 'tipo_spedizione_fixed', 'length': 1, 'numeric_pad_zero': False, 'decimals': 0, 'mandatory': True
|
| 142 |
-
{'name': '
|
| 143 |
-
{'name': '
|
| 144 |
-
{'name': 'orderref', 'length': 25, 'numeric_pad_zero': False, 'decimals': 0, 'mandatory': True
|
| 145 |
-
{'name': '
|
| 146 |
-
{'name': 'ritiro_mp_fixed', 'length': 1, 'numeric_pad_zero': False, 'decimals': 0, 'mandatory': True
|
| 147 |
-
{'name': 'codice_indirizzo_mittente_fixed', 'length': 20, 'numeric_pad_zero': False, 'decimals': 0, 'mandatory': True
|
| 148 |
-
{'name': 'ragione_sociale_mittente_fixed', 'length': 40, 'numeric_pad_zero': False, 'decimals': 0, 'mandatory': True
|
| 149 |
-
{'name': 'contatto_mittente_fixed', 'length': 20, 'numeric_pad_zero': False, 'decimals': 0, 'mandatory': True
|
| 150 |
-
{'name': 'tipo_via_mittente_fixed', 'length': 5, 'numeric_pad_zero': False, 'decimals': 0, 'mandatory': True
|
| 151 |
-
{'name': 'via_mittente_fixed', 'length': 30, 'numeric_pad_zero': False, 'decimals': 0, 'mandatory': True
|
| 152 |
-
{'name': 'numero_civico_mittente_fixed','length': 5, 'numeric_pad_zero': False, 'decimals': 0, 'mandatory': True
|
| 153 |
-
{'name': 'numero_telefono_mittente_fixed', 'length': 15, 'numeric_pad_zero': False, 'decimals': 0, 'mandatory': True
|
| 154 |
-
{'name': 'cap_mittente_fixed', 'length': 9, 'numeric_pad_zero': False, 'decimals': 0, 'mandatory': True
|
| 155 |
-
{'name': 'localita_mittente_fixed', 'length': 30, 'numeric_pad_zero': False, 'decimals': 0, 'mandatory': True
|
| 156 |
-
{'name': 'provincia_mittente_fixed', 'length': 2, 'numeric_pad_zero': False, 'decimals': 0, 'mandatory': True
|
|
|
|
| 157 |
|
| 158 |
# Campi 20-30 (Destinatario e Colli)
|
| 159 |
-
{'name': 'ragione_sociale_destinatario','length': 40, 'numeric_pad_zero': False, 'decimals': 0, 'mandatory': True
|
| 160 |
-
{'name': 'consignee_contact', 'length': 20, 'numeric_pad_zero': False, 'decimals': 0, 'mandatory': True
|
| 161 |
-
{'name': 'consignee_street_type', 'length': 5, 'numeric_pad_zero': False, 'decimals': 0, 'mandatory': True
|
| 162 |
-
{'name': 'consignee_address', 'length': 30, 'numeric_pad_zero': False, 'decimals': 0, 'mandatory': True
|
| 163 |
-
{'name': 'consignee_house_number', 'length': 5, 'numeric_pad_zero': False, 'decimals': 0, 'mandatory': True
|
| 164 |
-
{'name': 'consignee_phone', 'length': 15, 'numeric_pad_zero': False, 'decimals': 0, 'mandatory': True
|
| 165 |
-
{'name': 'consignee_zip_code', 'length': 9, 'numeric_pad_zero': False, 'decimals': 0, 'mandatory': True
|
| 166 |
-
{'name': 'consignee_city', 'length': 30, 'numeric_pad_zero': False, 'decimals': 0, 'mandatory': True
|
| 167 |
-
{'name': 'consignee_province', 'length': 2, 'numeric_pad_zero': False, 'decimals': 0, 'mandatory': True
|
| 168 |
-
{'name': '
|
| 169 |
-
{'name': '
|
| 170 |
|
| 171 |
# Campi 34, 42, 43, 49 (Servizio, Identificativo, Filiale, Tipo Cliente)
|
| 172 |
-
{'name': 'service_code_fixed', 'length': 3, 'numeric_pad_zero': False, 'decimals': 0, 'mandatory': True
|
| 173 |
-
{'name': 'shippingmark', 'length': 25, 'numeric_pad_zero': False, 'decimals': 0, 'mandatory': True
|
| 174 |
-
{'name': 'branch_code_fixed', 'length': 2, 'numeric_pad_zero': False, 'decimals': 0, 'mandatory': True
|
| 175 |
-
{'name': 'client_type_fixed', 'length': 4, 'numeric_pad_zero': False, 'decimals': 0, 'mandatory': True
|
| 176 |
]
|
| 177 |
|
| 178 |
# Genera il contenuto del file .dat
|
|
@@ -186,28 +165,38 @@ def generate_poste_dat_file(df_shipping_mark_input, df_shipment_order_input):
|
|
| 186 |
is_numeric_zero_padded = field_spec['numeric_pad_zero']
|
| 187 |
dec_places = field_spec['decimals']
|
| 188 |
is_mandatory = field_spec['mandatory']
|
| 189 |
-
source_type = field_spec['source_type']
|
| 190 |
|
| 191 |
value_to_format = ''
|
| 192 |
|
| 193 |
-
if
|
| 194 |
value_to_format = fixed_and_default_values.get(field_name, '')
|
| 195 |
-
elif
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 196 |
value_to_format = row.get(field_name)
|
| 197 |
# Gestisci NaN dopo la merge, se il campo dal file non è presente per quel record
|
| 198 |
if pd.isna(value_to_format):
|
| 199 |
value_to_format = ''
|
| 200 |
-
elif source_type == 'derived':
|
| 201 |
-
if field_name == 'shippingmark':
|
| 202 |
-
# Campo 42: Identificativo collo (shippingMark) prende il valore diretto da shippingmark
|
| 203 |
-
original_shipping_mark = row.get('shippingmark', '')
|
| 204 |
-
if pd.isna(original_shipping_mark):
|
| 205 |
-
original_shipping_mark = ''
|
| 206 |
-
value_to_format = str(original_shipping_mark).strip()
|
| 207 |
-
elif field_name == 'data_spedizione_derived':
|
| 208 |
-
value_to_format = datetime.now().strftime("%Y%m%d")
|
| 209 |
-
else:
|
| 210 |
-
value_to_format = '' # Fallback per derivati non implementati
|
| 211 |
|
| 212 |
# Conversione in stringa e pulizia iniziale
|
| 213 |
value_str = str(value_to_format).strip()
|
|
@@ -224,10 +213,10 @@ def generate_poste_dat_file(df_shipping_mark_input, df_shipment_order_input):
|
|
| 224 |
try:
|
| 225 |
clean_value = value_str.replace(',', '.') # Sostituisce virgole con punti
|
| 226 |
|
| 227 |
-
if dec_places > 0:
|
| 228 |
num_value = float(clean_value) * (10**dec_places)
|
| 229 |
formatted_value = str(int(round(num_value))).rjust(length, '0')
|
| 230 |
-
else:
|
| 231 |
formatted_value = str(int(float(clean_value))).rjust(length, '0')
|
| 232 |
except ValueError:
|
| 233 |
formatted_value = '0'.rjust(length, '0') # Fallback se la conversione fallisce
|
|
@@ -287,8 +276,8 @@ def app():
|
|
| 287 |
**Nota**: I file Excel devono avere l'intestazione sulla **seconda riga**.
|
| 288 |
""")
|
| 289 |
|
| 290 |
-
shipping_mark_file = st.file_uploader("Carica il file ShippingMark_ (con 'shippingMark'
|
| 291 |
-
shipment_order_file = st.file_uploader("Carica il file ShipmentOrder_ (con 'orderRef' e dettagli
|
| 292 |
|
| 293 |
dat_filepath = None
|
| 294 |
dis_filepath = None
|
|
@@ -303,9 +292,20 @@ def app():
|
|
| 303 |
st.write("Colonne lette da ShippingMark_:", df_shipping_mark.columns.tolist())
|
| 304 |
st.write("Colonne lette da ShipmentOrder_:", df_shipment_order.columns.tolist())
|
| 305 |
|
|
|
|
|
|
|
|
|
|
| 306 |
|
|
|
|
| 307 |
dat_filepath, dis_filepath = generate_poste_dat_file(df_shipping_mark, df_shipment_order)
|
| 308 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 309 |
except Exception as e:
|
| 310 |
st.error(f"Errore nella lettura dei file Excel: {e}")
|
| 311 |
st.warning("Assicurati che i file siano in formato .xlsx, non siano corrotti e che l'intestazione sia effettivamente alla seconda riga.")
|
|
@@ -335,15 +335,10 @@ def app():
|
|
| 335 |
st.info("""
|
| 336 |
**Nota sulle colonne:**
|
| 337 |
Assicurati che i tuoi file Excel contengano le colonne corrette.
|
| 338 |
-
- Il file `ShippingMark_` deve contenere le colonne 'shippingMark' e '
|
| 339 |
-
- Il file `ShipmentOrder_` deve contenere 'orderRef' e i campi specifici
|
| 340 |
-
I nomi delle colonne nel tuo file `ShipmentOrder_` devono corrispondere **esattamente** (ignorando maiuscole/minuscole)
|
| 341 |
-
|
| 342 |
-
**Riguardo il warning `Running pip as the 'root' user` su Hugging Face:**
|
| 343 |
-
Questo è un messaggio standard di `pip` che indica che il processo di installazione dei pacchetti sta avvenendo con privilegi di root.
|
| 344 |
-
Generalmente, **non è un problema critico** per la distribuzione su piattaforme come Hugging Face Spaces, dove l'ambiente di esecuzione è isolato e gestito. Puoi ignorarlo tranquillamente; non impedisce il funzionamento della tua applicazione.
|
| 345 |
""")
|
| 346 |
|
| 347 |
if __name__ == "__main__":
|
| 348 |
app()
|
| 349 |
-
|
|
|
|
| 10 |
|
| 11 |
Args:
|
| 12 |
df_shipping_mark_input (pd.DataFrame): DataFrame contenente le shipping mark e i riferimenti d'ordine.
|
| 13 |
+
Deve contenere le colonne 'shippingMark', 'totalWeight', 'totalVolume'.
|
| 14 |
df_shipment_order_input (pd.DataFrame): DataFrame contenente i dettagli dell'ordine.
|
| 15 |
+
Deve contenere 'orderRef', 'orderDate' e i campi relativi al destinatario.
|
| 16 |
|
| 17 |
Returns:
|
| 18 |
tuple: (str, str) contenente i percorsi dei file .dat e .dis generati, o (None, None) in caso di errore.
|
|
|
|
| 23 |
df_shipment_order = df_shipment_order_input.copy()
|
| 24 |
|
| 25 |
# Assicurati che i nomi delle colonne siano in minuscolo per una maggiore robustezza
|
|
|
|
| 26 |
df_shipping_mark.columns = df_shipping_mark.columns.str.lower()
|
| 27 |
df_shipment_order.columns = df_shipment_order.columns.str.lower()
|
| 28 |
|
| 29 |
# Verifica la presenza delle colonne necessarie nel file shipping mark
|
| 30 |
+
# Ora include totalweight e totalvolume
|
| 31 |
+
required_shipping_mark_cols = ['shippingmark', 'orderref', 'totalweight', 'totalvolume']
|
| 32 |
if not all(col in df_shipping_mark.columns for col in required_shipping_mark_cols):
|
| 33 |
st.error(f"Il file delle shipping mark deve contenere le colonne: {required_shipping_mark_cols}")
|
| 34 |
return None, None
|
|
|
|
| 36 |
# Mapping dei nomi delle colonne dal file ShipmentOrder_ ai nomi interni/standardizzati.
|
| 37 |
# **IMPORTANTE**: Le chiavi di questo dizionario devono corrispondere esattamente
|
| 38 |
# ai nomi delle colonne (in minuscolo) presenti nel tuo file ShipmentOrder_.xlsx.
|
| 39 |
+
# Se le tue colonne sono "consegnee.partner.address.address", DEVONO ESSERE ESATTAMENTE COSÌ.
|
| 40 |
+
# Se sono flat come "consignee_address", devi modificare queste chiavi.
|
| 41 |
column_mapping = {
|
| 42 |
+
# Campi destinatario e data che si aspettano dal file ShipmentOrder_.
|
| 43 |
+
'orderdate': 'order_date', # Per Campo 7
|
| 44 |
+
'consegnee.partner.businessname': 'ragione_sociale_destinatario', # Campo 20
|
| 45 |
+
'consegnee.partner.contact.name': 'consignee_contact', # Campo 21
|
| 46 |
+
'consegnee.partner.address.streettype': 'consignee_street_type', # Campo 22
|
| 47 |
+
'consegnee.partner.address.address': 'consignee_address', # Campo 23
|
| 48 |
+
'consegnee.partner.address.addressnumber': 'consignee_house_number', # Campo 24
|
| 49 |
+
'consegnee.partner.phone.number': 'consignee_phone', # Campo 25
|
| 50 |
+
'consegnee.partner.address.zipcode': 'consignee_zip_code', # Campo 26
|
| 51 |
+
'consegnee.partner.address.city': 'consignee_city', # Campo 27
|
| 52 |
+
'consegnee.partner.address.province': 'consignee_province', # Campo 28 (assumendo che esista questa colonna per la provincia)
|
| 53 |
+
# Nota: 'consegnee.partner.address.country' non è nel tracciato .dat fornito.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 54 |
}
|
| 55 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 56 |
# Applica il mapping delle colonne a df_shipment_order.
|
| 57 |
+
# La funzione lambda `x` viene applicata ai nomi delle colonne attuali (già in minuscolo)
|
| 58 |
# e poi cerca nel `column_mapping`.
|
| 59 |
df_shipment_order = df_shipment_order.rename(columns=lambda x: column_mapping.get(x, x))
|
| 60 |
|
| 61 |
+
# Assicurati che 'orderref' e 'order_date' siano presenti in df_shipment_order dopo il rename
|
| 62 |
+
required_shipment_order_cols = ['orderref', 'order_date']
|
| 63 |
+
if not all(col in df_shipment_order.columns for col in required_shipment_order_cols):
|
| 64 |
+
st.error(f"Il file degli ordini deve contenere le colonne: {required_shipment_order_cols} (anche dopo il mapping).")
|
| 65 |
return None, None
|
| 66 |
|
| 67 |
# Pulisci le colonne di join per una corrispondenza robusta (rimuovi spazi e assicurati che siano stringhe)
|
|
|
|
| 82 |
# Basato sul NUOVO tracciato fornito.
|
| 83 |
fixed_and_default_values = {
|
| 84 |
# Campi fissi Mittente (Campi 1-18) - Valori dalla tua ultima tabella
|
| 85 |
+
'codice_identificativo_cliente_fixed': 'CLI000000009', # Campo 1
|
| 86 |
'id_ldv_fixed': 'FIL01', # Campo 2
|
| 87 |
'tipo_spedizione_fixed': 'N', # Campo 3
|
| 88 |
+
'attivazione_ritiro_fixed': 'N', # Campo 4
|
| 89 |
+
'stampa_ldv_fixed': 'N', # Campo 5
|
| 90 |
# Campo 6 (orderref) dal file
|
| 91 |
+
# Campo 7 (data_spedizione_derived) è derivato da order_date dal file
|
| 92 |
'ritiro_mp_fixed': 'P', # Campo 8
|
| 93 |
'codice_indirizzo_mittente_fixed': 'IND001', # Campo 9
|
| 94 |
+
'ragione_sociale_mittente_fixed': 'Poste Logistics', # Campo 10
|
| 95 |
'contatto_mittente_fixed': 'postelogistics@poste.it', # Campo 11
|
| 96 |
'tipo_via_mittente_fixed': 'Via', # Campo 12
|
| 97 |
'via_mittente_fixed': 'Via da Inserire', # Campo 13
|
|
|
|
| 114 |
# 'derived' (calcolato, es. data_spedizione_derived)
|
| 115 |
dat_format_spec = [
|
| 116 |
# BLOCCO MITTENTE FISSO (Campi 1-18 dal NUOVO tracciato)
|
| 117 |
+
{'name': 'codice_identificativo_cliente_fixed', 'length': 12, 'numeric_pad_zero': False, 'decimals': 0, 'mandatory': True}, # 1
|
| 118 |
+
{'name': 'id_ldv_fixed', 'length': 30, 'numeric_pad_zero': False, 'decimals': 0, 'mandatory': True}, # 2
|
| 119 |
+
{'name': 'tipo_spedizione_fixed', 'length': 1, 'numeric_pad_zero': False, 'decimals': 0, 'mandatory': True}, # 3
|
| 120 |
+
{'name': 'attivazione_ritiro_fixed', 'length': 1, 'numeric_pad_zero': False, 'decimals': 0, 'mandatory': True}, # 4
|
| 121 |
+
{'name': 'stampa_ldv_fixed', 'length': 1, 'numeric_pad_zero': False, 'decimals': 0, 'mandatory': True}, # 5
|
| 122 |
+
{'name': 'orderref', 'length': 25, 'numeric_pad_zero': False, 'decimals': 0, 'mandatory': True}, # 6 (Numero Riferimento Spedizione - dal file shipping mark)
|
| 123 |
+
{'name': 'order_date', 'length': 8, 'numeric_pad_zero': False, 'decimals': 0, 'mandatory': True}, # 7 (Data della spedizione - da orderDate dal file shipmentOrder)
|
| 124 |
+
{'name': 'ritiro_mp_fixed', 'length': 1, 'numeric_pad_zero': False, 'decimals': 0, 'mandatory': True}, # 8
|
| 125 |
+
{'name': 'codice_indirizzo_mittente_fixed', 'length': 20, 'numeric_pad_zero': False, 'decimals': 0, 'mandatory': True}, # 9
|
| 126 |
+
{'name': 'ragione_sociale_mittente_fixed', 'length': 40, 'numeric_pad_zero': False, 'decimals': 0, 'mandatory': True}, # 10
|
| 127 |
+
{'name': 'contatto_mittente_fixed', 'length': 20, 'numeric_pad_zero': False, 'decimals': 0, 'mandatory': True}, # 11
|
| 128 |
+
{'name': 'tipo_via_mittente_fixed', 'length': 5, 'numeric_pad_zero': False, 'decimals': 0, 'mandatory': True}, # 12
|
| 129 |
+
{'name': 'via_mittente_fixed', 'length': 30, 'numeric_pad_zero': False, 'decimals': 0, 'mandatory': True}, # 13
|
| 130 |
+
{'name': 'numero_civico_mittente_fixed','length': 5, 'numeric_pad_zero': False, 'decimals': 0, 'mandatory': True}, # 14
|
| 131 |
+
{'name': 'numero_telefono_mittente_fixed', 'length': 15, 'numeric_pad_zero': False, 'decimals': 0, 'mandatory': True}, # 15
|
| 132 |
+
{'name': 'cap_mittente_fixed', 'length': 9, 'numeric_pad_zero': False, 'decimals': 0, 'mandatory': True}, # 16
|
| 133 |
+
{'name': 'localita_mittente_fixed', 'length': 30, 'numeric_pad_zero': False, 'decimals': 0, 'mandatory': True}, # 17
|
| 134 |
+
{'name': 'provincia_mittente_fixed', 'length': 2, 'numeric_pad_zero': False, 'decimals': 0, 'mandatory': True}, # 18
|
| 135 |
+
{'name': 'filler_19_gap', 'length': 60, 'numeric_pad_zero': False, 'decimals': 0, 'mandatory': False}, # Gap tra 18 e 20
|
| 136 |
|
| 137 |
# Campi 20-30 (Destinatario e Colli)
|
| 138 |
+
{'name': 'ragione_sociale_destinatario','length': 40, 'numeric_pad_zero': False, 'decimals': 0, 'mandatory': True}, # 20 (da file shipmentOrder)
|
| 139 |
+
{'name': 'consignee_contact', 'length': 20, 'numeric_pad_zero': False, 'decimals': 0, 'mandatory': True}, # 21 (da file shipmentOrder)
|
| 140 |
+
{'name': 'consignee_street_type', 'length': 5, 'numeric_pad_zero': False, 'decimals': 0, 'mandatory': True}, # 22 (da file shipmentOrder)
|
| 141 |
+
{'name': 'consignee_address', 'length': 30, 'numeric_pad_zero': False, 'decimals': 0, 'mandatory': True}, # 23 (da file shipmentOrder)
|
| 142 |
+
{'name': 'consignee_house_number', 'length': 5, 'numeric_pad_zero': False, 'decimals': 0, 'mandatory': True}, # 24 (da file shipmentOrder)
|
| 143 |
+
{'name': 'consignee_phone', 'length': 15, 'numeric_pad_zero': False, 'decimals': 0, 'mandatory': True}, # 25 (da file shipmentOrder)
|
| 144 |
+
{'name': 'consignee_zip_code', 'length': 9, 'numeric_pad_zero': False, 'decimals': 0, 'mandatory': True}, # 26 (da file shipmentOrder)
|
| 145 |
+
{'name': 'consignee_city', 'length': 30, 'numeric_pad_zero': False, 'decimals': 0, 'mandatory': True}, # 27 (da file shipmentOrder)
|
| 146 |
+
{'name': 'consignee_province', 'length': 2, 'numeric_pad_zero': False, 'decimals': 0, 'mandatory': True}, # 28 (da file shipmentOrder)
|
| 147 |
+
{'name': 'totalvolume', 'length': 3, 'numeric_pad_zero': True, 'decimals': 0, 'mandatory': True}, # 29 (Numero colli - da totalVolume da shippingMark)
|
| 148 |
+
{'name': 'totalweight', 'length': 7, 'numeric_pad_zero': True, 'decimals': 0, 'mandatory': True}, # 30 (Peso colli - da totalWeight da shippingMark)
|
| 149 |
|
| 150 |
# Campi 34, 42, 43, 49 (Servizio, Identificativo, Filiale, Tipo Cliente)
|
| 151 |
+
{'name': 'service_code_fixed', 'length': 3, 'numeric_pad_zero': False, 'decimals': 0, 'mandatory': True}, # 34 (STD)
|
| 152 |
+
{'name': 'shippingmark', 'length': 25, 'numeric_pad_zero': False, 'decimals': 0, 'mandatory': True}, # 42 (Identificativo collo - shippingMark)
|
| 153 |
+
{'name': 'branch_code_fixed', 'length': 2, 'numeric_pad_zero': False, 'decimals': 0, 'mandatory': True}, # 43 (FIL05)
|
| 154 |
+
{'name': 'client_type_fixed', 'length': 4, 'numeric_pad_zero': False, 'decimals': 0, 'mandatory': True}, # 49 (MGCS)
|
| 155 |
]
|
| 156 |
|
| 157 |
# Genera il contenuto del file .dat
|
|
|
|
| 165 |
is_numeric_zero_padded = field_spec['numeric_pad_zero']
|
| 166 |
dec_places = field_spec['decimals']
|
| 167 |
is_mandatory = field_spec['mandatory']
|
|
|
|
| 168 |
|
| 169 |
value_to_format = ''
|
| 170 |
|
| 171 |
+
if field_name.endswith('_fixed'): # Campi fissi
|
| 172 |
value_to_format = fixed_and_default_values.get(field_name, '')
|
| 173 |
+
elif field_name.endswith('_derived'): # Campi derivati (es. data)
|
| 174 |
+
if field_name == 'order_date': # Correzione: order_date è da 'file'
|
| 175 |
+
value_to_format = row.get(field_name, '')
|
| 176 |
+
if pd.notna(value_to_format):
|
| 177 |
+
# Tenta di convertire la data nel formato AAAAMMGG
|
| 178 |
+
try:
|
| 179 |
+
# Se è già una stringa e ha la lunghezza giusta, usala
|
| 180 |
+
if isinstance(value_to_format, str) and len(value_to_format) == 8 and value_to_format.isdigit():
|
| 181 |
+
pass
|
| 182 |
+
else: # Altrimenti, prova a convertirla da un formato data pandas
|
| 183 |
+
value_to_format = pd.to_datetime(value_to_format).strftime("%Y%m%d")
|
| 184 |
+
except Exception:
|
| 185 |
+
value_to_format = '' # Fallback se la conversione fallisce
|
| 186 |
+
else:
|
| 187 |
+
value_to_format = '' # Se NaN, usa vuoto
|
| 188 |
+
|
| 189 |
+
elif field_name == 'data_spedizione_derived': # Data corrente per il campo 7, se non da file
|
| 190 |
+
value_to_format = datetime.now().strftime("%Y%m%d")
|
| 191 |
+
else:
|
| 192 |
+
value_to_format = '' # Fallback per altri derivati
|
| 193 |
+
elif field_name == 'filler_19_gap':
|
| 194 |
+
value_to_format = ' ' * length
|
| 195 |
+
else: # Campi da prendere direttamente dal DataFrame unito (file)
|
| 196 |
value_to_format = row.get(field_name)
|
| 197 |
# Gestisci NaN dopo la merge, se il campo dal file non è presente per quel record
|
| 198 |
if pd.isna(value_to_format):
|
| 199 |
value_to_format = ''
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 200 |
|
| 201 |
# Conversione in stringa e pulizia iniziale
|
| 202 |
value_str = str(value_to_format).strip()
|
|
|
|
| 213 |
try:
|
| 214 |
clean_value = value_str.replace(',', '.') # Sostituisce virgole con punti
|
| 215 |
|
| 216 |
+
if dec_places > 0:
|
| 217 |
num_value = float(clean_value) * (10**dec_places)
|
| 218 |
formatted_value = str(int(round(num_value))).rjust(length, '0')
|
| 219 |
+
else:
|
| 220 |
formatted_value = str(int(float(clean_value))).rjust(length, '0')
|
| 221 |
except ValueError:
|
| 222 |
formatted_value = '0'.rjust(length, '0') # Fallback se la conversione fallisce
|
|
|
|
| 276 |
**Nota**: I file Excel devono avere l'intestazione sulla **seconda riga**.
|
| 277 |
""")
|
| 278 |
|
| 279 |
+
shipping_mark_file = st.file_uploader("Carica il file ShippingMark_ (con 'shippingMark', 'orderRef', 'totalWeight', 'totalVolume')", type=["xlsx"])
|
| 280 |
+
shipment_order_file = st.file_uploader("Carica il file ShipmentOrder_ (con 'orderRef', 'orderDate' e dettagli destinatario)", type=["xlsx"])
|
| 281 |
|
| 282 |
dat_filepath = None
|
| 283 |
dis_filepath = None
|
|
|
|
| 292 |
st.write("Colonne lette da ShippingMark_:", df_shipping_mark.columns.tolist())
|
| 293 |
st.write("Colonne lette da ShipmentOrder_:", df_shipment_order.columns.tolist())
|
| 294 |
|
| 295 |
+
# DEBUGGING: Mostra il DataFrame ShipmentOrder_ dopo la lettura e il mapping
|
| 296 |
+
st.subheader("Anteprima del DataFrame ShipmentOrder_ (dopo la lettura e il mapping delle colonne):")
|
| 297 |
+
st.dataframe(df_shipment_order)
|
| 298 |
|
| 299 |
+
# Esegui la funzione di generazione
|
| 300 |
dat_filepath, dis_filepath = generate_poste_dat_file(df_shipping_mark, df_shipment_order)
|
| 301 |
|
| 302 |
+
# DEBUGGING: Per mostrare df_merged, la variabile deve essere accessibile qui.
|
| 303 |
+
# Questo richiede una piccola modifica nella funzione generate_poste_dat_file
|
| 304 |
+
# per restituire anche df_merged, oppure duplicare la logica di merge qui solo per il debug.
|
| 305 |
+
# Per semplicità, la lascio commentata qui a meno che non ci sia una richiesta specifica.
|
| 306 |
+
# Oppure puoi ispezionare df_shipment_order e le colonne mappate per vedere i dati.
|
| 307 |
+
|
| 308 |
+
|
| 309 |
except Exception as e:
|
| 310 |
st.error(f"Errore nella lettura dei file Excel: {e}")
|
| 311 |
st.warning("Assicurati che i file siano in formato .xlsx, non siano corrotti e che l'intestazione sia effettivamente alla seconda riga.")
|
|
|
|
| 335 |
st.info("""
|
| 336 |
**Nota sulle colonne:**
|
| 337 |
Assicurati che i tuoi file Excel contengano le colonne corrette.
|
| 338 |
+
- Il file `ShippingMark_` deve contenere le colonne 'shippingMark', 'orderRef', 'totalWeight' e 'totalVolume'.
|
| 339 |
+
- Il file `ShipmentOrder_` deve contenere 'orderRef', 'orderDate' e i campi specifici del destinatario (es. 'consegnee.partner.businessname', 'consegnee.partner.address.address', ecc.).
|
| 340 |
+
I nomi delle colonne nel tuo file `ShipmentOrder_` devono corrispondere **esattamente** (ignorando maiuscole/minuscole) ai nomi completi con la notazione a punti (es. 'consegnee.partner.address.address') per un corretto mapping.
|
|
|
|
|
|
|
|
|
|
|
|
|
| 341 |
""")
|
| 342 |
|
| 343 |
if __name__ == "__main__":
|
| 344 |
app()
|
|
|