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',
'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):
lines = content.strip().split("'")
@@ -521,7 +521,7 @@ class EDIDelforCumminsParser:
return ""
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:
messagebox.showwarning("Upozornění", "Žádná data k exportu")
return
@@ -531,25 +531,68 @@ class EDIDelforCumminsParser:
ws = wb.active
ws.title = "Dodávky"
# Headers with week number
headers = ["Týden", "Datum", "Položka", "Popis", "Množství", "Typ", "SCC", "Release", "Dodací místo"]
# Get unique part numbers and assign colors
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):
cell = ws.cell(row=1, column=col_num, value=header)
cell.font = Font(bold=True)
cell.alignment = Alignment(horizontal='center')
# Add data - sort by date from oldest to newest
def parse_date(date_str):
try:
if '.' in date_str:
return datetime.strptime(date_str, '%d.%m.%Y').date()
else:
return datetime.strptime(date_str, '%Y%m%d').date()
except:
return datetime.min.date()
# Add legend headers
legend_headers = ["Legenda:", "Položka", "Popis"]
for col_num, header in enumerate(legend_headers, 10): # Start from column J
cell = ws.cell(row=1, column=col_num, value=header)
cell.font = Font(bold=True)
cell.alignment = Alignment(horizontal='center')
# 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
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
week_num = self.get_week_number(item.get('Datum', ''))
@@ -560,7 +603,7 @@ class EDIDelforCumminsParser:
try:
quantity = float(quantity) if quantity else 0
except (ValueError, TypeError):
quantity = item.get('Množství', '')
quantity = 0
# Format week number as number
try:
@@ -568,99 +611,139 @@ class EDIDelforCumminsParser:
except (ValueError, TypeError):
week_num = 0
# Format part number as number if possible
# Get part number
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
release = item.get('Release', '')
try:
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
# Get SCC description
scc = item.get('SCC', '')
scc_desc = self.get_scc_description(str(scc)) if scc else ''
# Add week number and date in the first two columns
ws.cell(row=row_num, column=1, value=week_num)
# Get delivery location
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', '')
try:
if date_str:
date_obj = datetime.strptime(date_str, '%d.%m.%Y')
ws.cell(row=row_num, column=2, value=date_obj).number_format = 'DD.MM.YYYY'
if '.' in date_str:
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:
ws.cell(row=row_num, column=2, value='')
except (ValueError, TypeError):
ws.cell(row=row_num, column=2, value=date_str)
cell = ws.cell(row=row_num, column=2, value='')
except:
cell = ws.cell(row=row_num, column=2, value=date_str)
# Part number - format as number if possible
if part_number is not None and part_number != '':
if isinstance(part_number, (int, float)):
ws.cell(row=row_num, column=3, value=part_number).number_format = '0'
# 3. Týden (week number) - not colored
cell = ws.cell(row=row_num, column=3, value=week_num)
cell.number_format = '0'
# 4. Množství (quantity as number) - not colored
try:
if isinstance(quantity, (int, float)):
qty_value = float(quantity)
else:
ws.cell(row=row_num, column=3, value=str(part_number)).number_format = '@' # Text format if not a number
else:
ws.cell(row=row_num, column=3, value='')
qty_str = str(quantity).strip().replace("'", "")
qty_value = float(qty_str) if qty_str.replace('.', '', 1).isdigit() else 0.0
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
ws.cell(row=row_num, column=4, value=str(item.get('Popis', ''))).number_format = '@' # Popis as text
ws.cell(row=row_num, column=5, value=quantity) # Quantity as number
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
# 5. SCC (as text) - not colored
cell = ws.cell(row=row_num, column=5, value=str(scc_desc))
cell.number_format = '@'
# Release - format as number if possible
if release is not None and release != '':
if isinstance(release, (int, float)):
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='')
# 6. Dodací místo (delivery address as text) - not colored
cell = ws.cell(row=row_num, column=6, value=delivery_location)
cell.number_format = '@'
# 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
# Apply number formatting to numeric columns
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
if row[0].value is not None and row[0].value != '': # Column 1 (0-based index 0)
if isinstance(row[0].value, (int, float)):
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
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)):
row[2].number_format = '0'
# Format release (column 8) as number with no decimal places if it's a number
if row[7].value is not None and row[7].value != '': # Column 8 (0-based index 7)
if isinstance(row[7].value, (int, float)):
row[7].number_format = '0'
# Format quantity (column 4) as number with no decimal places
if len(row) > 4 and 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'
# 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:
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:
try:
# 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 ''
else:
cell_value = str(cell.value) if cell.value is not None else ''
@@ -669,8 +752,17 @@ class EDIDelforCumminsParser:
max_length = len(cell_value)
except:
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
ws_summary = wb.create_sheet("Přehled")
+2
View File
@@ -30,8 +30,10 @@ class EDIUnifiedParser:
scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
def load_file(self):
initial_dir = r"M:\APLIKACE\Edirex\INArchiv"
filepath = filedialog.askopenfilename(
title="Vyberte EDI soubor",
initialdir=initial_dir if os.path.exists(initial_dir) else ".",
filetypes=[("EDI files", "*.edi"), ("All files", "*.*")]
)
+120 -108
View File
@@ -161,18 +161,18 @@ class EDIDelforParser:
def parse_edi_file(self, content):
"""Parsuje EDI DELFOR soubor"""
lines = content.strip().split("'")
lines = [line.strip() for line in content.strip().split("'") if line.strip()]
# Reset dat
self.header_info = {}
self.partner_info = {}
self.delivery_schedules = []
current_delivery = {}
for line in lines:
line = line.strip()
i = 0
while i < len(lines):
line = lines[i]
if not line:
i += 1
continue
# UNB - Interchange header
@@ -180,29 +180,16 @@ class EDIDelforParser:
parts = line.split('+')
if len(parts) >= 5:
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['Datum/Čas'] = self.parse_edi_datetime(parts[4])
i += 1
# BGM - Beginning of message
elif line.startswith('BGM'):
parts = line.split('+')
if len(parts) >= 3:
self.header_info['Číslo zprávy'] = parts[2]
# 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
i += 1
# NAD - Name and address
elif line.startswith('NAD'):
@@ -210,15 +197,13 @@ class EDIDelforParser:
if len(parts) >= 3:
role = parts[1]
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 ''
# Adresa začíná od parts[5]
# Process address parts
address_parts = []
for i in range(5, len(parts)):
if parts[i]: # Přidáme pouze neprázdné části
address_parts.append(parts[i])
for j in range(5, len(parts)):
if parts[j]:
address_parts.append(parts[j])
full_address = ', '.join(address_parts) if address_parts else ''
@@ -227,11 +212,9 @@ class EDIDelforParser:
if full_address:
self.partner_info['Kupující'] += f", {full_address}"
elif role == 'SE':
# Zkontrolujeme, zda SE obsahuje kód příjemce z UNB
if code == self.header_info.get('Příjemce_kód', ''):
self.header_info['Příjemce'] = name
# Pro prodávajícího použijeme název + adresu
if full_address:
self.partner_info['Prodávající'] = f"{name}, {full_address}"
else:
@@ -241,52 +224,77 @@ class EDIDelforParser:
self.partner_info['Dodací adresa'] = f"{name}, {full_address}"
else:
self.partner_info['Dodací adresa'] = name
i += 1
# LIN - Line item
elif line.startswith('LIN'):
parts = line.split('+')
if len(parts) >= 4:
self.header_info['Číslo položky'] = parts[3]
i += 1
# PIA - Product identification
elif line.startswith('PIA'):
parts = line.split('+')
if len(parts) >= 3:
self.header_info['Kód produktu'] = parts[2]
i += 1
# QTY - Quantity
elif line.startswith('QTY'):
parts = line.split('+')
if len(parts) >= 2:
qty_parts = parts[1].split(':')
if len(qty_parts) >= 3:
qty_type = qty_parts[0]
quantity = qty_parts[1]
unit = qty_parts[2]
# Handle delivery block (4 lines in specific order: QTY+113, SCC, DTM+63, DTM+64)
elif line.startswith('QTY+113'):
try:
# Create new delivery record
current_delivery = {}
if qty_type == '113': # Plánované množství k dodání
current_delivery['Množství'] = quantity
current_delivery['Jednotka'] = unit
# 1. Process QTY+113 line (current line)
qty_parts = line.split('+')
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í'
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
elif line.startswith('SCC'):
parts = line.split('+')
if len(parts) >= 2:
current_delivery['SCC'] = parts[1]
# Move to next line
i += 1
if i >= len(lines):
break
# Pokud máme kompletní dodávku, přidáme ji
if 'Datum od' in current_delivery and 'Množství' in current_delivery:
# 2. Process SCC line (next line)
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())
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):
"""Načte EDI soubor"""
@@ -408,90 +416,94 @@ class EDIDelforParser:
ws = wb.active
ws.title = "Dodávky"
# Hlavičky
headers = ["Týden", "Datum od", "Množství", "Typ", "SCC", "Dodací místo"]
# Hlavičky podle požadavku: datum, týden, množství, SCC, zbytek ad lib
headers = ["Datum", "Týden", "Množství", "SCC", "Dodací místo"]
for col_num, header in enumerate(headers, 1):
cell = ws.cell(row=1, column=col_num, value=header)
cell.font = Font(bold=True)
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
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', '')
week_num = self.get_week_number(date_from) if date_from else ""
scc_code = delivery.get('SCC', '')
scc_desc = self.get_scc_description(scc_code)
# Format week number as number
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', '')
# Datum (sloupec 1)
try:
if date_from:
# Remove time part if present
date_from = date_from.split(' ')[0]
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:
# Log error silently
ws.cell(row=row_num, column=2, value=date_from.split(' ')[0] if date_from else '')
# Add other data with proper formatting
# Format quantity as number
# Týden (sloupec 2)
try:
if isinstance(quantity, (int, float)):
qty_value = float(quantity)
week_num = int(week_num) if week_num else 0
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:
qty_str = str(quantity).strip().replace("'", "")
qty_value = float(qty_str) if qty_str.replace('.', '', 1).isdigit() else 0.0
ws.cell(row=row_num, column=3, value=qty_value)
except (ValueError, AttributeError):
ws.cell(row=row_num, column=3, value=0.0)
qty_value = float(quantity) if quantity is not None else 0.0
ws.cell(row=row_num, column=3, value=qty_value).number_format = '0'
except (ValueError, TypeError):
ws.cell(row=row_num, column=4, value=0.0).number_format = '0'
# Format text columns as text
ws.cell(row=row_num, column=4, value=str(delivery.get('Typ', ''))).number_format = '@' # Typ as text
# SCC (sloupec 4)
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
scc_desc = self.get_scc_description(str(scc))
ws.cell(row=row_num, column=5, value=str(scc_desc)).number_format = '@' # SCC as text
# Dodací místo (sloupec 5)
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 = '@'
# 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
# Apply number formatting to numeric columns
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)
row[2].number_format = '0'
# 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)
row[0].number_format = '0'
if row[1].value is not None: # Column 2 (0-based index 1)
row[1].number_format = '0'
# Automatické přizpůsobení šířky sloupců
for col in ws.columns:
+95 -105
View File
@@ -126,60 +126,50 @@ class EDITrwkobParser:
return False
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.partner_info = {}
self.delivery_schedules = []
current_delivery = {}
# Helper function to add a delivery if it's complete
def add_delivery_if_complete(delivery):
if all(key in delivery for key in ['Datum od', 'Množství', 'Typ', 'SCC']):
# Add the delivery - we'll handle duplicates later
self.delivery_schedules.append(delivery.copy())
for line in lines:
line = line.strip()
i = 0
while i < len(lines):
line = lines[i]
if not line:
i += 1
continue
# UNB - Interchange header
if line.startswith('UNB'):
parts = line.split('+')
if len(parts) >= 5:
self.header_info['Odesílatel'] = parts[2]
self.header_info['Příjemce_kód'] = parts[3]
self.header_info['Datum/Čas'] = self.parse_edi_datetime(parts[4])
i += 1
# BGM - Beginning of message
elif line.startswith('BGM'):
parts = line.split('+')
if len(parts) >= 3:
self.header_info['Číslo zprávy'] = parts[2]
i += 1
elif line.startswith('DTM'):
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
# NAD - Name and address
elif line.startswith('NAD'):
parts = line.split('+')
if len(parts) >= 3:
role = parts[1]
code = parts[2]
name = parts[4] if len(parts) > 4 else ''
# Process address parts
address_parts = []
for i in range(5, len(parts)):
if parts[i]:
address_parts.append(parts[i])
for j in range(5, len(parts)):
if parts[j]:
address_parts.append(parts[j])
full_address = ', '.join(address_parts) if address_parts else ''
if role == 'BY':
self.partner_info['Kupující'] = name if name else code
if full_address:
@@ -195,79 +185,77 @@ class EDITrwkobParser:
self.partner_info['Dodací adresa'] = f"{name if name else code}, {full_address}"
else:
self.partner_info['Dodací adresa'] = name if name else code
i += 1
# LIN - Line item
elif line.startswith('LIN'):
parts = line.split('+')
if len(parts) >= 4:
self.header_info['Číslo položky'] = parts[3]
i += 1
# PIA - Product identification
elif line.startswith('PIA'):
parts = line.split('+')
if len(parts) >= 3:
self.header_info['Kód produktu'] = parts[2]
i += 1
elif line.startswith('QTY'):
parts = line.split('+')
if len(parts) >= 2:
qty_parts = parts[1].split(':')
if qty_parts:
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
# Handle delivery block (4 lines in specific order: QTY+113, SCC, DTM+63, DTM+64)
elif line.startswith('QTY+113'):
try:
# Create new delivery record
current_delivery = {}
# Only process QTY if we have a date and SCC
if 'Datum od' in current_delivery and 'SCC' in current_delivery:
if qty_type in ['113', '12']: # Plánované množství k dodání
current_delivery['Množství'] = quantity
current_delivery['Jednotka'] = unit
current_delivery['Typ'] = 'Plánované množství'
add_delivery_if_complete(current_delivery)
# 1. Process QTY+113 line (current line)
qty_parts = line.split('+')
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] if len(qty_info) > 2 else 'PCE'
current_delivery['Typ'] = 'Plánované množství'
elif qty_type == '70':
current_delivery['Množství'] = quantity
current_delivery['Jednotka'] = unit
current_delivery['Typ'] = 'Minimální'
add_delivery_if_complete(current_delivery)
# Move to next line
i += 1
if i >= len(lines):
break
elif qty_type == '78':
current_delivery['Množství'] = quantity
current_delivery['Jednotka'] = unit
current_delivery['Typ'] = 'Maximální'
add_delivery_if_complete(current_delivery)
# 2. Process SCC line (next line)
if i < len(lines) and lines[i].startswith('SCC'):
scc_parts = lines[i].split('+')
if len(scc_parts) >= 2:
current_delivery['SCC'] = scc_parts[1]
i += 1
elif line.startswith('SCC'):
# Add the previous delivery if it's complete
if all(key in current_delivery for key in ['Datum od', 'Množství', 'Typ', 'SCC']):
add_delivery_if_complete(current_delivery)
# 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
parts = line.split('+')
if len(parts) >= 2:
# Start a new delivery with the SCC code
current_delivery = {'SCC': parts[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
# Handle end of delivery schedule
elif line.startswith('UNS+S'):
if all(key in current_delivery for key in ['Datum od', 'Množství', 'Typ', 'SCC']):
add_delivery_if_complete(current_delivery)
# Add completed delivery to schedules
if 'Množství' in current_delivery and 'Datum od' in current_delivery:
self.delivery_schedules.append(current_delivery.copy())
# After processing all lines, remove exact duplicates
unique_deliveries = []
seen = set()
for delivery in self.delivery_schedules:
# Create a unique key based on all fields
delivery_key = (
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
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 display_data(self):
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}')
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:
messagebox.showwarning("Upozornění", "Žádná data k exportu")
return
try:
# Process all deliveries without deduplication
# Process all deliveries
processed_deliveries = []
for delivery in self.delivery_schedules:
@@ -386,31 +374,34 @@ class EDITrwkobParser:
# Get delivery details
quantity = delivery.get('Množství', '').strip("'")
scc_code = delivery.get('SCC', '')
item = delivery.get('Položka', '') # Get item number if available
# Store with date object and original delivery for sorting
processed_deliveries.append((date_obj, {
# Store with item, date, and original delivery for sorting
processed_deliveries.append({
'item': item,
'date_obj': date_obj,
'date_str': date_str,
'quantity': quantity,
'type': delivery_type,
'scc_code': scc_code,
'scc_desc': self.get_scc_description(scc_code),
'delivery': delivery
}))
})
except (ValueError, TypeError) as e:
print(f"Chyba při zpracování data: {date_str}, {e}")
continue
# Sort by date (earliest first)
processed_deliveries.sort(key=lambda x: x[0])
# Sort by item and then by date
processed_deliveries.sort(key=lambda x: (str(x['item'] or ''), x['date_obj']))
# Create Excel workbook and worksheet
wb = openpyxl.Workbook()
ws = wb.active
ws.title = "Dodávky"
# Headers
headers = ["Týden", "Datum", "Množství", "Typ", "SCC", "Dodací místo"]
# Headers in requested order: datum, týden, množství, SCC, dodací místo
headers = ["Datum", "Týden", "Množství", "SCC", "Dodací místo"]
for col_num, header in enumerate(headers, 1):
cell = ws.cell(row=1, column=col_num, value=header)
cell.font = Font(bold=True)
@@ -418,32 +409,31 @@ class EDITrwkobParser:
# Data
row_num = 2
for date_obj, delivery_data in processed_deliveries:
# Add week number (ISO week)
ws.cell(row=row_num, column=1, value=date_obj.isocalendar()[1])
for delivery_data in processed_deliveries:
# 1. Datum (date) - formatted
ws.cell(row=row_num, column=1, value=delivery_data['date_obj']).number_format = 'DD.MM.YYYY'
# Add date with proper Excel formatting
ws.cell(row=row_num, column=2, value=date_obj).number_format = 'DD.MM.YYYY'
# 2. Týden (week number) - as number
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:
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):
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
ws.cell(row=row_num, column=4, value=delivery_data['type']).number_format = '@'
ws.cell(row=row_num, column=5, value=delivery_data['scc_desc']).number_format = '@'
# 4. SCC - as text
ws.cell(row=row_num, column=4, 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'
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
# 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 cell in row:
if isinstance(cell.value, (int, float)):