Compare commits
1
Commits
057cca6378
...
v1.5.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2600eea574 |
No files matched your search
+52
-5
@@ -195,8 +195,23 @@ class App:
|
|||||||
bd=0, opaqueresize=False, sashcursor="sb_h_double_arrow")
|
bd=0, opaqueresize=False, sashcursor="sb_h_double_arrow")
|
||||||
pw.pack(fill="both", expand=True, padx=6, pady=6)
|
pw.pack(fill="both", expand=True, padx=6, pady=6)
|
||||||
|
|
||||||
|
links = tk.Frame(pw, bg=w.FACE)
|
||||||
|
# --- Abrechnungen (ein Klick öffnet)
|
||||||
|
self.f_a = w.Fenster(links, "Abrechnungen", self.icons("oeffnen"))
|
||||||
|
self.f_a.pack(fill="x", pady=(0, 6))
|
||||||
|
r, self.atree = w.scroll_tree(self.f_a.inhalt, columns=("status",), show="tree headings", height=5,
|
||||||
|
selectmode="browse")
|
||||||
|
self.atree.heading("#0", text="Abrechnung", anchor="w")
|
||||||
|
self.atree.heading("status", text="Status")
|
||||||
|
self.atree.column("#0", width=170, minwidth=80, stretch=False)
|
||||||
|
self.atree.column("status", width=90, anchor="w", stretch=False)
|
||||||
|
w.auto_breite(self.atree, {"#0": 1})
|
||||||
|
r.pack(fill="both", expand=True)
|
||||||
|
self.atree.bind("<<TreeviewSelect>>", self._abrechnung_gewaehlt)
|
||||||
|
|
||||||
# --- Händler-Fenster
|
# --- Händler-Fenster
|
||||||
self.f_h = w.Fenster(pw, "Händler", self.icons("haendler"))
|
self.f_h = w.Fenster(links, "Händler", self.icons("haendler"))
|
||||||
|
self.f_h.pack(fill="both", expand=True)
|
||||||
r, self.htree = w.scroll_tree(self.f_h.inhalt, columns=("anzahl", "summe"), show="tree headings",
|
r, self.htree = w.scroll_tree(self.f_h.inhalt, columns=("anzahl", "summe"), show="tree headings",
|
||||||
selectmode="browse")
|
selectmode="browse")
|
||||||
self.htree.heading("#0", text="Händler", anchor="w")
|
self.htree.heading("#0", text="Händler", anchor="w")
|
||||||
@@ -211,7 +226,7 @@ class App:
|
|||||||
r.pack(fill="both", expand=True)
|
r.pack(fill="both", expand=True)
|
||||||
self.htree.bind("<<TreeviewSelect>>", self._haendler_gewaehlt)
|
self.htree.bind("<<TreeviewSelect>>", self._haendler_gewaehlt)
|
||||||
self.htree.bind("<Double-1>", lambda e: self._haendler_doppelklick())
|
self.htree.bind("<Double-1>", lambda e: self._haendler_doppelklick())
|
||||||
pw.add(self.f_h, minsize=240, width=310)
|
pw.add(links, minsize=240, width=310)
|
||||||
|
|
||||||
# --- Positionen-Fenster
|
# --- Positionen-Fenster
|
||||||
self.f_p = w.Fenster(pw, "Positionen", self.icons("neu"))
|
self.f_p = w.Fenster(pw, "Positionen", self.icons("neu"))
|
||||||
@@ -284,9 +299,26 @@ class App:
|
|||||||
self.positionen = []
|
self.positionen = []
|
||||||
self.rahmen.set_titel(w.APP_NAME)
|
self.rahmen.set_titel(w.APP_NAME)
|
||||||
self.status.set("info", " Keine Abrechnung geöffnet – Datei > Neue Abrechnung oder Import")
|
self.status.set("info", " Keine Abrechnung geöffnet – Datei > Neue Abrechnung oder Import")
|
||||||
|
self._abrechnungsliste()
|
||||||
self._haendlerliste(behalte_auswahl)
|
self._haendlerliste(behalte_auswahl)
|
||||||
self._tabelle()
|
self._tabelle()
|
||||||
|
|
||||||
|
def _abrechnungsliste(self):
|
||||||
|
t = self.atree
|
||||||
|
t.delete(*t.get_children())
|
||||||
|
for a in self.db.abrechnungen():
|
||||||
|
status = "erledigt" if a["erledigt"] else (f"{a['abgerechnet']} abgerechnet" if a["abgerechnet"] else "offen")
|
||||||
|
t.insert("", "end", iid=str(a["id"]), text=a["titel"], values=(status,))
|
||||||
|
if self.aid and t.exists(str(self.aid)):
|
||||||
|
t.selection_set(str(self.aid))
|
||||||
|
t.see(str(self.aid))
|
||||||
|
|
||||||
|
def _abrechnung_gewaehlt(self, _e=None):
|
||||||
|
sel = self.atree.selection()
|
||||||
|
if sel and int(sel[0]) != self.aid:
|
||||||
|
self.ansicht = ALLE
|
||||||
|
self.oeffnen(int(sel[0]))
|
||||||
|
|
||||||
def _suche_planen(self):
|
def _suche_planen(self):
|
||||||
"""Suche erst kurz nach dem letzten Tastendruck ausführen (flüssiges Tippen)."""
|
"""Suche erst kurz nach dem letzten Tastendruck ausführen (flüssiges Tippen)."""
|
||||||
if self._suche_geplant is not None:
|
if self._suche_geplant is not None:
|
||||||
@@ -466,7 +498,7 @@ class App:
|
|||||||
else:
|
else:
|
||||||
self._aktualisieren()
|
self._aktualisieren()
|
||||||
|
|
||||||
def importieren(self, dateien: list[str] | None = None):
|
def importieren(self, dateien: list[str] | None = None, ziel: int | None = None):
|
||||||
if not dateien:
|
if not dateien:
|
||||||
dateien = list(filedialog.askopenfilenames(
|
dateien = list(filedialog.askopenfilenames(
|
||||||
parent=self.root, title="Provisionsabrechnungen importieren",
|
parent=self.root, title="Provisionsabrechnungen importieren",
|
||||||
@@ -477,7 +509,22 @@ class App:
|
|||||||
if not dateien:
|
if not dateien:
|
||||||
return
|
return
|
||||||
self.settings["import_ordner"] = str(Path(dateien[0]).parent)
|
self.settings["import_ordner"] = str(Path(dateien[0]).parent)
|
||||||
if not self._brauche_abrechnung() or self._gesperrt():
|
if ziel is None: # jedes Mal fragen, in welche Abrechnung
|
||||||
|
if not self.db.abrechnungen():
|
||||||
|
if not self.neu():
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
res = dlg.AbrechnungenDialog(self.root, self.db, self.aid, fuer_import=True).zeigen()
|
||||||
|
if not res:
|
||||||
|
return
|
||||||
|
if res[0] == "neu":
|
||||||
|
if not self.neu():
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
self.aid, self.ansicht = res[1], ALLE
|
||||||
|
else:
|
||||||
|
self.aid = ziel
|
||||||
|
if self._gesperrt():
|
||||||
return
|
return
|
||||||
bericht = []
|
bericht = []
|
||||||
self.root.configure(cursor="watch")
|
self.root.configure(cursor="watch")
|
||||||
@@ -1018,7 +1065,7 @@ class App:
|
|||||||
self.ansicht = ALLE
|
self.ansicht = ALLE
|
||||||
self._aktualisieren()
|
self._aktualisieren()
|
||||||
if res["dateien"]:
|
if res["dateien"]:
|
||||||
self.importieren(res["dateien"])
|
self.importieren(res["dateien"], ziel=self.aid)
|
||||||
if res["rundgang"]:
|
if res["rundgang"]:
|
||||||
self.root.after(200, self.rundgang)
|
self.root.after(200, self.rundgang)
|
||||||
|
|
||||||
|
|||||||
+14
-3
@@ -558,10 +558,13 @@ class ProvisionsartenDialog(w.Dialog):
|
|||||||
# =========================================================================== Abrechnungen
|
# =========================================================================== Abrechnungen
|
||||||
|
|
||||||
class AbrechnungenDialog(w.Dialog):
|
class AbrechnungenDialog(w.Dialog):
|
||||||
def __init__(self, master, db: Datenbank, aktuelle_id: int | None):
|
def __init__(self, master, db: Datenbank, aktuelle_id: int | None, fuer_import: bool = False):
|
||||||
super().__init__(master, "Abrechnung öffnen", resizable=True)
|
"""fuer_import: Ziel eines Imports wählen (Ergebnis ("oeffnen", id) oder ("neu",))."""
|
||||||
|
super().__init__(master, "Import – in welche Abrechnung?" if fuer_import else "Abrechnung öffnen",
|
||||||
|
resizable=True)
|
||||||
self.db = db
|
self.db = db
|
||||||
tk.Label(self, text="Gespeicherte Abrechnungen:", anchor="w").pack(fill="x", padx=10, pady=(10, 2))
|
tk.Label(self, text="In welche Abrechnung sollen die Dateien importiert werden?" if fuer_import
|
||||||
|
else "Gespeicherte Abrechnungen:", anchor="w").pack(fill="x", padx=10, pady=(10, 2))
|
||||||
r, self.tree = w.scroll_tree(self, columns=("titel", "status", "geaendert", "anzahl", "dateien"),
|
r, self.tree = w.scroll_tree(self, columns=("titel", "status", "geaendert", "anzahl", "dateien"),
|
||||||
show="headings", height=12, selectmode="browse")
|
show="headings", height=12, selectmode="browse")
|
||||||
for c, t, b in (("titel", "Bezeichnung", 180), ("status", "Status", 110), ("geaendert", "Zuletzt geändert", 120),
|
for c, t, b in (("titel", "Bezeichnung", 180), ("status", "Status", 110), ("geaendert", "Zuletzt geändert", 120),
|
||||||
@@ -572,6 +575,10 @@ class AbrechnungenDialog(w.Dialog):
|
|||||||
self.tree.bind("<Double-1>", lambda e: self._oeffnen())
|
self.tree.bind("<Double-1>", lambda e: self._oeffnen())
|
||||||
k = tk.Frame(self, bg=w.FACE)
|
k = tk.Frame(self, bg=w.FACE)
|
||||||
k.pack(fill="x", padx=10, pady=10)
|
k.pack(fill="x", padx=10, pady=10)
|
||||||
|
if fuer_import:
|
||||||
|
w.knopf(k, "Hierhin importieren", self._oeffnen, default="active").pack(side="left")
|
||||||
|
w.knopf(k, "Neue Abrechnung...", self._neu).pack(side="left", padx=6)
|
||||||
|
else:
|
||||||
w.knopf(k, "Öffnen", self._oeffnen, breite=10, default="active").pack(side="left")
|
w.knopf(k, "Öffnen", self._oeffnen, breite=10, default="active").pack(side="left")
|
||||||
w.knopf(k, "Umbenennen...", self._umbenennen, breite=12).pack(side="left", padx=6)
|
w.knopf(k, "Umbenennen...", self._umbenennen, breite=12).pack(side="left", padx=6)
|
||||||
w.knopf(k, "Löschen", self._loeschen, breite=10).pack(side="left")
|
w.knopf(k, "Löschen", self._loeschen, breite=10).pack(side="left")
|
||||||
@@ -600,6 +607,10 @@ class AbrechnungenDialog(w.Dialog):
|
|||||||
self.result = ("oeffnen", self._sel())
|
self.result = ("oeffnen", self._sel())
|
||||||
self.destroy()
|
self.destroy()
|
||||||
|
|
||||||
|
def _neu(self):
|
||||||
|
self.result = ("neu",)
|
||||||
|
self.destroy()
|
||||||
|
|
||||||
def _umbenennen(self):
|
def _umbenennen(self):
|
||||||
aid = self._sel()
|
aid = self._sel()
|
||||||
if not aid:
|
if not aid:
|
||||||
|
|||||||
@@ -1129,3 +1129,35 @@ def _alle(widget):
|
|||||||
for kind in widget.winfo_children():
|
for kind in widget.winfo_children():
|
||||||
yield kind
|
yield kind
|
||||||
yield from _alle(kind)
|
yield from _alle(kind)
|
||||||
|
|
||||||
|
|
||||||
|
def test_abrechnungen_links_und_import_fragt_nach_ziel(lauf_modern, home, monkeypatch):
|
||||||
|
from provtool.gui import win95 as w
|
||||||
|
from provtool.gui.dialogs import AbrechnungenDialog, ImportBericht
|
||||||
|
monkeypatch.setattr(w, "frage", lambda *a, **k: True) # „bereits importiert – trotzdem?“ (Testdatei ist schon drin)
|
||||||
|
lauf = lauf_modern
|
||||||
|
app = lauf.app
|
||||||
|
alt = app.aid
|
||||||
|
neu = app.db.abrechnung_neu("November 2026")
|
||||||
|
app._aktualisieren()
|
||||||
|
assert set(app.atree.get_children()) == {str(alt), str(neu)}, "Abrechnungen fehlen links"
|
||||||
|
app.atree.selection_set(str(neu))
|
||||||
|
lauf.root.update()
|
||||||
|
assert app.aid == neu and not app.positionen, "Klick links öffnet die Abrechnung nicht"
|
||||||
|
app.atree.selection_set(str(alt))
|
||||||
|
lauf.root.update()
|
||||||
|
|
||||||
|
def ziel_waehlen():
|
||||||
|
d = lauf.dialog(AbrechnungenDialog)
|
||||||
|
d.tree.selection_set(str(neu))
|
||||||
|
d._oeffnen()
|
||||||
|
|
||||||
|
lauf.ablauf([
|
||||||
|
(IMMER, lambda: app.importieren([str(home / "freenet.csv")]), "Import"),
|
||||||
|
(lambda: lauf.dialog(AbrechnungenDialog) is not None, ziel_waehlen, "Ziel: November"),
|
||||||
|
(lambda: lauf.dialog(ImportBericht) is not None, lambda: lauf.dialog(ImportBericht).abbrechen(), "Bericht zu"),
|
||||||
|
])
|
||||||
|
assert not lauf.uebrig, lauf.diagnose()
|
||||||
|
assert app.aid == neu and len(app.positionen) == 3, "Import nicht in der gewählten Abrechnung"
|
||||||
|
assert app.atree.selection() == (str(neu),)
|
||||||
|
lauf.pruefen()
|
||||||
Reference in new issue
Block a user