pasquale991 commited on
Commit
f4eb392
·
verified ·
1 Parent(s): 49a7151

Update src/streamlit_app.py

Browse files
Files changed (1) hide show
  1. src/streamlit_app.py +124 -96
src/streamlit_app.py CHANGED
@@ -7,11 +7,14 @@ import os
7
  import pytz
8
 
9
  # --- Inizializzazione Session State ---
10
- # Chiavi uniche per i file uploader per evitare DuplicateWidgetID
11
  if 'file_uploader_key_json' not in st.session_state:
12
- st.session_state.file_uploader_key_json = 'json_uploader_0' # Chiave unica
13
  if 'file_uploader_key_customers' not in st.session_state:
14
- st.session_state.file_uploader_key_customers = 'customers_uploader_0' # Chiave unica
 
 
 
 
15
 
16
  # --- CSS personalizzato ---
17
  st.markdown(
@@ -43,12 +46,12 @@ st.markdown(
43
  unsafe_allow_html=True
44
  )
45
 
46
- # Definizione del fuso orario per l'Italia (per coerenza, anche se le date Outbound sembrano solo data)
47
  ITALY_TIMEZONE = pytz.timezone("Europe/Rome")
48
  UTC_TIMEZONE = pytz.utc
49
 
50
  # Funzione di conversione per Outbound
51
- def convert_json_to_excel_data_outbound(json_file_content: str, customers_df: pd.DataFrame = None):
52
  try:
53
  json_data = json.loads(json_file_content)
54
 
@@ -61,28 +64,24 @@ def convert_json_to_excel_data_outbound(json_file_content: str, customers_df: pd
61
 
62
  order_number = outbound_info.get('orderNumber')
63
  order_type_json = outbound_info.get('orderType')
64
- delivery_type_fixed = "DELIVERY" # Valore fisso
65
 
66
- order_doc_date_str = outbound_info.get('orderDocDate') # JSON field name
67
- date_detail_json = outbound_info.get('orderDateDetail') # JSON field name, becomes 'dateDetail'
68
- due_date_str = outbound_info.get('orderDate') # JSON field name, becomes 'dueDate'
69
 
70
  reason_code = outbound_info.get('reasonCode')
71
- # service_fixed = "PDB" # Rimosso
72
- # service_type_fixed = "Standard" # Rimosso
73
-
74
- payment_info_value = outbound_info.get('paymentInfo', {}).get('value') # JSON field name
75
 
76
  delivery_customer = outbound_info.get('deliveryCustomer', {})
77
  customer_code = delivery_customer.get('code')
78
- customer_mail = delivery_customer.get('refMail') # customer.mail dal JSON
79
 
80
  # Consignee data (from JSON)
81
  consignee_name = delivery_customer.get('name')
82
  consignee_address_base = delivery_customer.get('address', '')
83
- consignee_address_number = delivery_customer.get('addressNumber', '') # Nuovo campo da JSON
84
 
85
- # Concatenazione indirizzo e civico per consignee.address
86
  consignee_address = f"{consignee_address_base} {consignee_address_number}".strip() if consignee_address_base or consignee_address_number else ""
87
 
88
  consignee_city = delivery_customer.get('city')
@@ -92,25 +91,26 @@ def convert_json_to_excel_data_outbound(json_file_content: str, customers_df: pd
92
 
93
  # Customer data (from Excel lookup)
94
  customer_name = ""
95
- customer_address = "" # Questo sarà concatenazione
96
  customer_city = ""
97
  customer_province = ""
98
  customer_zip_code = ""
99
  customer_country = ""
100
 
101
- # Esegui la lookup solo se customers_df è fornito e non vuoto
102
- if customers_df is not None and not customers_df.empty and customer_code and company_code_json:
 
103
  # Filtra per 'code' e 'company.code' (assicurandosi che siano stringhe)
104
- customer_data = customers_df[
105
- (customers_df['code'].astype(str) == str(customer_code)) & # Modificato da 'Code' a 'code'
106
- (customers_df['company.code'].astype(str) == str(company_code_json))
 
107
  ]
108
 
109
  if not customer_data.empty:
110
  customer_row = customer_data.iloc[0]
111
  customer_name = customer_row.get('partner.name', '')
112
 
113
- # Concatenazione partner.address.address e partner.address.addressNumber
114
  excel_customer_address_base = customer_row.get('partner.address.address', '')
115
  excel_customer_address_number = customer_row.get('partner.address.addressNumber', '')
116
  customer_address = f"{excel_customer_address_base} {excel_customer_address_number}".strip() if excel_customer_address_base or excel_customer_address_number else ""
@@ -120,11 +120,20 @@ def convert_json_to_excel_data_outbound(json_file_content: str, customers_df: pd
120
  customer_zip_code = customer_row.get('partner.address.zipCode', '')
121
  customer_country = customer_row.get('partner.address.country', '')
122
  else:
123
- st.warning(f"Attenzione: Dettagli cliente non trovati nell'Excel per Code: '{customer_code}' e Company: '{company_code_json}'.")
 
 
 
 
 
 
 
 
 
124
 
125
  # Note fields
126
- note_ddt = outbound_info.get('noteDdt') # JSON field name
127
- note_warehouse = outbound_info.get('noteWarehouse') # JSON field name
128
 
129
  note_delivery = outbound_info.get('noteDelivery')
130
  note_shipment = outbound_info.get('noteShipment')
@@ -133,7 +142,7 @@ def convert_json_to_excel_data_outbound(json_file_content: str, customers_df: pd
133
  booking_info = outbound_info.get('bookingInfo')
134
 
135
  # Formattazione delle date
136
- order_date_formatted = None # Cambiato nome variabile
137
  if order_doc_date_str:
138
  try:
139
  dt_object = datetime.fromisoformat(order_doc_date_str.replace('Z', ''))
@@ -161,27 +170,25 @@ def convert_json_to_excel_data_outbound(json_file_content: str, customers_df: pd
161
  'orderNumber': order_number,
162
  'orderType': order_type_json,
163
  'deliveryType': delivery_type_fixed,
164
- 'orderDate': order_date_formatted, # Cambiato nome colonna
165
- 'dateDetail': date_detail_json, # Cambiato nome colonna
166
  'dueDate': due_date_formatted,
167
  'reasonCode': reason_code,
168
- # 'service': service_fixed, # Rimosso
169
- # 'serviceType': service_type_fixed, # Rimosso
170
- 'paymentValue': payment_info_value, # Cambiato nome colonna
171
  'customer.code': customer_code,
172
- 'customer.name': customer_name, # Da Excel
173
- 'customer.address': customer_address, # Da Excel (concatenato)
174
- 'customer.city': customer_city, # Da Excel
175
- 'customer.province': customer_province, # Da Excel
176
- 'customer.zipCode': customer_zip_code, # Da Excel
177
- 'customer.country': customer_country, # Da Excel
178
- 'consignee.name': consignee_name, # Dal JSON
179
- 'consignee.address': consignee_address, # Dal JSON (concatenato)
180
- 'consignee.city': consignee_city, # Dal JSON
181
- 'consignee.province': consignee_province, # Dal JSON
182
- 'consignee.zipCode': consignee_zip_code, # Dal JSON
183
- 'consignee.country': consignee_country, # Dal JSON
184
- 'customer.mail': customer_mail, # Dal JSON
185
  'details.positionId': item.get('positionId'),
186
  'details.itemNumber': item.get('itemCode'),
187
  'details.requestedQt': item.get('quantity'),
@@ -191,8 +198,8 @@ def convert_json_to_excel_data_outbound(json_file_content: str, customers_df: pd
191
  'details.availability': item.get('availability'),
192
  'details.accountingStatus': item.get('accountingStatus'),
193
  'details.refOrder': item.get('refOrder'),
194
- 'noteDDT': note_ddt, # Cambiato nome colonna
195
- 'noteMag': note_warehouse, # Cambiato nome colonna
196
  'noteDelivery': note_delivery,
197
  'noteShipment': note_shipment,
198
  'reasonCodeShp': reason_code_shp,
@@ -218,70 +225,96 @@ def convert_json_to_excel_data_outbound(json_file_content: str, customers_df: pd
218
 
219
  # --- Interfaccia Streamlit ---
220
  st.title("Convertitore JSON Outbound a Template Excel Outbound")
221
- st.write("Carica i tuoi file JSON degli ordini Outbound e un file Excel con i dati dei clienti.")
222
 
223
- st.header("1. Carica il file Excel dei Clienti")
224
- st.info("Questo file Excel deve contenere le colonne: `code`, `company.code`, `partner.name`, `partner.address.address`, `partner.address.addressNumber`, `partner.address.city`, `partner.address.province`, `partner.address.zipCode`, `partner.address.country`.")
225
  uploaded_customers_file = st.file_uploader(
226
- "Scegli il file Excel con i dettagli dei Clienti",
227
  type=["xlsx", "xls"],
228
- accept_multiple_files=False, # Limitato a un solo file
229
  key=st.session_state.file_uploader_key_customers
230
  )
231
 
232
- customers_df = pd.DataFrame()
 
 
233
  if uploaded_customers_file:
234
  try:
235
- customers_df = pd.read_excel(uploaded_customers_file)
236
- # Assicurati che le colonne per il merge siano stringhe per evitare problemi di tipo
237
- customers_df['code'] = customers_df['code'].astype(str) # Modificato da 'Code' a 'code'
238
- customers_df['company.code'] = customers_df['company.code'].astype(str)
239
- st.success("File Excel Clienti caricato con successo.")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
240
  except Exception as e:
241
- st.error(f"Errore nel caricamento del file Excel Clienti: {e}. Assicurati che sia un file Excel valido e che le colonne siano corrette.")
242
- customers_df = pd.DataFrame()
 
 
 
 
243
 
244
  st.header("2. Carica i tuoi file JSON degli Ordini Outbound")
245
  uploaded_json_files = st.file_uploader(
246
  "Scegli uno o più file JSON di tipo 'Ordine_Outbound'",
247
  type=["json"],
248
  accept_multiple_files=True,
249
- key=st.session_state.file_uploader_key_json
 
250
  )
251
 
252
  all_processed_data = []
 
 
253
 
254
- if uploaded_json_files and not customers_df.empty:
255
  st.info(f"Caricati {len(uploaded_json_files)} file JSON. Elaborazione in corso...")
256
 
257
- conversion_successful = True
258
-
259
  for uploaded_file in uploaded_json_files:
260
  file_name = uploaded_file.name
261
 
262
  if not file_name.lower().startswith("ordine_outbound"):
263
  st.error(f"Errore: Il file '{file_name}' non inizia con 'Ordine_Outbound'. Verrà ignorato.")
264
- conversion_successful = False
265
  continue
266
 
267
  try:
268
  json_content_bytes = uploaded_file.read()
269
  json_content_string = json_content_bytes.decode('utf-8')
270
 
271
- # Passa customers_df alla funzione di conversione
272
- df_this_file = convert_json_to_excel_data_outbound(json_content_string, customers_df)
273
 
274
  if df_this_file is not None and not df_this_file.empty:
275
  all_processed_data.append(df_this_file)
276
- elif df_this_file is None:
277
- conversion_successful = False
278
- st.error(f"Impossibile convertire il file '{file_name}'. Controlla i log per i dettagli.")
279
 
280
  except Exception as e:
281
  st.error(f"Si è verificato un errore imprevisto durante la lettura o l'elaborazione del file '{file_name}': {e}")
282
- conversion_successful = False
283
 
284
- if all_processed_data:
285
  final_df = pd.concat(all_processed_data, ignore_index=True)
286
  st.success(f"Elaborazione di {len(all_processed_data)} file JSON completata! Pronto per il download.")
287
 
@@ -291,13 +324,12 @@ if uploaded_json_files and not customers_df.empty:
291
  header_columns = [
292
  'siteCode', 'companyCode', 'orderNumber', 'orderType', 'deliveryType',
293
  'orderDate', 'dateDetail', 'dueDate', 'reasonCode',
294
- # 'service', 'serviceType', # Rimosso
295
  'paymentValue',
296
  'customer.code', 'customer.name', 'customer.address', 'customer.city',
297
  'customer.province', 'customer.zipCode', 'customer.country',
298
  'consignee.name', 'consignee.address', 'consignee.city', 'consignee.province',
299
  'consignee.zipCode', 'consignee.country', 'customer.mail',
300
- 'noteDDT', 'noteMag', 'noteDelivery', 'noteShipment', # Modificati
301
  'reasonCodeShp', 'infoDelivery', 'bookingInfo'
302
  ]
303
 
@@ -314,16 +346,15 @@ if uploaded_json_files and not customers_df.empty:
314
  output = io.BytesIO()
315
  writer = pd.ExcelWriter(output, engine='xlsxwriter')
316
 
317
- sheet_name_excel = 'OutboundOrder'
318
 
319
  workbook = writer.book
320
- worksheet = workbook.add_worksheet(sheet_name_excel) # Creiamo sempre un nuovo worksheet
321
 
322
  # Lista delle colonne finali nell'ordine desiderato
323
  final_columns_order = [
324
  'siteCode', 'companyCode', 'orderNumber', 'orderType', 'deliveryType',
325
  'orderDate', 'dateDetail', 'dueDate', 'reasonCode',
326
- # 'service', 'serviceType', # Rimosso
327
  'paymentValue',
328
  'customer.code', 'customer.name', 'customer.address', 'customer.city',
329
  'customer.province', 'customer.zipCode', 'customer.country',
@@ -332,24 +363,21 @@ if uploaded_json_files and not customers_df.empty:
332
  'details.positionId', 'details.itemNumber', 'details.requestedQt',
333
  'details.lotId', 'details.stockOwner', 'details.magCli', 'details.availability',
334
  'details.accountingStatus', 'details.refOrder',
335
- 'noteDDT', 'noteMag', 'noteDelivery', 'noteShipment', # Modificati
336
  'reasonCodeShp', 'infoDelivery', 'bookingInfo'
337
  ]
338
 
339
  # Assicurati che il DataFrame abbia tutte le colonne nell'ordine desiderato
340
- # Se una colonna non esiste nel DataFrame, la aggiungiamo vuota
341
  for col in final_columns_order:
342
  if col not in final_df.columns:
343
  final_df[col] = ""
344
- # Riordina le colonne del DataFrame
345
  final_df = final_df[final_columns_order]
346
 
347
- # Larghezze delle colonne aggiornate (adattate per Outbound)
348
  col_widths = {
349
  'siteCode': 10, 'companyCode': 15, 'orderNumber': 15, 'orderType': 10, 'deliveryType': 12,
350
- 'orderDate': 12, 'dateDetail': 15, 'dueDate': 12, 'reasonCode': 12, # Date solo gg/mm/aaaa
351
- # 'service': 10, 'serviceType': 12, # Rimosso
352
- 'paymentValue': 15, # Modificato
353
  'customer.code': 15, 'customer.name': 30, 'customer.address': 30, 'customer.city': 15,
354
  'customer.province': 10, 'customer.zipCode': 10, 'customer.country': 10,
355
  'consignee.name': 30, 'consignee.address': 30, 'consignee.city': 15, 'consignee.province': 10,
@@ -357,11 +385,11 @@ if uploaded_json_files and not customers_df.empty:
357
  'details.positionId': 12, 'details.itemNumber': 20, 'details.requestedQt': 15,
358
  'details.lotId': 10, 'details.stockOwner': 15, 'details.magCli': 15, 'details.availability': 15,
359
  'details.accountingStatus': 18, 'details.refOrder': 15,
360
- 'noteDDT': 20, 'noteMag': 20, 'noteDelivery': 20, 'noteShipment': 20, # Modificati
361
  'reasonCodeShp': 15, 'infoDelivery': 20, 'bookingInfo': 20
362
  }
363
 
364
- # Formato data per le colonne di data (solo data, senza ora)
365
  date_format = workbook.add_format({'num_format': 'dd/mm/yyyy'})
366
 
367
  # Scrivi i nomi delle colonne nella riga 2 (indice 1 in xlsxwriter)
@@ -375,7 +403,7 @@ if uploaded_json_files and not customers_df.empty:
375
  worksheet.set_column(col_num, col_num, 15)
376
 
377
  # Applica il formato data alle colonne 'orderDate' e 'dueDate'
378
- if col_name in ['orderDate', 'dueDate']: # Modificato 'orderDocDate' in 'orderDate'
379
  worksheet.set_column(col_num, col_num, col_widths[col_name], date_format)
380
 
381
  # Scrivi i dati a partire dalla riga 3 (indice 2 in xlsxwriter)
@@ -385,7 +413,7 @@ if uploaded_json_files and not customers_df.empty:
385
  output.seek(0)
386
 
387
  timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
388
- output_file_name = f"OutboundOrders_{timestamp}.xlsx"
389
 
390
  st.download_button(
391
  label="Scarica file Excel Unificato",
@@ -393,19 +421,19 @@ if uploaded_json_files and not customers_df.empty:
393
  file_name=output_file_name,
394
  mime="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
395
  )
396
- elif uploaded_json_files and customers_df.empty:
397
- st.warning("Per elaborare i file JSON, devi prima caricare un file Excel con i dettagli dei clienti.")
398
- elif not conversion_successful:
399
- st.error("Nessun file valido è stato elaborato con successo. Controlla i messaggi di errore sopra.")
400
  else:
401
- st.info("Carica i tuoi file JSON per iniziare.")
402
 
403
  # --- Funzione per il reset ---
404
  def clear_inputs():
405
- # Per forzare il reset del file uploader, cambiamo la sua chiave
406
- # Usiamo un timestamp per generare una chiave sempre nuova e unica
407
  st.session_state.file_uploader_key_json = f'json_uploader_{datetime.now().timestamp()}'
408
- st.session_state.file_uploader_key_customers = f'customers_uploader_{datetime.now().timestamp()}'
 
 
409
 
410
  st.markdown("---")
411
  st.button("Pulisci Input (RESET)", on_click=clear_inputs)
 
7
  import pytz
8
 
9
  # --- Inizializzazione Session State ---
 
10
  if 'file_uploader_key_json' not in st.session_state:
11
+ st.session_state.file_uploader_key_json = 'json_uploader_0'
12
  if 'file_uploader_key_customers' not in st.session_state:
13
+ st.session_state.file_uploader_key_customers = 'customers_uploader_0'
14
+ if 'customer_excel_loaded' not in st.session_state:
15
+ st.session_state.customer_excel_loaded = False
16
+ if 'customers_df' not in st.session_state:
17
+ st.session_state.customers_df = pd.DataFrame()
18
 
19
  # --- CSS personalizzato ---
20
  st.markdown(
 
46
  unsafe_allow_html=True
47
  )
48
 
49
+ # Definizione del fuso orario (per coerenza, anche se le date Outbound sembrano solo data)
50
  ITALY_TIMEZONE = pytz.timezone("Europe/Rome")
51
  UTC_TIMEZONE = pytz.utc
52
 
53
  # Funzione di conversione per Outbound
54
+ def convert_json_to_excel_data_outbound(json_file_content: str, customers_df_input: pd.DataFrame = None):
55
  try:
56
  json_data = json.loads(json_file_content)
57
 
 
64
 
65
  order_number = outbound_info.get('orderNumber')
66
  order_type_json = outbound_info.get('orderType')
67
+ delivery_type_fixed = "DELIVERY"
68
 
69
+ order_doc_date_str = outbound_info.get('orderDocDate')
70
+ date_detail_json = outbound_info.get('orderDateDetail')
71
+ due_date_str = outbound_info.get('orderDate')
72
 
73
  reason_code = outbound_info.get('reasonCode')
74
+ payment_info_value = outbound_info.get('paymentInfo', {}).get('value')
 
 
 
75
 
76
  delivery_customer = outbound_info.get('deliveryCustomer', {})
77
  customer_code = delivery_customer.get('code')
78
+ customer_mail = delivery_customer.get('refMail')
79
 
80
  # Consignee data (from JSON)
81
  consignee_name = delivery_customer.get('name')
82
  consignee_address_base = delivery_customer.get('address', '')
83
+ consignee_address_number = delivery_customer.get('addressNumber', '')
84
 
 
85
  consignee_address = f"{consignee_address_base} {consignee_address_number}".strip() if consignee_address_base or consignee_address_number else ""
86
 
87
  consignee_city = delivery_customer.get('city')
 
91
 
92
  # Customer data (from Excel lookup)
93
  customer_name = ""
94
+ customer_address = ""
95
  customer_city = ""
96
  customer_province = ""
97
  customer_zip_code = ""
98
  customer_country = ""
99
 
100
+ # Esegui la lookup solo se customers_df_input è fornito e non vuoto
101
+ if customers_df_input is not None and not customers_df_input.empty and customer_code and company_code_json:
102
+
103
  # Filtra per 'code' e 'company.code' (assicurandosi che siano stringhe)
104
+ # Aggiunto controllo per tipo stringa prima del confronto
105
+ customer_data = customers_df_input[
106
+ (customers_df_input['code'].astype(str) == str(customer_code)) &
107
+ (customers_df_input['company.code'].astype(str) == str(company_code_json))
108
  ]
109
 
110
  if not customer_data.empty:
111
  customer_row = customer_data.iloc[0]
112
  customer_name = customer_row.get('partner.name', '')
113
 
 
114
  excel_customer_address_base = customer_row.get('partner.address.address', '')
115
  excel_customer_address_number = customer_row.get('partner.address.addressNumber', '')
116
  customer_address = f"{excel_customer_address_base} {excel_customer_address_number}".strip() if excel_customer_address_base or excel_customer_address_number else ""
 
120
  customer_zip_code = customer_row.get('partner.address.zipCode', '')
121
  customer_country = customer_row.get('partner.address.country', '')
122
  else:
123
+ # Errore bloccante se l'anagrafica non viene trovata
124
+ st.error(f"ERRORE: Manca anagrafica per customer.code '{customer_code}' e company.code '{company_code_json}' nel file Excel dei clienti. Impossibile procedere con questo JSON.")
125
+ return None # Ritorna None per indicare un errore bloccante
126
+
127
+ elif not customer_code or not company_code_json:
128
+ st.warning(f"Attenzione: Customer Code o Company Code mancante nel JSON per l'ordine '{order_number}'. Impossibile effettuare la lookup sull'Excel.")
129
+
130
+ elif customers_df_input is None or customers_df_input.empty:
131
+ st.error("ERRORE: Il file Excel dell'anagrafica partner non è stato caricato o è vuoto. Caricare prima l'anagrafica.")
132
+ return None # Errore bloccante se l'Excel clienti non è disponibile
133
 
134
  # Note fields
135
+ note_ddt = outbound_info.get('noteDdt')
136
+ note_warehouse = outbound_info.get('noteWarehouse')
137
 
138
  note_delivery = outbound_info.get('noteDelivery')
139
  note_shipment = outbound_info.get('noteShipment')
 
142
  booking_info = outbound_info.get('bookingInfo')
143
 
144
  # Formattazione delle date
145
+ order_date_formatted = None
146
  if order_doc_date_str:
147
  try:
148
  dt_object = datetime.fromisoformat(order_doc_date_str.replace('Z', ''))
 
170
  'orderNumber': order_number,
171
  'orderType': order_type_json,
172
  'deliveryType': delivery_type_fixed,
173
+ 'orderDate': order_date_formatted,
174
+ 'dateDetail': date_detail_json,
175
  'dueDate': due_date_formatted,
176
  'reasonCode': reason_code,
177
+ 'paymentValue': payment_info_value,
 
 
178
  'customer.code': customer_code,
179
+ 'customer.name': customer_name,
180
+ 'customer.address': customer_address,
181
+ 'customer.city': customer_city,
182
+ 'customer.province': customer_province,
183
+ 'customer.zipCode': customer_zip_code,
184
+ 'customer.country': customer_country,
185
+ 'consignee.name': consignee_name,
186
+ 'consignee.address': consignee_address,
187
+ 'consignee.city': consignee_city,
188
+ 'consignee.province': consignee_province,
189
+ 'consignee.zipCode': consignee_zip_code,
190
+ 'consignee.country': consignee_country,
191
+ 'customer.mail': customer_mail,
192
  'details.positionId': item.get('positionId'),
193
  'details.itemNumber': item.get('itemCode'),
194
  'details.requestedQt': item.get('quantity'),
 
198
  'details.availability': item.get('availability'),
199
  'details.accountingStatus': item.get('accountingStatus'),
200
  'details.refOrder': item.get('refOrder'),
201
+ 'noteDDT': note_ddt,
202
+ 'noteMag': note_warehouse,
203
  'noteDelivery': note_delivery,
204
  'noteShipment': note_shipment,
205
  'reasonCodeShp': reason_code_shp,
 
225
 
226
  # --- Interfaccia Streamlit ---
227
  st.title("Convertitore JSON Outbound a Template Excel Outbound")
228
+ st.write("Questo strumento converte uno o più file JSON di ordini Outbound in un unico foglio Excel formattato, utilizzando un'anagrafica partner esterna.")
229
 
230
+ st.header("1. Carica Excel con Anagrafica Partner")
 
231
  uploaded_customers_file = st.file_uploader(
232
+ "Scegli il file Excel con l'anagrafica dei partner",
233
  type=["xlsx", "xls"],
234
+ accept_multiple_files=False,
235
  key=st.session_state.file_uploader_key_customers
236
  )
237
 
238
+ # Inizializza customers_df per il contesto corrente del rerun
239
+ customers_df = st.session_state.customers_df
240
+
241
  if uploaded_customers_file:
242
  try:
243
+ # Leggi l'Excel partendo dalla riga 2 (indice 1) per le intestazioni
244
+ temp_customers_df = pd.read_excel(uploaded_customers_file, header=1)
245
+
246
+ # Colonne richieste nell'Excel clienti
247
+ required_excel_cols = [
248
+ 'code', 'company.code', 'partner.name', 'partner.address.address',
249
+ 'partner.address.addressNumber', 'partner.address.city',
250
+ 'partner.address.province', 'partner.address.zipCode', 'partner.address.country'
251
+ ]
252
+
253
+ # Verifica che tutte le colonne richieste siano presenti
254
+ missing_cols = [col for col in required_excel_cols if col not in temp_customers_df.columns]
255
+
256
+ if missing_cols:
257
+ st.error(f"Errore: Il file Excel dell'anagrafica partner manca le seguenti colonne essenziali: {', '.join(missing_cols)}. Caricare un file corretto.")
258
+ st.session_state.customer_excel_loaded = False
259
+ st.session_state.customers_df = pd.DataFrame() # Resetta il DF in caso di errore
260
+ else:
261
+ # Assicurati che le colonne per il merge siano stringhe per evitare problemi di tipo
262
+ temp_customers_df['code'] = temp_customers_df['code'].astype(str)
263
+ temp_customers_df['company.code'] = temp_customers_df['company.code'].astype(str)
264
+ st.session_state.customers_df = temp_customers_df # Salva il DF nello session_state
265
+ st.session_state.customer_excel_loaded = True
266
+ st.success("File Excel Anagrafica Partner caricato con successo e verificato.")
267
+ customers_df = st.session_state.customers_df # Aggiorna la variabile locale
268
  except Exception as e:
269
+ st.error(f"Errore nel caricamento del file Excel Anagrafica Partner: {e}. Assicurati che sia un file Excel valido e che le intestazioni siano sulla seconda riga.")
270
+ st.session_state.customer_excel_loaded = False
271
+ st.session_state.customers_df = pd.DataFrame() # Resetta il DF in caso di errore
272
+
273
+ # Aggiorna la variabile locale customers_df ogni volta che il session_state cambia
274
+ customers_df = st.session_state.customers_df
275
 
276
  st.header("2. Carica i tuoi file JSON degli Ordini Outbound")
277
  uploaded_json_files = st.file_uploader(
278
  "Scegli uno o più file JSON di tipo 'Ordine_Outbound'",
279
  type=["json"],
280
  accept_multiple_files=True,
281
+ key=st.session_state.file_uploader_key_json,
282
+ disabled=not st.session_state.customer_excel_loaded # Disabilita se l'Excel non è stato caricato
283
  )
284
 
285
  all_processed_data = []
286
+ # Flag per tracciare se c'è stato un errore bloccante nella conversione di qualsiasi JSON
287
+ has_blocking_error = False
288
 
289
+ if uploaded_json_files and st.session_state.customer_excel_loaded:
290
  st.info(f"Caricati {len(uploaded_json_files)} file JSON. Elaborazione in corso...")
291
 
 
 
292
  for uploaded_file in uploaded_json_files:
293
  file_name = uploaded_file.name
294
 
295
  if not file_name.lower().startswith("ordine_outbound"):
296
  st.error(f"Errore: Il file '{file_name}' non inizia con 'Ordine_Outbound'. Verrà ignorato.")
297
+ has_blocking_error = True
298
  continue
299
 
300
  try:
301
  json_content_bytes = uploaded_file.read()
302
  json_content_string = json_content_bytes.decode('utf-8')
303
 
304
+ # Passa customers_df dallo session state alla funzione di conversione
305
+ df_this_file = convert_json_to_excel_data_outbound(json_content_string, st.session_state.customers_df)
306
 
307
  if df_this_file is not None and not df_this_file.empty:
308
  all_processed_data.append(df_this_file)
309
+ elif df_this_file is None: # Se la funzione ritorna None, c'è stato un errore bloccante
310
+ has_blocking_error = True
311
+ # Il messaggio di errore specifico è già gestito all'interno di convert_json_to_excel_data_outbound
312
 
313
  except Exception as e:
314
  st.error(f"Si è verificato un errore imprevisto durante la lettura o l'elaborazione del file '{file_name}': {e}")
315
+ has_blocking_error = True
316
 
317
+ if not has_blocking_error and all_processed_data:
318
  final_df = pd.concat(all_processed_data, ignore_index=True)
319
  st.success(f"Elaborazione di {len(all_processed_data)} file JSON completata! Pronto per il download.")
320
 
 
324
  header_columns = [
325
  'siteCode', 'companyCode', 'orderNumber', 'orderType', 'deliveryType',
326
  'orderDate', 'dateDetail', 'dueDate', 'reasonCode',
 
327
  'paymentValue',
328
  'customer.code', 'customer.name', 'customer.address', 'customer.city',
329
  'customer.province', 'customer.zipCode', 'customer.country',
330
  'consignee.name', 'consignee.address', 'consignee.city', 'consignee.province',
331
  'consignee.zipCode', 'consignee.country', 'customer.mail',
332
+ 'noteDDT', 'noteMag', 'noteDelivery', 'noteShipment',
333
  'reasonCodeShp', 'infoDelivery', 'bookingInfo'
334
  ]
335
 
 
346
  output = io.BytesIO()
347
  writer = pd.ExcelWriter(output, engine='xlsxwriter')
348
 
349
+ sheet_name_excel = 'Delivery' # Nome del foglio modificato
350
 
351
  workbook = writer.book
352
+ worksheet = workbook.add_worksheet(sheet_name_excel)
353
 
354
  # Lista delle colonne finali nell'ordine desiderato
355
  final_columns_order = [
356
  'siteCode', 'companyCode', 'orderNumber', 'orderType', 'deliveryType',
357
  'orderDate', 'dateDetail', 'dueDate', 'reasonCode',
 
358
  'paymentValue',
359
  'customer.code', 'customer.name', 'customer.address', 'customer.city',
360
  'customer.province', 'customer.zipCode', 'customer.country',
 
363
  'details.positionId', 'details.itemNumber', 'details.requestedQt',
364
  'details.lotId', 'details.stockOwner', 'details.magCli', 'details.availability',
365
  'details.accountingStatus', 'details.refOrder',
366
+ 'noteDDT', 'noteMag', 'noteDelivery', 'noteShipment',
367
  'reasonCodeShp', 'infoDelivery', 'bookingInfo'
368
  ]
369
 
370
  # Assicurati che il DataFrame abbia tutte le colonne nell'ordine desiderato
 
371
  for col in final_columns_order:
372
  if col not in final_df.columns:
373
  final_df[col] = ""
 
374
  final_df = final_df[final_columns_order]
375
 
376
+ # Larghezze delle colonne aggiornate
377
  col_widths = {
378
  'siteCode': 10, 'companyCode': 15, 'orderNumber': 15, 'orderType': 10, 'deliveryType': 12,
379
+ 'orderDate': 12, 'dateDetail': 15, 'dueDate': 12, 'reasonCode': 12,
380
+ 'paymentValue': 15,
 
381
  'customer.code': 15, 'customer.name': 30, 'customer.address': 30, 'customer.city': 15,
382
  'customer.province': 10, 'customer.zipCode': 10, 'customer.country': 10,
383
  'consignee.name': 30, 'consignee.address': 30, 'consignee.city': 15, 'consignee.province': 10,
 
385
  'details.positionId': 12, 'details.itemNumber': 20, 'details.requestedQt': 15,
386
  'details.lotId': 10, 'details.stockOwner': 15, 'details.magCli': 15, 'details.availability': 15,
387
  'details.accountingStatus': 18, 'details.refOrder': 15,
388
+ 'noteDDT': 20, 'noteMag': 20, 'noteDelivery': 20, 'noteShipment': 20,
389
  'reasonCodeShp': 15, 'infoDelivery': 20, 'bookingInfo': 20
390
  }
391
 
392
+ # Formato data per le colonne di data
393
  date_format = workbook.add_format({'num_format': 'dd/mm/yyyy'})
394
 
395
  # Scrivi i nomi delle colonne nella riga 2 (indice 1 in xlsxwriter)
 
403
  worksheet.set_column(col_num, col_num, 15)
404
 
405
  # Applica il formato data alle colonne 'orderDate' e 'dueDate'
406
+ if col_name in ['orderDate', 'dueDate']:
407
  worksheet.set_column(col_num, col_num, col_widths[col_name], date_format)
408
 
409
  # Scrivi i dati a partire dalla riga 3 (indice 2 in xlsxwriter)
 
413
  output.seek(0)
414
 
415
  timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
416
+ output_file_name = f"DeliveryOrders_{timestamp}.xlsx" # Nome del file di output
417
 
418
  st.download_button(
419
  label="Scarica file Excel Unificato",
 
421
  file_name=output_file_name,
422
  mime="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
423
  )
424
+ elif uploaded_json_files and has_blocking_error:
425
+ st.error("Si sono verificati errori bloccanti durante l'elaborazione dei JSON. Correggere gli errori per generare il file.")
426
+ elif uploaded_json_files and not st.session_state.customer_excel_loaded:
427
+ st.warning("Per elaborare i file JSON, devi prima caricare e validare un file Excel con l'anagrafica partner.")
428
  else:
429
+ st.info("Carica i tuoi file per iniziare.")
430
 
431
  # --- Funzione per il reset ---
432
  def clear_inputs():
 
 
433
  st.session_state.file_uploader_key_json = f'json_uploader_{datetime.now().timestamp()}'
434
+ st.session_state.file_uploader_key_customers = f'customers_uploader_{datetime.now().timestamp() + 1}' # Assicurati che le chiavi siano diverse
435
+ st.session_state.customer_excel_loaded = False
436
+ st.session_state.customers_df = pd.DataFrame() # Resetta il DataFrame dell'anagrafica
437
 
438
  st.markdown("---")
439
  st.button("Pulisci Input (RESET)", on_click=clear_inputs)