pasquale991 commited on
Commit
15536fc
·
verified ·
1 Parent(s): e31e5ab

Update src/streamlit_app.py

Browse files
Files changed (1) hide show
  1. src/streamlit_app.py +179 -66
src/streamlit_app.py CHANGED
@@ -4,12 +4,13 @@ import json
4
  from datetime import datetime
5
  import io
6
  import os
 
7
 
8
  # --- Inizializzazione Session State ---
9
- if 'site_code_input' not in st.session_state:
10
- st.session_state.site_code_input = ""
11
- if 'file_uploader_key' not in st.session_state:
12
- st.session_state.file_uploader_key = 0
13
 
14
  # --- CSS personalizzato ---
15
  st.markdown(
@@ -38,45 +39,84 @@ st.markdown(
38
  }
39
  </style>
40
  """,
41
- unsafe_allow_html=True # QUESTA RIGA DEVE ESSERCI
42
  )
43
 
44
- # Funzione di conversione
45
- def convert_json_to_excel_data(json_file_content: str):
 
 
 
 
46
  try:
47
  json_data = json.loads(json_file_content)
48
 
49
  # Campi a livello radice
50
  site_code_json = json_data.get('siteCode')
51
  company_code_json = json_data.get('companyCode')
52
-
53
  # Campi sotto 'outbound'
54
  outbound_info = json_data.get('outbound', {})
55
-
56
- order_number = outbound_info.get('orderNumber')
57
- order_doc_date_str = outbound_info.get('orderDocDate')
58
- order_type_json = outbound_info.get('orderType')
59
 
60
- # Fissi: deliveryType (non nel JSON), service (nel JSON ma fisso PDB), serviceType (nel JSON ma fisso Standard)
61
- delivery_type_fixed = "DELIVERY"
62
- service_fixed = "PDB"
63
- service_type_fixed = "Standard"
64
 
 
65
  order_date_detail = outbound_info.get('orderDateDetail')
66
- order_date_str = outbound_info.get('orderDate') # Questo sarà 'dueDate'
 
67
  reason_code = outbound_info.get('reasonCode')
68
- payment_info_value = outbound_info.get('paymentInfo', {}).get('value')
 
69
 
 
 
70
  delivery_customer = outbound_info.get('deliveryCustomer', {})
71
  customer_code = delivery_customer.get('code')
 
 
 
72
  consignee_name = delivery_customer.get('name')
73
  consignee_address = delivery_customer.get('address')
74
  consignee_city = delivery_customer.get('city')
75
  consignee_province = delivery_customer.get('province')
76
  consignee_zip_code = delivery_customer.get('zipCode')
77
  consignee_country = delivery_customer.get('country')
78
- customer_mail = delivery_customer.get('refMail')
 
 
 
 
 
 
 
79
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
80
  note_ddt = outbound_info.get('noteDdt')
81
  note_warehouse = outbound_info.get('noteWarehouse')
82
  note_delivery = outbound_info.get('noteDelivery')
@@ -85,25 +125,25 @@ def convert_json_to_excel_data(json_file_content: str):
85
  info_delivery = outbound_info.get('infoDelivery')
86
  booking_info = outbound_info.get('bookingInfo')
87
 
88
- # Formattazione delle date
89
  order_doc_date_formatted = None
90
  if order_doc_date_str:
91
  try:
92
- dt_object = datetime.fromisoformat(order_doc_date_str.replace('Z', '+00:00'))
 
93
  order_doc_date_formatted = dt_object.strftime('%d/%m/%Y')
94
  except ValueError:
95
  order_doc_date_formatted = order_doc_date_str
96
  st.warning(f"Attenzione: Impossibile parsare 'orderDocDate' '{order_doc_date_str}'. Verrà usata la stringa originale.")
97
-
98
  due_date_formatted = None
99
- if order_date_str:
100
  try:
101
- dt_object = datetime.fromisoformat(order_date_str.replace('Z', '+00:00'))
102
  due_date_formatted = dt_object.strftime('%d/%m/%Y')
103
  except ValueError:
104
- due_date_formatted = order_date_str
105
- st.warning(f"Attenzione: Impossibile parsare 'orderDate' '{order_date_str}'. Verrà usata la stringa originale.")
106
-
107
 
108
  details = outbound_info.get('details', [])
109
 
@@ -123,19 +163,25 @@ def convert_json_to_excel_data(json_file_content: str):
123
  'serviceType': service_type_fixed,
124
  'paymentInfo.value': payment_info_value,
125
  'customer.code': customer_code,
126
- 'consignee.name': consignee_name,
127
- 'consignee.address': consignee_address,
128
- 'consignee.city': consignee_city,
129
- 'consignee.province': consignee_province,
130
- 'consignee.zipCode': consignee_zip_code,
131
- 'consignee.country': consignee_country,
132
- 'customer.mail': customer_mail,
 
 
 
 
 
 
133
  'details.positionId': item.get('positionId'),
134
- 'details.itemNumber': item.get('itemCode'),
135
- 'details.requestedQt': item.get('quantity'),
136
  'details.lotId': item.get('lotId'),
137
  'details.stockOwner': item.get('stockOwner'),
138
- 'details.magCli': item.get('warehouse'),
139
  'details.availability': item.get('availability'),
140
  'details.accountingStatus': item.get('accountingStatus'),
141
  'details.refOrder': item.get('refOrder'),
@@ -166,27 +212,47 @@ def convert_json_to_excel_data(json_file_content: str):
166
 
167
  # --- Interfaccia Streamlit ---
168
  st.title("Convertitore JSON Outbound a Template Excel Outbound")
169
- st.write("Carica i tuoi file JSON degli ordini Outbound e scarica il foglio Excel formattato.")
170
-
171
- st.markdown("**(Il Site Code verrà letto direttamente dal JSON)**")
 
 
 
 
 
 
172
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
173
 
174
- st.header("1. Carica i tuoi file JSON")
175
- uploaded_files = st.file_uploader(
176
  "Scegli uno o più file JSON di tipo 'Ordine_Outbound'",
177
  type=["json"],
178
  accept_multiple_files=True,
179
- key=st.session_state.file_uploader_key # Usa la chiave dinamica qui
180
  )
181
 
182
  all_processed_data = []
183
 
184
- if uploaded_files:
185
- st.info(f"Caricati {len(uploaded_files)} file. Elaborazione in corso...")
186
 
187
  conversion_successful = True
188
 
189
- for uploaded_file in uploaded_files:
190
  file_name = uploaded_file.name
191
 
192
  if not file_name.lower().startswith("ordine_outbound"):
@@ -198,14 +264,15 @@ if uploaded_files:
198
  json_content_bytes = uploaded_file.read()
199
  json_content_string = json_content_bytes.decode('utf-8')
200
 
201
- df_this_file = convert_json_to_excel_data(json_content_string)
 
202
 
203
  if df_this_file is not None and not df_this_file.empty:
204
  all_processed_data.append(df_this_file)
205
  elif df_this_file is None:
206
  conversion_successful = False
207
  st.error(f"Impossibile convertire il file '{file_name}'. Controlla i log per i dettagli.")
208
-
209
  except Exception as e:
210
  st.error(f"Si è verificato un errore imprevisto durante la lettura o l'elaborazione del file '{file_name}': {e}")
211
  conversion_successful = False
@@ -214,19 +281,48 @@ if uploaded_files:
214
  final_df = pd.concat(all_processed_data, ignore_index=True)
215
  st.success(f"Elaborazione di {len(all_processed_data)} file JSON completata! Pronto per il download.")
216
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
217
  output = io.BytesIO()
218
  writer = pd.ExcelWriter(output, engine='xlsxwriter')
219
 
220
  sheet_name_excel = 'OutboundOrder'
221
- final_df.to_excel(writer, sheet_name=sheet_name_excel, index=False)
222
 
223
  workbook = writer.book
224
- worksheet = writer.sheets[sheet_name_excel]
225
-
 
 
 
226
  final_columns_order = [
227
  'siteCode', 'companyCode', 'orderNumber', 'orderType', 'deliveryType',
228
  'orderDocDate', 'orderDateDetail', 'dueDate', 'reasonCode',
229
- 'service', 'serviceType', 'paymentInfo.value', 'customer.code',
 
 
230
  'consignee.name', 'consignee.address', 'consignee.city', 'consignee.province',
231
  'consignee.zipCode', 'consignee.country', 'customer.mail',
232
  'details.positionId', 'details.itemNumber', 'details.requestedQt',
@@ -236,13 +332,19 @@ if uploaded_files:
236
  'reasonCodeShp', 'infoDelivery', 'bookingInfo'
237
  ]
238
 
239
- cols_present_and_ordered = [col for col in final_columns_order if col in final_df.columns]
240
- final_df = final_df[cols_present_and_ordered]
 
 
 
241
 
 
242
  col_widths = {
243
  'siteCode': 10, 'companyCode': 15, 'orderNumber': 15, 'orderType': 10, 'deliveryType': 12,
244
- 'orderDocDate': 15, 'orderDateDetail': 15, 'dueDate': 12, 'reasonCode': 12,
245
- 'service': 10, 'serviceType': 12, 'paymentInfo.value': 15, 'customer.code': 15,
 
 
246
  'consignee.name': 30, 'consignee.address': 30, 'consignee.city': 15, 'consignee.province': 10,
247
  'consignee.zipCode': 10, 'consignee.country': 10, 'customer.mail': 25,
248
  'details.positionId': 12, 'details.itemNumber': 20, 'details.requestedQt': 15,
@@ -252,18 +354,31 @@ if uploaded_files:
252
  'reasonCodeShp': 15, 'infoDelivery': 20, 'bookingInfo': 20
253
  }
254
 
 
 
 
 
255
  for col_num, col_name in enumerate(final_df.columns.values):
256
- worksheet.write(0, col_num, col_name)
 
 
257
  if col_name in col_widths:
258
  worksheet.set_column(col_num, col_num, col_widths[col_name])
259
  else:
260
  worksheet.set_column(col_num, col_num, 15)
 
 
 
 
 
 
 
261
 
262
  writer.close()
263
  output.seek(0)
264
 
265
  timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
266
- output_file_name = f"OrdiniOutbound_{timestamp}.xlsx"
267
 
268
  st.download_button(
269
  label="Scarica file Excel Unificato",
@@ -271,19 +386,17 @@ if uploaded_files:
271
  file_name=output_file_name,
272
  mime="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
273
  )
 
 
274
  elif not conversion_successful:
275
  st.error("Nessun file valido è stato elaborato con successo. Controlla i messaggi di errore sopra.")
276
  else:
277
- st.info("Nessun dato valido estratto dai file selezionati.")
278
- else:
279
- st.info("Carica i tuoi file JSON per iniziare.")
280
-
281
 
282
- # --- Funzione per il reset: versione definitiva senza st.rerun() e senza experimental_singleton ---
283
  def clear_inputs():
284
- st.session_state.file_uploader_key += 1 # Questo fa resettare il file_uploader
285
- st.session_state.site_code_input = "" # Questo fa resettare il text_input
286
- # Streamlit rileverà i cambiamenti in st.session_state e farà un rerun automatico.
287
 
288
  st.markdown("---")
289
  st.button("Pulisci Input (RESET)", on_click=clear_inputs)
 
4
  from datetime import datetime
5
  import io
6
  import os
7
+ import pytz # Assicurati che pytz sia nel requirements.txt
8
 
9
  # --- Inizializzazione Session State ---
10
+ if 'file_uploader_key_json' not in st.session_state:
11
+ st.session_state.file_uploader_key_json = 0
12
+ if 'file_uploader_key_customers' not in st.session_state:
13
+ st.session_state.file_uploader_key_customers = 0
14
 
15
  # --- CSS personalizzato ---
16
  st.markdown(
 
39
  }
40
  </style>
41
  """,
42
+ unsafe_allow_html=True
43
  )
