From d1614d78d5af2e05be25aa0569ab993b2237c4d6 Mon Sep 17 00:00:00 2001 From: Dvorinka Date: Wed, 16 Jul 2025 13:57:08 +0200 Subject: [PATCH] first commit --- .../edi_parser_cummins.cpython-313.pyc | Bin 0 -> 30865 bytes __pycache__/edi_parser_main.cpython-313.pyc | Bin 0 -> 9673 bytes .../edi_parser_minebea.cpython-313.pyc | Bin 0 -> 26891 bytes __pycache__/edi_parser_trwkob.cpython-313.pyc | Bin 0 -> 24148 bytes __pycache__/styles.cpython-313.pyc | Bin 0 -> 1331 bytes build_nuitka.py | 88 +++ edi_parser_cummins.py | 651 ++++++++++++++++++ edi_parser_main.py | 185 +++++ edi_parser_minebea.py | 521 ++++++++++++++ edi_parser_trwkob.py | 379 ++++++++++ 10 files changed, 1824 insertions(+) create mode 100644 __pycache__/edi_parser_cummins.cpython-313.pyc create mode 100644 __pycache__/edi_parser_main.cpython-313.pyc create mode 100644 __pycache__/edi_parser_minebea.cpython-313.pyc create mode 100644 __pycache__/edi_parser_trwkob.cpython-313.pyc create mode 100644 __pycache__/styles.cpython-313.pyc create mode 100644 build_nuitka.py create mode 100644 edi_parser_cummins.py create mode 100644 edi_parser_main.py create mode 100644 edi_parser_minebea.py create mode 100644 edi_parser_trwkob.py diff --git a/__pycache__/edi_parser_cummins.cpython-313.pyc b/__pycache__/edi_parser_cummins.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4d18e8aeabf67c133586e2fa2ced7c850b534f8a GIT binary patch literal 30865 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 IAuaQN0E8+i4FCWD literal 0 HcmV?d00001 diff --git a/__pycache__/edi_parser_main.cpython-313.pyc b/__pycache__/edi_parser_main.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..871948643f49b742bf48d9d6e8feaba951109177 GIT binary patch literal 9673 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 KEa9rPeg7BcHeGT6 literal 0 HcmV?d00001 diff --git a/__pycache__/edi_parser_minebea.cpython-313.pyc b/__pycache__/edi_parser_minebea.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1ed03151ed9567d0b5dd840ff73e49a4142f53a4 GIT binary patch literal 26891 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 literal 0 HcmV?d00001 diff --git a/__pycache__/edi_parser_trwkob.cpython-313.pyc b/__pycache__/edi_parser_trwkob.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a8e9354f4c6bdcc4005cdaaa87bb68458ead2501 GIT binary patch literal 24148 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 literal 0 HcmV?d00001 diff --git a/__pycache__/styles.cpython-313.pyc b/__pycache__/styles.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..14350bbc0401ec43a98c81834611c9930808ff7c GIT binary patch literal 1331 zcmah}yHgWE9Nv482Z?|Lc_@hBI0nYR=%9l-;0sI$CJr04Ff+_0*(7ID} zi_RssC(S7~`3N9@LJ$yvy~QBd5Q#Vqpfmx9loGshos&ae!IUV?#M?^iWoM(rp5|wP zyLr*IZFK^45ddbuQhSvJ+h9vMW@=BN07WP*v*0J#WvIYDXj_&}WZ0pj2gBJTMn7~x zWkKxW#SPD}dw{V9mz5K<8G>iw@G13pn2*!y9bvhiJ4HX*!#)PbSq2lW{kjai=R;;+ zWSywHsAQW>F7lhT)frRcnH0hKY{Ilf9^@&-**b5JSNv;-D237VcA(BbCa-APa%v)> zWmBe{#97MRZK^S(*LlB9M z<3LhFdg53n4uT75q@~i7xEP@{*irlZNaY!LCE{R)mTx(R^@LYwQTrq4HTAU%9e#!g z)#9q|i@-R+x*k&rE4rVuf>Y@@)-frK_%YE+te*c^CD$4unD|;Hni4??wgJv0CaASr zRy39dKLVx#X;DX}BaX94EtMyT1yKx+8SHR?X>%!lJGyNkVni8*s?kTdkkzr8FrrVl z!jE@y1iwTXT8vU;&}d8O%N0$RfoMLr(*npObHxrbF#fQi41TxBkqb577b>QGtbzzl5_d1aOK&|>#IJA1C3swRgx!ECwE3v~%mFLwv zK@M*o5jQHgYO`~-$&0nIs|_1aJimdY%r)%o1gf4Z)oW{9vwoy`Qb}3PyGYgJJuGtQ zemh>(Gyl~}{9%`~AFAYQV~Y*RiXkZ1nZ-3!-fXhs*_UJ5XhzFYL`;!ptS(sl^92z8 pTO$|PVA}8Q;zV9YD`bW(c_)lPHZ&wb5WWM?PcYh)r-UI(@elODG*tiq literal 0 HcmV?d00001 diff --git a/build_nuitka.py b/build_nuitka.py new file mode 100644 index 0000000..4d5af48 --- /dev/null +++ b/build_nuitka.py @@ -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() \ No newline at end of file diff --git a/edi_parser_cummins.py b/edi_parser_cummins.py new file mode 100644 index 0000000..dcdd0c2 --- /dev/null +++ b/edi_parser_cummins.py @@ -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() \ No newline at end of file diff --git a/edi_parser_main.py b/edi_parser_main.py new file mode 100644 index 0000000..17e60ae --- /dev/null +++ b/edi_parser_main.py @@ -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() diff --git a/edi_parser_minebea.py b/edi_parser_minebea.py new file mode 100644 index 0000000..7f5eea1 --- /dev/null +++ b/edi_parser_minebea.py @@ -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() \ No newline at end of file diff --git a/edi_parser_trwkob.py b/edi_parser_trwkob.py new file mode 100644 index 0000000..7575423 --- /dev/null +++ b/edi_parser_trwkob.py @@ -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()