926 lines
31 KiB
Plaintext
926 lines
31 KiB
Plaintext
|
||
from kivy.app import App
|
||
from kivy.uix.screenmanager import Screen
|
||
from kivy.uix.boxlayout import BoxLayout
|
||
from kivy.uix.floatlayout import FloatLayout
|
||
from kivy.uix.scrollview import ScrollView
|
||
from kivy.uix.button import Button
|
||
from kivy.metrics import dp
|
||
from kivy.core.window import Window
|
||
from kivy.effects.scroll import ScrollEffect
|
||
from kivy.uix.modalview import ModalView
|
||
from kivy.uix.gridlayout import GridLayout
|
||
from kivy.uix.textinput import TextInput
|
||
from kivy.uix.label import Label
|
||
from kivy.uix.button import Button
|
||
from kivy.metrics import dp
|
||
from kivy.clock import Clock
|
||
|
||
|
||
# IMPORT TVÝCH DAT
|
||
from data import Cenik, CenPol, CenPolCreate, Cena, Position
|
||
|
||
|
||
# =========================================================
|
||
# DUMMY CENÍK
|
||
# =========================================================
|
||
|
||
def create_dummy_cenik():
|
||
return Cenik(cenpol=[
|
||
CenPol(
|
||
id=1,
|
||
id_card=100,
|
||
d_name="Pivo",
|
||
ch_name="Pivo",
|
||
pokl="A",
|
||
sklad="A",
|
||
ceny=[Cena(cena=50, mena="CZK", dan="21", name="standard",
|
||
cena2=25, cena3=17, cena4=12)],
|
||
pos_pc=[Position(page=1, line=0, col=0)],
|
||
),
|
||
CenPol(
|
||
id=2,
|
||
id_card=101,
|
||
d_name="Káva",
|
||
ch_name="Káva",
|
||
pokl="A",
|
||
sklad="A",
|
||
ceny=[Cena(cena=60, mena="CZK", dan="21", name="standard",
|
||
cena2=30, cena3=20, cena4=15)],
|
||
pos_pc=[Position(page=1, line=1, col=0)],
|
||
),
|
||
])
|
||
|
||
# =========================================================
|
||
# EDITOR SCREEN
|
||
# =========================================================
|
||
class WidthDialog(ModalView):
|
||
def __init__(self, on_ok, default=3, **kwargs):
|
||
super().__init__(**kwargs)
|
||
self.on_ok = on_ok
|
||
self.size_hint = (None, None)
|
||
self.size = (dp(300), dp(200))
|
||
self.auto_dismiss = False
|
||
root = BoxLayout(orientation="vertical", spacing=dp(10), padding=dp(10))
|
||
self.input = TextInput(
|
||
text=str(default),
|
||
multiline=False,
|
||
input_filter="int"
|
||
)
|
||
btn_ok = Button(text="OK")
|
||
btn_cancel = Button(text="Zrušit")
|
||
btn_ok.bind(on_press=self._ok)
|
||
btn_cancel.bind(on_press=lambda *_: self.dismiss())
|
||
root.add_widget(self.input)
|
||
root.add_widget(btn_ok)
|
||
root.add_widget(btn_cancel)
|
||
self.add_widget(root)
|
||
def _ok(self, *_):
|
||
try:
|
||
val = int(self.input.text)
|
||
if val < 1:
|
||
val = 1
|
||
except:
|
||
val = 3
|
||
if self.on_ok:
|
||
self.on_ok(val)
|
||
self.dismiss()
|
||
|
||
class CenikItemEditor(ModalView):
|
||
def __init__(self, pol, on_save, **kwargs):
|
||
super().__init__(**kwargs)
|
||
self.pol = pol
|
||
self.on_save_cb = on_save
|
||
self.size_hint = (None, None)
|
||
self.size = (dp(500), dp(600))
|
||
self.auto_dismiss = False
|
||
root = GridLayout(
|
||
cols=2,
|
||
spacing=dp(6),
|
||
padding=dp(10),
|
||
size_hint=(1, 1)
|
||
)
|
||
# ---------------- NÁZEV ----------------
|
||
root.add_widget(Label(text="Display name"))
|
||
self.d_name = TextInput(text=pol.d_name or "")
|
||
root.add_widget(self.d_name)
|
||
root.add_widget(Label(text="Účet name"))
|
||
self.ch_name = TextInput(text=pol.ch_name or "")
|
||
root.add_widget(self.ch_name)
|
||
# ---------------- CENA ----------------
|
||
cena = pol.ceny[0] if pol.ceny else None
|
||
root.add_widget(Label(text="Cena"))
|
||
self.cena = TextInput(text=str(cena.cena if cena else "0"))
|
||
root.add_widget(self.cena)
|
||
root.add_widget(Label(text="Měna"))
|
||
self.mena = TextInput(text=cena.mena if cena else "CZK")
|
||
root.add_widget(self.mena)
|
||
root.add_widget(Label(text="DPH"))
|
||
self.dan = TextInput(text=cena.dan if cena else "21")
|
||
root.add_widget(self.dan)
|
||
root.add_widget(Label(text="Cenová hladina"))
|
||
self.price_name = TextInput(text=cena.name if cena else "standard")
|
||
root.add_widget(self.price_name)
|
||
# ---------------- FRAKCE ----------------
|
||
root.add_widget(Label(text="1/2"))
|
||
self.cena2 = TextInput(text=str(cena.cena2 if cena else "0"))
|
||
root.add_widget(self.cena2)
|
||
root.add_widget(Label(text="1/3"))
|
||
self.cena3 = TextInput(text=str(cena.cena3 if cena else "0"))
|
||
root.add_widget(self.cena3)
|
||
root.add_widget(Label(text="1/4"))
|
||
self.cena4 = TextInput(text=str(cena.cena4 if cena else "0"))
|
||
root.add_widget(self.cena4)
|
||
# ---------------- BUTTONY ----------------
|
||
btn_save = Button(text="Uložit")
|
||
btn_cancel = Button(text="Zrušit")
|
||
btn_save.bind(on_press=self._save)
|
||
btn_cancel.bind(on_press=lambda *_: self.dismiss())
|
||
root.add_widget(btn_save)
|
||
root.add_widget(btn_cancel)
|
||
self.add_widget(root)
|
||
|
||
def _save(self, *_):
|
||
try:
|
||
cena = Cena(
|
||
cena=float(self.cena.text),
|
||
mena=self.mena.text,
|
||
dan=self.dan.text,
|
||
name=self.price_name.text,
|
||
cena2=float(self.cena2.text),
|
||
cena3=float(self.cena3.text),
|
||
cena4=float(self.cena4.text),
|
||
)
|
||
self.pol.d_name = self.d_name.text
|
||
self.pol.ch_name = self.ch_name.text
|
||
self.pol.ceny = [cena]
|
||
if self.on_save_cb:
|
||
self.on_save_cb(self.pol)
|
||
self.dismiss()
|
||
except Exception as e:
|
||
print("chyba při ukládání:", e)
|
||
class CenikButton(Button):
|
||
def __init__(self, pol=None, on_select=None, on_long_press=None, **kwargs):
|
||
price_updater = kwargs.pop("price_updater", None)
|
||
super().__init__(**kwargs)
|
||
|
||
self.pol = pol
|
||
self.on_select_cb = on_select
|
||
self.on_long_press_cb = on_long_press
|
||
self.price_updater = price_updater
|
||
|
||
self._lp_event = None
|
||
self._long_pressed = False
|
||
|
||
Clock.schedule_once(lambda *_: self.refresh(), 0)
|
||
|
||
def refresh(self):
|
||
if self.pol and self.price_updater:
|
||
self.price_updater(self)
|
||
|
||
# 🔥 KLÍČOVÁ OPRAVA
|
||
def on_touch_down(self, touch):
|
||
if not self.collide_point(*touch.pos):
|
||
return False
|
||
|
||
self._long_pressed = False
|
||
|
||
self._lp_event = Clock.schedule_once(self._trigger_long_press, 0.5)
|
||
|
||
return True # ❗ NEVOLAT super()
|
||
|
||
def on_touch_up(self, touch):
|
||
if not self.collide_point(*touch.pos):
|
||
return False
|
||
|
||
if self._lp_event:
|
||
self._lp_event.cancel()
|
||
self._lp_event = None
|
||
|
||
if not self._long_pressed:
|
||
if self.on_select_cb and self.pol:
|
||
self.on_select_cb(self.pol)
|
||
|
||
return True # ❗ NEVOLAT super()
|
||
|
||
def _trigger_long_press(self, *_):
|
||
self._long_pressed = True
|
||
|
||
if self.on_long_press_cb and self.pol:
|
||
self.on_long_press_cb(self.pol)
|
||
|
||
class CenikEditor(Screen):
|
||
def __init__(self, cenik=None, menu_cols=10, menu_rows=8, **kwargs):
|
||
cenik = kwargs.pop("cenik", cenik)
|
||
super().__init__(**kwargs)
|
||
# ================= DATA =================
|
||
self.cenik = cenik
|
||
self.cols = menu_cols
|
||
self.rows = menu_rows
|
||
# ================= STAV =================
|
||
self.current_page = 1
|
||
levels = self.cenik.used_price_levels() if self.cenik else []
|
||
self.price_level = levels[0] if levels else "standard"
|
||
self._selected_pol = None
|
||
self._move_mode = False
|
||
self.cell_w = dp(80)
|
||
self.cell_h = dp(80)
|
||
# ================= ROOT =================
|
||
root = BoxLayout(
|
||
orientation="vertical",
|
||
spacing=dp(6),
|
||
padding=dp(6),
|
||
size_hint=(1, 1),
|
||
)
|
||
# ================= SCROLL + GRID =================
|
||
self.scroll = ScrollView(
|
||
do_scroll_x=True,
|
||
do_scroll_y=True,
|
||
size_hint=(1, 1),
|
||
bar_width=dp(6),
|
||
effect_cls=ScrollEffect,
|
||
)
|
||
self.grid = FloatLayout(size_hint=(None, None))
|
||
self.grid.size = (
|
||
self.cols * self.cell_w,
|
||
self.rows * self.cell_h
|
||
)
|
||
self.scroll.add_widget(self.grid)
|
||
root.add_widget(self.scroll)
|
||
# ================= SPODNÍ PANEL =================
|
||
bottom = BoxLayout(
|
||
size_hint=(1, None),
|
||
height=dp(90),
|
||
spacing=dp(6),
|
||
padding=dp(6)
|
||
)
|
||
# --- tlačítka ---
|
||
self.btn_new = Button(text="Nový\nbutton")
|
||
self.btn_page = Button(text="Nová\nstránka")
|
||
self.btn_switch = Button(text=f"Stránka\n{self.current_page}")
|
||
self.btn_save = Button(text="Uložit")
|
||
self.btn_delete = Button(text="Smazat")
|
||
self.btn_move = Button(text="Pozice")
|
||
btn_levels = Button(text="Cenové\nhladiny")
|
||
btn_price = Button(text=f"Cena\n{self.price_level}")
|
||
# --- bindy ---
|
||
self.btn_new.bind(on_press=self._new_button)
|
||
self.btn_page.bind(on_press=self._new_page)
|
||
self.btn_switch.bind(on_press=self._switch_page)
|
||
self.btn_save.bind(on_press=self._save)
|
||
self.btn_delete.bind(on_press=self._delete_item)
|
||
self.btn_move.bind(on_press=self._start_move)
|
||
btn_levels.bind(on_press=self._edit_price_levels)
|
||
btn_price.bind(on_press=self.on_select_price_level)
|
||
# --- ulož reference ---
|
||
self.btn_price = btn_price
|
||
self._btn_move_color = self.btn_move.background_color
|
||
# --- add ---
|
||
for w in [
|
||
self.btn_new,
|
||
self.btn_page,
|
||
self.btn_switch,
|
||
self.btn_save,
|
||
self.btn_delete,
|
||
self.btn_move,
|
||
btn_levels,
|
||
btn_price,
|
||
]:
|
||
bottom.add_widget(w)
|
||
root.add_widget(bottom)
|
||
# ================= ADD ROOT =================
|
||
self.add_widget(root)
|
||
# ================= INIT BUILD =================
|
||
Clock.schedule_once(lambda *_: self._build(), 0)
|
||
# scroll nahoru (jako POS)
|
||
Clock.schedule_once(lambda *_: setattr(self.scroll, "scroll_y", 1), 0)
|
||
|
||
def _open_item_editor(self, pol):
|
||
modal = ModalView(size_hint=(0.6, 0.9))
|
||
root = BoxLayout(
|
||
orientation="vertical",
|
||
spacing=dp(6),
|
||
padding=dp(10),
|
||
)
|
||
# ================= NÁZEV =================
|
||
ti_name = TextInput(
|
||
text=pol.d_name,
|
||
multiline=False,
|
||
size_hint=(1, None),
|
||
height=dp(50)
|
||
)
|
||
root.add_widget(ti_name)
|
||
# ================= BARVA =================
|
||
btn_color = Button(
|
||
text=f"Barva: {pol.color}",
|
||
size_hint=(1, None),
|
||
height=dp(50)
|
||
)
|
||
def change_color(*_):
|
||
pol.color = (pol.color + 1) % 6
|
||
btn_color.text = f"Barva: {pol.color}"
|
||
btn_color.bind(on_press=change_color)
|
||
root.add_widget(btn_color)
|
||
# ================= ŠÍŘKA =================
|
||
pos = next((p for p in pol.pos_pc if p.page == self.current_page), None)
|
||
ti_width = TextInput(
|
||
text=str(pos.sirka if pos else 1),
|
||
multiline=False,
|
||
size_hint=(1, None),
|
||
height=dp(50)
|
||
)
|
||
root.add_widget(ti_width)
|
||
# ================= CENY =================
|
||
levels = self.cenik.used_price_levels()
|
||
ceny_map = {c.name: c for c in pol.ceny}
|
||
grid = GridLayout(
|
||
cols=2,
|
||
size_hint=(1, None),
|
||
spacing=dp(4)
|
||
)
|
||
grid.bind(minimum_height=grid.setter("height"))
|
||
inputs = {}
|
||
for lvl in levels:
|
||
grid.add_widget(Button(text=lvl, size_hint=(1, None), height=dp(40)))
|
||
cena = ceny_map.get(lvl)
|
||
ti = TextInput(
|
||
text=str(cena.cena if cena else ""),
|
||
multiline=False,
|
||
size_hint=(1, None),
|
||
height=dp(40),
|
||
background_color=(0.2, 0.7, 0.2, 1) if cena else (0.5, 0.5, 0.5, 1)
|
||
)
|
||
inputs[lvl] = ti
|
||
grid.add_widget(ti)
|
||
root.add_widget(grid)
|
||
# ================= ULOŽIT =================
|
||
def save(*_):
|
||
# název
|
||
pol.d_name = ti_name.text
|
||
# šířka
|
||
try:
|
||
w = max(1, int(ti_width.text))
|
||
except:
|
||
w = 1
|
||
for p in pol.pos_pc:
|
||
if p.page == self.current_page:
|
||
p.sirka = w
|
||
# ceny
|
||
new_ceny = []
|
||
for lvl, ti in inputs.items():
|
||
txt = ti.text.strip()
|
||
if not txt:
|
||
continue
|
||
try:
|
||
val = float(txt)
|
||
except:
|
||
val = 0
|
||
new_ceny.append(Cena(
|
||
name=lvl,
|
||
cena=val,
|
||
mena="Kc",
|
||
dan="21",
|
||
cena2=0,
|
||
cena3=0,
|
||
cena4=0
|
||
))
|
||
pol.ceny = new_ceny
|
||
modal.dismiss()
|
||
self._build()
|
||
btn_save = Button(text="Uložit", size_hint=(1, None), height=dp(60))
|
||
btn_save.bind(on_press=save)
|
||
root.add_widget(btn_save)
|
||
modal.add_widget(root)
|
||
modal.open()
|
||
|
||
|
||
def _edit_price(self, pol, level):
|
||
root = BoxLayout(orientation="vertical", spacing=dp(6), padding=dp(10))
|
||
# najdi cenu
|
||
cena = next((c for c in pol.ceny if c.name == level), None)
|
||
ti = TextInput(
|
||
text=str(cena.cena if cena else 0),
|
||
multiline=False
|
||
)
|
||
root.add_widget(ti)
|
||
def save(*_):
|
||
try:
|
||
val = float(ti.text)
|
||
except:
|
||
val = 0
|
||
if cena:
|
||
cena.cena = val
|
||
else:
|
||
pol.ceny.append(Cena(
|
||
name=level,
|
||
cena=val,
|
||
mena="Kc",
|
||
dan="21",
|
||
cena2=0,
|
||
cena3=0,
|
||
cena4=0
|
||
))
|
||
modal.dismiss()
|
||
self._build()
|
||
btn_save = Button(text="Uložit")
|
||
btn_save.bind(on_press=save)
|
||
root.add_widget(btn_save)
|
||
modal = ModalView(size_hint=(0.4, 0.3))
|
||
modal.add_widget(root)
|
||
modal.open()
|
||
"""
|
||
def _open_price_menu(self, pol):
|
||
levels = self.cenik.used_price_levels()
|
||
root = BoxLayout(
|
||
orientation="vertical",
|
||
spacing=dp(6),
|
||
padding=dp(10),
|
||
)
|
||
# ===== HLAVIČKA =====
|
||
root.add_widget(Button(
|
||
text=pol.d_name,
|
||
size_hint=(1, None),
|
||
height=dp(50),
|
||
disabled=True
|
||
))
|
||
# ===== MAPA EXISTUJÍCÍCH CEN =====
|
||
existing = {c.name: c for c in pol.ceny}
|
||
# ===== TLAČÍTKA HLADIN =====
|
||
for lvl in levels:
|
||
cena = existing.get(lvl)
|
||
has_price = cena and cena.cena > 0
|
||
btn = Button(
|
||
text=lvl,
|
||
size_hint=(1, None),
|
||
height=dp(50),
|
||
background_color=(0.2, 0.7, 0.2, 1) if has_price else (0.5, 0.5, 0.5, 1)
|
||
)
|
||
btn.bind(on_press=lambda b, lvl=lvl: self._edit_price(pol, lvl))
|
||
root.add_widget(btn)
|
||
modal = ModalView(size_hint=(0.5, 0.7))
|
||
modal.add_widget(root)
|
||
modal.open()
|
||
"""
|
||
def on_select_price_level(self, *_):
|
||
levels = self.cenik.used_price_levels()
|
||
if not levels:
|
||
return
|
||
try:
|
||
idx = levels.index(self.price_level)
|
||
except ValueError:
|
||
idx = 0
|
||
self.price_level = levels[(idx + 1) % len(levels)]
|
||
print("price_level:", self.price_level)
|
||
# update tlačítka
|
||
if hasattr(self, "btn_price"):
|
||
self.btn_price.text = f"Cena\n{self.price_level}"
|
||
self._build()
|
||
|
||
def _select_price_level(self, *_):
|
||
levels = self.cenik.used_price_levels()
|
||
if not levels:
|
||
return
|
||
# jednoduchý cyklus
|
||
idx = levels.index(self.price_level)
|
||
self.price_level = levels[(idx + 1) % len(levels)]
|
||
print("price_level:", self.price_level)
|
||
self._build()
|
||
|
||
def _edit_price_levels(self, *_):
|
||
print("OPEN LEVEL EDITOR") # 👈 debug
|
||
levels = list(self.cenik.used_price_levels())
|
||
root = BoxLayout(
|
||
orientation="vertical",
|
||
spacing=dp(6),
|
||
padding=dp(10),
|
||
)
|
||
inputs = []
|
||
for lvl in levels:
|
||
ti = TextInput(text=lvl, multiline=False)
|
||
inputs.append(ti)
|
||
root.add_widget(ti)
|
||
def add_level(*_):
|
||
ti = TextInput(text="", multiline=False)
|
||
inputs.append(ti)
|
||
root.add_widget(ti)
|
||
btn_add = Button(text="Přidat")
|
||
btn_add.bind(on_press=add_level)
|
||
root.add_widget(btn_add)
|
||
def save(*_):
|
||
new_levels = [ti.text.strip() for ti in inputs if ti.text.strip()]
|
||
if not new_levels:
|
||
print("prázdné hladiny – ignoruji")
|
||
return
|
||
self._apply_price_levels(new_levels)
|
||
modal.dismiss()
|
||
btn_save = Button(text="Uložit")
|
||
btn_save.bind(on_press=save)
|
||
root.add_widget(btn_save)
|
||
modal = ModalView(size_hint=(0.6, 0.8))
|
||
modal.add_widget(root)
|
||
modal.open()
|
||
|
||
def _apply_price_levels(self, new_levels):
|
||
for pol in self.cenik.cenpol:
|
||
existing = {c.name: c for c in pol.ceny}
|
||
new_ceny = []
|
||
for lvl in new_levels:
|
||
if lvl in existing:
|
||
new_ceny.append(existing[lvl])
|
||
else:
|
||
# nová hladina → default cena
|
||
new_ceny.append(
|
||
Cena(
|
||
name=lvl,
|
||
cena=0.0,
|
||
mena="Kc",
|
||
dan="21",
|
||
cena2=0.0,
|
||
cena3=0.0,
|
||
cena4=0.0,
|
||
)
|
||
)
|
||
pol.ceny = new_ceny
|
||
# reset aktuální hladiny pokud zmizela
|
||
if self.price_level not in new_levels:
|
||
self.price_level = new_levels[0]
|
||
print("nové hladiny:", new_levels)
|
||
self._build()
|
||
|
||
# -----------------------------------------------------
|
||
def _move_selected_to(self, col, row):
|
||
if not self._selected_pol:
|
||
return
|
||
old_pos = None
|
||
for p in self._selected_pol.pos_pc:
|
||
if p.page == self.current_page:
|
||
old_pos = p
|
||
break
|
||
if old_pos is None:
|
||
sirka = 3
|
||
color = 0
|
||
else:
|
||
sirka = max(1, getattr(old_pos, "sirka", 1))
|
||
color = getattr(old_pos, "color", 0)
|
||
# ochrana proti přetečení doprava
|
||
if col + sirka > self.cols:
|
||
col = max(0, self.cols - sirka)
|
||
new_pos = Position(
|
||
page=self.current_page,
|
||
col=col,
|
||
line=row,
|
||
sirka=sirka,
|
||
color=color,
|
||
)
|
||
replaced = False
|
||
new_list = []
|
||
for p in self._selected_pol.pos_pc:
|
||
if p.page == self.current_page and not replaced:
|
||
new_list.append(new_pos)
|
||
replaced = True
|
||
else:
|
||
new_list.append(p)
|
||
if not replaced:
|
||
new_list.append(new_pos)
|
||
self._selected_pol.pos_pc = new_list
|
||
self._build()
|
||
|
||
def _select_item(self, pol):
|
||
self._selected_pol = pol
|
||
self._build()
|
||
|
||
def _find_free(self, sirka):
|
||
occupied = set()
|
||
# obsazené buňky (rozšířené o šířku)
|
||
for pol in self.cenik.cenpol:
|
||
for pos in pol.positions:
|
||
if pos.page != self.current_page:
|
||
continue
|
||
w = max(1, getattr(pos, "sirka", 1))
|
||
for dx in range(w):
|
||
occupied.add((pos.col + dx, pos.line))
|
||
# hledání místa
|
||
for row in range(self.rows):
|
||
for col in range(self.cols):
|
||
# nevejde se do gridu?
|
||
if col + sirka > self.cols:
|
||
continue
|
||
# kontrola kolize
|
||
collision = False
|
||
for dx in range(sirka):
|
||
if (col + dx, row) in occupied:
|
||
collision = True
|
||
break
|
||
if not collision:
|
||
return col, row
|
||
return None
|
||
|
||
def _new_button(self, *_):
|
||
dlg = WidthDialog(
|
||
on_ok=self._create_button_with_width,
|
||
default=3
|
||
)
|
||
dlg.open()
|
||
|
||
def _create_button_with_width(self, sirka):
|
||
free = self._find_free(sirka)
|
||
if not free:
|
||
print("Není místo pro button této šířky")
|
||
return
|
||
col, row = free
|
||
self._create_item(col, row, sirka)
|
||
|
||
def _new_page(self, *_):
|
||
max_page = 1
|
||
for pol in self.cenik.cenpol:
|
||
for pos in pol.positions:
|
||
max_page = max(max_page, pos.page)
|
||
self.current_page = max_page + 1
|
||
print("nová stránka:", self.current_page)
|
||
self._build()
|
||
|
||
def _switch_page(self, *_):
|
||
max_page = 1
|
||
for pol in self.cenik.cenpol:
|
||
for pos in pol.positions:
|
||
max_page = max(max_page, pos.page)
|
||
self.current_page += 1
|
||
if self.current_page > max_page:
|
||
self.current_page = 1
|
||
print("stránka:", self.current_page)
|
||
self._build()
|
||
|
||
def _save(self, *_):
|
||
print("UKLÁDÁM")
|
||
self.cenik.prn()
|
||
|
||
def _pos_to_xy(self, col, row):
|
||
x = col * self.cell_w
|
||
y = self.grid.height - (row + 1) * self.cell_h
|
||
return x, y
|
||
|
||
# -----------------------------------------------------
|
||
|
||
def _build(self):
|
||
self.grid.clear_widgets()
|
||
for pol in self.cenik.cenpol:
|
||
for pos in pol.positions:
|
||
if pos.page != self.current_page:
|
||
continue
|
||
x, y = self._pos_to_xy(pos.col, pos.line)
|
||
width_cells = max(1, getattr(pos, "sirka", 1))
|
||
btn = CenikButton(
|
||
pol=pol,
|
||
on_select=self._select_item,
|
||
on_long_press=self._open_item_editor,
|
||
price_updater=self._update_button_price,
|
||
text=pol.d_name,
|
||
size=(self.cell_w * width_cells, self.cell_h),
|
||
size_hint=(None, None),
|
||
pos=(x, y),
|
||
)
|
||
# zvýraznění výběru
|
||
if pol == self._selected_pol:
|
||
btn.background_color = (1, 0.5, 0.3, 1)
|
||
else:
|
||
btn.background_color = (1, 1, 1, 1)
|
||
self.grid.add_widget(btn)
|
||
|
||
def _update_button_price(self, btn):
|
||
pol = getattr(btn, "pol", None)
|
||
if not pol:
|
||
btn.text = ""
|
||
return
|
||
# najdi cenu podle hladiny
|
||
cena = None
|
||
for c in pol.ceny:
|
||
if c.name == self.price_level:
|
||
cena = c
|
||
break
|
||
if not cena and pol.ceny:
|
||
cena = pol.ceny[0]
|
||
if not cena:
|
||
btn.text = pol.d_name
|
||
return
|
||
# formát jako v POS
|
||
btn.text = f"{pol.d_name}\n{cena.cena:.2f} {cena.mena}"
|
||
# -----------------------------------------------------
|
||
|
||
def _find_item(self, col, row):
|
||
for pol in self.cenik.cenpol:
|
||
for pos in pol.positions:
|
||
if (
|
||
pos.page == self.current_page and
|
||
pos.col == col and
|
||
pos.line == row
|
||
):
|
||
return pol
|
||
return None
|
||
|
||
# -----------------------------------------------------
|
||
def _create_item(self, col, row, sirka=3):
|
||
import time
|
||
# ořez na grid
|
||
if col + sirka > self.cols:
|
||
sirka = self.cols - col
|
||
# fallback kdyby něco selhalo
|
||
if sirka < 1:
|
||
sirka = 1
|
||
new_id = int(time.time() * 1000)
|
||
pol = CenPolCreate(
|
||
id_card=new_id,
|
||
kod=new_id,
|
||
d_name=f"Item {len(self.cenik.cenpol) + 1}",
|
||
ch_name=f"Item {len(self.cenik.cenpol) + 1}",
|
||
pokl="A",
|
||
sklad="A",
|
||
ceny=[
|
||
Cena(
|
||
cena=100,
|
||
mena="CZK",
|
||
dan="21",
|
||
name="standard",
|
||
cena2=50,
|
||
cena3=33,
|
||
cena4=25,
|
||
)
|
||
],
|
||
pos_pc=[
|
||
Position(
|
||
page=self.current_page,
|
||
col=col,
|
||
line=row,
|
||
sirka=sirka,
|
||
)
|
||
],
|
||
)
|
||
# Převedení na CenPol (DB model)
|
||
self.cenik.cenpol.append(CenPol(**pol.model_dump()))
|
||
print(f"Přidán button: {pol.d_name} @ ({col},{row}) w={sirka}")
|
||
self._build()
|
||
|
||
# -----------------------------------------------------
|
||
"""
|
||
def _edit_item(self, pol):
|
||
self._selected_pol = pol
|
||
dlg = CenikItemEditor(
|
||
pol=pol,
|
||
on_save=self._on_item_saved
|
||
)
|
||
dlg.open()
|
||
self._build()
|
||
"""
|
||
def _on_item_saved(self, pol):
|
||
print("Uložen:", pol.d_name)
|
||
self._build()
|
||
|
||
def _delete_item(self, *_):
|
||
if not self._selected_pol:
|
||
print("Není vybraná položka")
|
||
return
|
||
print("Mažu:", self._selected_pol.d_name)
|
||
self.cenik.cenpol = [
|
||
p for p in self.cenik.cenpol
|
||
if p != self._selected_pol
|
||
]
|
||
self._selected_pol = None
|
||
self._build()
|
||
|
||
def _delete_item(self, *_):
|
||
if not self._selected_pol:
|
||
print("není vybraná položka")
|
||
return
|
||
print("Mažu:", self._selected_pol.d_name)
|
||
self.cenik.cenpol = [
|
||
p for p in self.cenik.cenpol
|
||
if p != self._selected_pol
|
||
]
|
||
self._selected_pol = None
|
||
self._build()
|
||
|
||
def _start_move(self, *_):
|
||
if not self._selected_pol:
|
||
print("Není vybraná položka")
|
||
return
|
||
print("Klikni do gridu pro novou pozici")
|
||
self._move_mode = True
|
||
# zvýraznění tlačítka
|
||
self.btn_move.background_color = (1, 0.4, 0.2, 1)
|
||
self.btn_move.text = "Klikni\ndo gridu"
|
||
|
||
def _can_place(self, col, row, sirka):
|
||
# mimo grid
|
||
if col < 0 or row < 0 or col + sirka > self.cols or row >= self.rows:
|
||
return False
|
||
occupied = set()
|
||
for pol in self.cenik.cenpol:
|
||
if pol == self._selected_pol:
|
||
continue # ignorujeme přesouvaný button
|
||
for pos in pol.positions:
|
||
if pos.page != self.current_page:
|
||
continue
|
||
w = max(1, getattr(pos, "sirka", 1))
|
||
for dx in range(w):
|
||
occupied.add((pos.col + dx, pos.line))
|
||
# kontrola kolize
|
||
for dx in range(sirka):
|
||
if (col + dx, row) in occupied:
|
||
return False
|
||
return True
|
||
|
||
def set_cenik(self, cenik):
|
||
self.cenik = cenik
|
||
levels = self.cenik.used_price_levels()
|
||
self.price_level = levels[0] if levels else "standard"
|
||
from kivy.clock import Clock
|
||
Clock.schedule_once(lambda *_: self._build(), 0)
|
||
|
||
|
||
def on_touch_down(self, touch):
|
||
# ================= MOVE REŽIM =================
|
||
if self._move_mode:
|
||
# není vybraná položka
|
||
if not self._selected_pol:
|
||
print("žádná vybraná položka")
|
||
self._move_mode = False
|
||
return super().on_touch_down(touch)
|
||
# klik mimo grid = zrušit move
|
||
if not self.grid.collide_point(*touch.pos):
|
||
self._move_mode = False
|
||
self.btn_move.background_color = self._btn_move_color
|
||
self.btn_move.text = "Pozice"
|
||
return super().on_touch_down(touch)
|
||
# ================= ZACHOVÁNÍ ŠÍŘKY =================
|
||
old_pos = None
|
||
for p in self._selected_pol.pos_pc:
|
||
if p.page == self.current_page:
|
||
old_pos = p
|
||
break
|
||
sirka = max(1, getattr(old_pos, "sirka", 3)) if old_pos else 3
|
||
color = getattr(old_pos, "color", 0) if old_pos else 0
|
||
# ================= SOUŘADNICE =================
|
||
lx = touch.x - self.grid.x
|
||
ly = touch.y - self.grid.y
|
||
col = int(lx // self.cell_w)
|
||
row = int((self.grid.height - ly) // self.cell_h)
|
||
print(f"move na: {col},{row} (w={sirka})")
|
||
# ================= OCHRANA GRID =================
|
||
if col + sirka > self.cols:
|
||
col = max(0, self.cols - sirka)
|
||
if col < 0 or row < 0 or row >= self.rows:
|
||
print("mimo grid")
|
||
return True
|
||
# ================= KOLIZE =================
|
||
if not self._can_place(col, row, sirka):
|
||
print("kolize, přesun zrušen")
|
||
return True
|
||
# ================= ULOŽENÍ =================
|
||
replaced = False
|
||
new_list = []
|
||
for p in self._selected_pol.pos_pc:
|
||
if p.page == self.current_page and not replaced:
|
||
new_list.append(Position(
|
||
page=self.current_page,
|
||
col=col,
|
||
line=row,
|
||
sirka=sirka,
|
||
color=color,
|
||
))
|
||
replaced = True
|
||
else:
|
||
new_list.append(p)
|
||
if not replaced:
|
||
new_list.append(Position(
|
||
page=self.current_page,
|
||
col=col,
|
||
line=row,
|
||
sirka=sirka,
|
||
color=color,
|
||
))
|
||
self._selected_pol.pos_pc = new_list
|
||
# ================= RESET =================
|
||
self._move_mode = False
|
||
self.btn_move.background_color = self._btn_move_color
|
||
self.btn_move.text = "Pozice"
|
||
self._build()
|
||
return True
|
||
# ================= DEFAULT =================
|
||
return super().on_touch_down(touch)
|
||
|
||
|
||
# =========================================================
|
||
# APP
|
||
# =========================================================
|
||
|
||
class CenikEditorApp(App):
|
||
def build(self):
|
||
cenik = create_dummy_cenik()
|
||
return CenikEditor(cenik=cenik)
|
||
|
||
# =========================================================
|
||
|
||
if __name__ == "__main__":
|
||
Window.size = (1000, 700)
|
||
Window.minimum_width = 800
|
||
Window.minimum_height = 600
|
||
CenikEditorApp().run() |