This commit is contained in:
Dvorinka
2025-08-05 14:09:37 +02:00
parent 96fced6eab
commit 5fbf1346d5
12 changed files with 400 additions and 304 deletions
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+170 -78
View File
@@ -125,7 +125,7 @@ class EDIDelforCumminsParser:
'1': 'Firm', '1': 'Firm',
'4': 'Forecast' '4': 'Forecast'
} }
return scc_map.get(scc_code, f'SCC-{scc_code}') return scc_map.get(scc_code, f'{scc_code}')
def parse_edi_file(self, content): def parse_edi_file(self, content):
lines = content.strip().split("'") lines = content.strip().split("'")
@@ -521,7 +521,7 @@ class EDIDelforCumminsParser:
return "" return ""
def export_to_excel(self): def export_to_excel(self):
"""Export delivery data to Excel with calendar weeks""" """Export delivery data to Excel with calendar weeks, color-coded by part"""
if not self.delivery_schedules: if not self.delivery_schedules:
messagebox.showwarning("Upozornění", "Žádná data k exportu") messagebox.showwarning("Upozornění", "Žádná data k exportu")
return return
@@ -531,25 +531,68 @@ class EDIDelforCumminsParser:
ws = wb.active ws = wb.active
ws.title = "Dodávky" ws.title = "Dodávky"
# Headers with week number # Get unique part numbers and assign colors
headers = ["Týden", "Datum", "Položka", "Popis", "Množství", "Typ", "SCC", "Release", "Dodací místo"] unique_parts = list(set([item.get('Položka', '') for item in self.delivery_schedules if item.get('Položka')]))
# Generate distinct colors for each part
colors = [
'FFE6B8', 'B8D1E6', 'E6B8B8', 'B8E6C3', 'E6D5B8',
'D1B8E6', 'B8E6E6', 'E6B8D1', 'B8C3E6', 'E6E6B8',
'B8E6D1', 'E6B8E6', 'B8E6B8', 'E6C3B8', 'B8D1E6',
'E6B8C3', 'B8E6D9', 'E6B8D9', 'B8E6B8', 'E6B8FF'
]
part_colors = {}
for i, part in enumerate(unique_parts):
part_colors[part] = colors[i % len(colors)]
# Headers in requested order: položka, datum, týden, množství, SCC, zbytek ad lib
headers = ["Položka", "Datum", "Týden", "Množství", "SCC", "Dodací místo"]
for col_num, header in enumerate(headers, 1): for col_num, header in enumerate(headers, 1):
cell = ws.cell(row=1, column=col_num, value=header) cell = ws.cell(row=1, column=col_num, value=header)
cell.font = Font(bold=True) cell.font = Font(bold=True)
cell.alignment = Alignment(horizontal='center') cell.alignment = Alignment(horizontal='center')
# Add data - sort by date from oldest to newest # Add legend headers
def parse_date(date_str): legend_headers = ["Legenda:", "Položka", "Popis"]
try: for col_num, header in enumerate(legend_headers, 10): # Start from column J
if '.' in date_str: cell = ws.cell(row=1, column=col_num, value=header)
return datetime.strptime(date_str, '%d.%m.%Y').date() cell.font = Font(bold=True)
else: cell.alignment = Alignment(horizontal='center')
return datetime.strptime(date_str, '%Y%m%d').date()
except:
return datetime.min.date()
# Prepare data for sorting by item and date
prepared_data = []
for item in self.delivery_schedules:
# Get item number (Položka)
part_number = item.get('Položka', '')
# Parse date for sorting
date_str = item.get('Datum', '')
date_for_sort = None
if date_str:
try:
if '.' in date_str:
date_parts = date_str.split('.')
if len(date_parts) == 3:
date_for_sort = datetime(int(date_parts[2]), int(date_parts[1]), int(date_parts[0]))
else:
# Handle YYYYMMDD format if needed
date_for_sort = datetime.strptime(date_str, '%Y%m%d')
except (ValueError, IndexError):
pass
prepared_data.append({
'part_number': part_number,
'date_for_sort': date_for_sort or datetime.max,
'item': item
})
# Sort by item and date
prepared_data.sort(key=lambda x: (str(x['part_number'] or ''), x['date_for_sort']))
# Add data to worksheet
row_num = 2 row_num = 2
for item in sorted(self.delivery_schedules, key=lambda x: parse_date(x.get('Datum', ''))): for data in prepared_data:
item = data['item']
# Get week number from date # Get week number from date
week_num = self.get_week_number(item.get('Datum', '')) week_num = self.get_week_number(item.get('Datum', ''))
@@ -560,7 +603,7 @@ class EDIDelforCumminsParser:
try: try:
quantity = float(quantity) if quantity else 0 quantity = float(quantity) if quantity else 0
except (ValueError, TypeError): except (ValueError, TypeError):
quantity = item.get('Množství', '') quantity = 0
# Format week number as number # Format week number as number
try: try:
@@ -568,99 +611,139 @@ class EDIDelforCumminsParser:
except (ValueError, TypeError): except (ValueError, TypeError):
week_num = 0 week_num = 0
# Format part number as number if possible # Get part number
part_number = item.get('Položka', '') part_number = item.get('Položka', '')
try:
if str(part_number).strip(): # Only convert if not empty
part_number = int(part_number)
else:
part_number = None
except (ValueError, AttributeError, TypeError):
part_number = str(part_number) # Keep as string if can't convert to int
# Format release as number if possible # Get SCC description
release = item.get('Release', '') scc = item.get('SCC', '')
try: scc_desc = self.get_scc_description(str(scc)) if scc else ''
if str(release).strip(): # Only convert if not empty
release = int(release)
else:
release = None
except (ValueError, AttributeError, TypeError):
release = str(release) # Keep as string if can't convert to int
# Add week number and date in the first two columns # Get delivery location
ws.cell(row=row_num, column=1, value=week_num) delivery_location = str(self.partner_info.get('Dodací adresa', '') or '')
if not delivery_location.strip():
delivery_location = 'Cummins Inc., 500 Jackson Street, Columbus, IN 47201, USA' # Default Cummins address
# Format date as Excel date # Get part description for legend
part_description = item.get('Popis', '')
# Get color for this part
part_color = part_colors.get(part_number, 'FFFFFF') # Default to white if part not found
# 1. Položka (as text with colored background and part number as text)
cell = ws.cell(row=row_num, column=1, value=str(part_number))
cell.number_format = '@'
cell.fill = openpyxl.styles.PatternFill(start_color=part_color, end_color=part_color, fill_type='solid')
cell.font = Font(color='000000') # Ensure text is black for visibility
# 2. Datum (formatted date) - not colored
date_str = item.get('Datum', '') date_str = item.get('Datum', '')
try: try:
if date_str: if date_str:
date_obj = datetime.strptime(date_str, '%d.%m.%Y') if '.' in date_str:
ws.cell(row=row_num, column=2, value=date_obj).number_format = 'DD.MM.YYYY' date_obj = datetime.strptime(date_str, '%d.%m.%Y')
else:
date_obj = datetime.strptime(date_str, '%Y%m%d')
cell = ws.cell(row=row_num, column=2, value=date_obj)
cell.number_format = 'DD.MM.YYYY'
else: else:
ws.cell(row=row_num, column=2, value='') cell = ws.cell(row=row_num, column=2, value='')
except (ValueError, TypeError): except:
ws.cell(row=row_num, column=2, value=date_str) cell = ws.cell(row=row_num, column=2, value=date_str)
# Part number - format as number if possible # 3. Týden (week number) - not colored
if part_number is not None and part_number != '': cell = ws.cell(row=row_num, column=3, value=week_num)
if isinstance(part_number, (int, float)): cell.number_format = '0'
ws.cell(row=row_num, column=3, value=part_number).number_format = '0'
# 4. Množství (quantity as number) - not colored
try:
if isinstance(quantity, (int, float)):
qty_value = float(quantity)
else: else:
ws.cell(row=row_num, column=3, value=str(part_number)).number_format = '@' # Text format if not a number qty_str = str(quantity).strip().replace("'", "")
else: qty_value = float(qty_str) if qty_str.replace('.', '', 1).isdigit() else 0.0
ws.cell(row=row_num, column=3, value='') cell = ws.cell(row=row_num, column=4, value=qty_value)
cell.number_format = '0'
except (ValueError, AttributeError):
cell = ws.cell(row=row_num, column=4, value=0.0)
cell.number_format = '0'
# Format text columns as text # 5. SCC (as text) - not colored
ws.cell(row=row_num, column=4, value=str(item.get('Popis', ''))).number_format = '@' # Popis as text cell = ws.cell(row=row_num, column=5, value=str(scc_desc))
ws.cell(row=row_num, column=5, value=quantity) # Quantity as number cell.number_format = '@'
ws.cell(row=row_num, column=6, value=str(item.get('Typ', ''))).number_format = '@' # Typ as text
ws.cell(row=row_num, column=7, value=str(item.get('SCC', ''))).number_format = '@' # SCC as text
# Release - format as number if possible # 6. Dodací místo (delivery address as text) - not colored
if release is not None and release != '': cell = ws.cell(row=row_num, column=6, value=delivery_location)
if isinstance(release, (int, float)): cell.number_format = '@'
ws.cell(row=row_num, column=8, value=release).number_format = '0'
else:
ws.cell(row=row_num, column=8, value=str(release)).number_format = '@' # Text format if not a number
else:
ws.cell(row=row_num, column=8, value='')
# Get delivery location - ensure we have a non-empty string
dodaci_misto = str(self.partner_info.get('Dodací adresa', '') or '').strip()
ws.cell(row=row_num, column=9, value=dodaci_misto).number_format = '@' # Dodací místo as text
row_num += 1 row_num += 1
# Apply number formatting to numeric columns # Apply number formatting to numeric columns
for row in ws.iter_rows(min_row=2, max_row=ws.max_row): for row in ws.iter_rows(min_row=2, max_row=ws.max_row):
# Skip empty rows or rows with insufficient columns
if len(row) < 6: # We need at least 6 columns (0-5)
continue
# Format week number (column 1) as number with no decimal places # Format week number (column 1) as number with no decimal places
if row[0].value is not None and row[0].value != '': # Column 1 (0-based index 0) if row[0].value is not None and row[0].value != '': # Column 1 (0-based index 0)
if isinstance(row[0].value, (int, float)): if isinstance(row[0].value, (int, float)):
row[0].number_format = '0' row[0].number_format = '0'
# Format quantity (column 5) as number with no decimal places
if row[4].value is not None and row[4].value != '': # Column 5 (0-based index 4)
if isinstance(row[4].value, (int, float)):
row[4].number_format = '0'
# Format part number (column 3) as number with no decimal places if it's a number # Format part number (column 3) as number with no decimal places if it's a number
if row[2].value is not None and row[2].value != '': # Column 3 (0-based index 2) if len(row) > 2 and row[2].value is not None and row[2].value != '': # Column 3 (0-based index 2)
if isinstance(row[2].value, (int, float)): if isinstance(row[2].value, (int, float)):
row[2].number_format = '0' row[2].number_format = '0'
# Format release (column 8) as number with no decimal places if it's a number # Format quantity (column 4) as number with no decimal places
if row[7].value is not None and row[7].value != '': # Column 8 (0-based index 7) if len(row) > 4 and row[4].value is not None and row[4].value != '': # Column 5 (0-based index 4)
if isinstance(row[7].value, (int, float)): if isinstance(row[4].value, (int, float)):
row[7].number_format = '0' row[4].number_format = '0'
# Auto-adjust column widths # Add legend headers (only in columns J and K)
ws.cell(row=1, column=10, value="Legenda:").font = Font(bold=True)
ws.cell(row=1, column=11, value="Popis").font = Font(bold=True)
# Clear any existing header in column L
if ws.cell(row=1, column=12).value == "Popis":
ws.cell(row=1, column=12, value="")
# Add legend items starting from row 2
legend_row = 2
for part_number, color in part_colors.items():
# Get part description
part_description = ''
for item in self.delivery_schedules:
if item.get('Položka') == part_number:
part_description = item.get('Popis', '')
break
# Set column J width to 0.75 inches (approximately 8.43 units in Excel)
ws.column_dimensions['J'].width = 10
# Create a cell with colored background and part number as text
legend_cell = ws.cell(row=legend_row, column=10, value=str(part_number))
legend_cell.fill = openpyxl.styles.PatternFill(
start_color=color, end_color=color, fill_type='solid')
legend_cell.font = Font(color='000000', bold=True) # Black text, bold
legend_cell.alignment = Alignment(horizontal='center')
# Part description in next column
ws.cell(row=legend_row, column=11, value=part_description)
legend_row += 1
# Auto-adjust column widths for all columns
for col in ws.columns: for col in ws.columns:
max_length = 0 max_length = 0
column = col[0].column_letter column_letter = get_column_letter(col[0].column)
# Skip the color swatch column (J) for width adjustment
if column_letter == 'J':
ws.column_dimensions[column_letter].width = 10 # Fixed width for color swatch (0.75")
continue
for cell in col: for cell in col:
try: try:
# For dates, use the formatted string length # For dates, use the formatted string length
if cell.is_date: if hasattr(cell, 'is_date') and cell.is_date:
cell_value = cell.value.strftime('%d.%m.%Y') if cell.value else '' cell_value = cell.value.strftime('%d.%m.%Y') if cell.value else ''
else: else:
cell_value = str(cell.value) if cell.value is not None else '' cell_value = str(cell.value) if cell.value is not None else ''
@@ -669,8 +752,17 @@ class EDIDelforCumminsParser:
max_length = len(cell_value) max_length = len(cell_value)
except: except:
pass pass
adjusted_width = (max_length + 2)
ws.column_dimensions[column].width = min(adjusted_width, 30) # Set a reasonable maximum width to prevent extremely wide columns
adjusted_width = min((max_length + 2), 30)
# Set minimum width for better readability
if column_letter in ['A', 'B', 'C', 'D', 'E', 'F', 'G']: # Main data columns
adjusted_width = max(adjusted_width, 12)
elif column_letter in ['K', 'L']: # Legend columns
adjusted_width = max(adjusted_width, 20)
ws.column_dimensions[column_letter].width = adjusted_width
# Add a summary sheet with just week and quantity # Add a summary sheet with just week and quantity
ws_summary = wb.create_sheet("Přehled") ws_summary = wb.create_sheet("Přehled")
+2
View File
@@ -30,8 +30,10 @@ class EDIUnifiedParser:
scrollbar.pack(side=tk.RIGHT, fill=tk.Y) scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
def load_file(self): def load_file(self):
initial_dir = r"M:\APLIKACE\Edirex\INArchiv"
filepath = filedialog.askopenfilename( filepath = filedialog.askopenfilename(
title="Vyberte EDI soubor", title="Vyberte EDI soubor",
initialdir=initial_dir if os.path.exists(initial_dir) else ".",
filetypes=[("EDI files", "*.edi"), ("All files", "*.*")] filetypes=[("EDI files", "*.edi"), ("All files", "*.*")]
) )
+120 -108
View File
@@ -161,18 +161,18 @@ class EDIDelforParser:
def parse_edi_file(self, content): def parse_edi_file(self, content):
"""Parsuje EDI DELFOR soubor""" """Parsuje EDI DELFOR soubor"""
lines = content.strip().split("'") lines = [line.strip() for line in content.strip().split("'") if line.strip()]
# Reset dat # Reset dat
self.header_info = {} self.header_info = {}
self.partner_info = {} self.partner_info = {}
self.delivery_schedules = [] self.delivery_schedules = []
current_delivery = {} i = 0
while i < len(lines):
for line in lines: line = lines[i]
line = line.strip()
if not line: if not line:
i += 1
continue continue
# UNB - Interchange header # UNB - Interchange header
@@ -180,29 +180,16 @@ class EDIDelforParser:
parts = line.split('+') parts = line.split('+')
if len(parts) >= 5: if len(parts) >= 5:
self.header_info['Odesílatel'] = parts[2] self.header_info['Odesílatel'] = parts[2]
# Uložíme kód příjemce, název doplníme později z NAD segmentu
self.header_info['Příjemce_kód'] = parts[3] self.header_info['Příjemce_kód'] = parts[3]
self.header_info['Datum/Čas'] = self.parse_edi_datetime(parts[4]) self.header_info['Datum/Čas'] = self.parse_edi_datetime(parts[4])
i += 1
# BGM - Beginning of message # BGM - Beginning of message
elif line.startswith('BGM'): elif line.startswith('BGM'):
parts = line.split('+') parts = line.split('+')
if len(parts) >= 3: if len(parts) >= 3:
self.header_info['Číslo zprávy'] = parts[2] self.header_info['Číslo zprávy'] = parts[2]
i += 1
# DTM - Date/time
elif line.startswith('DTM'):
parts = line.split('+')
if len(parts) >= 2:
dtm_parts = parts[1].split(':')
if len(dtm_parts) >= 3:
date_formatted = self.parse_date(dtm_parts[1], dtm_parts[2])
if dtm_parts[0] == '137':
self.header_info['Datum dokumentu'] = date_formatted
elif dtm_parts[0] == '63':
current_delivery['Datum do'] = date_formatted
elif dtm_parts[0] == '64':
current_delivery['Datum od'] = date_formatted
# NAD - Name and address # NAD - Name and address
elif line.startswith('NAD'): elif line.startswith('NAD'):
@@ -210,15 +197,13 @@ class EDIDelforParser:
if len(parts) >= 3: if len(parts) >= 3:
role = parts[1] role = parts[1]
code = parts[2] if len(parts) > 2 else '' code = parts[2] if len(parts) > 2 else ''
# Název společnosti je v parts[4] (index 4)
name = parts[4] if len(parts) > 4 else '' name = parts[4] if len(parts) > 4 else ''
# Adresa začíná od parts[5] # Process address parts
address_parts = [] address_parts = []
for i in range(5, len(parts)): for j in range(5, len(parts)):
if parts[i]: # Přidáme pouze neprázdné části if parts[j]:
address_parts.append(parts[i]) address_parts.append(parts[j])
full_address = ', '.join(address_parts) if address_parts else '' full_address = ', '.join(address_parts) if address_parts else ''
@@ -227,11 +212,9 @@ class EDIDelforParser:
if full_address: if full_address:
self.partner_info['Kupující'] += f", {full_address}" self.partner_info['Kupující'] += f", {full_address}"
elif role == 'SE': elif role == 'SE':
# Zkontrolujeme, zda SE obsahuje kód příjemce z UNB
if code == self.header_info.get('Příjemce_kód', ''): if code == self.header_info.get('Příjemce_kód', ''):
self.header_info['Příjemce'] = name self.header_info['Příjemce'] = name
# Pro prodávajícího použijeme název + adresu
if full_address: if full_address:
self.partner_info['Prodávající'] = f"{name}, {full_address}" self.partner_info['Prodávající'] = f"{name}, {full_address}"
else: else:
@@ -241,52 +224,77 @@ class EDIDelforParser:
self.partner_info['Dodací adresa'] = f"{name}, {full_address}" self.partner_info['Dodací adresa'] = f"{name}, {full_address}"
else: else:
self.partner_info['Dodací adresa'] = name self.partner_info['Dodací adresa'] = name
i += 1
# LIN - Line item # LIN - Line item
elif line.startswith('LIN'): elif line.startswith('LIN'):
parts = line.split('+') parts = line.split('+')
if len(parts) >= 4: if len(parts) >= 4:
self.header_info['Číslo položky'] = parts[3] self.header_info['Číslo položky'] = parts[3]
i += 1
# PIA - Product identification # PIA - Product identification
elif line.startswith('PIA'): elif line.startswith('PIA'):
parts = line.split('+') parts = line.split('+')
if len(parts) >= 3: if len(parts) >= 3:
self.header_info['Kód produktu'] = parts[2] self.header_info['Kód produktu'] = parts[2]
i += 1
# QTY - Quantity # Handle delivery block (4 lines in specific order: QTY+113, SCC, DTM+63, DTM+64)
elif line.startswith('QTY'): elif line.startswith('QTY+113'):
parts = line.split('+') try:
if len(parts) >= 2: # Create new delivery record
qty_parts = parts[1].split(':') current_delivery = {}
if len(qty_parts) >= 3:
qty_type = qty_parts[0]
quantity = qty_parts[1]
unit = qty_parts[2]
if qty_type == '113': # Plánované množství k dodání # 1. Process QTY+113 line (current line)
current_delivery['Množství'] = quantity qty_parts = line.split('+')
current_delivery['Jednotka'] = unit if len(qty_parts) >= 2:
qty_info = qty_parts[1].split(':')
if len(qty_info) >= 3:
current_delivery['Množství'] = qty_info[1]
current_delivery['Jednotka'] = qty_info[2]
current_delivery['Typ'] = 'Plánované množství' current_delivery['Typ'] = 'Plánované množství'
elif qty_type == '70': # Minimální množství
current_delivery['Množství'] = quantity
current_delivery['Jednotka'] = unit
current_delivery['Typ'] = 'Minimální'
elif qty_type == '78': # Maximální množství
current_delivery['Množství'] = quantity
current_delivery['Jednotka'] = unit
current_delivery['Typ'] = 'Maximální'
# SCC - Scheduling conditions # Move to next line
elif line.startswith('SCC'): i += 1
parts = line.split('+') if i >= len(lines):
if len(parts) >= 2: break
current_delivery['SCC'] = parts[1]
# Pokud máme kompletní dodávku, přidáme ji # 2. Process SCC line (next line)
if 'Datum od' in current_delivery and 'Množství' in current_delivery: if lines[i].startswith('SCC'):
scc_parts = lines[i].split('+')
if len(scc_parts) >= 2:
current_delivery['SCC'] = scc_parts[1]
i += 1
# 3. Process DTM+63 line (end date)
if i < len(lines) and lines[i].startswith('DTM+63'):
dtm_parts = lines[i].split('+')
if len(dtm_parts) >= 2:
dtm_info = dtm_parts[1].split(':')
if len(dtm_info) >= 3:
current_delivery['Datum do'] = self.parse_date(dtm_info[1], dtm_info[2])
i += 1
# 4. Process DTM+64 line (start date)
if i < len(lines) and lines[i].startswith('DTM+64'):
dtm_parts = lines[i].split('+')
if len(dtm_parts) >= 2:
dtm_info = dtm_parts[1].split(':')
if len(dtm_info) >= 3:
current_delivery['Datum od'] = self.parse_date(dtm_info[1], dtm_info[2])
i += 1
# Add completed delivery to schedules
if 'Množství' in current_delivery and 'Datum od' in current_delivery:
self.delivery_schedules.append(current_delivery.copy()) self.delivery_schedules.append(current_delivery.copy())
current_delivery = {'SCC': parts[1]} # Zachováme SCC pro další dodávky
except Exception as e:
print(f"Error processing delivery block: {e}")
i += 1 # Skip to next line on error
else:
# Skip any other lines we don't process
i += 1
def load_file(self, filepath): def load_file(self, filepath):
"""Načte EDI soubor""" """Načte EDI soubor"""
@@ -408,90 +416,94 @@ class EDIDelforParser:
ws = wb.active ws = wb.active
ws.title = "Dodávky" ws.title = "Dodávky"
# Hlavičky # Hlavičky podle požadavku: datum, týden, množství, SCC, zbytek ad lib
headers = ["Týden", "Datum od", "Množství", "Typ", "SCC", "Dodací místo"] headers = ["Datum", "Týden", "Množství", "SCC", "Dodací místo"]
for col_num, header in enumerate(headers, 1): for col_num, header in enumerate(headers, 1):
cell = ws.cell(row=1, column=col_num, value=header) cell = ws.cell(row=1, column=col_num, value=header)
cell.font = Font(bold=True) cell.font = Font(bold=True)
cell.alignment = Alignment(horizontal='center') cell.alignment = Alignment(horizontal='center')
# Připravíme data pro řazení
prepared_data = []
for delivery in self.delivery_schedules:
# Získáme položku (item) - pokud neexistuje, použijeme prázdný řetězec
item = delivery.get('Položka', '')
# Zpracujeme datum pro řazení
date_str = delivery.get('Datum od', '')
date_for_sort = None
if date_str:
try:
date_parts = date_str.split(' ')[0].split('.')
if len(date_parts) == 3:
date_for_sort = datetime(int(date_parts[2]), int(date_parts[1]), int(date_parts[0]))
except (ValueError, IndexError):
pass
prepared_data.append({
'item': item,
'date_for_sort': date_for_sort or datetime.max,
'delivery': delivery
})
# Seřadíme data podle položky a data
prepared_data.sort(key=lambda x: (x['item'] or '', x['date_for_sort']))
# Data # Data
row_num = 2 row_num = 2
for delivery in self.delivery_schedules: for item_data in prepared_data:
delivery = item_data['delivery']
date_from = delivery.get('Datum od', '') date_from = delivery.get('Datum od', '')
week_num = self.get_week_number(date_from) if date_from else "" week_num = self.get_week_number(date_from) if date_from else ""
scc_code = delivery.get('SCC', '') scc_code = delivery.get('SCC', '')
scc_desc = self.get_scc_description(scc_code) scc_desc = self.get_scc_description(scc_code)
# Format week number as number # Datum (sloupec 1)
try:
week_num = int(week_num) if week_num else 0
except (ValueError, TypeError):
week_num = 0
# Format quantity as number, removing any leading quotes
quantity = delivery.get('Množství', '')
if isinstance(quantity, str):
quantity = quantity.strip("'")
try:
quantity = float(quantity) if quantity else 0
except (ValueError, TypeError):
quantity = delivery.get('Množství', '')
# Format SCC as number if possible
scc = delivery.get('SCC', '')
try:
scc = int(scc) if scc.strip() else ''
except (ValueError, AttributeError):
pass
# Add week number
ws.cell(row=row_num, column=1, value=week_num)
# Format date as Excel date - handle both with and without time
date_from = delivery.get('Datum od', '')
try: try:
if date_from: if date_from:
# Remove time part if present # Remove time part if present
date_from = date_from.split(' ')[0] date_from = date_from.split(' ')[0]
date_from_obj = datetime.strptime(date_from, '%d.%m.%Y') date_from_obj = datetime.strptime(date_from, '%d.%m.%Y')
ws.cell(row=row_num, column=2, value=date_from_obj).number_format = 'DD.MM.YYYY' ws.cell(row=row_num, column=1, value=date_from_obj).number_format = 'DD.MM.YYYY'
except Exception as e: except Exception as e:
# Log error silently
ws.cell(row=row_num, column=2, value=date_from.split(' ')[0] if date_from else '') ws.cell(row=row_num, column=2, value=date_from.split(' ')[0] if date_from else '')
# Add other data with proper formatting # Týden (sloupec 2)
# Format quantity as number
try: try:
if isinstance(quantity, (int, float)): week_num = int(week_num) if week_num else 0
qty_value = float(quantity) ws.cell(row=row_num, column=2, value=week_num).number_format = '0'
except (ValueError, TypeError):
ws.cell(row=row_num, column=3, value=0)
# Množství (sloupec 3)
quantity = delivery.get('Množství', '')
try:
if isinstance(quantity, str):
quantity = quantity.strip("'")
qty_value = float(quantity) if quantity else 0.0
else: else:
qty_str = str(quantity).strip().replace("'", "") qty_value = float(quantity) if quantity is not None else 0.0
qty_value = float(qty_str) if qty_str.replace('.', '', 1).isdigit() else 0.0 ws.cell(row=row_num, column=3, value=qty_value).number_format = '0'
ws.cell(row=row_num, column=3, value=qty_value) except (ValueError, TypeError):
except (ValueError, AttributeError): ws.cell(row=row_num, column=4, value=0.0).number_format = '0'
ws.cell(row=row_num, column=3, value=0.0)
# Format text columns as text # SCC (sloupec 4)
ws.cell(row=row_num, column=4, value=str(delivery.get('Typ', ''))).number_format = '@' # Typ as text scc_desc = self.get_scc_description(str(delivery.get('SCC', '')))
ws.cell(row=row_num, column=4, value=str(scc_desc)).number_format = '@'
# Use SCC description instead of code and format as text # Dodací místo (sloupec 5)
scc_desc = self.get_scc_description(str(scc)) ws.cell(row=row_num, column=5, value=str(self.partner_info.get('Dodací adresa', '') or 'XTREME PRESSURE INJECTION JUAREZ, REC LOC 372, EL PASO, 79927')).number_format = '@'
ws.cell(row=row_num, column=5, value=str(scc_desc)).number_format = '@' # SCC as text
# Format delivery location as text
ws.cell(row=row_num, column=6, value=str(self.partner_info.get('Dodací adresa', '') or 'XTREME PRESSURE INJECTION JUAREZ, REC LOC 372, EL PASO, 79927')).number_format = '@'
row_num += 1 row_num += 1
# Apply number formatting to numeric columns # Apply number formatting to numeric columns
for row in ws.iter_rows(min_row=2, max_row=ws.max_row): for row in ws.iter_rows(min_row=2, max_row=ws.max_row):
# Format quantity column (column 3) as number with no decimal places # Format quantity column (column 2) as number with no decimal places
if row[2].value is not None: # Column 3 (0-based index 2) if row[2].value is not None: # Column 3 (0-based index 2)
row[2].number_format = '0' row[2].number_format = '0'
# Format week number column (column 1) as number with no decimal places # Format week number column (column 1) as number with no decimal places
if row[0].value is not None: # Column 1 (0-based index 0) if row[1].value is not None: # Column 2 (0-based index 1)
row[0].number_format = '0' row[1].number_format = '0'
# Automatické přizpůsobení šířky sloupců # Automatické přizpůsobení šířky sloupců
for col in ws.columns: for col in ws.columns:
+95 -105
View File
@@ -126,60 +126,50 @@ class EDITrwkobParser:
return False return False
def parse_edi_file(self, content): def parse_edi_file(self, content):
lines = content.strip().split("'") lines = [line.strip() for line in content.strip().split("'") if line.strip()]
self.header_info = {} self.header_info = {}
self.partner_info = {} self.partner_info = {}
self.delivery_schedules = [] self.delivery_schedules = []
current_delivery = {}
# Helper function to add a delivery if it's complete i = 0
def add_delivery_if_complete(delivery): while i < len(lines):
if all(key in delivery for key in ['Datum od', 'Množství', 'Typ', 'SCC']): line = lines[i]
# Add the delivery - we'll handle duplicates later
self.delivery_schedules.append(delivery.copy())
for line in lines:
line = line.strip()
if not line: if not line:
i += 1
continue continue
# UNB - Interchange header
if line.startswith('UNB'): if line.startswith('UNB'):
parts = line.split('+') parts = line.split('+')
if len(parts) >= 5: if len(parts) >= 5:
self.header_info['Odesílatel'] = parts[2] self.header_info['Odesílatel'] = parts[2]
self.header_info['Příjemce_kód'] = parts[3] self.header_info['Příjemce_kód'] = parts[3]
self.header_info['Datum/Čas'] = self.parse_edi_datetime(parts[4]) self.header_info['Datum/Čas'] = self.parse_edi_datetime(parts[4])
i += 1
# BGM - Beginning of message
elif line.startswith('BGM'): elif line.startswith('BGM'):
parts = line.split('+') parts = line.split('+')
if len(parts) >= 3: if len(parts) >= 3:
self.header_info['Číslo zprávy'] = parts[2] self.header_info['Číslo zprávy'] = parts[2]
i += 1
elif line.startswith('DTM'): # NAD - Name and address
parts = line.split('+')
if len(parts) >= 2:
dtm_parts = parts[1].split(':')
if len(dtm_parts) >= 2:
date_formatted = self.parse_date(dtm_parts[1], dtm_parts[2] if len(dtm_parts) > 2 else '')
if dtm_parts[0] == '137':
self.header_info['Datum dokumentu'] = date_formatted
elif dtm_parts[0] == '64': # Start date
current_delivery['Datum od'] = date_formatted
elif dtm_parts[0] == '63': # End date (we'll use this if no start date)
if 'Datum od' not in current_delivery:
current_delivery['Datum od'] = date_formatted
elif line.startswith('NAD'): elif line.startswith('NAD'):
parts = line.split('+') parts = line.split('+')
if len(parts) >= 3: if len(parts) >= 3:
role = parts[1] role = parts[1]
code = parts[2] code = parts[2]
name = parts[4] if len(parts) > 4 else '' name = parts[4] if len(parts) > 4 else ''
# Process address parts
address_parts = [] address_parts = []
for i in range(5, len(parts)): for j in range(5, len(parts)):
if parts[i]: if parts[j]:
address_parts.append(parts[i]) address_parts.append(parts[j])
full_address = ', '.join(address_parts) if address_parts else '' full_address = ', '.join(address_parts) if address_parts else ''
if role == 'BY': if role == 'BY':
self.partner_info['Kupující'] = name if name else code self.partner_info['Kupující'] = name if name else code
if full_address: if full_address:
@@ -195,79 +185,77 @@ class EDITrwkobParser:
self.partner_info['Dodací adresa'] = f"{name if name else code}, {full_address}" self.partner_info['Dodací adresa'] = f"{name if name else code}, {full_address}"
else: else:
self.partner_info['Dodací adresa'] = name if name else code self.partner_info['Dodací adresa'] = name if name else code
i += 1
# LIN - Line item
elif line.startswith('LIN'): elif line.startswith('LIN'):
parts = line.split('+') parts = line.split('+')
if len(parts) >= 4: if len(parts) >= 4:
self.header_info['Číslo položky'] = parts[3] self.header_info['Číslo položky'] = parts[3]
i += 1
# PIA - Product identification
elif line.startswith('PIA'): elif line.startswith('PIA'):
parts = line.split('+') parts = line.split('+')
if len(parts) >= 3: if len(parts) >= 3:
self.header_info['Kód produktu'] = parts[2] self.header_info['Kód produktu'] = parts[2]
i += 1
elif line.startswith('QTY'): # Handle delivery block (4 lines in specific order: QTY+113, SCC, DTM+63, DTM+64)
parts = line.split('+') elif line.startswith('QTY+113'):
if len(parts) >= 2: try:
qty_parts = parts[1].split(':') # Create new delivery record
if qty_parts: current_delivery = {}
qty_type = qty_parts[0]
quantity = qty_parts[1] if len(qty_parts) > 1 else ''
unit = qty_parts[2] if len(qty_parts) > 2 else 'PCE' # Default to PCE if not specified
# Only process QTY if we have a date and SCC # 1. Process QTY+113 line (current line)
if 'Datum od' in current_delivery and 'SCC' in current_delivery: qty_parts = line.split('+')
if qty_type in ['113', '12']: # Plánované množství k dodání if len(qty_parts) >= 2:
current_delivery['Množství'] = quantity qty_info = qty_parts[1].split(':')
current_delivery['Jednotka'] = unit if len(qty_info) >= 3:
current_delivery['Typ'] = 'Plánované množství' current_delivery['Množství'] = qty_info[1]
add_delivery_if_complete(current_delivery) current_delivery['Jednotka'] = qty_info[2] if len(qty_info) > 2 else 'PCE'
current_delivery['Typ'] = 'Plánované množství'
elif qty_type == '70': # Move to next line
current_delivery['Množství'] = quantity i += 1
current_delivery['Jednotka'] = unit if i >= len(lines):
current_delivery['Typ'] = 'Minimální' break
add_delivery_if_complete(current_delivery)
elif qty_type == '78': # 2. Process SCC line (next line)
current_delivery['Množství'] = quantity if i < len(lines) and lines[i].startswith('SCC'):
current_delivery['Jednotka'] = unit scc_parts = lines[i].split('+')
current_delivery['Typ'] = 'Maximální' if len(scc_parts) >= 2:
add_delivery_if_complete(current_delivery) current_delivery['SCC'] = scc_parts[1]
i += 1
elif line.startswith('SCC'): # 3. Process DTM+63 line (end date)
# Add the previous delivery if it's complete if i < len(lines) and lines[i].startswith('DTM+63'):
if all(key in current_delivery for key in ['Datum od', 'Množství', 'Typ', 'SCC']): dtm_parts = lines[i].split('+')
add_delivery_if_complete(current_delivery) if len(dtm_parts) >= 2:
dtm_info = dtm_parts[1].split(':')
if len(dtm_info) >= 3:
current_delivery['Datum do'] = self.parse_date(dtm_info[1], dtm_info[2])
i += 1
parts = line.split('+') # 4. Process DTM+64 line (start date)
if len(parts) >= 2: if i < len(lines) and lines[i].startswith('DTM+64'):
# Start a new delivery with the SCC code dtm_parts = lines[i].split('+')
current_delivery = {'SCC': parts[1]} if len(dtm_parts) >= 2:
dtm_info = dtm_parts[1].split(':')
if len(dtm_info) >= 3:
current_delivery['Datum od'] = self.parse_date(dtm_info[1], dtm_info[2])
i += 1
# Handle end of delivery schedule # Add completed delivery to schedules
elif line.startswith('UNS+S'): if 'Množství' in current_delivery and 'Datum od' in current_delivery:
if all(key in current_delivery for key in ['Datum od', 'Množství', 'Typ', 'SCC']): self.delivery_schedules.append(current_delivery.copy())
add_delivery_if_complete(current_delivery)
# After processing all lines, remove exact duplicates except Exception as e:
unique_deliveries = [] print(f"Error processing delivery block: {e}")
seen = set() i += 1 # Skip to next line on error
for delivery in self.delivery_schedules: else:
# Create a unique key based on all fields # Skip any other lines we don't process
delivery_key = ( i += 1
delivery.get('Datum od', ''),
delivery.get('Množství', ''),
delivery.get('Typ', ''),
delivery.get('SCC', ''),
delivery.get('Jednotka', '')
)
if delivery_key not in seen:
seen.add(delivery_key)
unique_deliveries.append(delivery)
# Replace the delivery schedules with the deduplicated list
self.delivery_schedules = unique_deliveries
def display_data(self): def display_data(self):
self.info_text.delete(1.0, tk.END) self.info_text.delete(1.0, tk.END)
@@ -362,13 +350,13 @@ class EDITrwkobParser:
return scc_mapping.get(scc_code, f'Neznámý kód: {scc_code}') return scc_mapping.get(scc_code, f'Neznámý kód: {scc_code}')
def export_to_excel(self): def export_to_excel(self):
"""Export delivery data to Excel with calendar weeks, sorted chronologically and without duplicates""" """Export delivery data to Excel with requested column order and sorting"""
if not self.delivery_schedules: if not self.delivery_schedules:
messagebox.showwarning("Upozornění", "Žádná data k exportu") messagebox.showwarning("Upozornění", "Žádná data k exportu")
return return
try: try:
# Process all deliveries without deduplication # Process all deliveries
processed_deliveries = [] processed_deliveries = []
for delivery in self.delivery_schedules: for delivery in self.delivery_schedules:
@@ -386,31 +374,34 @@ class EDITrwkobParser:
# Get delivery details # Get delivery details
quantity = delivery.get('Množství', '').strip("'") quantity = delivery.get('Množství', '').strip("'")
scc_code = delivery.get('SCC', '') scc_code = delivery.get('SCC', '')
item = delivery.get('Položka', '') # Get item number if available
# Store with date object and original delivery for sorting # Store with item, date, and original delivery for sorting
processed_deliveries.append((date_obj, { processed_deliveries.append({
'item': item,
'date_obj': date_obj,
'date_str': date_str, 'date_str': date_str,
'quantity': quantity, 'quantity': quantity,
'type': delivery_type, 'type': delivery_type,
'scc_code': scc_code, 'scc_code': scc_code,
'scc_desc': self.get_scc_description(scc_code), 'scc_desc': self.get_scc_description(scc_code),
'delivery': delivery 'delivery': delivery
})) })
except (ValueError, TypeError) as e: except (ValueError, TypeError) as e:
print(f"Chyba při zpracování data: {date_str}, {e}") print(f"Chyba při zpracování data: {date_str}, {e}")
continue continue
# Sort by date (earliest first) # Sort by item and then by date
processed_deliveries.sort(key=lambda x: x[0]) processed_deliveries.sort(key=lambda x: (str(x['item'] or ''), x['date_obj']))
# Create Excel workbook and worksheet # Create Excel workbook and worksheet
wb = openpyxl.Workbook() wb = openpyxl.Workbook()
ws = wb.active ws = wb.active
ws.title = "Dodávky" ws.title = "Dodávky"
# Headers # Headers in requested order: datum, týden, množství, SCC, dodací místo
headers = ["Týden", "Datum", "Množství", "Typ", "SCC", "Dodací místo"] headers = ["Datum", "Týden", "Množství", "SCC", "Dodací místo"]
for col_num, header in enumerate(headers, 1): for col_num, header in enumerate(headers, 1):
cell = ws.cell(row=1, column=col_num, value=header) cell = ws.cell(row=1, column=col_num, value=header)
cell.font = Font(bold=True) cell.font = Font(bold=True)
@@ -418,32 +409,31 @@ class EDITrwkobParser:
# Data # Data
row_num = 2 row_num = 2
for date_obj, delivery_data in processed_deliveries: for delivery_data in processed_deliveries:
# Add week number (ISO week) # 1. Datum (date) - formatted
ws.cell(row=row_num, column=1, value=date_obj.isocalendar()[1]) ws.cell(row=row_num, column=1, value=delivery_data['date_obj']).number_format = 'DD.MM.YYYY'
# Add date with proper Excel formatting # 2. Týden (week number) - as number
ws.cell(row=row_num, column=2, value=date_obj).number_format = 'DD.MM.YYYY' ws.cell(row=row_num, column=2, value=delivery_data['date_obj'].isocalendar()[1])
# Add quantity as number # 3. Množství (quantity) - as number
try: try:
quantity = float(delivery_data['quantity']) if delivery_data['quantity'] else 0 quantity = float(delivery_data['quantity']) if delivery_data['quantity'] else 0
ws.cell(row=row_num, column=3, value=quantity) ws.cell(row=row_num, column=3, value=quantity).number_format = '0'
except (ValueError, TypeError): except (ValueError, TypeError):
ws.cell(row=row_num, column=3, value=delivery_data['quantity']) ws.cell(row=row_num, column=3, value=delivery_data['quantity']).number_format = '0'
# Add type and SCC description as text # 4. SCC - as text
ws.cell(row=row_num, column=4, value=delivery_data['type']).number_format = '@' ws.cell(row=row_num, column=4, value=delivery_data['scc_desc']).number_format = '@'
ws.cell(row=row_num, column=5, value=delivery_data['scc_desc']).number_format = '@'
# Add delivery address # 5. Dodací místo (delivery address) - as text
delivery_address = self.partner_info.get('Dodací adresa', '') or 'XTREME PRESSURE INJECTION JUAREZ, REC LOC 372, EL PASO, 79927' delivery_address = self.partner_info.get('Dodací adresa', '') or 'XTREME PRESSURE INJECTION JUAREZ, REC LOC 372, EL PASO, 79927'
ws.cell(row=row_num, column=6, value=delivery_address).number_format = '@' ws.cell(row=row_num, column=5, value=delivery_address).number_format = '@'
row_num += 1 row_num += 1
# Apply number formatting to numeric columns # Apply number formatting to numeric columns
for col in [1, 3]: # Only format week number and quantity columns for col in [2]: # Only format week number column (quantity is already formatted)
for row in ws.iter_rows(min_row=2, min_col=col, max_col=col): for row in ws.iter_rows(min_row=2, min_col=col, max_col=col):
for cell in row: for cell in row:
if isinstance(cell.value, (int, float)): if isinstance(cell.value, (int, float)):