mirror of
https://github.com/Dvorinka/EDI_DELFOR_Parser.git
synced 2026-07-28 22:13:48 +00:00
sefs
This commit is contained in:
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.
+173
-81
@@ -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 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')
|
||||
|
||||
# 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()
|
||||
# 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 = '@'
|
||||
|
||||
# 6. Dodací místo (delivery address as text) - not colored
|
||||
cell = ws.cell(row=row_num, column=6, value=delivery_location)
|
||||
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='')
|
||||
|
||||
# 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")
|
||||
|
||||
@@ -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", "*.*")]
|
||||
)
|
||||
|
||||
|
||||
+122
-110
@@ -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
|
||||
|
||||
# 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 = {}
|
||||
|
||||
# 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]
|
||||
|
||||
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]
|
||||
|
||||
# Pokud máme kompletní dodávku, přidáme ji
|
||||
if 'Datum od' in current_delivery and 'Množství' in current_delivery:
|
||||
# Move to next line
|
||||
i += 1
|
||||
if i >= len(lines):
|
||||
break
|
||||
|
||||
# 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:
|
||||
|
||||
+103
-113
@@ -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]
|
||||
|
||||
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
|
||||
|
||||
i += 1
|
||||
|
||||
# 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]
|
||||
|
||||
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
|
||||
|
||||
# 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)
|
||||
|
||||
elif qty_type == '70':
|
||||
current_delivery['Množství'] = quantity
|
||||
current_delivery['Jednotka'] = unit
|
||||
current_delivery['Typ'] = 'Minimální'
|
||||
add_delivery_if_complete(current_delivery)
|
||||
|
||||
elif qty_type == '78':
|
||||
current_delivery['Množství'] = quantity
|
||||
current_delivery['Jednotka'] = unit
|
||||
current_delivery['Typ'] = 'Maximální'
|
||||
add_delivery_if_complete(current_delivery)
|
||||
|
||||
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)
|
||||
i += 1
|
||||
|
||||
parts = line.split('+')
|
||||
if len(parts) >= 2:
|
||||
# Start a new delivery with the SCC code
|
||||
current_delivery = {'SCC': parts[1]}
|
||||
# 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 = {}
|
||||
|
||||
# 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)
|
||||
|
||||
# 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
|
||||
# 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í'
|
||||
|
||||
# Move to next line
|
||||
i += 1
|
||||
if i >= len(lines):
|
||||
break
|
||||
|
||||
# 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
|
||||
|
||||
# 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())
|
||||
|
||||
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)):
|
||||
|
||||
Reference in New Issue
Block a user