44
 
45
+ # Definizione del fuso orario per l'Italia (per coerenza, anche se le date Outbound sembrano solo data)
46
+ ITALY_TIMEZONE = pytz.timezone("Europe/Rome")
47
+ UTC_TIMEZONE = pytz.utc
48
+
49
+ # Funzione di conversione per Outbound
50
+ def convert_json_to_excel_data_outbound(json_file_content: str, customers_df: pd.DataFrame = None):
51
  try:
52
  json_data = json.loads(json_file_content)
53
 
54
  # Campi a livello radice
55
  site_code_json = json_data.get('siteCode')
56
  company_code_json = json_data.get('companyCode')
57
+
58
  # Campi sotto 'outbound'
59
  outbound_info = json_data.get('outbound', {})
 
 
 
 
60
 
61
+ order_number = outbound_info.get('orderNumber')
62
+ order_type_json = outbound_info.get('orderType')
63
+ delivery_type_fixed = "DELIVERY" # Valore fisso
 
64
 
65
+ order_doc_date_str = outbound_info.get('orderDocDate')
66
  order_date_detail = outbound_info.get('orderDateDetail')
67
+ due_date_str = outbound_info.get('orderDate') # Corrisponde a 'dueDate'
68
+
69
  reason_code = outbound_info.get('reasonCode')
