pasquale991 commited on
Commit
b968882
·
verified ·
1 Parent(s): b300dd8

Update src/streamlit_app.py

Browse files
Files changed (1) hide show
  1. 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' e 'orderRef'.
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
- required_shipping_mark_cols = ['shippingmark', 'orderref']
 
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 mittente non presi da file, ma dal mapping fisso.
41
- # Campi destinatario che si aspettano dal file ShipmentOrder_.
42
- 'rag. sociale destinatario': 'ragione_sociale_destinatario',
43
- 'contatto destinatario': 'consignee_contact', # Assunto il nome della colonna in Excel sia 'Contatto Destinatario'
44
- 'tipo via destinatario': 'consignee_street_type',
45
- 'via destinatario': 'consignee_address',
46
- 'numero civico destinatario': 'consignee_house_number',
47
- 'numero di telefono destinatario': 'consignee_phone',
48
- 'cap destinatario': 'consignee_zip_code',
49
- 'località destinatario': 'consignee_city',
50
- 'provincia destinatario': 'consignee_province',
51
- 'numero colli': 'num_packages',
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.lower()` viene applicata ai nomi delle colonne attuali
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
- # Assicurati che 'orderref' sia presente in df_shipment_order dopo il rename
85
- if 'orderref' not in df_shipment_order.columns:
86
- st.error(f"Il file degli ordini deve contenere la colonna 'orderRef' (anche dopo il mapping).")
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
- 'codice_identificativo_cliente': 'CLI000000009', # Campo 1
108
  'id_ldv_fixed': 'FIL01', # Campo 2
109
  'tipo_spedizione_fixed': 'N', # Campo 3
110
- 'attivazione_ritiro': 'N', # Campo 4
111
- 'stampa_ldv': 'N', # Campo 5
112
  # Campo 6 (orderref) dal file
113
- 'data_spedizione_derived': datetime.now().strftime("%Y%m%d"), # Campo 7 (Data corrente)
114
  'ritiro_mp_fixed': 'P', # Campo 8
115
  'codice_indirizzo_mittente_fixed': 'IND001', # Campo 9
116
- 'ragione_sociale_mittente_fixed': 'POSTE LOGISTICS', # Campo 10
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': 'codice_identificativo_cliente', 'length': 12, 'numeric_pad_zero': False, 'decimals': 0, 'mandatory': True, 'source_type': 'fixed'}, # 1
140
- {'name': 'id_ldv_fixed', 'length': 30, 'numeric_pad_zero': False, 'decimals': 0, 'mandatory': True, 'source_type': 'fixed'}, # 2
141
- {'name': 'tipo_spedizione_fixed', 'length': 1, 'numeric_pad_zero': False, 'decimals': 0, 'mandatory': True, 'source_type': 'fixed'}, # 3
142
- {'name': 'attivazione_ritiro', 'length': 1, 'numeric_pad_zero': False, 'decimals': 0, 'mandatory': True, 'source_type': 'fixed'}, # 4
143
- {'name': 'stampa_ldv', 'length': 1, 'numeric_pad_zero': False, 'decimals': 0, 'mandatory': True, 'source_type': 'fixed'}, # 5
144
- {'name': 'orderref', 'length': 25, 'numeric_pad_zero': False, 'decimals': 0, 'mandatory': True, 'source_type': 'file'}, # 6 (Numero Riferimento Spedizione - dal file)
145
- {'name': 'data_spedizione_derived', 'length': 8, 'numeric_pad_zero': False, 'decimals': 0, 'mandatory': True, 'source_type': 'derived'}, # 7 (Data corrente AAAAMMGG)
146
- {'name': 'ritiro_mp_fixed', 'length': 1, 'numeric_pad_zero': False, 'decimals': 0, 'mandatory': True, 'source_type': 'fixed'}, # 8
147
- {'name': 'codice_indirizzo_mittente_fixed', 'length': 20, 'numeric_pad_zero': False, 'decimals': 0, 'mandatory': True, 'source_type': 'fixed'}, # 9
148
- {'name': 'ragione_sociale_mittente_fixed', 'length': 40, 'numeric_pad_zero': False, 'decimals': 0, 'mandatory': True, 'source_type': 'fixed'}, # 10
149
- {'name': 'contatto_mittente_fixed', 'length': 20, 'numeric_pad_zero': False, 'decimals': 0, 'mandatory': True, 'source_type': 'fixed'}, # 11
150
- {'name': 'tipo_via_mittente_fixed', 'length': 5, 'numeric_pad_zero': False, 'decimals': 0, 'mandatory': True, 'source_type': 'fixed'}, # 12
151
- {'name': 'via_mittente_fixed', 'length': 30, 'numeric_pad_zero': False, 'decimals': 0, 'mandatory': True, 'source_type': 'fixed'}, # 13
152
- {'name': 'numero_civico_mittente_fixed','length': 5, 'numeric_pad_zero': False, 'decimals': 0, 'mandatory': True, 'source_type': 'fixed'}, # 14
153
- {'name': 'numero_telefono_mittente_fixed', 'length': 15, 'numeric_pad_zero': False, 'decimals': 0, 'mandatory': True, 'source_type': 'fixed'}, # 15
154
- {'name': 'cap_mittente_fixed', 'length': 9, 'numeric_pad_zero': False, 'decimals': 0, 'mandatory': True, 'source_type': 'fixed'}, # 16
155
- {'name': 'localita_mittente_fixed', 'length': 30, 'numeric_pad_zero': False, 'decimals': 0, 'mandatory': True, 'source_type': 'fixed'}, # 17
156
- {'name': 'provincia_mittente_fixed', 'length': 2, 'numeric_pad_zero': False, 'decimals': 0, 'mandatory': True, 'source_type': 'fixed'}, # 18
 
157
 
158
  # Campi 20-30 (Destinatario e Colli)
159
- {'name': 'ragione_sociale_destinatario','length': 40, 'numeric_pad_zero': False, 'decimals': 0, 'mandatory': True, 'source_type': 'file'}, # 20
160
- {'name': 'consignee_contact', 'length': 20, 'numeric_pad_zero': False, 'decimals': 0, 'mandatory': True, 'source_type': 'file'}, # 21
161
- {'name': 'consignee_street_type', 'length': 5, 'numeric_pad_zero': False, 'decimals': 0, 'mandatory': True, 'source_type': 'file'}, # 22
162
- {'name': 'consignee_address', 'length': 30, 'numeric_pad_zero': False, 'decimals': 0, 'mandatory': True, 'source_type': 'file'}, # 23
163
- {'name': 'consignee_house_number', 'length': 5, 'numeric_pad_zero': False, 'decimals': 0, 'mandatory': True, 'source_type': 'file'}, # 24
164
- {'name': 'consignee_phone', 'length': 15, 'numeric_pad_zero': False, 'decimals': 0, 'mandatory': True, 'source_type': 'file'}, # 25
165
- {'name': 'consignee_zip_code', 'length': 9, 'numeric_pad_zero': False, 'decimals': 0, 'mandatory': True, 'source_type': 'file'}, # 26
166
- {'name': 'consignee_city', 'length': 30, 'numeric_pad_zero': False, 'decimals': 0, 'mandatory': True, 'source_type': 'file'}, # 27
167
- {'name': 'consignee_province', 'length': 2, 'numeric_pad_zero': False, 'decimals': 0, 'mandatory': True, 'source_type': 'file'}, # 28
168
- {'name': 'num_packages', 'length': 3, 'numeric_pad_zero': True, 'decimals': 0, 'mandatory': True, 'source_type': 'file'}, # 29
169
- {'name': 'package_weight', 'length': 7, 'numeric_pad_zero': True, 'decimals': 0, 'mandatory': True, 'source_type': 'file'}, # 30
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, 'source_type': 'fixed'}, # 34 (STD)
173
- {'name': 'shippingmark', 'length': 25, 'numeric_pad_zero': False, 'decimals': 0, 'mandatory': True, 'source_type': 'file'}, # 42 (Identificativo collo - shippingMark)
174
- {'name': 'branch_code_fixed', 'length': 2, 'numeric_pad_zero': False, 'decimals': 0, 'mandatory': True, 'source_type': 'fixed'}, # 43 (FIL05)
175
- {'name': 'client_type_fixed', 'length': 4, 'numeric_pad_zero': False, 'decimals': 0, 'mandatory': True, 'source_type': 'fixed'}, # 49 (MGCS)
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 source_type == 'fixed':
194
  value_to_format = fixed_and_default_values.get(field_name, '')
195
- elif source_type == '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
- 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: # Campi valuta (non presenti nel nuovo tracciato, ma logica mantenuta)
228
  num_value = float(clean_value) * (10**dec_places)
229
  formatted_value = str(int(round(num_value))).rjust(length, '0')
230
- else: # Campi numerici interi
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' e 'orderRef')", type=["xlsx"])
291
- shipment_order_file = st.file_uploader("Carica il file ShipmentOrder_ (con 'orderRef' e dettagli spedizione)", type=["xlsx"])
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 'orderRef'.
339
- - Il file `ShipmentOrder_` deve contenere 'orderRef' e i campi specifici per il destinatario e la spedizione (es. 'Contatto Destinatario', 'Via Destinatario', 'Numero colli', 'Peso colli', ecc.).
340
- I nomi delle colonne nel tuo file `ShipmentOrder_` devono corrispondere **esattamente** (ignorando maiuscole/minuscole) a quelli indicati nel documento delle specifiche per un corretto mapping. In caso di dubbi, puoi stampare i nomi delle colonne dei DataFrame (ad esempio, aggiungendo `st.write(df_shipping_mark.columns)` dopo la lettura) per verificare.
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()