343 lines
13 KiB
Python
343 lines
13 KiB
Python
import tkinter
|
|
from PIL import Image
|
|
import requests
|
|
import webbrowser
|
|
import customtkinter as ctk
|
|
from tkinter import messagebox
|
|
from tkinter import ttk
|
|
from reportlab.lib.pagesizes import A4
|
|
from reportlab.pdfgen import canvas
|
|
from datetime import datetime
|
|
import datetime
|
|
import check_api
|
|
|
|
STATUS_PAID = "in_progress"
|
|
keys = check_api.run()
|
|
CLIENT_ID = keys[0]
|
|
CLIENT_SECRET = keys[1]
|
|
SHOPWARE_URL = keys[2]
|
|
|
|
current_order_id = None
|
|
|
|
def get_order_line_items(access_token, order_id):
|
|
headers = {
|
|
"sw-access-key": CLIENT_ID,
|
|
"Authorization": f"Bearer {access_token}",
|
|
"Content-Type": "application/json"
|
|
}
|
|
items_url = f"{SHOPWARE_URL}/api/order/{order_id}/line-items"
|
|
response = requests.get(items_url, headers=headers)
|
|
response.raise_for_status()
|
|
return response.json()["data"]
|
|
|
|
def get_order_customer(access_token, customer_id):
|
|
headers = {
|
|
"sw-access-key": CLIENT_ID,
|
|
"Authorization": f"Bearer {access_token}",
|
|
"Content-Type": "application/json"
|
|
}
|
|
customer_url = f"{SHOPWARE_URL}/api/order-customer/{customer_id}"
|
|
response = requests.get(customer_url, headers=headers)
|
|
response.raise_for_status()
|
|
return response.json()["data"]
|
|
|
|
def get_order_billing_address(access_token, order_id):
|
|
headers = {
|
|
"sw-access-key": CLIENT_ID,
|
|
"Authorization": f"Bearer {access_token}",
|
|
"Content-Type": "application/json"
|
|
}
|
|
address_url = f"{SHOPWARE_URL}/api/order/{order_id}/billing-address/"
|
|
response = requests.get(address_url, headers=headers)
|
|
response.raise_for_status()
|
|
return response.json()["data"][0]
|
|
|
|
def get_access_token():
|
|
token_url = f"{SHOPWARE_URL}/api/oauth/token"
|
|
data = {
|
|
"client_id": CLIENT_ID,
|
|
"client_secret": CLIENT_SECRET,
|
|
"grant_type": "client_credentials"
|
|
}
|
|
response = requests.post(token_url, data=data)
|
|
response.raise_for_status()
|
|
return response.json()["access_token"]
|
|
|
|
def get_paid_orders(access_token):
|
|
headers = {
|
|
"sw-access-key": CLIENT_ID,
|
|
"authorization": f"Bearer {access_token}",
|
|
"Content-Type": "application/json"
|
|
}
|
|
orders_url = f"{SHOPWARE_URL}/api/search/order"
|
|
query = {
|
|
"limit": 100,
|
|
"page": 1,
|
|
"filter": [{
|
|
"type": "equals",
|
|
"field": "stateMachineState.technicalName",
|
|
"value": STATUS_PAID
|
|
}]
|
|
}
|
|
response = requests.post(orders_url, headers=headers, json=query)
|
|
response.raise_for_status()
|
|
return response.json()["data"]
|
|
|
|
def get_order_details(access_token, order_id):
|
|
headers = {
|
|
"sw-access-key": CLIENT_ID,
|
|
"Authorization": f"Bearer {access_token}",
|
|
"Content-Type": "application/json"
|
|
}
|
|
items_url = f"{SHOPWARE_URL}/api/order/{order_id}/line-items"
|
|
address_url = f"{SHOPWARE_URL}/api/order/{order_id}/billing-address/"
|
|
|
|
response_items = requests.get(items_url, headers=headers)
|
|
response_address = requests.get(address_url, headers=headers)
|
|
|
|
response_items.raise_for_status()
|
|
response_address.raise_for_status()
|
|
|
|
return response_items.json()["data"], response_address.json()["data"][0]
|
|
|
|
def show_order_details(event):
|
|
global current_order_id
|
|
|
|
selected_item = tree_orders.focus()
|
|
if not selected_item:
|
|
return
|
|
|
|
values = tree_orders.item(selected_item, "values")
|
|
order_number = values[0]
|
|
customer_name = values[1]
|
|
|
|
access_token = get_access_token()
|
|
orders = get_paid_orders(access_token)
|
|
selected_order = next((o for o in orders if o['attributes']['orderNumber'] == order_number), None)
|
|
|
|
if selected_order:
|
|
order_id = selected_order['id']
|
|
current_order_id = order_id
|
|
|
|
product_line, billing_address = get_order_details(access_token, order_id)
|
|
|
|
frame_orders.pack_forget()
|
|
tree_details.delete(*tree_details.get_children())
|
|
|
|
for product in product_line:
|
|
label = product['attributes'].get('label', 'Unbekanntes Produkt')
|
|
quantity = product['attributes']['quantity']
|
|
price = product['attributes']['totalPrice']
|
|
tree_details.insert("", "end", values=(label, quantity, f"{price:.2f} €"))
|
|
|
|
label_details.configure(text=f"Bestellung: {order_number} - {customer_name}")
|
|
frame_details.pack(fill="both", expand=True)
|
|
|
|
def back_to_orders():
|
|
frame_details.pack_forget()
|
|
frame_orders.pack(fill="both", expand=True)
|
|
|
|
def print_single_order():
|
|
if not current_order_id:
|
|
messagebox.showerror("Fehler", "Keine Bestellung ausgewählt!")
|
|
return
|
|
|
|
access_token = get_access_token()
|
|
orders = get_paid_orders(access_token)
|
|
selected_order = next((o for o in orders if o['id'] == current_order_id), None)
|
|
|
|
if selected_order:
|
|
filename = generate_combined_pdf([selected_order], access_token)
|
|
messagebox.showinfo("Erfolg", f"Lieferschein {filename} wurde erstellt!")
|
|
else:
|
|
messagebox.showerror("Fehler", "Bestellung nicht gefunden!")
|
|
|
|
def generate_combined_pdf(orders, access_token):
|
|
current_date = datetime.datetime.today().strftime("%d.%m.%Y")
|
|
filename = f"Lieferschein_{current_date}.pdf"
|
|
|
|
pdf = canvas.Canvas(filename, pagesize=A4)
|
|
pdf.setTitle(f"Lieferschein {current_date}")
|
|
pdf.setFont("Helvetica-Bold", 16)
|
|
pdf.drawString(50, 800, "Sammel-Lieferschein")
|
|
|
|
pdf.setFont("Helvetica", 12)
|
|
pdf.drawString(50, 780, f"Erstellt am: {current_date}")
|
|
|
|
y_position = 750
|
|
for order in orders:
|
|
try:
|
|
customer_id = order["relationships"]["orderCustomer"]["data"]["id"]
|
|
customer = get_order_customer(access_token, customer_id)
|
|
billing_address = get_order_billing_address(access_token, order["id"])
|
|
product_line = get_order_line_items(access_token, order["id"])
|
|
|
|
# Bestell-Header
|
|
pdf.setFont("Helvetica-Bold", 12)
|
|
pdf.drawString(50, y_position, f"Bestellnummer: {order['attributes']['orderNumber']}")
|
|
|
|
date_obj = datetime.datetime.strptime(order['attributes']['orderDate'], "%Y-%m-%dT%H:%M:%S.%f%z")
|
|
date_str = date_obj.strftime("%d.%m.%Y")
|
|
pdf.setFont("Helvetica", 10)
|
|
pdf.drawString(50, y_position - 20, f"Bestelldatum: {date_str}")
|
|
|
|
# Rahmen um Lieferadresse
|
|
pdf.setStrokeColorRGB(0, 0, 0) # Schwarz
|
|
#pdf.rect(45, y_position - 110, 300, 80, stroke=1, fill=0) # Rechteck um Adresse
|
|
|
|
# Lieferadresse
|
|
pdf.setFont("Helvetica-Bold", 10)
|
|
pdf.drawString(50, y_position - 100, "Lieferadresse:")
|
|
pdf.setFont("Helvetica", 10)
|
|
pdf.drawString(50, y_position - 120, f"{customer['attributes']['firstName']} {customer['attributes']['lastName']}")
|
|
pdf.drawString(50, y_position - 140, f"{billing_address['attributes']['street']}")
|
|
pdf.drawString(50, y_position - 160, f"{billing_address['attributes']['zipcode']} {billing_address['attributes']['city']}")
|
|
|
|
y_position -= 180 # Platz nach Adresse
|
|
|
|
# Produktliste
|
|
for product in product_line:
|
|
pdf.rect(50, y_position - 2, 10, 10, stroke=1, fill=0) # Checkbox für Abhaken
|
|
label = product['attributes'].get('label', 'Unbekanntes Produkt')
|
|
product_number = product['attributes']['payload'].get('productNumber', 'Keine Nummer')
|
|
quantity = product['attributes']['quantity']
|
|
price = product['attributes']['totalPrice']
|
|
|
|
pdf.drawString(70, y_position, f"{label}")
|
|
pdf.drawString(250, y_position, f"{product_number}")
|
|
pdf.drawString(350, y_position, f"{quantity}")
|
|
pdf.drawString(400, y_position, f"{price:.2f} €")
|
|
|
|
y_position -= 20
|
|
|
|
# Falls die Seite voll ist, neue Seite beginnen
|
|
if y_position < 100:
|
|
pdf.showPage()
|
|
y_position = 800
|
|
|
|
y_position -= 40 # Platz nach jeder Bestellung
|
|
|
|
except Exception as e:
|
|
print(f"Fehler beim Erstellen des Lieferscheins für Bestellung {order['attributes']['orderNumber']}: {str(e)}")
|
|
|
|
pdf.save()
|
|
print(f"Lieferschein gespeichert: {filename}")
|
|
|
|
return filename
|
|
|
|
def print_all_orders():
|
|
access_token = get_access_token()
|
|
orders = get_paid_orders(access_token)
|
|
|
|
if not orders:
|
|
messagebox.showinfo("Info", "Keine Bestellungen gefunden.")
|
|
return
|
|
|
|
filename = generate_combined_pdf(orders, access_token)
|
|
messagebox.showinfo("Erfolg", f"Gesammelter Lieferschein {filename} wurde erstellt!")
|
|
|
|
def update_table():
|
|
access_token = get_access_token()
|
|
orders = get_paid_orders(access_token)
|
|
# Tabelle leeren
|
|
for row in tree_orders.get_children():
|
|
tree_orders.delete(row)
|
|
|
|
for order in orders:
|
|
order_number = order['attributes']['orderNumber']
|
|
_, billing_address = get_order_details(access_token, order['id'])
|
|
|
|
tree_orders.insert("", "end", values=(
|
|
order_number,
|
|
f"{billing_address['attributes']['firstName']} {billing_address['attributes']['lastName']}",
|
|
billing_address['attributes']['street'],
|
|
f"{billing_address['attributes']['zipcode']} {billing_address['attributes']['city']}"
|
|
))
|
|
|
|
|
|
def open_link(event=None):
|
|
webbrowser.open("https://itdata-gera.de")
|
|
|
|
def execute_with_error_handling(func, *args, **kwargs):
|
|
try:
|
|
func(*args, **kwargs) # Die gewünschte Funktion ausführen
|
|
except Exception as e:
|
|
root.after(0, show_error_message, str(e)) # Fehler in GUI anzeigen
|
|
|
|
# GUI-Fehlermeldung anzeigen
|
|
def show_error_message(error_text):
|
|
tkinter.messagebox.showerror("Fehler", f"Ein Fehler ist aufgetreten:\n{error_text}")
|
|
|
|
# GUI-Erstellung
|
|
root = ctk.CTk()
|
|
root.title("Lieferschein-Drucker")
|
|
root.resizable(False, False)
|
|
|
|
window_width = 900
|
|
window_height = 500
|
|
|
|
# Bildschirmgröße abrufen
|
|
screen_width = root.winfo_screenwidth()
|
|
screen_height = root.winfo_screenheight()
|
|
|
|
# Position berechnen (Mitte des Bildschirms)
|
|
x_position = (screen_width // 2) - (window_width // 2)
|
|
y_position = (screen_height // 2) - (window_height // 2)
|
|
|
|
# Fenstergröße & Position setzen
|
|
root.geometry(f"{window_width}x{window_height}+{x_position}+{y_position}")
|
|
|
|
# **Bestellübersicht**
|
|
frame_orders = ctk.CTkFrame(root)
|
|
button_all_print = ctk.CTkButton(frame_orders, text="API ändern", command=lambda: execute_with_error_handling(check_api.rerun),fg_color="#2b2b2b",text_color="white",anchor=tkinter.RIGHT)
|
|
button_all_print.pack(pady=5,anchor="ne")
|
|
|
|
label_orders = ctk.CTkLabel(frame_orders, text=f"Bezahlte Bestellungen - {datetime.datetime.now().strftime("%d.%m.%Y")}", font=("Helvetica", 15))
|
|
label_orders.pack(pady=5)
|
|
|
|
columns_orders = ("Bestellnummer", "Kunde", "Straße", "PLZ/Stadt")
|
|
tree_orders = ttk.Treeview(frame_orders, columns=columns_orders, show="headings", cursor="hand2")
|
|
for col in columns_orders:
|
|
tree_orders.heading(col, text=col)
|
|
tree_orders.column(col, width=150)
|
|
tree_orders.pack(expand=True, fill="both")
|
|
tree_orders.bind("<<TreeviewSelect>>", show_order_details)
|
|
|
|
button_update = ctk.CTkButton(frame_orders, text="Bestellungen laden", command=lambda: execute_with_error_handling(update_table), fg_color="lightblue",text_color="black",anchor=tkinter.RIGHT)
|
|
button_update.pack(side="left",pady=5,padx=10)
|
|
|
|
logo = ctk.CTkImage(dark_image=Image.open('logo.png'),size=(130,30))
|
|
logo_label = ctk.CTkLabel(root, text="", image=logo, fg_color="#2b2b2b",cursor="hand2")
|
|
logo_label.bind("<Button-1>", open_link)
|
|
logo_label.place(relx=0.5, rely=1.0, anchor="s",y=-15)
|
|
|
|
button_all_print = ctk.CTkButton(frame_orders, text="Gesammelten Lieferschein drucken", command=lambda: execute_with_error_handling(print_all_orders),fg_color="orange",text_color="black")
|
|
button_all_print.pack(side="right",pady=15, padx=10)
|
|
|
|
frame_orders.pack(fill="both", expand=True)
|
|
|
|
# **Detailansicht**
|
|
frame_details = ctk.CTkFrame(root)
|
|
label_details = ctk.CTkLabel(frame_details, text="", font=("Helvetica", 12))
|
|
label_details.pack(pady=5)
|
|
|
|
columns_details = ("Produkt", "Anzahl", "Preis")
|
|
tree_details = ttk.Treeview(frame_details, columns=columns_details, show="headings")
|
|
for col in columns_details:
|
|
tree_details.heading(col, text=col)
|
|
tree_details.pack(expand=True, fill="both")
|
|
|
|
button_back = ctk.CTkButton(frame_details, text="Zurück", command=back_to_orders)
|
|
button_back.pack(side="left", padx=20, pady=15)
|
|
|
|
logo_label_1 = ctk.CTkLabel(root, text="", image=logo, fg_color="#2b2b2b",cursor="hand2")
|
|
logo_label_1.bind("<Button-1>", open_link)
|
|
logo_label_1.place(relx=0.5, rely=1.0, anchor="s",y=-15)
|
|
|
|
button_print = ctk.CTkButton(frame_details, text="Diese Bestellung drucken", command=print_single_order, fg_color="lightgreen", text_color="black")
|
|
button_print.pack(side="right", padx=20,pady=15)
|
|
|
|
# Error Handling für Timeout
|
|
root.after(100, lambda: execute_with_error_handling(update_table))
|
|
root.mainloop()
|