70
+ service_fixed = "PDB" # Valore fisso
71
+ service_type_fixed = "Standard" # Valore fisso
72
 
73
+ payment_info_value = outbound_info.get('paymentInfo', {}).get('value')
74
+
75
  delivery_customer = outbound_info.get('deliveryCustomer', {})
76
  customer_code = delivery_customer.get('code')
77
+ customer_mail = delivery_customer.get('refMail') # customer.mail dal JSON
78
+
79
+ # Consignee data (from JSON)
80
  consignee_name = delivery_customer.get('name')
81
  consignee_address = delivery_customer.get('address')
82
  consignee_city = delivery_customer.get('city')
83
  consignee_province = delivery_customer.get('province')
84
  consignee_zip_code = delivery_customer.get('zipCode')
85
  consignee_country = delivery_customer.get('country')
86
+
87
+ # Customer data (from Excel lookup)
88
+ customer_name = ""
89
+ customer_address = ""
90
+ customer_city = ""
91
+ customer_province = ""
92
+ customer_zip_code = ""
93
+ customer_country = ""
94
 
95
+ # Esegui la lookup solo se customers_df è fornito e non vuoto
96
+ if customers_df is not None and not customers_df.empty and customer_code and company_code_json:
97
+ # Assicurati che le colonne di join siano dello stesso tipo
98
+ # customers_df['Code'] = customers_df['Code'].astype(str)
99
+ # customers_df['company.code'] = customers_df['company.code'].astype(str)
100
+
101
+ # Filtra per Code e company.code
102
+ customer_data = customers_df[
103
+ (customers_df['Code'].astype(str) == str(customer_code)) &
104
+ (customers_df['company.code'].astype(str) == str(company_code_json))
105
+ ]
106
+
107
+ if not customer_data.empty:
108
+ # Prendiamo la prima riga se ce ne sono più di una
109
+ customer_row = customer_data.iloc[0]
110
+ customer_name = customer_row.get('partner.name', '')
111
+ customer_address = customer_row.get('partner.address', '')
112
+ customer_city = customer_row.get('partner.address.city', '')
113
+ customer_province = customer_row.get('partner.address.province', '')
114
+ customer_zip_code = customer_row.get('partner.address.zipCode', '')
115
+ customer_country = customer_row.get('partner.address.country', '')
116
+ else:
117
+ st.warning(f"Attenzione: Dettagli cliente non trovati nell'Excel per Code: '{customer_code}' e Company: '{company_code_json}'.")
118
+
119
+ # Note fields
120
  note_ddt = outbound_info.get('noteDdt')
