python-simpletables 0.1.28__py3-none-any.whl → 0.2.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,243 @@
1
+ import os, os.path as op, re, json, sqlite3, tkinter as tk, atexit
2
+ from tkinter import ttk
3
+ from tkinter import messagebox as mb
4
+ from tkinter import simpledialog as sd
5
+ from tkinter import filedialog as fd
6
+ from . import Table, Field
7
+
8
+ def q(s):
9
+ return json.dumps(s, ensure_ascii=False)
10
+
11
+ def showid(table, field, value):
12
+ return str(value).rjust(5, "0")
13
+
14
+ def showbool(table, field, value):
15
+ return "Да" if value else "Нет"
16
+
17
+ def askstring(table, field, value):
18
+ return sd.askstring(f"Введите значение", f"Введите значение текстового поля {q(field.title)}", initialvalue=value)
19
+
20
+ def askint(table, field, value):
21
+ return sd.askinteger(f"Введите значение", f"Введите значение числового поля {q(field.title)}", initialvalue=value)
22
+
23
+ def askfloat(table, field, value):
24
+ return sd.askfloat(f"Введите значение", f"Введите значение числового поля {q(field.title)}", initialvalue=value)
25
+
26
+ def askbool(table, field, value):
27
+ return mb.askyesno(f"Выберите значение", f"Выберите значение булевого поля {q(field.title)} (текущее значение: {showbool(table, field, value).lower()})")
28
+
29
+ class ExampleApp(tk.Tk):
30
+ def __init__(self, database_path):
31
+ self.conn = conn = sqlite3.connect(database_path)
32
+ with open(op.join(op.dirname(__file__), "__main__.sql"), "r", encoding="utf8") as script:
33
+ for cmd in script.read().split(";"):
34
+ cmd = cmd.strip()
35
+ try:
36
+ conn.execute(cmd) if cmd else None
37
+ except:
38
+ print(repr(cmd))
39
+ super().__init__()
40
+ self.geometry("800x560")
41
+ self.minsize(400, 300)
42
+
43
+ tabs = tk.Frame(self)
44
+ tabs.pack(side="top", fill="x")
45
+ box = tk.Frame(self)
46
+ box.pack(side="top", fill="both", expand=True)
47
+
48
+ self.all_tables = []
49
+ self.all_buttons = {}
50
+
51
+ def selector(name):
52
+ return lambda: self.select_table(name)
53
+
54
+ def addtable(name, title, fields, primary):
55
+ self.all_tables.append(Table(conn, box, name, fields, primary))
56
+ self.all_buttons[name] = tk.Button(tabs, text=title, command=selector(name))
57
+ self.all_buttons[name].pack(side="left")
58
+
59
+ addtable("Customers", "Заказчики", [
60
+ Field("id", "ID", show=showid, ask=askint),
61
+ Field("name", "ФИО / Организация"),
62
+ Field("inn", "ИНН"),
63
+ Field("addres", "Адрес"),
64
+ Field("phone", "Телефон"),
65
+ Field("salesman", "Продавец?", show=showbool, ask=askbool),
66
+ Field("buyer", "Покупатель?", show=showbool, ask=askbool),
67
+ ], "id")
68
+
69
+ addtable("Goods", "Товары", [
70
+ Field("id", "ID", show=showid, ask=askint),
71
+ Field("name", "Наименование", multiply=2.5),
72
+ Field("price_for_unit", "Цена за ед."),
73
+ Field("unit", "Ед.изм."),
74
+ ], "id")
75
+
76
+ addtable("Orders", "Заказы", [
77
+ Field("id", "ID", show=showid, ask=askint),
78
+ Field("executor", "Исполнитель", show=showid, ask=askint, ref=self.gettable("Customers")),
79
+ Field("customer", "Заказчик", show=showid, ask=askint, ref=self.gettable("Customers")),
80
+ Field("order_date", "Дата"),
81
+ ], "id")
82
+ tk.Button(self.gettable("Orders").buttons,text="Рассчитать стоимость",command=self.calcPrice).pack(side="left")
83
+
84
+ def showproduct(table,field,v):
85
+ row = self.gettable("Goods").data_get(int(v),True)
86
+ return showid(table,field,v) if row==None else self.gettable("Goods").normalize(row)["name"]
87
+ addtable("OrderPositions", "Позиции заказов", [
88
+ Field("id", "ID", show=showid, ask=askint),
89
+ Field("order_id", "Заказ", show=showid, ask=askint, ref=self.gettable("Orders")),
90
+ Field("product", "Продукт", show=showproduct, ask=askint, ref=self.gettable("Goods")),
91
+ Field("amount", "Количество", ask=askfloat),
92
+ ], "id")
93
+
94
+ addtable("Productions", "Производства", [
95
+ Field("id", "ID", show=showid, ask=askint),
96
+ Field("production_date", "Дата производства"),
97
+ ], "id")
98
+
99
+ addtable("ProductionMaterials", "Материалы производства", [
100
+ Field("id", "ID", show=showid, ask=askint),
101
+ Field("prod_id", "Производство", show=showid, ask=askint, ref=self.gettable("Productions")),
102
+ Field("product", "Продукт", show=showid, ask=askint, ref=self.gettable("Goods")),
103
+ Field("amount", "Количество", ask=askfloat),
104
+ ], "id")
105
+
106
+ addtable("ProductionProducts", "Продукты производства", [
107
+ Field("id", "ID", show=showid, ask=askint),
108
+ Field("prod_id", "Производство", show=showid, ask=askint, ref=self.gettable("Productions")),
109
+ Field("product", "Продукт", show=showid, ask=askint, ref=self.gettable("Goods")),
110
+ Field("amount", "Количество", ask=askfloat),
111
+ ], "id")
112
+
113
+ addtable("Specs", "Спецификации", [
114
+ Field("id", "ID", show=showid, ask=askint),
115
+ Field("executor", "Исполнитель", show=showid, ask=askint, ref=self.gettable("Customers")),
116
+ Field("production", "Продукт", show=showid, ask=askint, ref=self.gettable("Goods")),
117
+ Field("amount", "Количество", ask=askfloat),
118
+ ], "id")
119
+
120
+ addtable("SpecMaterial", "Материалы спецификации", [
121
+ Field("id", "ID", show=showid, ask=askint),
122
+ Field("spec_id", "Спецификация", show=showid, ask=askint, ref=self.gettable("Specs")),
123
+ Field("product", "Продукт", show=showid, ask=askint, ref=self.gettable("Goods")),
124
+ Field("amount", "Количество", ask=askfloat),
125
+ ], "id")
126
+
127
+ self.select_table(self.all_tables[0].name)
128
+ def calcPrice(self,order_id=None):
129
+ class CalcPriceDialog(tk.Toplevel):
130
+ """Диалог для отображения расчёта стоимости заказа."""
131
+ def __init__(self, master, order_id, data, total_price):
132
+ super().__init__(master)
133
+ self.title(f"Расчёт стоимости заказа №{order_id}")
134
+ self.geometry("900x500")
135
+ self.minsize(600, 300)
136
+
137
+ frame = ttk.Frame(self)
138
+ frame.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
139
+
140
+ columns = (
141
+ "product_id", "product_name", "amount", "unit",
142
+ "price_per_unit", "price",
143
+ "production_amount",
144
+ "ingredient_id", "ingredient_name", "ingredient_amount",
145
+ "ingredient_unit", "ingredient_price_per_unit", "price_piece"
146
+ )
147
+ headers = (
148
+ "ID товара", "Наименование", "Кол-во", "Ед.изм.",
149
+ "Цена за ед.", "Стоимость",
150
+ "Размер партии",
151
+ "ID ингр.", "Ингредиент", "Кол-во ингр.",
152
+ "Ед.изм. ингр.", "Цена ингр. за ед.", "Итоговая стоимость"
153
+ )
154
+
155
+ self.tree = ttk.Treeview(frame, columns=columns, show="headings")
156
+ for col, head in zip(columns, headers):
157
+ self.tree.heading(col, text=head)
158
+ width = 80 if col in ("product_id", "ingredient_id") else 120
159
+ self.tree.column(col, width=width, minwidth=50)
160
+ for row in data:
161
+ values = [str(v) if v is not None else "" for v in row]
162
+ self.tree.insert("", tk.END, values=values)
163
+ vsb = ttk.Scrollbar(frame, orient="vertical", command=self.tree.yview)
164
+ hsb = ttk.Scrollbar(frame, orient="horizontal", command=self.tree.xview)
165
+ self.tree.configure(yscrollcommand=vsb.set, xscrollcommand=hsb.set)
166
+ self.tree.grid(row=0, column=0, sticky="nsew")
167
+ vsb.grid(row=0, column=1, sticky="ns")
168
+ hsb.grid(row=1, column=0, sticky="ew")
169
+ frame.grid_rowconfigure(0, weight=1)
170
+ frame.grid_columnconfigure(0, weight=1)
171
+ summary_frame = tk.Frame(self)
172
+ summary_frame.pack(fill=tk.X, padx=5, pady=5)
173
+
174
+ tk.Label(summary_frame, text="Общая стоимость заказа:", font=("Arial", 10, "bold")).pack(side=tk.LEFT)
175
+ tk.Label(summary_frame, text=f"{total_price:.2f} руб.", font=("Arial", 10, "bold"), fg="blue").pack(side=tk.LEFT, padx=10)
176
+ btn_close = tk.Button(self, text="Закрыть", command=self.destroy)
177
+ btn_close.pack(pady=5)
178
+ self.transient(master)
179
+ self.grab_set()
180
+ self.focus_set()
181
+ self.wait_window()
182
+ if order_id==None:
183
+ t=self.gettable("Orders")
184
+ sel=t.data_selected(True)
185
+ if sel==None: return
186
+ order_id=sel[0]
187
+ result=self.conn.execute(f'''
188
+ ''').fetchall()
189
+ result=self.conn.execute(f'''
190
+ SELECT
191
+ op.product AS product_id,
192
+ g.name AS product_name,
193
+ op.amount AS amount,
194
+ g.unit AS unit,
195
+ g.price_for_unit AS price_per_unit,
196
+ (op.amount * g.price_for_unit) AS price,
197
+ s.amount AS production_amount,
198
+ sm.product AS ingredient_id,
199
+ gm.name AS ingredient_name,
200
+ sm.amount AS ingredient_amount,
201
+ gm.unit AS ingredient_unit,
202
+ gm.price_for_unit AS ingredient_price_per_unit,
203
+ COALESCE(
204
+ op.amount * gm.price_for_unit * sm.amount / s.amount,
205
+ op.amount * g.price_for_unit
206
+ ) AS price_piece
207
+ FROM Orders o
208
+ LEFT JOIN OrderPositions op ON op.order_id = o.id
209
+ LEFT JOIN Goods g ON g.id = op.product
210
+ LEFT JOIN Specs s ON s.production = op.product
211
+ LEFT JOIN SpecMaterial sm ON sm.spec_id = s.id
212
+ LEFT JOIN Goods gm ON gm.id = sm.product
213
+ WHERE o.id = ?
214
+ ''',[order_id]).fetchall()
215
+ columns = "product,amount,unit,price_for_unit,price,production_amount,ingidient,ingidient_amount,ingidient_unit,ingidient_price,price_piece".split(",")
216
+ headers = "ID,Кол-во,Ед.изм.,Цена за ед.,Товар,Покупка,Размер партии,Ингидиент ID,Кол-во,Ед.изм.,Цена за ед.,Цена при производстве".split(",")
217
+ if not result:
218
+ mb.showinfo("Информация", "Для данного заказа нет позиций.")
219
+ return
220
+ total = sum(row[12] for row in result if row[12] is not None)
221
+ CalcPriceDialog(self, order_id, result, total)
222
+ def gettable(self, name):
223
+ return [t for t in self.all_tables if t.name == name][0]
224
+
225
+ def select_table(self, name):
226
+ res = [t for t in self.all_tables if t.name == name]
227
+ if len(res) != 1:
228
+ return
229
+ table = res[0]
230
+ for v in self.all_tables:
231
+ v.pack_forget()
232
+ for k, v in self.all_buttons.items():
233
+ v.config(relief="raised")
234
+ self.all_buttons[table.name].config(relief="sunken")
235
+ table.pack(fill="both", expand=True)
236
+
237
+ def example_main():
238
+ database = op.join(op.dirname(__file__), "__main__.db")
239
+ app=ExampleApp(database)
240
+ app.mainloop()
241
+
242
+ if __name__ == "__main__":
243
+ example_main()
@@ -0,0 +1,358 @@
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ Главный модуль приложения, реализующий графический интерфейс для управления
4
+ базой данных заказов, товаров, спецификаций и производств.
5
+ """
6
+
7
+ import os
8
+ import os.path as op
9
+ import json
10
+ import sqlite3
11
+ import tkinter as tk
12
+ from tkinter import ttk
13
+ from tkinter import messagebox as mb
14
+ from tkinter import simpledialog as sd
15
+ from tkinter import filedialog as fd
16
+
17
+ from . import Table, Field
18
+
19
+
20
+ # --------------------------- Вспомогательные функции ---------------------------
21
+
22
+ def q(s):
23
+ """Возвращает строку s в JSON-формате с экранированием."""
24
+ return json.dumps(s, ensure_ascii=False)
25
+
26
+
27
+ def showid(table, field, value):
28
+ """Отображение идентификатора с ведущими нулями (ширина 5)."""
29
+ return str(value).rjust(5, "0")
30
+
31
+
32
+ def showbool(table, field, value):
33
+ """Отображение булева значения как 'Да' / 'Нет'."""
34
+ return "Да" if value else "Нет"
35
+
36
+
37
+ def askstring(table, field, value):
38
+ """Диалог ввода текстового значения."""
39
+ return sd.askstring(
40
+ f"Введите значение",
41
+ f"Введите значение текстового поля {q(field.title)}",
42
+ initialvalue=value
43
+ )
44
+
45
+
46
+ def askint(table, field, value):
47
+ """Диалог ввода целочисленного значения."""
48
+ return sd.askinteger(
49
+ f"Введите значение",
50
+ f"Введите значение числового поля {q(field.title)}",
51
+ initialvalue=value
52
+ )
53
+
54
+
55
+ def askfloat(table, field, value):
56
+ """Диалог ввода вещественного значения."""
57
+ return sd.askfloat(
58
+ f"Введите значение",
59
+ f"Введите значение числового поля {q(field.title)}",
60
+ initialvalue=value
61
+ )
62
+
63
+
64
+ def askbool(table, field, value):
65
+ """Диалог выбора булева значения (Да/Нет)."""
66
+ return mb.askyesno(
67
+ f"Выберите значение",
68
+ f"Выберите значение булевого поля {q(field.title)} (текущее значение: {showbool(table, field, value).lower()})"
69
+ )
70
+
71
+
72
+ # --------------------------- Главное окно приложения ---------------------------
73
+
74
+ class ExampleApp(tk.Tk):
75
+ """Главное окно с вкладками таблиц."""
76
+
77
+ def __init__(self, database_path):
78
+ """
79
+ Инициализация приложения: подключение к БД, создание таблиц (если их нет),
80
+ построение интерфейса с вкладками для каждой таблицы.
81
+ """
82
+ # Подключение к SQLite и выполнение скрипта инициализации
83
+ self.conn = conn = sqlite3.connect(database_path)
84
+ script_path = op.join(op.dirname(__file__), "__main__.sql")
85
+ with open(script_path, "r", encoding="utf8") as script:
86
+ for cmd in script.read().split(";"):
87
+ cmd = cmd.strip()
88
+ if cmd:
89
+ try:
90
+ conn.execute(cmd)
91
+ except Exception:
92
+ print(repr(cmd))
93
+
94
+ super().__init__()
95
+ self.geometry("800x560")
96
+ self.minsize(400, 300)
97
+
98
+ # Верхняя панель с кнопками-вкладками
99
+ tab_frame = tk.Frame(self)
100
+ tab_frame.pack(side="top", fill="x")
101
+
102
+ # Основная область для отображения выбранной таблицы
103
+ content_frame = tk.Frame(self)
104
+ content_frame.pack(side="top", fill="both", expand=True)
105
+
106
+ self.tables = [] # список объектов Table
107
+ self.tab_buttons = {} # словарь кнопок для переключения вкладок
108
+
109
+ def switch_to_table(name):
110
+ """Возвращает функцию, переключающую на таблицу с именем name."""
111
+ return lambda: self.select_table(name)
112
+
113
+ def add_table(name, title, fields, primary):
114
+ """
115
+ Добавляет новую таблицу во вкладки.
116
+ :param name: имя таблицы в БД
117
+ :param title: отображаемый заголовок на кнопке
118
+ :param fields: список объектов Field
119
+ :param primary: имя первичного ключа
120
+ """
121
+ table_obj = Table(conn, content_frame, name, fields, primary)
122
+ self.tables.append(table_obj)
123
+ btn = tk.Button(tab_frame, text=title, command=switch_to_table(name))
124
+ btn.pack(side="left")
125
+ self.tab_buttons[name] = btn
126
+
127
+ # ---------- Описание таблиц ----------
128
+ add_table("Customers", "Заказчики", [
129
+ Field("id", "ID", show=showid, ask=askint),
130
+ Field("name", "ФИО / Организация"),
131
+ Field("inn", "ИНН"),
132
+ Field("addres", "Адрес"),
133
+ Field("phone", "Телефон"),
134
+ Field("salesman", "Продавец?", show=showbool, ask=askbool),
135
+ Field("buyer", "Покупатель?", show=showbool, ask=askbool),
136
+ ], "id")
137
+
138
+ add_table("Goods", "Товары", [
139
+ Field("id", "ID", show=showid, ask=askint),
140
+ Field("name", "Наименование", multiply=2.5),
141
+ Field("price_for_unit", "Цена за ед."),
142
+ Field("unit", "Ед.изм."),
143
+ ], "id")
144
+
145
+ add_table("Orders", "Заказы", [
146
+ Field("id", "ID", show=showid, ask=askint),
147
+ Field("executor", "Исполнитель", show=showid, ask=askint, ref=self.get_table("Customers")),
148
+ Field("customer", "Заказчик", show=showid, ask=askint, ref=self.get_table("Customers")),
149
+ Field("order_date", "Дата"),
150
+ ], "id")
151
+ # Кнопка расчёта стоимости для таблицы заказов
152
+ tk.Button(
153
+ self.get_table("Orders").buttons,
154
+ text="Рассчитать стоимость",
155
+ command=self.calc_price
156
+ ).pack(side="left")
157
+
158
+ def show_product(table, field, v):
159
+ """Отображение названия продукта по его ID."""
160
+ row = self.get_table("Goods").data_get(int(v), True)
161
+ if row is None:
162
+ return showid(table, field, v)
163
+ return self.get_table("Goods").normalize(row)["name"]
164
+
165
+ add_table("OrderPositions", "Позиции заказов", [
166
+ Field("id", "ID", show=showid, ask=askint),
167
+ Field("order_id", "Заказ", show=showid, ask=askint, ref=self.get_table("Orders")),
168
+ Field("product", "Продукт", show=show_product, ask=askint, ref=self.get_table("Goods")),
169
+ Field("amount", "Количество", ask=askfloat),
170
+ ], "id")
171
+
172
+ add_table("Productions", "Производства", [
173
+ Field("id", "ID", show=showid, ask=askint),
174
+ Field("production_date", "Дата производства"),
175
+ ], "id")
176
+
177
+ add_table("ProductionMaterials", "Материалы производства", [
178
+ Field("id", "ID", show=showid, ask=askint),
179
+ Field("prod_id", "Производство", show=showid, ask=askint, ref=self.get_table("Productions")),
180
+ Field("product", "Продукт", show=showid, ask=askint, ref=self.get_table("Goods")),
181
+ Field("amount", "Количество", ask=askfloat),
182
+ ], "id")
183
+
184
+ add_table("ProductionProducts", "Продукты производства", [
185
+ Field("id", "ID", show=showid, ask=askint),
186
+ Field("prod_id", "Производство", show=showid, ask=askint, ref=self.get_table("Productions")),
187
+ Field("product", "Продукт", show=showid, ask=askint, ref=self.get_table("Goods")),
188
+ Field("amount", "Количество", ask=askfloat),
189
+ ], "id")
190
+
191
+ add_table("Specs", "Спецификации", [
192
+ Field("id", "ID", show=showid, ask=askint),
193
+ Field("executor", "Исполнитель", show=showid, ask=askint, ref=self.get_table("Customers")),
194
+ Field("production", "Продукт", show=showid, ask=askint, ref=self.get_table("Goods")),
195
+ Field("amount", "Количество", ask=askfloat),
196
+ ], "id")
197
+
198
+ add_table("SpecMaterial", "Материалы спецификации", [
199
+ Field("id", "ID", show=showid, ask=askint),
200
+ Field("spec_id", "Спецификация", show=showid, ask=askint, ref=self.get_table("Specs")),
201
+ Field("product", "Продукт", show=showid, ask=askint, ref=self.get_table("Goods")),
202
+ Field("amount", "Количество", ask=askfloat),
203
+ ], "id")
204
+
205
+ # По умолчанию показываем первую таблицу
206
+ self.select_table(self.tables[0].name)
207
+
208
+ # --------------------------- Расчёт стоимости заказа ---------------------------
209
+
210
+ def calc_price(self, order_id=None):
211
+ """
212
+ Вычисляет полную стоимость заказа, учитывая спецификации (прямые покупки
213
+ или производство). Результат отображается в отдельном диалоге.
214
+ Если order_id не передан, используется выбранная строка в таблице заказов.
215
+ """
216
+ class CalcPriceDialog(tk.Toplevel):
217
+ """Диалог для отображения детального расчёта стоимости заказа."""
218
+ def __init__(self, master, order_id, data, total_price):
219
+ super().__init__(master)
220
+ self.title(f"Расчёт стоимости заказа №{order_id}")
221
+ self.geometry("900x500")
222
+ self.minsize(600, 300)
223
+
224
+ frame = ttk.Frame(self)
225
+ frame.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
226
+
227
+ columns = (
228
+ "product_id", "product_name", "amount", "unit",
229
+ "price_per_unit", "price",
230
+ "production_amount",
231
+ "ingredient_id", "ingredient_name", "ingredient_amount",
232
+ "ingredient_unit", "ingredient_price_per_unit", "price_piece"
233
+ )
234
+ headers = (
235
+ "ID товара", "Наименование", "Кол-во", "Ед.изм.",
236
+ "Цена за ед.", "Стоимость",
237
+ "Размер партии",
238
+ "ID ингр.", "Ингредиент", "Кол-во ингр.",
239
+ "Ед.изм. ингр.", "Цена ингр. за ед.", "Итоговая стоимость"
240
+ )
241
+
242
+ self.tree = ttk.Treeview(frame, columns=columns, show="headings")
243
+ for col, head in zip(columns, headers):
244
+ self.tree.heading(col, text=head)
245
+ width = 80 if col in ("product_id", "ingredient_id") else 120
246
+ self.tree.column(col, width=width, minwidth=50)
247
+
248
+ for row in data:
249
+ values = [str(v) if v is not None else "" for v in row]
250
+ self.tree.insert("", tk.END, values=values)
251
+
252
+ vsb = ttk.Scrollbar(frame, orient="vertical", command=self.tree.yview)
253
+ hsb = ttk.Scrollbar(frame, orient="horizontal", command=self.tree.xview)
254
+ self.tree.configure(yscrollcommand=vsb.set, xscrollcommand=hsb.set)
255
+
256
+ self.tree.grid(row=0, column=0, sticky="nsew")
257
+ vsb.grid(row=0, column=1, sticky="ns")
258
+ hsb.grid(row=1, column=0, sticky="ew")
259
+ frame.grid_rowconfigure(0, weight=1)
260
+ frame.grid_columnconfigure(0, weight=1)
261
+
262
+ # Итоговая сумма
263
+ summary_frame = tk.Frame(self)
264
+ summary_frame.pack(fill=tk.X, padx=5, pady=5)
265
+ tk.Label(summary_frame, text="Общая стоимость заказа:", font=("Arial", 10, "bold")).pack(side=tk.LEFT)
266
+ tk.Label(summary_frame, text=f"{total_price:.2f} руб.", font=("Arial", 10, "bold"), fg="blue").pack(side=tk.LEFT, padx=10)
267
+
268
+ btn_close = tk.Button(self, text="Закрыть", command=self.destroy)
269
+ btn_close.pack(pady=5)
270
+
271
+ self.transient(master)
272
+ self.grab_set()
273
+ self.focus_set()
274
+ self.wait_window()
275
+
276
+ # Если order_id не указан, берём выделенную запись из таблицы заказов
277
+ if order_id is None:
278
+ orders_table = self.get_table("Orders")
279
+ selected = orders_table.data_selected(True)
280
+ if selected is None:
281
+ return
282
+ order_id = selected[0]
283
+
284
+ # Основной запрос: для каждой позиции заказа вычисляем стоимость,
285
+ # если есть спецификация – считаем через ингредиенты, иначе прямая покупка.
286
+ query = """
287
+ SELECT
288
+ op.product AS product_id,
289
+ g.name AS product_name,
290
+ op.amount AS amount,
291
+ g.unit AS unit,
292
+ g.price_for_unit AS price_per_unit,
293
+ (op.amount * g.price_for_unit) AS price,
294
+ s.amount AS production_amount,
295
+ sm.product AS ingredient_id,
296
+ gm.name AS ingredient_name,
297
+ sm.amount AS ingredient_amount,
298
+ gm.unit AS ingredient_unit,
299
+ gm.price_for_unit AS ingredient_price_per_unit,
300
+ COALESCE(
301
+ op.amount * gm.price_for_unit * sm.amount / s.amount,
302
+ op.amount * g.price_for_unit
303
+ ) AS price_piece
304
+ FROM Orders o
305
+ LEFT JOIN OrderPositions op ON op.order_id = o.id
306
+ LEFT JOIN Goods g ON g.id = op.product
307
+ LEFT JOIN Specs s ON s.production = op.product
308
+ LEFT JOIN SpecMaterial sm ON sm.spec_id = s.id
309
+ LEFT JOIN Goods gm ON gm.id = sm.product
310
+ WHERE o.id = ?
311
+ """
312
+ result = self.conn.execute(query, [order_id]).fetchall()
313
+
314
+ if not result:
315
+ mb.showinfo("Информация", "Для данного заказа нет позиций.")
316
+ return
317
+
318
+ total = sum(row[12] for row in result if row[12] is not None)
319
+ CalcPriceDialog(self, order_id, result, total)
320
+
321
+ # --------------------------- Вспомогательные методы ---------------------------
322
+
323
+ def get_table(self, name):
324
+ """Возвращает объект Table по имени."""
325
+ return next(t for t in self.tables if t.name == name)
326
+
327
+ def select_table(self, name):
328
+ """
329
+ Переключает видимую вкладку на таблицу с именем name.
330
+ Скрывает все остальные таблицы и меняет состояние кнопок.
331
+ """
332
+ try:
333
+ table = self.get_table(name)
334
+ except StopIteration:
335
+ return
336
+
337
+ # Скрыть все таблицы
338
+ for t in self.tables:
339
+ t.pack_forget()
340
+
341
+ # Сбросить состояние всех кнопок
342
+ for btn in self.tab_buttons.values():
343
+ btn.config(relief="raised")
344
+
345
+ # Отметить выбранную кнопку и показать таблицу
346
+ self.tab_buttons[table.name].config(relief="sunken")
347
+ table.pack(fill="both", expand=True)
348
+
349
+
350
+ def example_main():
351
+ """Точка входа приложения."""
352
+ database = op.join(op.dirname(__file__), "__main__.db")
353
+ app = ExampleApp(database)
354
+ app.mainloop()
355
+
356
+
357
+ if __name__ == "__main__":
358
+ example_main()
Binary file