Spaces:
Sleeping
Sleeping
Update src/streamlit_app.py
Browse files- src/streamlit_app.py +69 -48
src/streamlit_app.py
CHANGED
|
@@ -38,43 +38,43 @@ fixed_and_default_values = {
|
|
| 38 |
dat_format_spec = [
|
| 39 |
# Id | Nome | Obbl | Tipo | Lungh | Inizio | Descrizione | Default | Campi
|
| 40 |
{'id': '1', 'name': 'codice_identificativo_cliente', 'length': 12, 'numeric_pad_zero': False, 'decimals': 0, 'mandatory': True, 'source': 'fixed', 'excel_col': None, 'description': "Valore costante che identifica il cliente, viene comunicato al cliente al momento dell'attivazione del servizio, un esempio di codice è CLI 000000009", 'start_pos': 1},
|
| 41 |
-
{'id': '2', 'name': 'id_ldv', 'length': 30, 'numeric_pad_zero': False, 'decimals': 0, 'mandatory': True, 'source': 'shipping_mark_file', 'excel_col': 'shippingMark', 'description': "Fillare con la numerazione della ldv (uguale a shippingMark)", 'start_pos':
|
| 42 |
-
{'id': '3', 'name': 'tipo_spedizione', 'length': 1, 'numeric_pad_zero': False, 'decimals': 0, 'mandatory': True, 'source': 'fixed', 'excel_col': None, 'description': "Indica il tipo di spedizione N Porto Franco P porto assegnato T triangolazione", 'start_pos':
|
| 43 |
-
{'id': '4', 'name': 'attivazione_ritiro', 'length': 1, 'numeric_pad_zero': False, 'decimals': 0, 'mandatory': True, 'source': 'fixed', 'excel_col': None, 'description': "Indica se deve essere attivato il ritiro. In assenza di accordi con Poste impostare il valore costante N", 'start_pos':
|
| 44 |
-
{'id': '5', 'name': 'stampa_ldv', 'length': 1, 'numeric_pad_zero': False, 'decimals': 0, 'mandatory': True, 'source': 'fixed', 'excel_col': None, 'description': "Mettere N", 'start_pos':
|
| 45 |
-
{'id': '6', 'name': 'numero_riferimento_spedizione', 'length': 25, 'numeric_pad_zero': False, 'decimals': 0, 'mandatory': True, 'source': 'shipping_mark_file', 'excel_col': 'orderRef', 'description': "Numero Rif. Ord. Cliente, è il riferimento della spedizione per il cliente", 'start_pos':
|
| 46 |
-
{'id': '7', 'name': 'data_spedizione', 'length': 8, 'numeric_pad_zero': False, 'decimals': 0, 'mandatory': True, 'source': 'shipping_mark_file', 'excel_col': 'requestedShipment', 'description': "Data della spedizione in formato AAAAMMGG (Mese, Giorno)", 'start_pos':
|
| 47 |
-
{'id': '8', 'name': 'ritiro_mp', 'length': 1, 'numeric_pad_zero': False, 'decimals': 0, 'mandatory': True, 'source': 'fixed', 'excel_col': None, 'description': "Indica il tipo di ritiro. In assenza di accordi con Poste impostare il valore costante P", 'start_pos':
|
| 48 |
-
{'id': '9', 'name': 'codice_indirizzo_mittente', 'length': 20, 'numeric_pad_zero': False, 'decimals': 0, 'mandatory': True, 'source': 'fixed', 'excel_col': None, 'description': "Identifica la codifica dell'indirizzo di ritiro del mittente", 'start_pos':
|
| 49 |
-
{'id': '10', 'name': 'ragione_sociale_mittente', 'length': 40, 'numeric_pad_zero': False, 'decimals': 0, 'mandatory': True, 'source': 'fixed', 'excel_col': None, 'description': "Ignorato se è valorizzato il campo 9", 'start_pos':
|
| 50 |
-
{'id': '11', 'name': 'contatto_mittente', 'length': 20, 'numeric_pad_zero': False, 'decimals': 0, 'mandatory': True, 'source': 'fixed', 'excel_col': None, 'description': "Ignorato se è valorizzato il campo 9", 'start_pos':
|
| 51 |
-
{'id': '12', 'name': 'tipo_via_mittente', 'length': 5, 'numeric_pad_zero': False, 'decimals': 0, 'mandatory': True, 'source': 'fixed', 'excel_col': None, 'description': "Ignorato se è valorizzato il campo 9", 'start_pos':
|
| 52 |
-
{'id': '13', 'name': 'via_mittente', 'length': 30, 'numeric_pad_zero': False, 'decimals': 0, 'mandatory': True, 'source': 'fixed', 'excel_col': None, 'description': "Ignorato se è valorizzato il campo 9", 'start_pos':
|
| 53 |
-
{'id': '14', 'name': 'numero_civico_mittente', 'length': 5, 'numeric_pad_zero': False, 'decimals': 0, 'mandatory': True, 'source': 'fixed', 'excel_col': None, 'description': "Ignorato se è valorizzato il campo 9", 'start_pos':
|
| 54 |
-
{'id': '15', 'name': 'numero_telefono_mittente', 'length': 15, 'numeric_pad_zero': False, 'decimals': 0, 'mandatory': True, 'source': 'fixed', 'excel_col': None, 'description': "Ignorato se è valorizzato il campo 9", 'start_pos':
|
| 55 |
-
{'id': '16', 'name': 'cap_mittente', 'length': 9, 'numeric_pad_zero': False, 'decimals': 0, 'mandatory': True, 'source': 'fixed', 'excel_col': None, 'description': "Ignorato se è valorizzato il campo 9", 'start_pos':
|
| 56 |
-
{'id': '17', 'name': 'localita_mittente', 'length': 30, 'numeric_pad_zero': False, 'decimals': 0, 'mandatory': True, 'source': 'fixed', 'excel_col': None, 'description': "Ignorato se è valorizzato il campo 9", 'start_pos':
|
| 57 |
-
{'id': '18', 'name': 'provincia_mittente', 'length': 2, 'numeric_pad_zero': False, 'decimals': 0, 'mandatory': True, 'source': 'fixed', 'excel_col': None, 'description': "Ignorato se è valorizzato il campo 9", 'start_pos':
|
| 58 |
# Filler per il gap tra Campo 18 (fine a 254) e Campo 20 (inizio a 275)
|
| 59 |
-
{'id': '', 'name': 'filler_gap_18_20', 'length': 20, 'numeric_pad_zero': False, 'decimals': 0, 'mandatory': False, 'source': 'filler', 'excel_col': None, 'description': "Gap tra i campi 18 e 20", 'start_pos':
|
| 60 |
# Campi Destinatario e Colli
|
| 61 |
# Nota: Questi campi si riferiscono a colonne nel file ShipmentOrder_, i nomi sono mantenuti come nel tracciato
|
| 62 |
-
{'id': '20', 'name': 'ragione_sociale_destinatario', 'length': 40, 'numeric_pad_zero': False, 'decimals': 0, 'mandatory': True, 'source': 'shipment_order_file', 'excel_col': 'consegnee.partner.name', 'description': "Ragione sociale del destinatario", 'start_pos':
|
| 63 |
-
{'id': '21', 'name': 'contatto_destinatario', 'length': 20, 'numeric_pad_zero': False, 'decimals': 0, 'mandatory': True, 'source': 'shipment_order_file', 'excel_col': 'consegnee.partner.name', 'description': "Nome Contatto presso il Destinatario", 'start_pos':
|
| 64 |
-
{'id': '22', 'name': 'tipo_via_destinatario', 'length': 5, 'numeric_pad_zero': False, 'decimals': 0, 'mandatory': True, 'source': 'fixed', 'excel_col': None, 'description': "Descrizione tipo via", 'start_pos':
|
| 65 |
-
{'id': '23', 'name': 'via_destinatario', 'length': 30, 'numeric_pad_zero': False, 'decimals': 0, 'mandatory': True, 'source': 'shipment_order_file', 'excel_col': 'consegnee.partner.address.address', 'description': "Indirizzo del destinatario", 'start_pos':
|
| 66 |
-
{'id': '24', 'name': 'numero_civico_destinatario', 'length': 5, 'numeric_pad_zero': False, 'decimals': 0, 'mandatory': True, 'source': 'shipment_order_file', 'excel_col': 'consegnee.partner.address.addressNumber', 'description': "Numero Civico Destinatario allineato a sinistra", 'start_pos':
|
| 67 |
-
{'id': '25', 'name': 'numero_telefono_destinatario','length': 15, 'numeric_pad_zero': False, 'decimals': 0, 'mandatory': True, 'source': 'shipment_order_file', 'excel_col': 'consegnee.partner.phoneNumber', 'description': "Numero di telefono Destinatario", 'start_pos':
|
| 68 |
-
{'id': '26', 'name': 'cap_destinatario', 'length': 9, 'numeric_pad_zero': False, 'decimals': 0, 'mandatory': True, 'source': 'shipment_order_file', 'excel_col': 'consegnee.partner.address.zipCode', 'description': "CAP del destinatario allineato a sinistra", 'start_pos':
|
| 69 |
-
{'id': '27', 'name': 'localita_destinatario', 'length': 30, 'numeric_pad_zero': False, 'decimals': 0, 'mandatory': True, 'source': 'shipment_order_file', 'excel_col': 'consegnee.partner.address.city', 'description': "Località del destinatario", 'start_pos':
|
| 70 |
-
{'id': '28', 'name': 'provincia_destinatario', 'length': 2, 'numeric_pad_zero': False, 'decimals': 0, 'mandatory': True, 'source': 'fixed', 'excel_col': None, 'description': "Provincia del destinatario", 'start_pos':
|
| 71 |
-
{'id': '29', 'name': 'numero_colli', 'length': 3, 'numeric_pad_zero': True, 'decimals': 0, 'mandatory': True, 'source': 'shipping_mark_file', 'excel_col': 'totalLdv', 'description': "Numero dei colli totali che compongono la spedizione", 'start_pos':
|
| 72 |
-
{'id': '30', 'name': 'peso_colli', 'length': 7, 'numeric_pad_zero': True, 'decimals': 0, 'mandatory': True, 'source': 'shipping_mark_file', 'excel_col': 'totalWeight', 'description': "Peso espresso in grammi del singolo collo", 'start_pos':
|
| 73 |
# Altri Campi Obbligatori dal tracciato
|
| 74 |
-
{'id': '34', 'name': 'codice_servizio', 'length': 3, 'numeric_pad_zero': False, 'decimals': 0, 'mandatory': True, 'source': 'fixed', 'excel_col': None, 'description': "Vedi codifica", 'start_pos':
|
| 75 |
-
{'id': '42', 'name': 'identificativo_collo', 'length': 25, 'numeric_pad_zero': False, 'decimals': 0, 'mandatory': True, 'source': 'shipping_mark_file', 'excel_col': 'shippingMark', 'description': "Identificativo del singolo collo (barcode)", 'start_pos':
|
| 76 |
-
{'id': '43', 'name': 'filiale', 'length': 2, 'numeric_pad_zero': False, 'decimals': 0, 'mandatory': True, 'source': 'fixed', 'excel_col': None, 'description': "Codice Filiale per il ritiro (Comunicato da Poste)", 'start_pos':
|
| 77 |
-
{'id': '49', 'name': 'tipo_cliente', 'length': 4, 'numeric_pad_zero': False, 'decimals': 0, 'mandatory': True, 'source': 'fixed', 'excel_col': None, 'description': "Valore costante: MGCS", 'start_pos':
|
| 78 |
]
|
| 79 |
|
| 80 |
# --- Funzione per generare il file DAT e DIS ---
|
|
@@ -100,7 +100,7 @@ def generate_poste_dat_file(df_shipping_mark_input, df_shipment_order_input):
|
|
| 100 |
# Verifica la presenza delle colonne necessarie nel file shipping mark
|
| 101 |
required_shipping_mark_cols = ['shippingMark', 'orderRef', 'totalWeight', 'totalLdv', 'requestedShipment']
|
| 102 |
if not all(col in df_shipping_mark.columns for col in required_shipping_mark_cols):
|
| 103 |
-
st.error(f"Errore: Il file delle shipping mark deve contenere le colonne: {required_shipping_mark_cols}. Verifica il naming esatto.")
|
| 104 |
return None, None
|
| 105 |
|
| 106 |
# Verifica le colonne necessarie nel file shipment order
|
|
@@ -116,7 +116,7 @@ def generate_poste_dat_file(df_shipping_mark_input, df_shipment_order_input):
|
|
| 116 |
]
|
| 117 |
if not all(col in df_shipment_order.columns for col in required_shipment_order_cols):
|
| 118 |
missing_cols = [col for col in required_shipment_order_cols if col not in df_shipment_order.columns]
|
| 119 |
-
st.error(f"Errore: Il file degli ordini (ShipmentOrder_) deve contenere le seguenti colonne: {missing_cols}. Verifica attentamente la corrispondenza dei nomi (con punti o underscore). Ricorda che l'intestazione è alla seconda riga.")
|
| 120 |
return None, None
|
| 121 |
|
| 122 |
# Pulisci le colonne di join per una corrispondenza robusta (rimuovi spazi e assicurati che siano stringhe)
|
|
@@ -124,10 +124,7 @@ def generate_poste_dat_file(df_shipping_mark_input, df_shipment_order_input):
|
|
| 124 |
df_shipment_order['orderRef'] = df_shipment_order['orderRef'].astype(str).str.strip()
|
| 125 |
|
| 126 |
# Esegue la join: cruciale 'how=inner' per una join "secca"
|
| 127 |
-
#
|
| 128 |
-
# Le altre colonne, se non hanno nomi duplicati tra i due DF, non vengono suffisse.
|
| 129 |
-
# Poiché i nomi delle colonne di ShipmentOrder sono nidificati (.name, .address.address),
|
| 130 |
-
# non ci dovrebbero essere duplicati a parte 'orderRef', quindi non vengono suffisse.
|
| 131 |
df_merged = pd.merge(df_shipping_mark, df_shipment_order, on='orderRef', how='inner')
|
| 132 |
|
| 133 |
if df_merged.empty:
|
|
@@ -138,6 +135,9 @@ def generate_poste_dat_file(df_shipping_mark_input, df_shipment_order_input):
|
|
| 138 |
dat_content = []
|
| 139 |
for index, row in df_merged.iterrows():
|
| 140 |
record_parts = []
|
|
|
|
|
|
|
|
|
|
| 141 |
for field_spec in dat_format_spec:
|
| 142 |
field_name = field_spec['name']
|
| 143 |
length = field_spec['length']
|
|
@@ -146,17 +146,25 @@ def generate_poste_dat_file(df_shipping_mark_input, df_shipment_order_input):
|
|
| 146 |
mandatory = field_spec['mandatory']
|
| 147 |
source_type = field_spec['source']
|
| 148 |
excel_col_name = field_spec['excel_col'] # Nome della colonna come specificato nel dat_format_spec
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 149 |
|
| 150 |
value_to_format = ''
|
| 151 |
if source_type == 'fixed':
|
| 152 |
value_to_format = fixed_and_default_values.get(field_name, '')
|
| 153 |
elif source_type == 'shipping_mark_file':
|
| 154 |
-
# Accede alle colonne di shipping_mark direttamente con il loro nome originale
|
| 155 |
col_key = excel_col_name
|
| 156 |
-
if col_key in row:
|
| 157 |
value_to_format = row.get(col_key)
|
| 158 |
else:
|
| 159 |
-
|
|
|
|
| 160 |
|
| 161 |
if pd.isna(value_to_format):
|
| 162 |
value_to_format = ''
|
|
@@ -168,7 +176,9 @@ def generate_poste_dat_file(df_shipping_mark_input, df_shipment_order_input):
|
|
| 168 |
if isinstance(value_to_format, datetime):
|
| 169 |
value_to_format = value_to_format.strftime("%Y%m%d")
|
| 170 |
elif isinstance(value_to_format, (int, float)): # Le date di Excel possono essere numeri
|
| 171 |
-
# L'epoch di Excel è 1899-12-30 per le date
|
|
|
|
|
|
|
| 172 |
value_to_format = pd.to_datetime(value_to_format, unit='D', origin='1899-12-30', errors='coerce')
|
| 173 |
if pd.notna(value_to_format):
|
| 174 |
value_to_format = value_to_format.strftime("%Y%m%d")
|
|
@@ -184,20 +194,30 @@ def generate_poste_dat_file(df_shipping_mark_input, df_shipment_order_input):
|
|
| 184 |
else: # Fallback per tipi inaspettati
|
| 185 |
value_to_format = ''
|
| 186 |
except Exception as e:
|
| 187 |
-
st.warning(f"Errore durante la conversione della data per campo '{field_name}': {value_to_format}
|
| 188 |
value_to_format = ''
|
| 189 |
else: # Se NaN o vuoto, usa stringa vuota
|
| 190 |
value_to_format = ''
|
| 191 |
elif source_type == 'shipment_order_file':
|
| 192 |
-
# Accede alle colonne di shipment_order direttamente con il loro nome originale
|
| 193 |
col_key = excel_col_name
|
| 194 |
-
if col_key in row:
|
| 195 |
value_to_format = row.get(col_key)
|
| 196 |
else:
|
| 197 |
-
st.warning(f"Colonna '{excel_col_name}' dal file ShipmentOrder_ non trovata nel DataFrame fuso
|
| 198 |
|
| 199 |
if pd.isna(value_to_format):
|
| 200 |
value_to_format = ''
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 201 |
elif source_type == 'filler':
|
| 202 |
value_to_format = ' ' * length # Riempie il filler con spazi
|
| 203 |
|
|
@@ -230,6 +250,7 @@ def generate_poste_dat_file(df_shipping_mark_input, df_shipment_order_input):
|
|
| 230 |
# Trunca se la stringa è più lunga della lunghezza specificata
|
| 231 |
final_field_value = formatted_value[:length]
|
| 232 |
record_parts.append(final_field_value)
|
|
|
|
| 233 |
|
| 234 |
dat_content.append("".join(record_parts))
|
| 235 |
|
|
@@ -496,4 +517,4 @@ def app():
|
|
| 496 |
|
| 497 |
# Punto di ingresso dell'applicazione Streamlit
|
| 498 |
if __name__ == "__main__":
|
| 499 |
-
app()
|
|
|
|
| 38 |
dat_format_spec = [
|
| 39 |
# Id | Nome | Obbl | Tipo | Lungh | Inizio | Descrizione | Default | Campi
|
| 40 |
{'id': '1', 'name': 'codice_identificativo_cliente', 'length': 12, 'numeric_pad_zero': False, 'decimals': 0, 'mandatory': True, 'source': 'fixed', 'excel_col': None, 'description': "Valore costante che identifica il cliente, viene comunicato al cliente al momento dell'attivazione del servizio, un esempio di codice è CLI 000000009", 'start_pos': 1},
|
| 41 |
+
{'id': '2', 'name': 'id_ldv', 'length': 30, 'numeric_pad_zero': False, 'decimals': 0, 'mandatory': True, 'source': 'shipping_mark_file', 'excel_col': 'shippingMark', 'description': "Fillare con la numerazione della ldv (uguale a shippingMark)", 'start_pos': 12},
|
| 42 |
+
{'id': '3', 'name': 'tipo_spedizione', 'length': 1, 'numeric_pad_zero': False, 'decimals': 0, 'mandatory': True, 'source': 'fixed', 'excel_col': None, 'description': "Indica il tipo di spedizione N Porto Franco P porto assegnato T triangolazione", 'start_pos': 42},
|
| 43 |
+
{'id': '4', 'name': 'attivazione_ritiro', 'length': 1, 'numeric_pad_zero': False, 'decimals': 0, 'mandatory': True, 'source': 'fixed', 'excel_col': None, 'description': "Indica se deve essere attivato il ritiro. In assenza di accordi con Poste impostare il valore costante N", 'start_pos': 43},
|
| 44 |
+
{'id': '5', 'name': 'stampa_ldv', 'length': 1, 'numeric_pad_zero': False, 'decimals': 0, 'mandatory': True, 'source': 'fixed', 'excel_col': None, 'description': "Mettere N", 'start_pos': 44},
|
| 45 |
+
{'id': '6', 'name': 'numero_riferimento_spedizione', 'length': 25, 'numeric_pad_zero': False, 'decimals': 0, 'mandatory': True, 'source': 'shipping_mark_file', 'excel_col': 'orderRef', 'description': "Numero Rif. Ord. Cliente, è il riferimento della spedizione per il cliente", 'start_pos': 45},
|
| 46 |
+
{'id': '7', 'name': 'data_spedizione', 'length': 8, 'numeric_pad_zero': False, 'decimals': 0, 'mandatory': True, 'source': 'shipping_mark_file', 'excel_col': 'requestedShipment', 'description': "Data della spedizione in formato AAAAMMGG (Mese, Giorno)", 'start_pos': 70},
|
| 47 |
+
{'id': '8', 'name': 'ritiro_mp', 'length': 1, 'numeric_pad_zero': False, 'decimals': 0, 'mandatory': True, 'source': 'fixed', 'excel_col': None, 'description': "Indica il tipo di ritiro. In assenza di accordi con Poste impostare il valore costante P", 'start_pos': 78},
|
| 48 |
+
{'id': '9', 'name': 'codice_indirizzo_mittente', 'length': 20, 'numeric_pad_zero': False, 'decimals': 0, 'mandatory': True, 'source': 'fixed', 'excel_col': None, 'description': "Identifica la codifica dell'indirizzo di ritiro del mittente", 'start_pos': 79},
|
| 49 |
+
{'id': '10', 'name': 'ragione_sociale_mittente', 'length': 40, 'numeric_pad_zero': False, 'decimals': 0, 'mandatory': True, 'source': 'fixed', 'excel_col': None, 'description': "Ignorato se è valorizzato il campo 9", 'start_pos': 99},
|
| 50 |
+
{'id': '11', 'name': 'contatto_mittente', 'length': 20, 'numeric_pad_zero': False, 'decimals': 0, 'mandatory': True, 'source': 'fixed', 'excel_col': None, 'description': "Ignorato se è valorizzato il campo 9", 'start_pos': 139},
|
| 51 |
+
{'id': '12', 'name': 'tipo_via_mittente', 'length': 5, 'numeric_pad_zero': False, 'decimals': 0, 'mandatory': True, 'source': 'fixed', 'excel_col': None, 'description': "Ignorato se è valorizzato il campo 9", 'start_pos': 159},
|
| 52 |
+
{'id': '13', 'name': 'via_mittente', 'length': 30, 'numeric_pad_zero': False, 'decimals': 0, 'mandatory': True, 'source': 'fixed', 'excel_col': None, 'description': "Ignorato se è valorizzato il campo 9", 'start_pos': 164},
|
| 53 |
+
{'id': '14', 'name': 'numero_civico_mittente', 'length': 5, 'numeric_pad_zero': False, 'decimals': 0, 'mandatory': True, 'source': 'fixed', 'excel_col': None, 'description': "Ignorato se è valorizzato il campo 9", 'start_pos': 194},
|
| 54 |
+
{'id': '15', 'name': 'numero_telefono_mittente', 'length': 15, 'numeric_pad_zero': False, 'decimals': 0, 'mandatory': True, 'source': 'fixed', 'excel_col': None, 'description': "Ignorato se è valorizzato il campo 9", 'start_pos': 199},
|
| 55 |
+
{'id': '16', 'name': 'cap_mittente', 'length': 9, 'numeric_pad_zero': False, 'decimals': 0, 'mandatory': True, 'source': 'fixed', 'excel_col': None, 'description': "Ignorato se è valorizzato il campo 9", 'start_pos': 214},
|
| 56 |
+
{'id': '17', 'name': 'localita_mittente', 'length': 30, 'numeric_pad_zero': False, 'decimals': 0, 'mandatory': True, 'source': 'fixed', 'excel_col': None, 'description': "Ignorato se è valorizzato il campo 9", 'start_pos': 223},
|
| 57 |
+
{'id': '18', 'name': 'provincia_mittente', 'length': 2, 'numeric_pad_zero': False, 'decimals': 0, 'mandatory': True, 'source': 'fixed', 'excel_col': None, 'description': "Ignorato se è valorizzato il campo 9", 'start_pos': 253},
|
| 58 |
# Filler per il gap tra Campo 18 (fine a 254) e Campo 20 (inizio a 275)
|
| 59 |
+
{'id': '', 'name': 'filler_gap_18_20', 'length': 20, 'numeric_pad_zero': False, 'decimals': 0, 'mandatory': False, 'source': 'filler', 'excel_col': None, 'description': "Gap tra i campi 18 e 20", 'start_pos': 255},
|
| 60 |
# Campi Destinatario e Colli
|
| 61 |
# Nota: Questi campi si riferiscono a colonne nel file ShipmentOrder_, i nomi sono mantenuti come nel tracciato
|
| 62 |
+
{'id': '20', 'name': 'ragione_sociale_destinatario', 'length': 40, 'numeric_pad_zero': False, 'decimals': 0, 'mandatory': True, 'source': 'shipment_order_file', 'excel_col': 'consegnee.partner.name', 'description': "Ragione sociale del destinatario", 'start_pos': 275},
|
| 63 |
+
{'id': '21', 'name': 'contatto_destinatario', 'length': 20, 'numeric_pad_zero': False, 'decimals': 0, 'mandatory': True, 'source': 'shipment_order_file', 'excel_col': 'consegnee.partner.name', 'description': "Nome Contatto presso il Destinatario", 'start_pos': 315},
|
| 64 |
+
{'id': '22', 'name': 'tipo_via_destinatario', 'length': 5, 'numeric_pad_zero': False, 'decimals': 0, 'mandatory': True, 'source': 'fixed', 'excel_col': None, 'description': "Descrizione tipo via", 'start_pos': 335},
|
| 65 |
+
{'id': '23', 'name': 'via_destinatario', 'length': 30, 'numeric_pad_zero': False, 'decimals': 0, 'mandatory': True, 'source': 'shipment_order_file', 'excel_col': 'consegnee.partner.address.address', 'description': "Indirizzo del destinatario", 'start_pos': 340},
|
| 66 |
+
{'id': '24', 'name': 'numero_civico_destinatario', 'length': 5, 'numeric_pad_zero': False, 'decimals': 0, 'mandatory': True, 'source': 'shipment_order_file', 'excel_col': 'consegnee.partner.address.addressNumber', 'description': "Numero Civico Destinatario allineato a sinistra", 'start_pos': 370},
|
| 67 |
+
{'id': '25', 'name': 'numero_telefono_destinatario','length': 15, 'numeric_pad_zero': False, 'decimals': 0, 'mandatory': True, 'source': 'shipment_order_file', 'excel_col': 'consegnee.partner.phoneNumber', 'description': "Numero di telefono Destinatario", 'start_pos': 375},
|
| 68 |
+
{'id': '26', 'name': 'cap_destinatario', 'length': 9, 'numeric_pad_zero': False, 'decimals': 0, 'mandatory': True, 'source': 'shipment_order_file', 'excel_col': 'consegnee.partner.address.zipCode', 'description': "CAP del destinatario allineato a sinistra", 'start_pos': 390},
|
| 69 |
+
{'id': '27', 'name': 'localita_destinatario', 'length': 30, 'numeric_pad_zero': False, 'decimals': 0, 'mandatory': True, 'source': 'shipment_order_file', 'excel_col': 'consegnee.partner.address.city', 'description': "Località del destinatario", 'start_pos': 399},
|
| 70 |
+
{'id': '28', 'name': 'provincia_destinatario', 'length': 2, 'numeric_pad_zero': False, 'decimals': 0, 'mandatory': True, 'source': 'fixed', 'excel_col': None, 'description': "Provincia del destinatario", 'start_pos': 429},
|
| 71 |
+
{'id': '29', 'name': 'numero_colli', 'length': 3, 'numeric_pad_zero': True, 'decimals': 0, 'mandatory': True, 'source': 'shipping_mark_file', 'excel_col': 'totalLdv', 'description': "Numero dei colli totali che compongono la spedizione", 'start_pos': 431},
|
| 72 |
+
{'id': '30', 'name': 'peso_colli', 'length': 7, 'numeric_pad_zero': True, 'decimals': 0, 'mandatory': True, 'source': 'shipping_mark_file', 'excel_col': 'totalWeight', 'description': "Peso espresso in grammi del singolo collo", 'start_pos': 434},
|
| 73 |
# Altri Campi Obbligatori dal tracciato
|
| 74 |
+
{'id': '34', 'name': 'codice_servizio', 'length': 3, 'numeric_pad_zero': False, 'decimals': 0, 'mandatory': True, 'source': 'fixed', 'excel_col': None, 'description': "Vedi codifica", 'start_pos': 456},
|
| 75 |
+
{'id': '42', 'name': 'identificativo_collo', 'length': 25, 'numeric_pad_zero': False, 'decimals': 0, 'mandatory': True, 'source': 'shipping_mark_file', 'excel_col': 'shippingMark', 'description': "Identificativo del singolo collo (barcode)", 'start_pos': 553},
|
| 76 |
+
{'id': '43', 'name': 'filiale', 'length': 2, 'numeric_pad_zero': False, 'decimals': 0, 'mandatory': True, 'source': 'fixed', 'excel_col': None, 'description': "Codice Filiale per il ritiro (Comunicato da Poste)", 'start_pos': 578},
|
| 77 |
+
{'id': '49', 'name': 'tipo_cliente', 'length': 4, 'numeric_pad_zero': False, 'decimals': 0, 'mandatory': True, 'source': 'fixed', 'excel_col': None, 'description': "Valore costante: MGCS", 'start_pos': 627},
|
| 78 |
]
|
| 79 |
|
| 80 |
# --- Funzione per generare il file DAT e DIS ---
|
|
|
|
| 100 |
# Verifica la presenza delle colonne necessarie nel file shipping mark
|
| 101 |
required_shipping_mark_cols = ['shippingMark', 'orderRef', 'totalWeight', 'totalLdv', 'requestedShipment']
|
| 102 |
if not all(col in df_shipping_mark.columns for col in required_shipping_mark_cols):
|
| 103 |
+
st.error(f"Errore: Il file delle shipping mark deve contenere le colonne: {required_shipping_mark_cols}. Verifica il naming esatto e la capitalizzazione.")
|
| 104 |
return None, None
|
| 105 |
|
| 106 |
# Verifica le colonne necessarie nel file shipment order
|
|
|
|
| 116 |
]
|
| 117 |
if not all(col in df_shipment_order.columns for col in required_shipment_order_cols):
|
| 118 |
missing_cols = [col for col in required_shipment_order_cols if col not in df_shipment_order.columns]
|
| 119 |
+
st.error(f"Errore: Il file degli ordini (ShipmentOrder_) deve contenere le seguenti colonne: {missing_cols}. Verifica attentamente la corrispondenza dei nomi (con punti o underscore) e la capitalizzazione. Ricorda che l'intestazione è alla seconda riga.")
|
| 120 |
return None, None
|
| 121 |
|
| 122 |
# Pulisci le colonne di join per una corrispondenza robusta (rimuovi spazi e assicurati che siano stringhe)
|
|
|
|
| 124 |
df_shipment_order['orderRef'] = df_shipment_order['orderRef'].astype(str).str.strip()
|
| 125 |
|
| 126 |
# Esegue la join: cruciale 'how=inner' per una join "secca"
|
| 127 |
+
# Le colonne manterranno i loro nomi originali nel DataFrame fuso, a meno che non ci siano duplicati (non il caso qui).
|
|
|
|
|
|
|
|
|
|
| 128 |
df_merged = pd.merge(df_shipping_mark, df_shipment_order, on='orderRef', how='inner')
|
| 129 |
|
| 130 |
if df_merged.empty:
|
|
|
|
| 135 |
dat_content = []
|
| 136 |
for index, row in df_merged.iterrows():
|
| 137 |
record_parts = []
|
| 138 |
+
# Mantieni traccia della lunghezza corrente della riga per gestire i posizionamenti precisi
|
| 139 |
+
current_line_length = 0
|
| 140 |
+
|
| 141 |
for field_spec in dat_format_spec:
|
| 142 |
field_name = field_spec['name']
|
| 143 |
length = field_spec['length']
|
|
|
|
| 146 |
mandatory = field_spec['mandatory']
|
| 147 |
source_type = field_spec['source']
|
| 148 |
excel_col_name = field_spec['excel_col'] # Nome della colonna come specificato nel dat_format_spec
|
| 149 |
+
expected_start_pos = field_spec['start_pos'] # Posizione di inizio 1-indexed dal tracciato
|
| 150 |
+
|
| 151 |
+
# Calcola il padding necessario per raggiungere la posizione di inizio esatta
|
| 152 |
+
# Se la lunghezza attuale è inferiore alla posizione di inizio desiderata (0-indexed)
|
| 153 |
+
padding_needed = (expected_start_pos - 1) - current_line_length
|
| 154 |
+
if padding_needed > 0:
|
| 155 |
+
record_parts.append(' ' * padding_needed)
|
| 156 |
+
current_line_length += padding_needed
|
| 157 |
|
| 158 |
value_to_format = ''
|
| 159 |
if source_type == 'fixed':
|
| 160 |
value_to_format = fixed_and_default_values.get(field_name, '')
|
| 161 |
elif source_type == 'shipping_mark_file':
|
|
|
|
| 162 |
col_key = excel_col_name
|
| 163 |
+
if col_key in row:
|
| 164 |
value_to_format = row.get(col_key)
|
| 165 |
else:
|
| 166 |
+
# Non dovrebbe succedere se il controllo iniziale delle colonne è robusto
|
| 167 |
+
st.warning(f"Colonna '{excel_col_name}' dal file ShippingMark_ non trovata nel DataFrame fuso. Controllare il nome originale della colonna.")
|
| 168 |
|
| 169 |
if pd.isna(value_to_format):
|
| 170 |
value_to_format = ''
|
|
|
|
| 176 |
if isinstance(value_to_format, datetime):
|
| 177 |
value_to_format = value_to_format.strftime("%Y%m%d")
|
| 178 |
elif isinstance(value_to_format, (int, float)): # Le date di Excel possono essere numeri
|
| 179 |
+
# L'epoch di Excel è 1899-12-30 per le date, ma è meglio usare il proprio parser di Excel se possibile
|
| 180 |
+
# O provare a convertire da numero seriale di Excel
|
| 181 |
+
# Per ora, proviamo con il formato stringa diretto se numerico
|
| 182 |
value_to_format = pd.to_datetime(value_to_format, unit='D', origin='1899-12-30', errors='coerce')
|
| 183 |
if pd.notna(value_to_format):
|
| 184 |
value_to_format = value_to_format.strftime("%Y%m%d")
|
|
|
|
| 194 |
else: # Fallback per tipi inaspettati
|
| 195 |
value_to_format = ''
|
| 196 |
except Exception as e:
|
| 197 |
+
st.warning(f"Errore durante la conversione della data per campo '{field_name}' (valore: {value_to_format}). Assicurati che sia un formato data valido: {e}")
|
| 198 |
value_to_format = ''
|
| 199 |
else: # Se NaN o vuoto, usa stringa vuota
|
| 200 |
value_to_format = ''
|
| 201 |
elif source_type == 'shipment_order_file':
|
|
|
|
| 202 |
col_key = excel_col_name
|
| 203 |
+
if col_key in row:
|
| 204 |
value_to_format = row.get(col_key)
|
| 205 |
else:
|
| 206 |
+
st.warning(f"Colonna '{excel_col_name}' dal file ShipmentOrder_ non trovata nel DataFrame fuso. Controllare il nome originale della colonna.")
|
| 207 |
|
| 208 |
if pd.isna(value_to_format):
|
| 209 |
value_to_format = ''
|
| 210 |
+
|
| 211 |
+
# Gestione specifica per CAP Destinatario per evitare .0
|
| 212 |
+
if field_name == 'cap_destinatario':
|
| 213 |
+
if pd.notna(value_to_format) and value_to_format != '':
|
| 214 |
+
try:
|
| 215 |
+
value_to_format = str(int(float(value_to_format))) # Converte in float, poi int, poi stringa
|
| 216 |
+
except ValueError:
|
| 217 |
+
st.warning(f"Impossibile convertire '{value_to_format}' in un CAP numerico intero. Verrà trattato come stringa.")
|
| 218 |
+
value_to_format = str(value_to_format) # Fallback a stringa
|
| 219 |
+
else:
|
| 220 |
+
value_to_format = '' # Se vuoto o NaN, lascia vuoto
|
| 221 |
elif source_type == 'filler':
|
| 222 |
value_to_format = ' ' * length # Riempie il filler con spazi
|
| 223 |
|
|
|
|
| 250 |
# Trunca se la stringa è più lunga della lunghezza specificata
|
| 251 |
final_field_value = formatted_value[:length]
|
| 252 |
record_parts.append(final_field_value)
|
| 253 |
+
current_line_length += len(final_field_value)
|
| 254 |
|
| 255 |
dat_content.append("".join(record_parts))
|
| 256 |
|
|
|
|
| 517 |
|
| 518 |
# Punto di ingresso dell'applicazione Streamlit
|
| 519 |
if __name__ == "__main__":
|
| 520 |
+
app()
|