121
  note_warehouse = outbound_info.get('noteWarehouse')
122
  note_delivery = outbound_info.get('noteDelivery')
 
125
  info_delivery = outbound_info.get('infoDelivery')
126
  booking_info = outbound_info.get('bookingInfo')
127
 
128
+ # Formattazione delle date (Outbound sembra avere solo date, senza ora)
129
  order_doc_date_formatted = None
130
  if order_doc_date_str:
131
  try:
132
+ # Rimuovi 'Z' se presente e converti a datetime senza info fuso orario, poi formatta
133
+ dt_object = datetime.fromisoformat(order_doc_date_str.replace('Z', ''))
134
  order_doc_date_formatted = dt_object.strftime('%d/%m/%Y')
135
  except ValueError:
136
  order_doc_date_formatted = order_doc_date_str
137
  st.warning(f"Attenzione: Impossibile parsare 'orderDocDate' '{order_doc_date_str}'. Verrà usata la stringa originale.")
138
+
139
  due_date_formatted = None
140
+ if due_date_str: # Questo è il campo 'orderDate' del JSON che diventa 'dueDate'
141
  try:
142
+ dt_object = datetime.fromisoformat(due_date_str.replace('Z', ''))
143
  due_date_formatted = dt_object.strftime('%d/%m/%Y')
