mirror of
https://github.com/Dvorinka/EDI_DELFOR_Parser.git
synced 2026-06-03 20:12:59 +00:00
first commit
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.
@@ -0,0 +1,88 @@
|
||||
# build_nuitka.py
|
||||
import os
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
def run_command(command):
|
||||
"""Helper function to run shell commands"""
|
||||
print(f"Running: {' '.join(command)}")
|
||||
result = subprocess.run(command, capture_output=True, text=True)
|
||||
if result.returncode != 0:
|
||||
print("Error:", result.stderr)
|
||||
sys.exit(1)
|
||||
return result
|
||||
|
||||
def main():
|
||||
# Configuration
|
||||
script_name = "edi_parser_main.py"
|
||||
app_name = "EDI_Parser"
|
||||
icon_path = None # Set to path of your .ico file if you have one
|
||||
output_dir = "dist"
|
||||
temp_dir = "build"
|
||||
|
||||
# Clean up previous builds
|
||||
for dir_path in [output_dir, temp_dir]:
|
||||
if os.path.exists(dir_path):
|
||||
print(f"Cleaning up {dir_path}...")
|
||||
shutil.rmtree(dir_path)
|
||||
|
||||
# Create output directories
|
||||
os.makedirs(output_dir, exist_ok=True)
|
||||
|
||||
# Base Nuitka command
|
||||
cmd = [
|
||||
"python", "-m", "nuitka",
|
||||
"--standalone",
|
||||
"--onefile",
|
||||
"--windows-disable-console", # For GUI apps
|
||||
f"--output-dir={output_dir}",
|
||||
f"--windows-icon-from-ico={icon_path}" if icon_path else "",
|
||||
f"--include-package=tkinter",
|
||||
f"--include-package=openpyxl",
|
||||
"--enable-plugin=tk-inter",
|
||||
"--remove-output",
|
||||
"--assume-yes-for-downloads",
|
||||
"--follow-imports",
|
||||
"--follow-import-to=*",
|
||||
"--nofollow-import-to=*.test",
|
||||
"--nofollow-import-to=*.tests",
|
||||
"--nofollow-import-to=*.unittest",
|
||||
"--nofollow-import-to=*.test_*",
|
||||
"--nofollow-import-to=*conftest*",
|
||||
"--nofollow-import-to=*pytest*",
|
||||
"--nofollow-import-to=*setuptools*",
|
||||
"--nofollow-import-to=*pip*",
|
||||
"--nofollow-import-to=*distutils*",
|
||||
"--nofollow-import-to=*numpy*", # Exclude if not needed
|
||||
"--nofollow-import-to=*matplotlib*", # Exclude if not needed
|
||||
"--windows-company-name=YourCompany",
|
||||
f"--windows-file-version=1.0",
|
||||
f"--windows-product-version=1.0",
|
||||
f"--windows-file-description={app_name}",
|
||||
f"--windows-product-name={app_name}",
|
||||
script_name
|
||||
]
|
||||
|
||||
# Remove empty strings from command list
|
||||
cmd = [x for x in cmd if x]
|
||||
|
||||
# Run Nuitka
|
||||
print("Starting Nuitka compilation...")
|
||||
result = run_command(cmd)
|
||||
|
||||
# Move the final executable if needed
|
||||
if os.name == 'nt': # Windows
|
||||
exe_name = f"{app_name}.exe"
|
||||
src = os.path.join(output_dir, script_name.replace('.py', '.exe'))
|
||||
dst = os.path.join(".", exe_name)
|
||||
if os.path.exists(dst):
|
||||
os.remove(dst)
|
||||
shutil.move(src, dst)
|
||||
print(f"\nCompilation complete! Executable created: {exe_name}")
|
||||
else:
|
||||
print("\nCompilation complete! Check the dist/ directory for the output.")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,651 @@
|
||||
import tkinter as tk
|
||||
from tkinter import ttk, filedialog, messagebox
|
||||
import re
|
||||
from datetime import datetime, date
|
||||
import os
|
||||
import openpyxl
|
||||
from openpyxl.styles import Font, Alignment
|
||||
from openpyxl.utils import get_column_letter
|
||||
|
||||
class EDIDelforCumminsParser:
|
||||
def __init__(self, filepath=None):
|
||||
self.root = tk.Tk()
|
||||
self.root.title("EDI Cummins Parser")
|
||||
self.root.geometry("1200x800")
|
||||
self.header_info = {}
|
||||
self.partner_info = {}
|
||||
self.delivery_schedules = []
|
||||
self.line_items = []
|
||||
|
||||
# Handle window close event
|
||||
self.root.protocol("WM_DELETE_WINDOW", self.on_closing)
|
||||
|
||||
self.setup_ui()
|
||||
if filepath:
|
||||
self.load_file(filepath)
|
||||
|
||||
def setup_ui(self):
|
||||
main_frame = ttk.Frame(self.root)
|
||||
main_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
|
||||
btn_frame = ttk.Frame(main_frame)
|
||||
btn_frame.pack(fill=tk.X, pady=(0, 10))
|
||||
# Add buttons
|
||||
ttk.Button(btn_frame, text="Zpět na hlavní okno", command=self.back_to_main).pack(side=tk.LEFT, padx=(10, 0))
|
||||
ttk.Button(btn_frame, text="Export do Excelu", command=self.export_to_excel).pack(side=tk.LEFT, padx=5)
|
||||
self.notebook = ttk.Notebook(main_frame)
|
||||
self.notebook.pack(fill=tk.BOTH, expand=True)
|
||||
self.info_frame = ttk.Frame(self.notebook)
|
||||
self.notebook.add(self.info_frame, text="Základní informace")
|
||||
self.delivery_frame = ttk.Frame(self.notebook)
|
||||
self.notebook.add(self.delivery_frame, text="Plán dodávek")
|
||||
self.stats_frame = ttk.Frame(self.notebook)
|
||||
self.notebook.add(self.stats_frame, text="Statistiky")
|
||||
self.setup_info_tab()
|
||||
self.setup_delivery_tab()
|
||||
self.setup_stats_tab()
|
||||
|
||||
def setup_info_tab(self):
|
||||
text_frame = ttk.Frame(self.info_frame)
|
||||
text_frame.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
|
||||
self.info_text = tk.Text(text_frame, wrap=tk.WORD, font=('Courier', 10))
|
||||
scrollbar = ttk.Scrollbar(text_frame, orient=tk.VERTICAL, command=self.info_text.yview)
|
||||
self.info_text.configure(yscrollcommand=scrollbar.set)
|
||||
self.info_text.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
|
||||
scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
|
||||
|
||||
def setup_delivery_tab(self):
|
||||
tree_frame = ttk.Frame(self.delivery_frame)
|
||||
tree_frame.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
|
||||
# Removed 'Jednotka' column as requested
|
||||
columns = ('Položka', 'Popis', 'Datum', 'Množství', 'Typ', 'SCC', 'Release')
|
||||
self.delivery_tree = ttk.Treeview(tree_frame, columns=columns, show='headings', height=15)
|
||||
for col in columns:
|
||||
self.delivery_tree.heading(col, text=col)
|
||||
if col == 'Popis':
|
||||
self.delivery_tree.column(col, width=200)
|
||||
elif col == 'Položka':
|
||||
self.delivery_tree.column(col, width=100)
|
||||
else:
|
||||
self.delivery_tree.column(col, width=80)
|
||||
v_scrollbar = ttk.Scrollbar(tree_frame, orient=tk.VERTICAL, command=self.delivery_tree.yview)
|
||||
h_scrollbar = ttk.Scrollbar(tree_frame, orient=tk.HORIZONTAL, command=self.delivery_tree.xview)
|
||||
self.delivery_tree.configure(yscrollcommand=v_scrollbar.set, xscrollcommand=h_scrollbar.set)
|
||||
self.delivery_tree.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
|
||||
v_scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
|
||||
h_scrollbar.pack(side=tk.BOTTOM, fill=tk.X)
|
||||
|
||||
def setup_stats_tab(self):
|
||||
stats_frame = ttk.Frame(self.stats_frame)
|
||||
stats_frame.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
|
||||
self.stats_text = tk.Text(stats_frame, wrap=tk.WORD, font=('Courier', 10))
|
||||
stats_scrollbar = ttk.Scrollbar(stats_frame, orient=tk.VERTICAL, command=self.stats_text.yview)
|
||||
self.stats_text.configure(yscrollcommand=stats_scrollbar.set)
|
||||
self.stats_text.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
|
||||
stats_scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
|
||||
|
||||
def parse_date(self, date_str, format_code):
|
||||
try:
|
||||
if format_code == '102':
|
||||
return datetime.strptime(date_str, '%Y%m%d').strftime('%d.%m.%Y')
|
||||
else:
|
||||
return date_str
|
||||
except:
|
||||
return date_str
|
||||
|
||||
def parse_edi_datetime(self, datetime_str):
|
||||
try:
|
||||
if ':' in datetime_str:
|
||||
date_part, time_part = datetime_str.split(':')
|
||||
full_date = '20' + date_part
|
||||
formatted_date = datetime.strptime(full_date, '%Y%m%d').strftime('%d.%m.%Y')
|
||||
formatted_time = datetime.strptime(time_part, '%H%M').strftime('%H:%M')
|
||||
return f"{formatted_date} {formatted_time}"
|
||||
return datetime_str
|
||||
except:
|
||||
return datetime_str
|
||||
|
||||
def get_scc_description(self, scc_code):
|
||||
scc_map = {
|
||||
'10': 'Backlog',
|
||||
'1': 'Firm',
|
||||
'4': 'Forecast'
|
||||
}
|
||||
return scc_map.get(scc_code, f'SCC-{scc_code}')
|
||||
|
||||
def parse_edi_file(self, content):
|
||||
lines = content.strip().split("'")
|
||||
self.header_info = {}
|
||||
self.partner_info = {}
|
||||
self.delivery_schedules = []
|
||||
self.line_items = []
|
||||
|
||||
# Current parsing state
|
||||
current_part_number = ''
|
||||
current_description = ''
|
||||
current_location = ''
|
||||
current_po = ''
|
||||
current_scc = ''
|
||||
current_release = ''
|
||||
|
||||
# Track current line item details
|
||||
current_line_item = None
|
||||
|
||||
# Temporary storage for quantity waiting for date
|
||||
pending_quantities = []
|
||||
|
||||
def create_or_update_line_item():
|
||||
nonlocal current_line_item
|
||||
if not current_part_number:
|
||||
return None
|
||||
|
||||
line_item = next((item for item in self.line_items
|
||||
if item['Položka'] == current_part_number), None)
|
||||
|
||||
if not line_item:
|
||||
line_item = {
|
||||
'Položka': current_part_number,
|
||||
'Popis': current_description,
|
||||
'Objednávka': current_po,
|
||||
'Lokace': current_location,
|
||||
'RFF': {}
|
||||
}
|
||||
self.line_items.append(line_item)
|
||||
|
||||
# Update current line item reference
|
||||
current_line_item = line_item
|
||||
return line_item
|
||||
|
||||
for line in lines:
|
||||
line = line.strip()
|
||||
if not line:
|
||||
continue
|
||||
|
||||
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])
|
||||
|
||||
elif line.startswith('UNH'):
|
||||
parts = line.split('+')
|
||||
if len(parts) >= 2:
|
||||
self.header_info['ID zprávy'] = parts[1]
|
||||
|
||||
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) >= 3:
|
||||
code = dtm_parts[0]
|
||||
value = dtm_parts[1]
|
||||
fmt = dtm_parts[2]
|
||||
formatted_date = self.parse_date(value, fmt)
|
||||
if code == '137':
|
||||
self.header_info['Datum dokumentu'] = formatted_date
|
||||
elif code == '2':
|
||||
# This is a delivery date - match with pending quantities
|
||||
# Only create entries if we have quantities to process
|
||||
if pending_quantities:
|
||||
# For SCC 10 (Backlog), we only take the first quantity
|
||||
if current_scc == '10' and len(pending_quantities) > 0:
|
||||
qty_info = pending_quantities[0]
|
||||
# Create line item if it doesn't exist
|
||||
line_item = next((item for item in self.line_items if item['Položka'] == current_part_number), None)
|
||||
if not line_item:
|
||||
line_item = {
|
||||
'Položka': current_part_number,
|
||||
'Popis': current_description,
|
||||
'Objednávka': current_po,
|
||||
'Lokace': current_location
|
||||
}
|
||||
self.line_items.append(line_item)
|
||||
|
||||
delivery = {
|
||||
'Položka': current_part_number,
|
||||
'Popis': current_description,
|
||||
'Datum': formatted_date,
|
||||
'Množství': qty_info['quantity'],
|
||||
'Typ': qty_info['type'],
|
||||
'SCC': self.get_scc_description(current_scc),
|
||||
'Release': current_release,
|
||||
'Objednávka': current_po
|
||||
}
|
||||
self.delivery_schedules.append(delivery)
|
||||
else:
|
||||
# For other SCCs, process all quantities
|
||||
for qty_info in pending_quantities:
|
||||
delivery = {
|
||||
'Položka': current_part_number,
|
||||
'Popis': current_description,
|
||||
'Datum': formatted_date,
|
||||
'Množství': qty_info['quantity'],
|
||||
'Typ': qty_info['type'],
|
||||
'SCC': self.get_scc_description(current_scc),
|
||||
'Release': current_release
|
||||
}
|
||||
self.delivery_schedules.append(delivery)
|
||||
pending_quantities.clear()
|
||||
# Don't reset release here to maintain it for next entries
|
||||
|
||||
elif line.startswith('NAD'):
|
||||
parts = line.split('+')
|
||||
if len(parts) >= 3:
|
||||
role = parts[1]
|
||||
if role == 'SU': # Supplier
|
||||
name_parts = [p.replace('?+', '').replace('?', '').strip() for p in parts[4:] if p]
|
||||
self.partner_info['Dodavatel'] = ' '.join(name_parts)
|
||||
elif role == 'ST': # Ship To
|
||||
name_parts = [p.replace('?+', '').replace('?', '').strip() for p in parts[4:] if p]
|
||||
self.partner_info['Příjemce'] = ' '.join(name_parts)
|
||||
# Store the full address for delivery location
|
||||
if len(parts) > 5: # If there are address components
|
||||
address_parts = []
|
||||
# Get address lines (parts[5] and beyond)
|
||||
for part in parts[5:]:
|
||||
if ':' in part: # Skip parts with qualifiers
|
||||
break
|
||||
address_parts.append(part.replace('?+', '').replace('?', '').strip())
|
||||
if address_parts:
|
||||
self.partner_info['Dodací adresa'] = ', '.join(address_parts)
|
||||
# If no specific address found, use the recipient name as fallback
|
||||
if not self.partner_info['Dodací adresa'] and name_parts:
|
||||
self.partner_info['Dodací adresa'] = ' '.join(name_parts)
|
||||
|
||||
elif line.startswith('LIN'):
|
||||
# Save previous line item if it exists
|
||||
if current_part_number:
|
||||
# Process previous line item if exists
|
||||
create_or_update_line_item()
|
||||
|
||||
parts = line.split('+')
|
||||
# Process LIN segment
|
||||
|
||||
if len(parts) >= 4:
|
||||
# Reset part information for new line item
|
||||
current_part_number = ''
|
||||
current_description = ''
|
||||
current_scc = ''
|
||||
current_release = ''
|
||||
current_line_item = None
|
||||
pending_quantities = []
|
||||
|
||||
# Try to find part number in the LIN segment
|
||||
for i, part in enumerate(parts[3:], 3): # Skip the first 3 parts (LIN, line number, action code)
|
||||
# Process part
|
||||
if ':' in part: # If the part contains a colon, it might be a part number
|
||||
part_info = part.split(':')
|
||||
# Process part info
|
||||
if len(part_info) >= 2 and part_info[1] == 'IN': # Look for part number with 'IN' qualifier
|
||||
current_part_number = part_info[0]
|
||||
# Found part number with IN qualifier
|
||||
break
|
||||
elif not current_part_number: # If no 'IN' qualifier found, take the first part
|
||||
current_part_number = part_info[0]
|
||||
# Using first part as part number
|
||||
|
||||
# If still no part number found, try to get it from the last part
|
||||
if not current_part_number and parts[3:]:
|
||||
current_part_number = parts[3].split(':')[0]
|
||||
# Using fallback part number
|
||||
|
||||
# Final part number processed
|
||||
|
||||
elif line.startswith('IMD'):
|
||||
parts = line.split('+')
|
||||
if len(parts) >= 4:
|
||||
# Extract item description - fixed to properly handle the format
|
||||
# Looking for the 4th element which contains the description
|
||||
desc_part = parts[3] if len(parts) > 3 else ''
|
||||
|
||||
# Remove leading colons and extract the actual description
|
||||
if desc_part.startswith(':::'):
|
||||
current_description = desc_part[3:].strip()
|
||||
elif desc_part.startswith('::'):
|
||||
current_description = desc_part[2:].strip()
|
||||
elif desc_part.startswith(':'):
|
||||
current_description = desc_part[1:].strip()
|
||||
else:
|
||||
current_description = desc_part.strip()
|
||||
|
||||
# Clean up any remaining formatting
|
||||
current_description = current_description.replace(':', '').strip()
|
||||
|
||||
elif line.startswith('LOC'):
|
||||
parts = line.split('+')
|
||||
if len(parts) >= 3:
|
||||
current_location = parts[2]
|
||||
|
||||
elif line.startswith('RFF'):
|
||||
parts = line.split('+')
|
||||
# Process RFF segment
|
||||
|
||||
if len(parts) >= 2:
|
||||
ref_parts = parts[1].split(':')
|
||||
if len(ref_parts) >= 2:
|
||||
ref_type = ref_parts[0]
|
||||
ref_value = ref_parts[1]
|
||||
|
||||
# Found RFF reference
|
||||
|
||||
# Create or update line item if it doesn't exist
|
||||
if not current_line_item:
|
||||
# Create new line item if none exists
|
||||
create_or_update_line_item()
|
||||
|
||||
# Store the reference in the current line item
|
||||
if current_line_item:
|
||||
if 'RFF' not in current_line_item:
|
||||
current_line_item['RFF'] = {}
|
||||
current_line_item['RFF'][ref_type] = ref_value
|
||||
# RFF stored in line item
|
||||
|
||||
# Special handling for order numbers
|
||||
if ref_type == 'ON':
|
||||
current_po = ref_value
|
||||
current_line_item['Objednávka'] = current_po
|
||||
# Order number set
|
||||
elif ref_type == 'RE':
|
||||
current_release = ref_value
|
||||
# Clear any pending quantities to ensure release number is applied to new quantities
|
||||
pending_quantities = []
|
||||
# Release number set
|
||||
else:
|
||||
# No line item available for RFF
|
||||
pass
|
||||
|
||||
elif line.startswith('SCC'):
|
||||
parts = line.split('+')
|
||||
if len(parts) >= 2:
|
||||
current_scc = parts[1]
|
||||
# Clear pending quantities when new SCC starts to prevent duplicates
|
||||
pending_quantities = []
|
||||
# Only reset release for backlog (SCC 10)
|
||||
if current_scc == '10':
|
||||
current_release = ''
|
||||
|
||||
elif line.startswith('QTY'):
|
||||
parts = line.split('+')
|
||||
if len(parts) >= 2:
|
||||
qty_parts = parts[1].split(':')
|
||||
if len(qty_parts) >= 2:
|
||||
qty_type = qty_parts[0]
|
||||
quantity = qty_parts[1]
|
||||
# Removed unit extraction as we don't need it
|
||||
|
||||
# Determine quantity type
|
||||
qty_type_desc = 'Neznámý'
|
||||
if qty_type == '1':
|
||||
qty_type_desc = 'Dodávka'
|
||||
elif qty_type == '3':
|
||||
qty_type_desc = 'Kumulativní'
|
||||
elif qty_type == '48':
|
||||
qty_type_desc = 'Plánované'
|
||||
|
||||
# Store quantity info waiting for corresponding date
|
||||
pending_quantities.append({
|
||||
'quantity': quantity,
|
||||
'type': qty_type_desc
|
||||
})
|
||||
|
||||
# Store line items for reference
|
||||
unique_parts = {}
|
||||
for delivery in self.delivery_schedules:
|
||||
part_num = delivery['Položka']
|
||||
if part_num not in unique_parts:
|
||||
unique_parts[part_num] = {
|
||||
'Položka': part_num,
|
||||
'Popis': delivery['Popis']
|
||||
}
|
||||
|
||||
self.line_items = list(unique_parts.values())
|
||||
|
||||
def load_file(self, filepath=None):
|
||||
if filepath:
|
||||
try:
|
||||
with open(filepath, 'r', encoding='utf-8') as f:
|
||||
content = f.read()
|
||||
self.parse_edi_file(content)
|
||||
self.display_data()
|
||||
return True
|
||||
except Exception as e:
|
||||
messagebox.showerror("Chyba", f"Nelze načíst soubor: {str(e)}")
|
||||
return False
|
||||
return False
|
||||
|
||||
def display_data(self):
|
||||
# Display header info
|
||||
self.info_text.delete(1.0, tk.END)
|
||||
info_content = "=== HLAVIČKA DOKUMENTU ===\n"
|
||||
for key, value in self.header_info.items():
|
||||
if key != 'Příjemce_kód':
|
||||
info_content += f"{key}: {value}\n"
|
||||
info_content += "\n=== INFORMACE O PARTNERECH ===\n"
|
||||
for key, value in self.partner_info.items():
|
||||
info_content += f"{key}: {value}\n"
|
||||
self.info_text.insert(1.0, info_content)
|
||||
|
||||
# Display delivery schedules
|
||||
for item in self.delivery_tree.get_children():
|
||||
self.delivery_tree.delete(item)
|
||||
|
||||
# Sort deliveries by date
|
||||
def date_sort_key(delivery):
|
||||
date_str = delivery.get('Datum', '')
|
||||
try:
|
||||
return datetime.strptime(date_str, '%d.%m.%Y') if date_str else datetime.max
|
||||
except:
|
||||
return datetime.max
|
||||
|
||||
sorted_deliveries = sorted(self.delivery_schedules, key=date_sort_key)
|
||||
|
||||
for delivery in sorted_deliveries:
|
||||
self.delivery_tree.insert('', tk.END, values=(
|
||||
delivery.get('Položka', ''),
|
||||
delivery.get('Popis', ''),
|
||||
delivery.get('Datum', ''),
|
||||
delivery.get('Množství', ''),
|
||||
delivery.get('Typ', ''),
|
||||
delivery.get('SCC', ''),
|
||||
delivery.get('Release', '')
|
||||
))
|
||||
|
||||
# Display statistics
|
||||
self.stats_text.delete(1.0, tk.END)
|
||||
stats_content = "=== STATISTIKY ===\n"
|
||||
stats_content += f"Celkový počet dodávek: {len(self.delivery_schedules)}\n"
|
||||
stats_content += f"Počet různých položek: {len(self.line_items)}\n"
|
||||
|
||||
# Group by SCC
|
||||
scc_stats = {}
|
||||
total_qty = 0
|
||||
for delivery in self.delivery_schedules:
|
||||
scc = delivery.get('SCC', 'Neznámý')
|
||||
qty_str = delivery.get('Množství', '0')
|
||||
try:
|
||||
qty = int(qty_str)
|
||||
total_qty += qty
|
||||
if scc not in scc_stats:
|
||||
scc_stats[scc] = {'count': 0, 'total_qty': 0}
|
||||
scc_stats[scc]['count'] += 1
|
||||
scc_stats[scc]['total_qty'] += qty
|
||||
except:
|
||||
pass
|
||||
|
||||
stats_content += f"Celkové množství: {total_qty:,} kusů\n\n"
|
||||
stats_content += "=== STATISTIKY PO SCC ===\n"
|
||||
for scc, stats in scc_stats.items():
|
||||
stats_content += f"{scc}: {stats['count']} dodávek, {stats['total_qty']:,} kusů\n"
|
||||
|
||||
self.stats_text.insert(1.0, stats_content)
|
||||
|
||||
def on_closing(self):
|
||||
"""Handle window close event"""
|
||||
self.root.destroy() # Close the current window
|
||||
|
||||
def back_to_main(self):
|
||||
"""Closes the current window"""
|
||||
self.root.destroy()
|
||||
|
||||
def get_week_number(self, date_str):
|
||||
"""Convert date string to ISO week number"""
|
||||
try:
|
||||
# Handle different date formats
|
||||
if '.' in date_str:
|
||||
date_obj = datetime.strptime(date_str, '%d.%m.%Y').date()
|
||||
else:
|
||||
date_obj = datetime.strptime(date_str, '%Y%m%d').date()
|
||||
return date_obj.isocalendar()[1] # Returns ISO week number
|
||||
except Exception as e:
|
||||
print(f"Error parsing date {date_str}: {e}")
|
||||
return ""
|
||||
|
||||
def export_to_excel(self):
|
||||
"""Export delivery data to Excel with calendar weeks"""
|
||||
if not self.delivery_schedules:
|
||||
messagebox.showwarning("Upozornění", "Žádná data k exportu")
|
||||
return
|
||||
|
||||
try:
|
||||
wb = openpyxl.Workbook()
|
||||
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"]
|
||||
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()
|
||||
|
||||
row_num = 2
|
||||
for item in sorted(self.delivery_schedules, key=lambda x: parse_date(x.get('Datum', ''))):
|
||||
# Get week number from date
|
||||
week_num = self.get_week_number(item.get('Datum', ''))
|
||||
|
||||
# Format quantity as number, removing any leading quotes
|
||||
quantity = item.get('Množství', '')
|
||||
if isinstance(quantity, str):
|
||||
quantity = quantity.strip("'")
|
||||
try:
|
||||
quantity = float(quantity) if quantity else 0
|
||||
except (ValueError, TypeError):
|
||||
quantity = item.get('Množství', '')
|
||||
|
||||
# Format week number as number
|
||||
try:
|
||||
week_num = int(week_num) if week_num else 0
|
||||
except (ValueError, TypeError):
|
||||
week_num = 0
|
||||
|
||||
# Format part number as number if possible
|
||||
part_number = item.get('Položka', '')
|
||||
try:
|
||||
part_number = int(part_number) if part_number.strip() else ''
|
||||
except (ValueError, AttributeError):
|
||||
pass
|
||||
|
||||
# Format release as number if possible
|
||||
release = item.get('Release', '')
|
||||
try:
|
||||
release = int(release) if release.strip() else ''
|
||||
except (ValueError, AttributeError):
|
||||
pass
|
||||
|
||||
# Add week number and date in the first two columns
|
||||
ws.cell(row=row_num, column=1, value=week_num)
|
||||
|
||||
# Format date as Excel date
|
||||
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'
|
||||
else:
|
||||
ws.cell(row=row_num, column=2, value='')
|
||||
except (ValueError, TypeError):
|
||||
ws.cell(row=row_num, column=2, value=date_str)
|
||||
|
||||
# Add other data with proper number formatting
|
||||
ws.cell(row=row_num, column=3, value=part_number)
|
||||
ws.cell(row=row_num, column=4, value=item.get('Popis', ''))
|
||||
ws.cell(row=row_num, column=5, value=quantity) # Use formatted quantity
|
||||
ws.cell(row=row_num, column=6, value=item.get('Typ', ''))
|
||||
ws.cell(row=row_num, column=7, value=item.get('SCC', ''))
|
||||
ws.cell(row=row_num, column=8, value=release)
|
||||
ws.cell(row=row_num, column=9, value=self.partner_info.get('Dodací adresa', '')) # Add delivery location from partner info
|
||||
row_num += 1
|
||||
|
||||
# Apply number formatting to all numeric columns
|
||||
for col in range(1, 10):
|
||||
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)):
|
||||
if col == 1: # Week number
|
||||
cell.number_format = '0'
|
||||
elif col == 3: # Part number
|
||||
cell.number_format = '0'
|
||||
elif col == 5: # Quantity
|
||||
cell.number_format = '0'
|
||||
elif col == 8: # Release
|
||||
cell.number_format = '0'
|
||||
|
||||
# Auto-adjust column widths
|
||||
for col in ws.columns:
|
||||
max_length = 0
|
||||
column = col[0].column_letter
|
||||
for cell in col:
|
||||
try:
|
||||
# For dates, use the formatted string length
|
||||
if 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 ''
|
||||
|
||||
if len(cell_value) > max_length:
|
||||
max_length = len(cell_value)
|
||||
except:
|
||||
pass
|
||||
adjusted_width = (max_length + 2)
|
||||
ws.column_dimensions[column].width = min(adjusted_width, 30)
|
||||
|
||||
# Add a summary sheet with just week and quantity
|
||||
ws_summary = wb.create_sheet("Přehled")
|
||||
|
||||
# Save the file
|
||||
filename = f"dodavky_cummins_{datetime.now().strftime('%Y%m%d_%H%M%S')}.xlsx"
|
||||
filepath = filedialog.asksaveasfilename(
|
||||
defaultextension=".xlsx",
|
||||
filetypes=[("Excel files", "*.xlsx"), ("All files", "*.*")],
|
||||
initialfile=filename
|
||||
)
|
||||
|
||||
if filepath:
|
||||
wb.save(filepath)
|
||||
messagebox.showinfo("Hotovo", f"Data byla úspěšně exportována do souboru:\n{filepath}")
|
||||
|
||||
except Exception as e:
|
||||
messagebox.showerror("Chyba", f"Při exportu došlo k chybě: {str(e)}")
|
||||
|
||||
def run(self):
|
||||
self.root.mainloop()
|
||||
|
||||
if __name__ == "__main__":
|
||||
# When run directly, use the main parser to handle file selection
|
||||
from edi_parser_main import EDIUnifiedParser
|
||||
EDIUnifiedParser()
|
||||
@@ -0,0 +1,185 @@
|
||||
import tkinter as tk
|
||||
from tkinter import ttk, filedialog, messagebox
|
||||
import os
|
||||
from edi_parser_cummins import EDIDelforCumminsParser
|
||||
from edi_parser_trwkob import EDITrwkobParser
|
||||
from edi_parser_minebea import EDIDelforParser as EDIDelforMinebeaParser
|
||||
|
||||
class EDIUnifiedParser:
|
||||
def __init__(self):
|
||||
self.root = tk.Tk()
|
||||
self.root.title("EDI Unified Parser")
|
||||
self.root.geometry("600x400")
|
||||
self.setup_ui()
|
||||
# Store reference to main window instance
|
||||
self.main_window = self
|
||||
|
||||
def setup_ui(self):
|
||||
main_frame = ttk.Frame(self.root)
|
||||
main_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
|
||||
|
||||
btn_frame = ttk.Frame(main_frame)
|
||||
btn_frame.pack(fill=tk.X, pady=(0, 10))
|
||||
|
||||
ttk.Button(btn_frame, text="Načíst EDI soubor", command=self.load_file).pack(side=tk.LEFT)
|
||||
|
||||
self.info_text = tk.Text(main_frame, wrap=tk.WORD, font=('Courier', 10))
|
||||
scrollbar = ttk.Scrollbar(main_frame, orient=tk.VERTICAL, command=self.info_text.yview)
|
||||
self.info_text.configure(yscrollcommand=scrollbar.set)
|
||||
self.info_text.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
|
||||
scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
|
||||
|
||||
def load_file(self):
|
||||
filepath = filedialog.askopenfilename(
|
||||
title="Vyberte EDI soubor",
|
||||
filetypes=[("EDI files", "*.edi"), ("All files", "*.*")]
|
||||
)
|
||||
|
||||
if not filepath:
|
||||
return
|
||||
|
||||
try:
|
||||
with open(filepath, 'r', encoding='utf-8', errors='replace') as f:
|
||||
content = f.read()
|
||||
|
||||
# Detect file type based on both filename and content
|
||||
file_type = self.detect_file_type(filepath, content)
|
||||
|
||||
def run_parser(parser_func):
|
||||
try:
|
||||
return parser_func(filepath)
|
||||
except Exception as e:
|
||||
messagebox.showerror("Chyba", f"Chyba při spouštění parseru: {str(e)}")
|
||||
return False
|
||||
|
||||
success = False
|
||||
if file_type == "cummins":
|
||||
success = run_parser(self.run_cummins_parser)
|
||||
elif file_type == "trwkob":
|
||||
success = run_parser(self.run_trwkob_parser)
|
||||
elif file_type == "minebea":
|
||||
success = run_parser(self.run_minebea_parser)
|
||||
else:
|
||||
messagebox.showerror("Chyba", "Nepodporovaný typ souboru")
|
||||
|
||||
return success
|
||||
|
||||
except Exception as e:
|
||||
messagebox.showerror("Chyba", f"Chyba při načítání souboru: {str(e)}")
|
||||
return False
|
||||
|
||||
def detect_file_type(self, filepath, content):
|
||||
# Look for patterns in both filename and content
|
||||
filename = os.path.basename(filepath).upper()
|
||||
content_upper = content.upper()
|
||||
|
||||
# Check for Cummins patterns (both in filename and content)
|
||||
cummins_patterns = [
|
||||
"CUMMINS", "CMI", "CMI-", "CMI_",
|
||||
"DELFOR_CUMMINS", "CUMMINS_DELFOR"
|
||||
]
|
||||
if any(pattern in filename for pattern in cummins_patterns) or \
|
||||
any(pattern in content_upper for pattern in cummins_patterns):
|
||||
return "cummins"
|
||||
|
||||
# Check for Minebea patterns
|
||||
minebea_patterns = [
|
||||
"MINEBEA", "MINOL", "MINEBEA-MINOL", "MBM",
|
||||
"DELFOR_MINEBEA", "MINEBEA_DELFOR"
|
||||
]
|
||||
if any(pattern in filename for pattern in minebea_patterns) or \
|
||||
any(pattern in content_upper for pattern in minebea_patterns):
|
||||
return "minebea"
|
||||
|
||||
# Check for Trwkob patterns
|
||||
trwkob_patterns = [
|
||||
"TRWKOB", "TRW-KOB", "TRW_KOB", "KOBALT",
|
||||
"DELFOR_TRWKOB", "TRWKOB_DELFOR"
|
||||
]
|
||||
if any(pattern in filename for pattern in trwkob_patterns) or \
|
||||
any(pattern in content_upper for pattern in trwkob_patterns):
|
||||
return "trwkob"
|
||||
|
||||
# If no specific pattern found, try to detect by file structure
|
||||
if content.startswith("UNB") or content.startswith("UNA"):
|
||||
# This is a standard EDI file structure
|
||||
return "minebea" # Default to Minebea as fallback
|
||||
|
||||
return None
|
||||
|
||||
def run_cummins_parser(self, filepath):
|
||||
try:
|
||||
# Create parser instance with Tk() root window
|
||||
parser = EDIDelforCumminsParser()
|
||||
|
||||
# Load the file
|
||||
success = parser.load_file(filepath)
|
||||
|
||||
if success:
|
||||
# Start the parser's main loop
|
||||
parser.root.mainloop()
|
||||
return True
|
||||
return False
|
||||
|
||||
except Exception as e:
|
||||
messagebox.showerror("Chyba", f"Chyba při načítání souboru: {str(e)}")
|
||||
return False
|
||||
|
||||
def on_parser_close(self, parser):
|
||||
"""Handle parser window closing"""
|
||||
try:
|
||||
# First show the main window if it exists
|
||||
if hasattr(self, 'root') and self.root.winfo_exists():
|
||||
self.root.deiconify()
|
||||
|
||||
# Then safely destroy the parser window if it exists
|
||||
if parser and hasattr(parser, 'root') and parser.root and parser.root.winfo_exists():
|
||||
# Schedule the destroy to happen after this method completes
|
||||
parser.root.after(100, parser.root.destroy)
|
||||
except Exception as e:
|
||||
# If anything goes wrong, just try to show the main window
|
||||
if hasattr(self, 'root') and self.root.winfo_exists():
|
||||
self.root.deiconify()
|
||||
|
||||
def run_trwkob_parser(self, filepath):
|
||||
try:
|
||||
# Create parser instance - don't set main_window to avoid circular references
|
||||
parser = EDITrwkobParser()
|
||||
|
||||
# Load the file
|
||||
success = parser.load_file(filepath)
|
||||
|
||||
if success:
|
||||
# Start the parser's main loop
|
||||
parser.root.mainloop()
|
||||
return True
|
||||
return False
|
||||
|
||||
except Exception as e:
|
||||
messagebox.showerror("Chyba", f"Chyba při načítání souboru: {str(e)}")
|
||||
return False
|
||||
|
||||
def run_minebea_parser(self, filepath):
|
||||
try:
|
||||
# Create parser instance with Tk() root window
|
||||
parser = EDIDelforMinebeaParser()
|
||||
|
||||
# Load the file
|
||||
success = parser.load_file(filepath)
|
||||
|
||||
if success:
|
||||
# Start the parser's main loop
|
||||
parser.root.mainloop()
|
||||
return True
|
||||
return False
|
||||
|
||||
except Exception as e:
|
||||
messagebox.showerror("Chyba", f"Chyba při načítání souboru: {str(e)}")
|
||||
return False
|
||||
|
||||
def main():
|
||||
app = EDIUnifiedParser()
|
||||
app.root.mainloop()
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,521 @@
|
||||
import tkinter as tk
|
||||
from tkinter import ttk, filedialog, messagebox
|
||||
import re
|
||||
from datetime import datetime, date
|
||||
import os
|
||||
import openpyxl
|
||||
from openpyxl.styles import Font, Alignment
|
||||
from openpyxl.utils import get_column_letter
|
||||
|
||||
class EDIDelforParser:
|
||||
def __init__(self, filepath=None):
|
||||
self.root = tk.Tk()
|
||||
self.root.title("EDI MINEBEA Parser")
|
||||
self.root.geometry("1200x800")
|
||||
|
||||
# Hlavní data
|
||||
self.header_info = {}
|
||||
self.partner_info = {}
|
||||
self.delivery_schedules = []
|
||||
|
||||
# Handle window close event
|
||||
self.root.protocol("WM_DELETE_WINDOW", self.on_closing)
|
||||
|
||||
self.setup_ui()
|
||||
|
||||
# If filepath was provided, load it automatically
|
||||
if filepath:
|
||||
self.load_file(filepath)
|
||||
|
||||
def setup_ui(self):
|
||||
# Hlavní frame
|
||||
main_frame = ttk.Frame(self.root)
|
||||
main_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
|
||||
|
||||
# Tlačítka pro ovládání
|
||||
btn_frame = ttk.Frame(main_frame)
|
||||
btn_frame.pack(fill=tk.X, pady=(0, 10))
|
||||
ttk.Button(btn_frame, text="Export do Excelu", command=self.export_to_excel).pack(side=tk.LEFT)
|
||||
ttk.Button(btn_frame, text="Zpět na hlavní okno", command=self.back_to_main).pack(side=tk.LEFT, padx=(10, 0))
|
||||
|
||||
# Notebook pro záložky
|
||||
self.notebook = ttk.Notebook(main_frame)
|
||||
self.notebook.pack(fill=tk.BOTH, expand=True)
|
||||
|
||||
# Záložka - Základní informace
|
||||
self.info_frame = ttk.Frame(self.notebook)
|
||||
self.notebook.add(self.info_frame, text="Základní informace")
|
||||
|
||||
# Záložka - Dodávky
|
||||
self.delivery_frame = ttk.Frame(self.notebook)
|
||||
self.notebook.add(self.delivery_frame, text="Plán dodávek")
|
||||
|
||||
# Záložka - Statistiky
|
||||
self.stats_frame = ttk.Frame(self.notebook)
|
||||
self.notebook.add(self.stats_frame, text="Statistiky")
|
||||
|
||||
self.setup_info_tab()
|
||||
self.setup_delivery_tab()
|
||||
self.setup_stats_tab()
|
||||
|
||||
def setup_info_tab(self):
|
||||
# Scrollable text widget pro základní informace
|
||||
text_frame = ttk.Frame(self.info_frame)
|
||||
text_frame.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
|
||||
|
||||
self.info_text = tk.Text(text_frame, wrap=tk.WORD, font=('Courier', 10))
|
||||
scrollbar = ttk.Scrollbar(text_frame, orient=tk.VERTICAL, command=self.info_text.yview)
|
||||
self.info_text.configure(yscrollcommand=scrollbar.set)
|
||||
|
||||
self.info_text.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
|
||||
scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
|
||||
|
||||
def get_scc_description(self, scc_code):
|
||||
"""Convert SCC code to descriptive name"""
|
||||
scc_mapping = {
|
||||
'10': 'Backlog',
|
||||
'1': 'Fix',
|
||||
'4': 'Forecast',
|
||||
'': 'Neznámé',
|
||||
}
|
||||
return scc_mapping.get(scc_code, f'Neznámý kód: {scc_code}')
|
||||
|
||||
def setup_delivery_tab(self):
|
||||
# Treeview pro plán dodávek
|
||||
tree_frame = ttk.Frame(self.delivery_frame)
|
||||
tree_frame.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
|
||||
|
||||
columns = ('Datum od', 'Množství', 'Typ', 'SCC')
|
||||
self.delivery_tree = ttk.Treeview(tree_frame, columns=columns, show='headings', height=15)
|
||||
|
||||
# Definice sloupců
|
||||
for col in columns:
|
||||
self.delivery_tree.heading(col, text=col)
|
||||
self.delivery_tree.column(col, width=120)
|
||||
|
||||
# Scrollbary
|
||||
v_scrollbar = ttk.Scrollbar(tree_frame, orient=tk.VERTICAL, command=self.delivery_tree.yview)
|
||||
h_scrollbar = ttk.Scrollbar(tree_frame, orient=tk.HORIZONTAL, command=self.delivery_tree.xview)
|
||||
self.delivery_tree.configure(yscrollcommand=v_scrollbar.set, xscrollcommand=h_scrollbar.set)
|
||||
|
||||
self.delivery_tree.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
|
||||
v_scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
|
||||
h_scrollbar.pack(side=tk.BOTTOM, fill=tk.X)
|
||||
|
||||
def setup_stats_tab(self):
|
||||
# Statistiky
|
||||
stats_frame = ttk.Frame(self.stats_frame)
|
||||
stats_frame.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
|
||||
|
||||
self.stats_text = tk.Text(stats_frame, wrap=tk.WORD, font=('Courier', 10))
|
||||
stats_scrollbar = ttk.Scrollbar(stats_frame, orient=tk.VERTICAL, command=self.stats_text.yview)
|
||||
self.stats_text.configure(yscrollcommand=stats_scrollbar.set)
|
||||
|
||||
self.stats_text.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
|
||||
stats_scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
|
||||
|
||||
def parse_date(self, date_str, format_code):
|
||||
"""Parsuje datum podle EDI formátu"""
|
||||
try:
|
||||
if format_code == '203': # CCYYMMDDHHMMSS
|
||||
# Return only date part without time
|
||||
return datetime.strptime(date_str, '%Y%m%d%H%M%S').strftime('%d.%m.%Y')
|
||||
elif format_code == '102': # CCYYMMDD
|
||||
return datetime.strptime(date_str, '%Y%m%d').strftime('%d.%m.%Y')
|
||||
else:
|
||||
return date_str.split(' ')[0] # Return only date part if time is present
|
||||
except Exception as e:
|
||||
print(f"Error parsing date {date_str} with format {format_code}: {e}")
|
||||
return date_str.split(' ')[0] if date_str else ''
|
||||
|
||||
def parse_edi_datetime(self, datetime_str):
|
||||
"""Parsuje EDI datum/čas z UNB segmentu (YYMMDD:HHMM)"""
|
||||
try:
|
||||
if ':' in datetime_str:
|
||||
date_part, time_part = datetime_str.split(':')
|
||||
# Přidáme 20 na začátek roku (předpokládáme 21. století)
|
||||
full_date = '20' + date_part
|
||||
formatted_date = datetime.strptime(full_date, '%Y%m%d').strftime('%d.%m.%Y')
|
||||
formatted_time = datetime.strptime(time_part, '%H%M').strftime('%H:%M')
|
||||
return f"{formatted_date} {formatted_time}"
|
||||
return datetime_str
|
||||
except:
|
||||
return datetime_str
|
||||
|
||||
def parse_edi_file(self, content):
|
||||
"""Parsuje EDI DELFOR soubor"""
|
||||
lines = content.strip().split("'")
|
||||
|
||||
# Reset dat
|
||||
self.header_info = {}
|
||||
self.partner_info = {}
|
||||
self.delivery_schedules = []
|
||||
|
||||
current_delivery = {}
|
||||
|
||||
for line in lines:
|
||||
line = line.strip()
|
||||
if not line:
|
||||
continue
|
||||
|
||||
# UNB - Interchange header
|
||||
if line.startswith('UNB'):
|
||||
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])
|
||||
|
||||
# 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
|
||||
|
||||
# NAD - Name and address
|
||||
elif line.startswith('NAD'):
|
||||
parts = line.split('+')
|
||||
if len(parts) >= 3:
|
||||
role = parts[1]
|
||||
code = parts[2] if len(parts) > 2 else ''
|
||||
|
||||
# Debug - vypíšeme co parsujeme
|
||||
print(f"NAD Debug - Role: {role}, Code: {code}, Parts: {parts}")
|
||||
|
||||
# Název společnosti je v parts[4] (index 4)
|
||||
name = parts[4] if len(parts) > 4 else ''
|
||||
|
||||
# Adresa začíná od parts[5]
|
||||
address_parts = []
|
||||
for i in range(5, len(parts)):
|
||||
if parts[i]: # Přidáme pouze neprázdné části
|
||||
address_parts.append(parts[i])
|
||||
|
||||
full_address = ', '.join(address_parts) if address_parts else ''
|
||||
|
||||
if role == 'BY':
|
||||
self.partner_info['Kupující'] = name
|
||||
if full_address:
|
||||
self.partner_info['Kupující'] += f", {full_address}"
|
||||
elif role == 'SE':
|
||||
# Zkontrolujeme, zda SE obsahuje kód příjemce z UNB
|
||||
print(f"SE Debug - Checking code: {code}, name: {name}")
|
||||
if '1000500120' in code:
|
||||
print(f"Found matching code! Setting recipient to: {name}")
|
||||
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:
|
||||
self.partner_info['Prodávající'] = name
|
||||
elif role == 'CN':
|
||||
if full_address:
|
||||
self.partner_info['Dodací adresa'] = f"{name}, {full_address}"
|
||||
else:
|
||||
self.partner_info['Dodací adresa'] = name
|
||||
|
||||
# LIN - Line item
|
||||
elif line.startswith('LIN'):
|
||||
parts = line.split('+')
|
||||
if len(parts) >= 4:
|
||||
self.header_info['Číslo položky'] = parts[3]
|
||||
|
||||
# PIA - Product identification
|
||||
elif line.startswith('PIA'):
|
||||
parts = line.split('+')
|
||||
if len(parts) >= 3:
|
||||
self.header_info['Kód produktu'] = parts[2]
|
||||
|
||||
# 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': # Cumulative quantity
|
||||
current_delivery['Množství'] = quantity
|
||||
current_delivery['Jednotka'] = unit
|
||||
current_delivery['Typ'] = 'Kumulativní'
|
||||
elif qty_type == '70': # Minimum quantity
|
||||
current_delivery['Množství'] = quantity
|
||||
current_delivery['Jednotka'] = unit
|
||||
current_delivery['Typ'] = 'Minimální'
|
||||
elif qty_type == '78': # Maximum quantity
|
||||
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:
|
||||
self.delivery_schedules.append(current_delivery.copy())
|
||||
current_delivery = {'SCC': parts[1]} # Zachováme SCC pro další dodávky
|
||||
|
||||
def load_file(self, filepath):
|
||||
"""Načte EDI soubor"""
|
||||
try:
|
||||
# Check if the window still exists
|
||||
if not hasattr(self, 'root') or not self.root.winfo_exists():
|
||||
return False
|
||||
|
||||
with open(filepath, 'r', encoding='utf-8', errors='replace') as f:
|
||||
content = f.read()
|
||||
|
||||
self.parse_edi_file(content)
|
||||
|
||||
# Check again before updating UI
|
||||
if hasattr(self, 'root') and self.root.winfo_exists():
|
||||
self.display_data()
|
||||
return True
|
||||
return False
|
||||
|
||||
except Exception as e:
|
||||
# Safely show error if window still exists
|
||||
if hasattr(self, 'root') and self.root.winfo_exists():
|
||||
messagebox.showerror("Chyba", f"Nelze načíst soubor: {str(e)}")
|
||||
return False
|
||||
|
||||
def display_data(self):
|
||||
"""Zobrazí naparsovaná data"""
|
||||
# Check if window still exists
|
||||
if not hasattr(self, 'info_text') or not hasattr(self, 'root') or not self.root.winfo_exists():
|
||||
return
|
||||
|
||||
try:
|
||||
# Základní informace
|
||||
self.info_text.delete(1.0, tk.END)
|
||||
info_content = "=== HLAVIČKA DOKUMENTU ===\n"
|
||||
for key, value in self.header_info.items():
|
||||
# Přeskočíme pomocný klíč
|
||||
if key != 'Příjemce_kód':
|
||||
info_content += f"{key}: {value}\n"
|
||||
|
||||
# Pokud nemáme název příjemce, zobrazíme alespoň kód
|
||||
if 'Příjemce' not in self.header_info and 'Příjemce_kód' in self.header_info:
|
||||
info_content += f"Příjemce: {self.header_info['Příjemce_kód']}\n"
|
||||
except Exception as e:
|
||||
# Skip if there's an error during display
|
||||
print(f"Error displaying data: {e}")
|
||||
return
|
||||
|
||||
info_content += "\n=== INFORMACE O PARTNERECH ===\n"
|
||||
for key, value in self.partner_info.items():
|
||||
info_content += f"{key}: {value}\n"
|
||||
|
||||
self.info_text.insert(1.0, info_content)
|
||||
|
||||
# Plán dodávek
|
||||
for item in self.delivery_tree.get_children():
|
||||
self.delivery_tree.delete(item)
|
||||
|
||||
for delivery in self.delivery_schedules:
|
||||
scc_code = delivery.get('SCC', '')
|
||||
scc_desc = self.get_scc_description(scc_code)
|
||||
self.delivery_tree.insert('', tk.END, values=(
|
||||
delivery.get('Datum od', ''),
|
||||
delivery.get('Množství', ''),
|
||||
delivery.get('Typ', ''),
|
||||
scc_desc
|
||||
))
|
||||
|
||||
# Statistiky
|
||||
self.stats_text.delete(1.0, tk.END)
|
||||
stats_content = "=== STATISTIKY ===\n"
|
||||
stats_content += f"Celkový počet dodávek: {len(self.delivery_schedules)}\n"
|
||||
|
||||
total_qty = sum(int(d.get('Množství', 0)) for d in self.delivery_schedules if d.get('Množství', '').isdigit())
|
||||
stats_content += f"Celkové množství: {total_qty:,} kusů\n"
|
||||
|
||||
# Statistiky podle typu
|
||||
type_stats = {}
|
||||
for delivery in self.delivery_schedules:
|
||||
delivery_type = delivery.get('Typ', 'Neznámý')
|
||||
if delivery_type not in type_stats:
|
||||
type_stats[delivery_type] = {'počet': 0, 'množství': 0}
|
||||
type_stats[delivery_type]['počet'] += 1
|
||||
if delivery.get('Množství', '').isdigit():
|
||||
type_stats[delivery_type]['množství'] += int(delivery.get('Množství', 0))
|
||||
|
||||
stats_content += "\n=== STATISTIKY PODLE TYPU ===\n"
|
||||
for delivery_type, stats in type_stats.items():
|
||||
stats_content += f"{delivery_type}: {stats['počet']} dodávek, {stats['množství']:,} kusů\n"
|
||||
|
||||
self.stats_text.insert(1.0, stats_content)
|
||||
|
||||
def get_week_number(self, date_str):
|
||||
"""Převede řetězec s datem na číslo kalendářního týdne (WW)"""
|
||||
if not date_str:
|
||||
return ""
|
||||
try:
|
||||
# Handle case where time might be included
|
||||
date_part = date_str.split(' ')[0]
|
||||
day, month, year = map(int, date_part.split('.'))
|
||||
# Handle 2-digit year
|
||||
if year < 100:
|
||||
year += 2000 # Assuming 21st century for 2-digit years
|
||||
dt = date(year, month, day)
|
||||
week_num = dt.isocalendar()[1]
|
||||
return week_num
|
||||
except Exception as e:
|
||||
print(f"Error getting week number from {date_str}: {e}")
|
||||
return ""
|
||||
|
||||
def export_to_excel(self):
|
||||
"""Exportuje data o dodávkách do Excelu s kalendářními týdny"""
|
||||
if not self.delivery_schedules:
|
||||
messagebox.showwarning("Upozornění", "Žádná data k exportu")
|
||||
return
|
||||
|
||||
try:
|
||||
wb = openpyxl.Workbook()
|
||||
ws = wb.active
|
||||
ws.title = "Dodávky"
|
||||
|
||||
# Hlavičky
|
||||
headers = ["Týden", "Datum od", "Množství", "Typ", "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')
|
||||
|
||||
# Data
|
||||
row_num = 2
|
||||
for delivery in self.delivery_schedules:
|
||||
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', '')
|
||||
|
||||
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'
|
||||
except Exception as e:
|
||||
print(f"Error formatting date: {e}")
|
||||
ws.cell(row=row_num, column=2, value=date_from.split(' ')[0] if date_from else '')
|
||||
|
||||
# Add other data with proper number formatting
|
||||
ws.cell(row=row_num, column=3, value=quantity) # Use formatted quantity
|
||||
ws.cell(row=row_num, column=4, value=delivery.get('Typ', ''))
|
||||
# Use SCC description instead of code
|
||||
scc_desc = self.get_scc_description(str(scc))
|
||||
ws.cell(row=row_num, column=5, value=scc_desc)
|
||||
ws.cell(row=row_num, column=6, value=self.partner_info.get('Dodací adresa', '') or 'XTREME PRESSURE INJECTION JUAREZ, REC LOC 372, EL PASO, 79927') # Add delivery location
|
||||
row_num += 1
|
||||
|
||||
# Apply number formatting to all numeric columns
|
||||
for col in range(1, 8):
|
||||
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)):
|
||||
if col == 1: # Week number
|
||||
cell.number_format = '0'
|
||||
elif col == 4: # Quantity
|
||||
cell.number_format = '0'
|
||||
elif col == 6: # SCC
|
||||
cell.number_format = '0'
|
||||
|
||||
# Automatické přizpůsobení šířky sloupců
|
||||
for col in ws.columns:
|
||||
max_length = 0
|
||||
column = col[0].column_letter
|
||||
for cell in col:
|
||||
try:
|
||||
# For dates, use the formatted string length
|
||||
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 ''
|
||||
|
||||
if len(cell_value) > max_length:
|
||||
max_length = len(cell_value)
|
||||
except:
|
||||
pass
|
||||
adjusted_width = (max_length + 2)
|
||||
ws.column_dimensions[column].width = min(adjusted_width, 30)
|
||||
|
||||
# Uložení souboru
|
||||
filename = f"dodavky_minebea_{datetime.now().strftime('%Y%m%d_%H%M%S')}.xlsx"
|
||||
filepath = filedialog.asksaveasfilename(
|
||||
defaultextension=".xlsx",
|
||||
filetypes=[("Excel files", "*.xlsx"), ("All files", "*.*")],
|
||||
initialfile=filename
|
||||
)
|
||||
|
||||
if filepath:
|
||||
wb.save(filepath)
|
||||
messagebox.showinfo("Hotovo", f"Data byla úspěšně exportována do souboru:\n{filepath}")
|
||||
|
||||
except Exception as e:
|
||||
messagebox.showerror("Chyba", f"Chyba při exportu do Excelu: {str(e)}")
|
||||
|
||||
def on_closing(self):
|
||||
"""Handle window close event"""
|
||||
self.root.destroy() # Close the current window
|
||||
|
||||
def back_to_main(self):
|
||||
"""Closes the current window"""
|
||||
self.root.destroy()
|
||||
|
||||
def run(self):
|
||||
"""Spustí aplikaci"""
|
||||
self.root.mainloop()
|
||||
|
||||
if __name__ == "__main__":
|
||||
app = EDIDelforParser()
|
||||
app.run()
|
||||
@@ -0,0 +1,379 @@
|
||||
import tkinter as tk
|
||||
from tkinter import ttk, filedialog, messagebox
|
||||
from datetime import datetime, date
|
||||
import os
|
||||
import openpyxl
|
||||
from openpyxl.styles import Font, Alignment
|
||||
from openpyxl.utils import get_column_letter
|
||||
|
||||
class EDITrwkobParser:
|
||||
def __init__(self, filepath=None):
|
||||
self.root = tk.Tk()
|
||||
self.root.title("EDI TRWKOB Parser")
|
||||
self.root.geometry("1200x800")
|
||||
self.header_info = {}
|
||||
self.partner_info = {}
|
||||
self.delivery_schedules = []
|
||||
self.setup_ui()
|
||||
self.main_window = None
|
||||
|
||||
def setup_ui(self):
|
||||
main_frame = ttk.Frame(self.root)
|
||||
main_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
|
||||
btn_frame = ttk.Frame(main_frame)
|
||||
btn_frame.pack(fill=tk.X, pady=(0, 10))
|
||||
ttk.Button(btn_frame, text="Export do Excelu", command=self.export_to_excel).pack(side=tk.LEFT)
|
||||
ttk.Button(btn_frame, text="Zpět na hlavní okno", command=self.back_to_main).pack(side=tk.LEFT, padx=(10, 0))
|
||||
self.notebook = ttk.Notebook(main_frame)
|
||||
self.notebook.pack(fill=tk.BOTH, expand=True)
|
||||
self.info_frame = ttk.Frame(self.notebook)
|
||||
self.notebook.add(self.info_frame, text="Základní informace")
|
||||
self.delivery_frame = ttk.Frame(self.notebook)
|
||||
self.notebook.add(self.delivery_frame, text="Plán dodávek")
|
||||
self.stats_frame = ttk.Frame(self.notebook)
|
||||
self.notebook.add(self.stats_frame, text="Statistiky")
|
||||
self.setup_info_tab()
|
||||
self.setup_delivery_tab()
|
||||
self.setup_stats_tab()
|
||||
|
||||
def setup_info_tab(self):
|
||||
text_frame = ttk.Frame(self.info_frame)
|
||||
text_frame.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
|
||||
self.info_text = tk.Text(text_frame, wrap=tk.WORD, font=('Courier', 10))
|
||||
scrollbar = ttk.Scrollbar(text_frame, orient=tk.VERTICAL, command=self.info_text.yview)
|
||||
self.info_text.configure(yscrollcommand=scrollbar.set)
|
||||
self.info_text.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
|
||||
scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
|
||||
|
||||
def setup_delivery_tab(self):
|
||||
tree_frame = ttk.Frame(self.delivery_frame)
|
||||
tree_frame.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
|
||||
columns = ('Datum od', 'Datum do', 'Množství', 'Typ', 'SCC')
|
||||
self.delivery_tree = ttk.Treeview(tree_frame, columns=columns, show='headings', height=15)
|
||||
for col in columns:
|
||||
self.delivery_tree.heading(col, text=col)
|
||||
self.delivery_tree.column(col, width=120)
|
||||
v_scrollbar = ttk.Scrollbar(tree_frame, orient=tk.VERTICAL, command=self.delivery_tree.yview)
|
||||
h_scrollbar = ttk.Scrollbar(tree_frame, orient=tk.HORIZONTAL, command=self.delivery_tree.xview)
|
||||
self.delivery_tree.configure(yscrollcommand=v_scrollbar.set, xscrollcommand=h_scrollbar.set)
|
||||
self.delivery_tree.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
|
||||
v_scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
|
||||
h_scrollbar.pack(side=tk.BOTTOM, fill=tk.X)
|
||||
|
||||
def setup_stats_tab(self):
|
||||
stats_frame = ttk.Frame(self.stats_frame)
|
||||
stats_frame.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
|
||||
self.stats_text = tk.Text(stats_frame, wrap=tk.WORD, font=('Courier', 10))
|
||||
stats_scrollbar = ttk.Scrollbar(stats_frame, orient=tk.VERTICAL, command=self.stats_text.yview)
|
||||
self.stats_text.configure(yscrollcommand=stats_scrollbar.set)
|
||||
self.stats_text.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
|
||||
stats_scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
|
||||
|
||||
def parse_date(self, date_str, format_code):
|
||||
try:
|
||||
if format_code == '102':
|
||||
return datetime.strptime(date_str, '%Y%m%d').strftime('%d.%m.%Y')
|
||||
else:
|
||||
return date_str
|
||||
except:
|
||||
return date_str
|
||||
|
||||
def parse_edi_datetime(self, datetime_str):
|
||||
"""Parsuje EDI datum/čas z UNB segmentu (YYMMDD:HHMM)"""
|
||||
try:
|
||||
if ':' in datetime_str:
|
||||
date_part, time_part = datetime_str.split(':')
|
||||
full_date = '20' + date_part
|
||||
formatted_date = datetime.strptime(full_date, '%Y%m%d').strftime('%d.%m.%Y')
|
||||
formatted_time = datetime.strptime(time_part, '%H%M').strftime('%H:%M')
|
||||
return f"{formatted_date} {formatted_time}"
|
||||
return datetime_str
|
||||
except:
|
||||
return datetime_str
|
||||
|
||||
def load_file(self, filepath):
|
||||
"""Load and parse the specified EDI file"""
|
||||
try:
|
||||
with open(filepath, 'r', encoding='utf-8', errors='replace') as f:
|
||||
content = f.read()
|
||||
self.parse_edi_file(content)
|
||||
self.display_data()
|
||||
return True
|
||||
except Exception as e:
|
||||
messagebox.showerror("Chyba", f"Nelze načíst soubor: {str(e)}")
|
||||
return False
|
||||
|
||||
def parse_edi_file(self, content):
|
||||
lines = content.strip().split("'")
|
||||
self.header_info = {}
|
||||
self.partner_info = {}
|
||||
self.delivery_schedules = []
|
||||
current_delivery = {}
|
||||
for line in lines:
|
||||
line = line.strip()
|
||||
if not line:
|
||||
continue
|
||||
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])
|
||||
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] == '63':
|
||||
current_delivery['Datum do'] = date_formatted
|
||||
elif dtm_parts[0] == '64':
|
||||
current_delivery['Datum od'] = date_formatted
|
||||
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 ''
|
||||
address_parts = []
|
||||
for i in range(5, len(parts)):
|
||||
if parts[i]:
|
||||
address_parts.append(parts[i])
|
||||
full_address = ', '.join(address_parts) if address_parts else ''
|
||||
if role == 'BY':
|
||||
# Kupující: prefer name, fallback to code
|
||||
self.partner_info['Kupující'] = name if name else code
|
||||
if full_address:
|
||||
self.partner_info['Kupující'] += f", {full_address}"
|
||||
elif role == 'SE':
|
||||
# Prodávající: prefer name, fallback to code
|
||||
self.header_info['Příjemce'] = name if name else code
|
||||
if full_address:
|
||||
self.partner_info['Prodávající'] = f"{name if name else code}, {full_address}"
|
||||
else:
|
||||
self.partner_info['Prodávající'] = name if name else code
|
||||
elif role == 'CN':
|
||||
# Dodací adresa: show code if name missing
|
||||
if full_address:
|
||||
self.partner_info['Dodací adresa'] = f"{name if name else code}, {full_address}"
|
||||
else:
|
||||
self.partner_info['Dodací adresa'] = name if name else code
|
||||
elif line.startswith('LIN'):
|
||||
parts = line.split('+')
|
||||
if len(parts) >= 4:
|
||||
self.header_info['Číslo položky'] = parts[3]
|
||||
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 len(qty_parts) >= 3:
|
||||
qty_type = qty_parts[0]
|
||||
quantity = qty_parts[1]
|
||||
unit = qty_parts[2]
|
||||
if qty_type == '113':
|
||||
current_delivery['Množství'] = quantity
|
||||
current_delivery['Jednotka'] = unit
|
||||
current_delivery['Typ'] = 'Kumulativní'
|
||||
elif qty_type == '70':
|
||||
current_delivery['Množství'] = quantity
|
||||
current_delivery['Jednotka'] = unit
|
||||
current_delivery['Typ'] = 'Minimální'
|
||||
elif qty_type == '78':
|
||||
current_delivery['Množství'] = quantity
|
||||
current_delivery['Jednotka'] = unit
|
||||
current_delivery['Typ'] = 'Maximální'
|
||||
elif line.startswith('SCC'):
|
||||
parts = line.split('+')
|
||||
if len(parts) >= 2:
|
||||
current_delivery['SCC'] = parts[1]
|
||||
if 'Datum od' in current_delivery and 'Množství' in current_delivery:
|
||||
self.delivery_schedules.append(current_delivery.copy())
|
||||
current_delivery = {'SCC': parts[1]}
|
||||
|
||||
def display_data(self):
|
||||
self.info_text.delete(1.0, tk.END)
|
||||
info_content = "=== HLAVIČKA DOKUMENTU ===\n"
|
||||
for key, value in self.header_info.items():
|
||||
if key != 'Příjemce_kód':
|
||||
info_content += f"{key}: {value}\n"
|
||||
if 'Příjemce' not in self.header_info and 'Příjemce_kód' in self.header_info:
|
||||
info_content += f"Příjemce: {self.header_info['Příjemce_kód']}\n"
|
||||
info_content += "\n=== INFORMACE O PARTNERECH ===\n"
|
||||
for key, value in self.partner_info.items():
|
||||
info_content += f"{key}: {value}\n"
|
||||
self.info_text.insert(1.0, info_content)
|
||||
for item in self.delivery_tree.get_children():
|
||||
self.delivery_tree.delete(item)
|
||||
for delivery in self.delivery_schedules:
|
||||
scc_code = delivery.get('SCC', '')
|
||||
scc_desc = self.get_scc_description(scc_code)
|
||||
self.delivery_tree.insert('', tk.END, values=(
|
||||
delivery.get('Datum od', ''),
|
||||
delivery.get('Datum do', ''),
|
||||
delivery.get('Množství', ''),
|
||||
delivery.get('Typ', ''),
|
||||
scc_desc
|
||||
))
|
||||
self.stats_text.delete(1.0, tk.END)
|
||||
stats_content = "=== STATISTIKY ===\n"
|
||||
stats_content += f"Celkový počet dodávek: {len(self.delivery_schedules)}\n"
|
||||
total_qty = sum(int(d.get('Množství', 0)) for d in self.delivery_schedules if d.get('Množství', '').isdigit())
|
||||
stats_content += f"Celkové množství: {total_qty:,} kusů\n"
|
||||
type_stats = {}
|
||||
for delivery in self.delivery_schedules:
|
||||
delivery_type = delivery.get('Typ', 'Neznámý')
|
||||
if delivery_type not in type_stats:
|
||||
type_stats[delivery_type] = {'počet': 0, 'množství': 0}
|
||||
type_stats[delivery_type]['počet'] += 1
|
||||
if delivery.get('Množství', '').isdigit():
|
||||
type_stats[delivery_type]['množství'] += int(delivery.get('Množství', 0))
|
||||
stats_content += "\n=== STATISTIKY PODLE TYPU ===\n"
|
||||
for delivery_type, stats in type_stats.items():
|
||||
stats_content += f"{delivery_type}: {stats['počet']} dodávek, {stats['množství']:,} kusů\n"
|
||||
self.stats_text.insert(1.0, stats_content)
|
||||
|
||||
def get_week_number(self, date_str):
|
||||
"""Convert date string to ISO week number (WW)"""
|
||||
try:
|
||||
day, month, year = map(int, date_str.split('.'))
|
||||
dt = date(year, month, day)
|
||||
return dt.isocalendar()[1]
|
||||
except (ValueError, AttributeError):
|
||||
return ""
|
||||
|
||||
def get_scc_description(self, scc_code):
|
||||
"""Convert SCC code to descriptive name"""
|
||||
scc_mapping = {
|
||||
'10': 'Backlog',
|
||||
'1': 'Fix',
|
||||
'4': 'Forecast',
|
||||
'': 'Neznámé',
|
||||
}
|
||||
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"""
|
||||
if not self.delivery_schedules:
|
||||
messagebox.showwarning("Upozornění", "Žádná data k exportu")
|
||||
return
|
||||
|
||||
try:
|
||||
wb = openpyxl.Workbook()
|
||||
ws = wb.active
|
||||
ws.title = "Dodávky"
|
||||
|
||||
# Headers
|
||||
headers = ["Týden", "Datum", "Množství", "Typ", "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')
|
||||
|
||||
# Data
|
||||
row_num = 2
|
||||
for delivery in self.delivery_schedules:
|
||||
date_str = delivery.get('Datum od', '')
|
||||
week_num = self.get_week_number(date_str) if date_str 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í', '')
|
||||
|
||||
# Add week number
|
||||
ws.cell(row=row_num, column=1, value=week_num)
|
||||
|
||||
# Format date as Excel date
|
||||
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'
|
||||
else:
|
||||
ws.cell(row=row_num, column=2, value='')
|
||||
except (ValueError, TypeError):
|
||||
ws.cell(row=row_num, column=2, value=date_str)
|
||||
|
||||
# Add other data with proper number formatting
|
||||
ws.cell(row=row_num, column=3, value=quantity) # Use formatted quantity
|
||||
ws.cell(row=row_num, column=4, value=delivery.get('Typ', ''))
|
||||
ws.cell(row=row_num, column=5, value=scc_desc) # Use SCC description
|
||||
ws.cell(row=row_num, column=6, value=self.partner_info.get('Dodací adresa', '') or 'XTREME PRESSURE INJECTION JUAREZ, REC LOC 372, EL PASO, 79927') # Add delivery location
|
||||
row_num += 1
|
||||
|
||||
# Apply number formatting to numeric columns
|
||||
for col in [1, 3]: # Only format week number and quantity columns
|
||||
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)):
|
||||
cell.number_format = '0'
|
||||
|
||||
# Auto-adjust column widths
|
||||
for column in ws.columns:
|
||||
max_length = 0
|
||||
column_letter = get_column_letter(column[0].column)
|
||||
for cell in column:
|
||||
try:
|
||||
# For dates, use the formatted string length
|
||||
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 ''
|
||||
|
||||
if len(cell_value) > max_length:
|
||||
max_length = len(cell_value)
|
||||
except:
|
||||
pass
|
||||
adjusted_width = (max_length + 2)
|
||||
ws.column_dimensions[column_letter].width = min(adjusted_width, 30)
|
||||
|
||||
# Save the file
|
||||
filename = f"dodavky_{datetime.now().strftime('%Y%m%d_%H%M%S')}.xlsx"
|
||||
filepath = filedialog.asksaveasfilename(
|
||||
defaultextension=".xlsx",
|
||||
filetypes=[("Excel files", "*.xlsx"), ("All files", "*.*")],
|
||||
initialfile=filename
|
||||
)
|
||||
|
||||
if filepath:
|
||||
wb.save(filepath)
|
||||
messagebox.showinfo("Hotovo", f"Data byla úspěšně exportována do souboru:\n{filepath}")
|
||||
|
||||
except Exception as e:
|
||||
messagebox.showerror("Chyba", f"Chyba při exportu do Excelu: {str(e)}")
|
||||
|
||||
def back_to_main(self):
|
||||
"""Closes the current window and returns to the main application"""
|
||||
# Close current window
|
||||
self.root.destroy()
|
||||
# Return to main window
|
||||
if self.main_window:
|
||||
self.main_window.root.deiconify() # Show the main window if it exists
|
||||
|
||||
def run(self):
|
||||
self.root.mainloop()
|
||||
|
||||
if __name__ == "__main__":
|
||||
app = EDITrwkobParser()
|
||||
app.run()
|
||||
Reference in New Issue
Block a user