144
  except ValueError:
145
+ due_date_formatted = due_date_str
146
+ st.warning(f"Attenzione: Impossibile parsare 'orderDate' (dueDate) '{due_date_str}'. Verrà usata la stringa originale.")
 
147
 
148
  details = outbound_info.get('details', [])
149
 
 
163
  'serviceType': service_type_fixed,
164
  'paymentInfo.value': payment_info_value,
165
  'customer.code': customer_code,
166
+ 'customer.name': customer_name, # Da Excel
167
+ 'customer.address': customer_address, # Da Excel
168
+ 'customer.city': customer_city, # Da Excel
169
+ 'customer.province': customer_province, # Da Excel
170
+ 'customer.zipCode': customer_zip_code, # Da Excel
171
+ 'customer.country': customer_country, # Da Excel
172
+ 'consignee.name': consignee_name, # Dal JSON
173
+ 'consignee.address': consignee_address, # Dal JSON
174
+ 'consignee.city': consignee_city, # Dal JSON
175
+ 'consignee.province': consignee_province, # Dal JSON
176
+ 'consignee.zipCode': consignee_zip_code, # Dal JSON
177
+ 'consignee.country': consignee_country, # Dal JSON
178
+ 'customer.mail': customer_mail, # Dal JSON
179
  'details.positionId': item.get('positionId'),
180
+ 'details.itemNumber': item.get('itemCode'), # Rinominato da itemCode
181
+ 'details.requestedQt': item.get('quantity'), # Rinominato da quantity
182
  'details.lotId': item.get('lotId'),
183
  'details.stockOwner': item.get('stockOwner'),
184
+ 'details.magCli': item.get('warehouse'), # Rinominato da warehouse
185
  'details.availability': item.get('availability'),
186
  'details.accountingStatus': item.get('accountingStatus'),
187
  'details.refOrder': item.get('refOrder'),
 
212
 
213
  # --- Interfaccia Streamlit ---
214
  st.title("Convertitore JSON Outbound a Template Excel Outbound")
215
+ st.write("Carica i tuoi file JSON degli ordini Outbound e un file Excel con i dati dei clienti.")
216
+
217
+ st.header("1. Carica il file Excel dei Clienti")
218
+ st.info("Questo file Excel deve contenere le colonne: `Code`, `company.code`, `partner.name`, `partner.address`, `partner.address.city`, `partner.address.province`, `partner.address.zipCode`, `partner.address.country`.")
219
+ uploaded_customers_file = st.file_uploader(
220
+ "Scegli il file Excel con i dettagli dei Clienti",
221
+ type=["xlsx", "xls"],
222
+ key=st.session_state.file_uploader_key_customers
223
+ )
224
 
225
+ customers_df = pd.DataFrame()
226
+ if uploaded_customers_file:
227
+ try:
228
+ customers_df = pd.read_excel(uploaded_customers_file)
229
+ # Rinomina le colonne per facilitare l'accesso, se necessario, o assicurati che i nomi corrispondano
230
+ # Es: customers_df.rename(columns={'Codice': 'Code', 'Nome': 'partner.name'}, inplace=True)
231
+ # Assicurati che le colonne per il merge siano stringhe per evitare problemi di tipo
232
+ customers_df['Code'] = customers_df['Code'].astype(str)
233
+ customers_df['company.code'] = customers_df['company.code'].astype(str)
234
+ st.success("File Excel Clienti caricato con successo.")
235
+ # Debug: st.dataframe(customers_df.head())
236
+ except Exception as e:
237
+ st.error(f"Errore nel caricamento del file Excel Clienti: {e}. Assicurati che sia un file Excel valido e che le colonne siano corrette.")
238
+ customers_df = pd.DataFrame() # Reset in caso di errore
239
 
240
+ st.header("2. Carica i tuoi file JSON degli Ordini Outbound")
241
+ uploaded_json_files = st.file_uploader(
242
  "Scegli uno o più file JSON di tipo 'Ordine_Outbound'",
243
  type=["json"],
244
  accept_multiple_files=True,
245
+ key=st.session_state.file_uploader_key_json
246
  )
247
 
248
  all_processed_data = []
249
 
250
+ if uploaded_json_files and not customers_df.empty:
251
+ st.info(f"Caricati {len(uploaded_json_files)} file JSON. Elaborazione in corso...")
252
 
253
  conversion_successful = True
254
 
255
+ for uploaded_file in uploaded_json_files:
256
  file_name = uploaded_file.name
257
 
258
  if not file_name.lower().startswith("ordine_outbound"):
 
264
  json_content_bytes = uploaded_file.read()
265
  json_content_string = json_content_bytes.decode('utf-8')
266
 
267
+ # Passa customers_df alla funzione di conversione
268
+ df_this_file = convert_json_to_excel_data_outbound(json_content_string, customers_df)
269
 
270
  if df_this_file is not None and not df_this_file.empty:
271
  all_processed_data.append(df_this_file)
272
  elif df_this_file is None:
273
  conversion_successful = False
274
  st.error(f"Impossibile convertire il file '{file_name}'. Controlla i log per i dettagli.")
275
+
276
  except Exception as e:
277
  st.error(f"Si è verificato un errore imprevisto durante la lettura o l'elaborazione del file '{file_name}': {e}")
278
  conversion_successful = False
 
281
  final_df = pd.concat(all_processed_data, ignore_index=True)
282
  st.success(f"Elaborazione di {len(all_processed_data)} file JSON completata! Pronto per il download.")
283
 
284
+ # --- Post-processing per la formattazione Excel ---
285
+
286
+ # Definizione delle colonne di testata
287
+ header_columns = [
288
+ 'siteCode', 'companyCode', 'orderNumber', 'orderType', 'deliveryType',
289
+ 'orderDocDate', 'orderDateDetail', 'dueDate', 'reasonCode',
290
+ 'service', 'serviceType', 'paymentInfo.value',
291
+ 'customer.code', 'customer.name', 'customer.address', 'customer.city',
292
+ 'customer.province', 'customer.zipCode', 'customer.country',
293
+ 'consignee.name', 'consignee.address', 'consignee.city', 'consignee.province',
294
+ 'consignee.zipCode', 'consignee.country', 'customer.mail',
295
+ 'noteDdt', 'noteWarehouse', 'noteDelivery', 'noteShipment',
296
+ 'reasonCodeShp', 'infoDelivery', 'bookingInfo'
297
+ ]
298
+
299
+ # Ordina il DataFrame per orderNumber e details.positionId per raggruppare gli ordini
300
+ final_df = final_df.sort_values(by=['orderNumber', 'details.positionId']).reset_index(drop=True)
301
+
302
+ # Crea una maschera booleana per identificare le righe duplicate per orderNumber
303
+ rows_to_clear_headers = final_df.duplicated(subset=['orderNumber'], keep='first')
304
+
305
+ # Azzerare le colonne di testata per le righe identificate
306
+ final_df.loc[rows_to_clear_headers, header_columns] = ""
307
+
308
+ # --- Creazione file Excel ---
309
  output = io.BytesIO()
310
  writer = pd.ExcelWriter(output, engine='xlsxwriter')
311
 
312
  sheet_name_excel = 'OutboundOrder'
 
313
 
314
  workbook = writer.book
315
+ worksheet = writer.sheets.get(sheet_name_excel)
316
+ if worksheet is None:
317
+ worksheet = workbook.add_worksheet(sheet_name_excel)
318
+
319
+ # Lista delle colonne finali nell'ordine desiderato
320
  final_columns_order = [
321
  'siteCode', 'companyCode', 'orderNumber', 'orderType', 'deliveryType',
322
  'orderDocDate', 'orderDateDetail', 'dueDate', 'reasonCode',
323
+ 'service', 'serviceType', 'paymentInfo.value',
324
+ 'customer.code', 'customer.name', 'customer.address', 'customer.city',
325
+ 'customer.province', 'customer.zipCode', 'customer.country',
326
  'consignee.name', 'consignee.address', 'consignee.city', 'consignee.province',
327
  'consignee.zipCode', 'consignee.country', 'customer.mail',
328
  'details.positionId', 'details.itemNumber', 'details.requestedQt',
 
332
  'reasonCodeShp', 'infoDelivery', 'bookingInfo'
333
  ]
334
 
335
+ # Assicurati che il DataFrame abbia tutte le colonne nell'ordine desiderato
336
+ for col in final_columns_order:
337
+ if col not in final_df.columns:
338
+ final_df[col] = ""
339
+ final_df = final_df[final_columns_order]
340
 
341
+ # Larghezze delle colonne aggiornate (adattate per Outbound)
342
  col_widths = {
343
  'siteCode': 10, 'companyCode': 15, 'orderNumber': 15, 'orderType': 10, 'deliveryType': 12,
344
+ 'orderDocDate': 12, 'orderDateDetail': 15, 'dueDate': 12, 'reasonCode': 12, # Date solo gg/mm/aaaa
345
+ 'service': 10, 'serviceType': 12, 'paymentInfo.value': 15,
346
+ 'customer.code': 15, 'customer.name': 30, 'customer.address': 30, 'customer.city': 15,
347
+ 'customer.province': 10, 'customer.zipCode': 10, 'customer.country': 10,
348
  'consignee.name': 30, 'consignee.address': 30, 'consignee.city': 15, 'consignee.province': 10,
349
  'consignee.zipCode': 10, 'consignee.country': 10, 'customer.mail': 25,
350
  'details.positionId': 12, 'details.itemNumber': 20, 'details.requestedQt': 15,
 
354
  'reasonCodeShp': 15, 'infoDelivery': 20, 'bookingInfo': 20
355
  }
356
 
357
+ # Formato data per le colonne di data (solo data, senza ora)
358
+ date_format = workbook.add_format({'num_format': 'dd/mm/yyyy'})
359
+
360
+ # Scrivi i nomi delle colonne nella riga 2 (indice 1 in xlsxwriter)
361
  for col_num, col_name in enumerate(final_df.columns.values):
362
+ worksheet.write(1, col_num, col_name)
363
+
364
+ # Imposta le larghezze delle colonne
365
  if col_name in col_widths:
366
  worksheet.set_column(col_num, col_num, col_widths[col_name])
367
  else:
368
  worksheet.set_column(col_num, col_num, 15)
369
+
370
+ # Applica il formato data alle colonne 'orderDocDate' e 'dueDate'
371
+ if col_name in ['orderDocDate', 'dueDate']:
372
+ worksheet.set_column(col_num, col_num, col_widths[col_name], date_format)
373
+
374
+ # Scrivi i dati a partire dalla riga 3 (indice 2 in xlsxwriter)
375
+ final_df.to_excel(writer, sheet_name=sheet_name_excel, index=False, header=False, startrow=2)
376
 
377
  writer.close()
378
  output.seek(0)
379
 
380
  timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
381
+ output_file_name = f"OutboundOrders_{timestamp}.xlsx"
382
 
383
  st.download_button(
384
  label="Scarica file Excel Unificato",
 
386
  file_name=output_file_name,
387
  mime="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
388
  )
389
+ elif uploaded_json_files and customers_df.empty:
390
+ st.warning("Per elaborare i file JSON, devi prima caricare un file Excel con i dettagli dei clienti.")
391
  elif not conversion_successful:
392
  st.error("Nessun file valido è stato elaborato con successo. Controlla i messaggi di errore sopra.")
393
  else:
394
+ st.info("Carica i tuoi file JSON per iniziare.")
 
 
 
395
 
396
+ # --- Funzione per il reset ---
397
  def clear_inputs():
398
+ st.session_state.file_uploader_key_json += 1
399
+ st.session_state.file_uploader_key_customers += 1
 
400
 
401
  st.markdown("---")
402
  st.button("Pulisci Input (RESET)", on_click=clear_inputs)