diehard1207 1.0.0__tar.gz

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.
Files changed (39) hide show
  1. diehard1207-1.0.0/MANIFEST.in +7 -0
  2. diehard1207-1.0.0/PKG-INFO +8 -0
  3. diehard1207-1.0.0/README.md +0 -0
  4. diehard1207-1.0.0/core/__init__.py +0 -0
  5. diehard1207-1.0.0/core/admin.py +56 -0
  6. diehard1207-1.0.0/core/apps.py +5 -0
  7. diehard1207-1.0.0/core/backends.py +16 -0
  8. diehard1207-1.0.0/core/csv_import/SQL.txt +50 -0
  9. diehard1207-1.0.0/core/csv_import/backup.txt +297 -0
  10. diehard1207-1.0.0/core/csv_import/order_items.csv +21 -0
  11. diehard1207-1.0.0/core/csv_import/orders.csv +11 -0
  12. diehard1207-1.0.0/core/csv_import/pickup_points.csv +37 -0
  13. diehard1207-1.0.0/core/csv_import/positions.csv +4 -0
  14. diehard1207-1.0.0/core/csv_import/products.csv +11 -0
  15. diehard1207-1.0.0/core/csv_import/roles.csv +4 -0
  16. diehard1207-1.0.0/core/csv_import/users.csv +11 -0
  17. diehard1207-1.0.0/core/migrations/0001_initial.py +112 -0
  18. diehard1207-1.0.0/core/migrations/__init__.py +0 -0
  19. diehard1207-1.0.0/core/models.py +114 -0
  20. diehard1207-1.0.0/core/static/core/css/custom_admin.css +30 -0
  21. diehard1207-1.0.0/core/static/core/js/admin_discount.js +11 -0
  22. diehard1207-1.0.0/core/templates/admin/base_site.html +10 -0
  23. diehard1207-1.0.0/core/templates/admin/login.html +8 -0
  24. diehard1207-1.0.0/core/tests.py +3 -0
  25. diehard1207-1.0.0/core/views.py +3 -0
  26. diehard1207-1.0.0/diehard1207.egg-info/PKG-INFO +8 -0
  27. diehard1207-1.0.0/diehard1207.egg-info/SOURCES.txt +37 -0
  28. diehard1207-1.0.0/diehard1207.egg-info/dependency_links.txt +1 -0
  29. diehard1207-1.0.0/diehard1207.egg-info/requires.txt +2 -0
  30. diehard1207-1.0.0/diehard1207.egg-info/top_level.txt +4 -0
  31. diehard1207-1.0.0/main.py +16 -0
  32. diehard1207-1.0.0/manage.py +22 -0
  33. diehard1207-1.0.0/pyproject.toml +21 -0
  34. diehard1207-1.0.0/setup.cfg +4 -0
  35. diehard1207-1.0.0/toy_shop_project/__init__.py +0 -0
  36. diehard1207-1.0.0/toy_shop_project/asgi.py +16 -0
  37. diehard1207-1.0.0/toy_shop_project/settings.py +132 -0
  38. diehard1207-1.0.0/toy_shop_project/urls.py +39 -0
  39. diehard1207-1.0.0/toy_shop_project/wsgi.py +16 -0
@@ -0,0 +1,7 @@
1
+ graft core
2
+ graft toy_shop_project
3
+ include manage.py
4
+ include main.py
5
+ include README.md
6
+ global-exclude *.pyc
7
+ global-exclude __pycache__
@@ -0,0 +1,8 @@
1
+ Metadata-Version: 2.4
2
+ Name: diehard1207
3
+ Version: 1.0.0
4
+ Summary: Полный слепок проекта МирИгрушек для развертывания
5
+ Requires-Python: >=3.10
6
+ Description-Content-Type: text/markdown
7
+ Requires-Dist: Django>=5.0
8
+ Requires-Dist: psycopg2-binary>=2.9
File without changes
File without changes
@@ -0,0 +1,56 @@
1
+ from django.contrib import admin
2
+
3
+ # Register your models here.
4
+ import os
5
+ from django.contrib import admin
6
+ from django.utils.html import format_html
7
+ from django.conf import settings
8
+ from .models import Roles, Users, PickupPoints, Products, Orders, OrderItems
9
+
10
+ admin.site.site_header = "ООО «МирИгрушек» — Панель управления"
11
+
12
+ class OrderItemsInline(admin.TabularInline):
13
+ model = OrderItems
14
+ extra = 1
15
+
16
+ @admin.register(Orders)
17
+ class OrdersAdmin(admin.ModelAdmin):
18
+ list_display = ('order_id', 'order_date', 'delivery_date', 'pickup_point', 'client', 'order_status')
19
+ list_display_links = ('order_id',)
20
+ list_filter = ('order_status', 'order_date')
21
+ search_fields = ('order_id', 'client__fio')
22
+ inlines = [OrderItemsInline]
23
+
24
+ def has_change_permission(self, request, obj=None): return request.user.role_id == 1
25
+ def has_add_permission(self, request): return request.user.role_id == 1
26
+ def has_delete_permission(self, request, obj=None): return request.user.role_id == 1
27
+
28
+ class Media:
29
+ css = {'all': ('core/css/custom_admin.css',)}
30
+
31
+ @admin.register(Products)
32
+ class ProductsAdmin(admin.ModelAdmin):
33
+ list_display = ('display_photo', 'product_name', 'manufacturer', 'price', 'discount', 'stock_quantity')
34
+ list_filter = ('manufacturer',)
35
+ search_fields = ('product_name',)
36
+ ordering = ('price',)
37
+
38
+ def has_add_permission(self, request): return request.user.role_id == 1
39
+ def has_delete_permission(self, request, obj=None): return request.user.role_id == 1
40
+
41
+ def display_photo(self, obj):
42
+ photo_name = obj.photo.strip() if obj.photo else "11.jpg"
43
+ full_path = os.path.join(settings.MEDIA_ROOT, photo_name)
44
+ if os.path.exists(full_path):
45
+ url = f"{settings.MEDIA_URL}{photo_name}"
46
+ return format_html('<img src="{}" width="65" height="65" style="object-fit: contain; border: 1px solid #DEB887; background: #FAFAFA;"/>', url)
47
+ return format_html('<span style="color: #777777; font-size: 11px;">Нет фото</span>')
48
+ display_photo.short_description = "Изображение"
49
+
50
+ class Media:
51
+ css = {'all': ('core/css/custom_admin.css',)}
52
+ js = ('core/js/admin_discount.js',)
53
+
54
+ admin.site.register(Roles)
55
+ admin.site.register(Users)
56
+ admin.site.register(PickupPoints)
@@ -0,0 +1,5 @@
1
+ from django.apps import AppConfig
2
+
3
+
4
+ class CoreConfig(AppConfig):
5
+ name = 'core'
@@ -0,0 +1,16 @@
1
+ from django.contrib.auth.backends import BaseBackend
2
+ from core.models import Users
3
+
4
+ class ToyShopAuthBackend(BaseBackend):
5
+ def authenticate(self, request, username=None, password=None, **kwargs):
6
+ try:
7
+ user = Users.objects.get(login=username)
8
+ if user.password == password:
9
+ user.update_last_login = False
10
+ return user
11
+ except Users.DoesNotExist:
12
+ return None
13
+
14
+ def get_user(self, user_id):
15
+ try: return Users.objects.get(pk=user_id)
16
+ except Users.DoesNotExist: return None
@@ -0,0 +1,50 @@
1
+ CREATE TABLE roles (
2
+ role_id SERIAL PRIMARY KEY,
3
+ role_name VARCHAR(50) UNIQUE NOT NULL
4
+ );
5
+
6
+ CREATE TABLE users (
7
+ user_id SERIAL PRIMARY KEY,
8
+ fio VARCHAR(150) NOT NULL,
9
+ login VARCHAR(100) UNIQUE NOT NULL,
10
+ password VARCHAR(100) NOT NULL,
11
+ role_id INT REFERENCES roles(role_id) ON DELETE RESTRICT,
12
+ last_login TIMESTAMP WITH TIME ZONE
13
+ );
14
+
15
+ CREATE TABLE pickup_points (
16
+ point_id INT PRIMARY KEY,
17
+ address TEXT NOT NULL
18
+ );
19
+
20
+ CREATE TABLE products (
21
+ sku VARCHAR(50) PRIMARY KEY,
22
+ product_name TEXT NOT NULL,
23
+ unit_of_measure VARCHAR(20) NOT NULL,
24
+ price NUMERIC(10, 2) NOT NULL,
25
+ supplier VARCHAR(100) NOT NULL,
26
+ manufacturer VARCHAR(100) NOT NULL,
27
+ category VARCHAR(100) NOT NULL,
28
+ discount INT DEFAULT 0,
29
+ stock_quantity INT NOT NULL,
30
+ description TEXT,
31
+ photo VARCHAR(255)
32
+ );
33
+
34
+ CREATE TABLE orders (
35
+ order_id INT PRIMARY KEY,
36
+ order_date DATE NOT NULL,
37
+ delivery_date DATE NOT NULL,
38
+ pickup_point_id INT REFERENCES pickup_points(point_id) ON DELETE RESTRICT,
39
+ client_id INT REFERENCES users(user_id) ON DELETE RESTRICT,
40
+ pickup_code INT NOT NULL,
41
+ order_status VARCHAR(50) NOT NULL
42
+ );
43
+
44
+ CREATE TABLE order_items (
45
+ id SERIAL PRIMARY KEY,
46
+ order_id INT REFERENCES orders(order_id) ON DELETE CASCADE,
47
+ sku VARCHAR(50) REFERENCES products(sku) ON DELETE RESTRICT,
48
+ quantity INT NOT NULL,
49
+ CONSTRAINT order_items_order_id_sku_key UNIQUE (order_id, sku)
50
+ );
@@ -0,0 +1,297 @@
1
+ import os, sys, psycopg2
2
+ from psycopg2.extras import DictCursor
3
+ from PyQt6.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout, QLabel, QLineEdit,
4
+ QPushButton, QStackedWidget, QListWidget, QListWidgetItem, QComboBox, QMessageBox, QDialog,
5
+ QFormLayout, QDateEdit, QSpinBox, QDoubleSpinBox)
6
+ from PyQt6.QtCore import QDate, Qt
7
+ from PyQt6.QtGui import QPixmap, QColor
8
+
9
+ DB_CFG = {"host": "localhost", "port": 5432, "user": "postgres", "password": "12072005", "dbname": "381KashutinDE"}
10
+ IMG_DIR = r"D:\Загрузки\17 июня\17 июня\Прил_В4_КОД 09.02.07-2-2026-ПУ\Модуль 1\import"
11
+ CSS = "* { font-family: 'Arial'; font-size: 14px; background-color: #FFFFFF; } QPushButton { background-color: #F5DEB3; font-weight: bold; }"
12
+
13
+ class DB:
14
+ @staticmethod
15
+ def run(q, p=(), f=None):
16
+ try:
17
+ c = psycopg2.connect(**DB_CFG, cursor_factory=DictCursor)
18
+ cur = c.cursor()
19
+ cur.execute(q, p)
20
+ res = cur.fetchall() if f == 'all' else cur.fetchone() if f == 'one' else c.commit() or True
21
+ c.close()
22
+ return res
23
+ except Exception as e:
24
+ QMessageBox.critical(None, "Ошибка БД", str(e))
25
+ return None
26
+
27
+ class ProdWidget(QWidget):
28
+ def __init__(self, i, p=None):
29
+ super().__init__(p)
30
+ lay = QHBoxLayout(self)
31
+ img = QLabel()
32
+ pix = QPixmap(os.path.join(IMG_DIR, str(i.get('photo') or '11.jpg').strip() or '11.jpg')).scaled(100, 100)
33
+ img.setPixmap(pix)
34
+ txt = QLabel(
35
+ f"<b>{i['product_name']}</b> (Артикул: {i['sku']})<br>{i['description'] or '-'}<br>{i['manufacturer']} | <b>{i['price']} руб.</b><br>Скидка: {i['discount']}% | В наличии: {i['stock_quantity']}")
36
+ txt.setWordWrap(True)
37
+ lay.addWidget(img)
38
+ lay.addWidget(txt)
39
+
40
+ class ProdDlg(QDialog):
41
+ def __init__(self, sku=None, p=None):
42
+ super().__init__(p)
43
+ self.sku = sku
44
+ self.setWindowTitle("Редактирование товара")
45
+ self.ui = {
46
+ 'sku': QLineEdit(),
47
+ 'name': QLineEdit(),
48
+ 'desc': QLineEdit(),
49
+ 'mfg': QLineEdit(),
50
+ 'price': QDoubleSpinBox(),
51
+ 'disc': QSpinBox(),
52
+ 'stock': QSpinBox()
53
+ }
54
+ self.ui['sku'].setEnabled(not sku)
55
+ self.ui['price'].setRange(0, 999999999)
56
+ self.ui['disc'].setRange(0, 100)
57
+ self.ui['stock'].setRange(0, 99999999)
58
+
59
+ lay = QFormLayout(self)
60
+ labels = ["Артикул:", "Название:", "Описание:", "ПОставшик:", "Цена:", "Скидка:", "Кол-во:"]
61
+ for l, k in zip(labels, self.ui.keys()):
62
+ lay.addRow(l, self.ui[k])
63
+
64
+ b1, b2 = QPushButton("Сохранить"), QPushButton("Отмена")
65
+ b1.clicked.connect(self.save)
66
+ b2.clicked.connect(self.reject)
67
+ lay.addRow(b1, b2)
68
+
69
+ if sku and (r := DB.run("SELECT * FROM products WHERE sku = %s;", (sku,), 'one')):
70
+ self.ui['sku'].setText(r['sku'])
71
+ self.ui['name'].setText(r['product_name'])
72
+ self.ui['desc'].setText(r['description'] or '')
73
+ self.ui['mfg'].setText(r['manufacturer'] or '')
74
+ self.ui['price'].setValue(float(r['price']))
75
+ self.ui['disc'].setValue(r['discount'] or 0)
76
+ self.ui['stock'].setValue(r['stock_quantity'] or 0)
77
+
78
+ def save(self):
79
+ v = {k: w.text() if isinstance(w, QLineEdit) else w.value() for k, w in self.ui.items()}
80
+ if not v['sku'] or not v['name']:
81
+ return QMessageBox.warning(self, "!", "Заполните Артикул и Название")
82
+
83
+ if self.sku:
84
+ DB.run(
85
+ "UPDATE products SET product_name=%s, description=%s, manufacturer=%s, price=%s, discount=%s, stock_quantity=%s WHERE sku=%s;",
86
+ (v['name'], v['desc'], v['mfg'], v['price'], v['disc'], v['stock'], self.sku))
87
+ else:
88
+ if DB.run("SELECT 1 FROM products WHERE sku=%s;", (v['sku'],), 'one'):
89
+ return QMessageBox.warning(self, "!", "Товар с таким артикулом уже есть")
90
+ DB.run(
91
+ "INSERT INTO products (sku, product_name, description, manufacturer, price, discount, stock_quantity) VALUES (%s, %s, %s, %s, %s, %s, %s);",
92
+ (v['sku'], v['name'], v['desc'], v['mfg'], v['price'], v['disc'], v['stock']))
93
+ self.accept()
94
+
95
+ class OrderDlg(QDialog):
96
+ def __init__(self, oid=None, p=None):
97
+ super().__init__(p)
98
+ self.oid = oid
99
+ self.ui = {'id': QSpinBox(), 'd1': QDateEdit(QDate.currentDate()),
100
+ 'd2': QDateEdit(QDate.currentDate().addDays(3)), 'pt': QComboBox(), 'cl': QComboBox(),
101
+ 'cd': QSpinBox(), 'st': QComboBox()}
102
+ self.ui['id'].setRange(1, 999999)
103
+ self.ui['id'].setValue(oid or 1)
104
+ self.ui['id'].setEnabled(not oid)
105
+ self.ui['cd'].setRange(100, 999)
106
+ self.ui['st'].addItems(["Новый", "Завершен"])
107
+
108
+ lay = QFormLayout(self)
109
+ for l, k in zip(["№:", "Дата:", "Доставка:", "Пункт:", "Клиент:", "Код получения:", "Статус:"],
110
+ self.ui.keys()): lay.addRow(l, self.ui[k])
111
+
112
+ b1, b2 = QPushButton("Сохранить"), QPushButton("Отмена")
113
+ b1.clicked.connect(self.save)
114
+ b2.clicked.connect(self.reject)
115
+ lay.addRow(b1, b2)
116
+
117
+ for r in DB.run("SELECT point_id, address FROM pickup_points;", f='all') or []: self.ui['pt'].addItem(
118
+ r['address'], r['point_id'])
119
+ for r in DB.run("SELECT user_id, fio FROM users WHERE role_id = 3;", f='all') or []: self.ui['cl'].addItem(
120
+ r['fio'], r['user_id'])
121
+
122
+ if oid and (r := DB.run("SELECT * FROM orders WHERE order_id = %s;", (oid,), 'one')):
123
+ self.ui['d1'].setDate(r['order_date'])
124
+ self.ui['d2'].setDate(r['delivery_date'])
125
+ self.ui['pt'].setCurrentIndex(self.ui['pt'].findData(r['pickup_point_id']))
126
+ self.ui['cl'].setCurrentIndex(self.ui['cl'].findData(r['client_id']))
127
+ self.ui['cd'].setValue(r['pickup_code'])
128
+ self.ui['st'].setCurrentText(r['order_status'])
129
+
130
+ def save(self):
131
+ v = {k: w.currentData() if k in ('pt', 'cl') else
132
+ w.currentText() if k == 'st' else
133
+ w.date().toPyDate() if k in ('d1', 'd2') else
134
+ w.value() for k, w in self.ui.items()}
135
+
136
+ if not v['pt'] or not v['cl']:
137
+ return QMessageBox.warning(self, "!", "Заполните списки")
138
+ if self.oid:
139
+ DB.run(
140
+ "UPDATE orders SET order_date=%s, delivery_date=%s, pickup_point_id=%s, client_id=%s, pickup_code=%s, order_status=%s WHERE order_id=%s;",
141
+ (v['d1'], v['d2'], v['pt'], v['cl'], v['cd'], v['st'], self.oid))
142
+ elif DB.run("SELECT 1 FROM orders WHERE order_id=%s;", (v['id'],), 'one'):
143
+ return QMessageBox.warning(self, "!", "Такой заказ уже есть")
144
+ else:
145
+ DB.run(
146
+ "INSERT INTO orders (order_id, order_date, delivery_date, pickup_point_id, client_id, pickup_code, order_status) VALUES (%s, %s, %s, %s, %s, %s, %s);",
147
+ (v['id'], v['d1'], v['d2'], v['pt'], v['cl'], v['cd'], v['st']))
148
+ self.accept()
149
+
150
+ class MainWin(QMainWindow):
151
+ def __init__(self):
152
+ super().__init__()
153
+ self.stk = QStackedWidget()
154
+ self.setCentralWidget(self.stk)
155
+
156
+ lw, mw = QWidget(), QWidget()
157
+ ll, ml = QVBoxLayout(lw), QVBoxLayout(mw)
158
+
159
+ self.l_in, self.p_in = QLineEdit(placeholderText="Логин"), QLineEdit(placeholderText="Пароль",
160
+ echoMode=QLineEdit.EchoMode.Password)
161
+ b_in, b_g = QPushButton("Войти"), QPushButton("Войти как гость")
162
+ b_in.clicked.connect(self.log)
163
+ b_g.clicked.connect(lambda: self.set_u({"role_id": 0, "fio": "Гость"}))
164
+ for w in (QLabel("ВХОД"), self.l_in, self.p_in, b_in, b_g): ll.addWidget(w)
165
+
166
+ self.info, self.b_vw, b_out = QLabel(), QPushButton("Заказы"), QPushButton("Выйти")
167
+ self.b_vw.clicked.connect(self.tg)
168
+ b_out.clicked.connect(self.logout)
169
+ hl = QHBoxLayout()
170
+ hl.addWidget(self.info)
171
+ hl.addWidget(self.b_vw)
172
+ hl.addWidget(b_out)
173
+ ml.addLayout(hl)
174
+
175
+ self.sub = QStackedWidget()
176
+ pw, ow = QWidget(), QWidget()
177
+ pl, ol = QVBoxLayout(pw), QVBoxLayout(ow)
178
+
179
+ self.srch, self.srt, self.mfg = QLineEdit(placeholderText="Поиск"), QComboBox(), QComboBox()
180
+ self.srt.addItems(["Без сортировки", "Цена (возр.)", "Цена (убыв.)"])
181
+ for w in (self.srch, self.srt, self.mfg):
182
+ pl.addWidget(w)
183
+ if w != self.mfg: w.textChanged.connect(self.l_pr) if w == self.srch else w.currentIndexChanged.connect(
184
+ self.l_pr)
185
+ self.mfg.currentIndexChanged.connect(self.l_pr)
186
+
187
+ self.p_lst = QListWidget()
188
+ pl.addWidget(self.p_lst)
189
+
190
+ self.b_edit_prod = QPushButton("Редактировать товар")
191
+ self.b_edit_prod.clicked.connect(self.ed_prod)
192
+ pl.addWidget(self.b_edit_prod)
193
+
194
+ self.btns = [QPushButton(t) for t in ("Добавить", "Изменить", "Удалить")]
195
+ self.btns[0].clicked.connect(lambda: self.ed(0))
196
+ self.btns[1].clicked.connect(lambda: self.ed(1))
197
+ self.btns[2].clicked.connect(self.dl)
198
+ for b in self.btns: ol.addWidget(b)
199
+ self.o_lst = QListWidget()
200
+ ol.addWidget(self.o_lst)
201
+
202
+ self.sub.addWidget(pw)
203
+ self.sub.addWidget(ow)
204
+ ml.addWidget(self.sub)
205
+ self.stk.addWidget(lw)
206
+ self.stk.addWidget(mw)
207
+
208
+ def log(self):
209
+ if r := DB.run("SELECT * FROM users WHERE login=%s AND password=%s;", (self.l_in.text(), self.p_in.text()),
210
+ 'one'):
211
+ self.set_u(dict(r))
212
+ else:
213
+ QMessageBox.warning(self, "!", "Ошибка входа")
214
+
215
+ def set_u(self, d):
216
+ self.u = d
217
+ self.info.setText(d['fio'])
218
+ for w in (self.srch, self.srt, self.mfg, self.b_vw): w.setVisible(d.get('role_id') in [1, 2])
219
+ for b in self.btns: b.setVisible(d.get('role_id') == 1)
220
+ self.b_edit_prod.setVisible(d.get('role_id') == 1)
221
+
222
+ self.mfg.blockSignals(True)
223
+ self.mfg.clear()
224
+ self.mfg.addItem("Все поставщики")
225
+ for m in DB.run("SELECT DISTINCT manufacturer FROM products WHERE manufacturer IS NOT NULL;",
226
+ f='all') or []: self.mfg.addItem(m[0])
227
+ self.mfg.blockSignals(False)
228
+
229
+ self.sub.setCurrentIndex(0)
230
+ self.b_vw.setText("Заказы")
231
+ self.l_pr()
232
+ self.stk.setCurrentIndex(1)
233
+ self.showMaximized()
234
+
235
+ def logout(self):
236
+ self.stk.setCurrentIndex(0)
237
+ self.showNormal()
238
+ self.resize(400, 300)
239
+
240
+ def tg(self):
241
+ p = self.sub.currentIndex() == 0
242
+ self.sub.setCurrentIndex(int(p))
243
+ self.b_vw.setText("Товары" if p else "Заказы")
244
+ self.l_ord() if p else self.l_pr()
245
+
246
+ def l_pr(self):
247
+ self.p_lst.clear()
248
+ q, p = "SELECT * FROM products WHERE 1=1", []
249
+ if self.u.get('role_id') in [1, 2]:
250
+ if t := self.srch.text(): q += " AND product_name ILIKE %s"; p.append(f"%{t}%")
251
+ if (m := self.mfg.currentText()) != "Все поставщики": q += " AND manufacturer=%s"; p.append(m)
252
+ q += ["", " ORDER BY price ASC", " ORDER BY price DESC"][self.srt.currentIndex()]
253
+
254
+ for i in DB.run(q, p, 'all') or []:
255
+ li = QListWidgetItem(self.p_lst)
256
+ li.setData(Qt.ItemDataRole.UserRole, i['sku'])
257
+ w = ProdWidget(i)
258
+ li.setSizeHint(w.sizeHint())
259
+ if i['discount'] > 17: li.setBackground(QColor("#FFDEAD"))
260
+ self.p_lst.setItemWidget(li, w)
261
+
262
+ def ed_prod(self):
263
+ if not self.p_lst.currentItem():
264
+ return QMessageBox.warning(self, "!", "Выберите товар для редактирования!")
265
+ sku = self.p_lst.currentItem().data(Qt.ItemDataRole.UserRole)
266
+ dlg = ProdDlg(sku, self)
267
+ if dlg.exec():
268
+ self.l_pr()
269
+
270
+ def l_ord(self):
271
+ self.o_lst.clear()
272
+ q = "SELECT o.*, p.address, u.fio, COALESCE(string_agg(oi.sku || ' (' || oi.quantity || ' шт)', ', '), '-') as items FROM orders o LEFT JOIN pickup_points p ON o.pickup_point_id=p.point_id LEFT JOIN users u ON o.client_id=u.user_id LEFT JOIN order_items oi ON o.order_id=oi.order_id GROUP BY o.order_id, p.address, u.fio ORDER BY o.order_id DESC;"
273
+ for o in DB.run(q, f='all') or []:
274
+ i = QListWidgetItem(
275
+ f"№ {o['order_id']} | Статус: {o['order_status']} | Доставка: {o['delivery_date']}\nПункт: {o['address']}\nКлиент: {o['fio']}\nТовары: {o['items']}")
276
+ i.setData(Qt.ItemDataRole.UserRole, o['order_id'])
277
+ self.o_lst.addItem(i)
278
+
279
+ def ed(self, e=0):
280
+ if e and not self.o_lst.currentItem(): return QMessageBox.warning(self, "!", "Выберите заказ")
281
+ oid = self.o_lst.currentItem().data(Qt.ItemDataRole.UserRole) if e else None
282
+
283
+ dlg = OrderDlg(oid, self)
284
+ if dlg.exec(): self.l_ord()
285
+
286
+ def dl(self):
287
+ if (i := self.o_lst.currentItem()) and QMessageBox.question(self, "?",
288
+ "Удалить?") == QMessageBox.StandardButton.Yes:
289
+ if DB.run("DELETE FROM orders WHERE order_id=%s;", (i.data(Qt.ItemDataRole.UserRole),)): self.l_ord()
290
+
291
+ if __name__ == "__main__":
292
+ app = QApplication(sys.argv)
293
+ app.setStyleSheet(CSS)
294
+ w = MainWin()
295
+ w.resize(400, 300)
296
+ w.show()
297
+ sys.exit(app.exec())
@@ -0,0 +1,21 @@
1
+ order_id;sku;quantity
2
+ 1;PMEZMH;2
3
+ 1;BPV4MM;2
4
+ 2;JVL42J;1
5
+ 2;F895RB;1
6
+ 3;3XBOTN;10
7
+ 3;3L7RCZ;10
8
+ 4;S72AM3;5
9
+ 4;2G3280;4
10
+ 5;MIO8YV;2
11
+ 5;UER2QD;2
12
+ 6;PMEZMH;2
13
+ 6;BPV4MM;2
14
+ 7;JVL42J;1
15
+ 7;F895RB;1
16
+ 8;3XBOTN;10
17
+ 8;3L7RCZ;10
18
+ 9;S72AM3;5
19
+ 9;2G3280;4
20
+ 10;MIO8YV;2
21
+ 10;UER2QD;2
@@ -0,0 +1,11 @@
1
+ order_id;order_date;delivery_date;pickup_point_id;client_id;pickup_code;order_status
2
+ 1;2025-02-27;2025-04-20;1;7;901;Завершен
3
+ 2;2024-09-28;2025-04-21;11;8;902;Завершен
4
+ 3;2025-03-21;2025-04-22;2;9;903;Завершен
5
+ 4;2025-02-20;2025-04-23;11;10;904;Завершен
6
+ 5;2025-03-17;2025-04-24;2;7;905;Завершен
7
+ 6;2025-03-01;2025-04-25;15;8;906;Завершен
8
+ 7;2025-03-01;2025-04-26;3;9;907;Завершен
9
+ 8;2025-03-31;2025-04-27;19;10;908;Новый
10
+ 9;2025-04-02;2025-04-28;5;9;909;Новый
11
+ 10;2025-04-03;2025-04-29;19;10;910;Новый
@@ -0,0 +1,37 @@
1
+ point_id;address
2
+ 1;420151, г. Лесной, ул. Вишневая, 32
3
+ 2;125061, г. Лесной, ул. Подгорная, 8
4
+ 3;630370, г. Лесной, ул. Шоссейная, 24
5
+ 4;400562, г. Лесной, ул. Зеленая, 32
6
+ 5;614510, г. Лесной, ул. Маяковского, 47
7
+ 6;410542, г. Лесной, ул. Светлая, 46
8
+ 7;620839, г. Лесной, ул. Цветочная, 8
9
+ 8;443890, г. Лесной, ул. Коммунистическая, 1
10
+ 9;603379, г. Лесной, ул. Спортивная, 46
11
+ 10;603721, г. Лесной, ул. Гоголя, 41
12
+ 11;410172, г. Лесной, ул. Северная, 13
13
+ 12;614611, г. Лесной, ул. Молодежная, 50
14
+ 13;454311, г.Лесной, ул. Новая, 19
15
+ 14;660007, г.Лесной, ул. Октябрьская, 19
16
+ 15;603036, г. Лесной, ул. Садовая, 4
17
+ 16;394060, г.Лесной, ул. Фрунзе, 43
18
+ 17;410661, г. Лесной, ул. Школьная, 50
19
+ 18;625590, г. Лесной, ул. Коммунистическая, 20
20
+ 19;625683, г. Лесной, ул. 8 Марта
21
+ 20;450983, г.Лесной, ул. Комсомольская, 26
22
+ 21;394782, г. Лесной, ул. Чехова, 3
23
+ 22;603002, г. Лесной, ул. Дзержинского, 28
24
+ 23;450558, г. Лесной, ул. Набережная, 30
25
+ 24;344288, г. Лесной, ул. Чехова, 1
26
+ 25;614164, г.Лесной, ул. Степная, 30
27
+ 26;394242, г. Лесной, ул. Коммунистическая, 43
28
+ 27;660540, г. Лесной, ул. Солнечная, 25
29
+ 28;125837, г. Лесной, ул. Шоссейная, 40
30
+ 29;125703, г. Лесной, ул. Партизанская, 49
31
+ 30;625283, г. Лесной, ул. Победы, 46
32
+ 31;614753, г. Лесной, ул. Полевая, 35
33
+ 32;426030, г. Лесной, ул. Маяковского, 44
34
+ 33;450375, г. Лесной ул. Клубная, 44
35
+ 34;625560, г. Лесной, ул. Некрасова, 12
36
+ 35;630201, г. Лесной, ул. Комсомольская, 17
37
+ 36;190949, г. Лесной, ул. Мичурина, 26
@@ -0,0 +1,4 @@
1
+ position_id;position_name
2
+ 1;Администратор
3
+ 2;Менеджер
4
+ 3;Авторизированный клиент
@@ -0,0 +1,11 @@
1
+ sku;product_name;unit_of_measure;price;supplier;manufacturer;category;discount;stock_quantity;description;photo
2
+ PMEZMH;Детский игровой набор машинок Щенячий патруль / Dogs mini . 9 героев + 9 инерфионных машинок;шт.;1414;Pikeshop;ABSпластик;Игровой набор;22;50;Детский набор машинок с героями мультсериала «Щенячий патруль» подойдет как для мальчиков, так и для девочек. В детский набор входит 9 фигурок щенков спасателей. ;1.jpg
3
+ BPV4MM;Конструктор Гарри Поттер Сова Букля 630 деталей совместим с lego harry potter, лего совместимый);шт.;771;Playbig;ABSпластик;Конструктор;15;26;Коллекционная модель Букля состоит из множества потрясающих элементов, а также специального механизма внутри. С его помощью можно плавно поднимать-опускать крылья птицы.;2.jpg
4
+ JVL42J;Музыкальные инструменты для детей, ксилофон, барабаны, развивающие игрушки, игрушки для детей;шт.;2750;Playbig;BambiniFelici;Детский музыкальный инструмент;15;0;Откройте мир музыки для вашего ребенка с этой уникальной игрушкой! Это многофункциональное музыкальное чудо объединяет в себе всё, что нужно для творческого развития.;3.jpg
5
+ F895RB;Машинка игрушка диско шар светящаяся музыкальная;шт.;368;Knauf;ABSпластик;Машинка;6;7;Светящаяся музыкальная машина с диско шаром переливается разными цветами, играет ритмичные мелодии, объезжает препятствия и крутится, поэтому с ней точно не будет скучно.;4.jpg
6
+ 3XBOTN;Игровой набор Hot Wheels Action Loop Cyclone Challenge Track, с машинкой и удобным хранением, HTK16;шт.;3426;Knauf;BambiniFelici;Игровой набор;10;21;Игровой набор Hot Wheels Action Loop Cyclone Challenge Track - это уникальная игра, которая позволит вам испытать себя и своих друзей в скорости и ловкости. Этот набор состоит из металлической дорожки с циклоном, которая создает потрясающий эффект и добавляет дополнительную сложность в игру.;5.jpg
7
+ 3L7RCZ;Игровой набор с деревянными машинками Стройплощадка Кран-Паркс, Junion;шт.;7400;Knauf;Junion;Игровой набор;15;0;Игровой набор «Стройплощадка Кран-Паркс Junion» — это большая игрушечная парковка с деревянными машинками и настоящим подъёмным краном, придуманная в Яндексе настоящими родителями.;6.jpg
8
+ S72AM3;Синтезатор детский с микрофоном 61 клавиша;шт.;1749;CHILITOY;Junion;Детский музыкальный инструмент;10;35;Откройте для ребенка дверь в мир музыки с детским синтезатором! Этот компактный инструмент с микрофоном станет верным другом для юных музыкантов, помогая им развивать творческий потенциал и получать удовольствие от игры.;7.jpg
9
+ 2G3280;"Деревянный игровой набор JUNION Стройплощадка ""Кран-Паркс"" с подъёмным, строительным краном и машинками, 18 предметов, подвижные элементы";шт.;1624;Vinylon;Junion;Игровой набор;9;20;Игровой набор «Стройплощадка Кран-Паркс Junion» — это большая игрушечная парковка с деревянными машинками и настоящим подъёмным краном, придуманная в Яндексе настоящими родителями.;8.jpg
10
+ MIO8YV;Музыкальная игрушка интерактивная Пульт, детский прорезыватель для малышей;шт.;305;Vinylon;BambiniFelici;Детский музыкальный инструмент;9;31;Музыкальная игрушка интерактивная Пульт, детский прорезыватель для малышей;9.jpg
11
+ UER2QD;Большой набор опытов и экспериментов для детей 14 в 1;шт.;2506;Vinylon;BambiniFelici;Игровой набор;8;27;Большой набор опытов и экспериментов для детей 14 в 1;10.jpg
@@ -0,0 +1,4 @@
1
+ role_id;role_name
2
+ 1;Администратор
3
+ 2;Менеджер
4
+ 3;Авторизированный клиент
@@ -0,0 +1,11 @@
1
+ user_id;fio;login;password;role_id
2
+ 1;Ворсин Петр Евгеньевич;94d5ous@gmail.com;uzWC67;1
3
+ 2;Старикова Елена Павловна;uth4iz@mail.com;2L6KZG;1
4
+ 3;Одинцов Серафим Артёмович;yzls62@outlook.com;JlFRCZ;1
5
+ 4;Михайлюк Анна Вячеславовна;1diph5e@tutanota.com;8ntwUp;2
6
+ 5;Ситдикова Елена Анатольевна;tjde7c@yahoo.com;YOyhfR;2
7
+ 6;Никифорова Весения Николаевна;wpmrc3do@tutanota.com;RSbvHv;2
8
+ 7;Степанов Михаил Артёмович;5d4zbu@tutanota.com;rwVDh9;3
9
+ 8;Ворсин Петр Евгеньевич;ptec8ym@yahoo.com;LdNyos;3
10
+ 9;Старикова Елена Павловна;1qz4kw@mail.com;gynQMT;3
11
+ 10;Сазонов Руслан Германович;4np6se@mail.com;AtnDjr;3
@@ -0,0 +1,112 @@
1
+ # Generated by Django 6.0.6 on 2026-06-16 15:27
2
+
3
+ import django.db.models.deletion
4
+ from django.conf import settings
5
+ from django.db import migrations, models
6
+
7
+
8
+ class Migration(migrations.Migration):
9
+
10
+ initial = True
11
+
12
+ dependencies = [
13
+ ]
14
+
15
+ operations = [
16
+ migrations.CreateModel(
17
+ name='Users',
18
+ fields=[
19
+ ('user_id', models.AutoField(primary_key=True, serialize=False)),
20
+ ('fio', models.CharField(max_length=150, verbose_name='ФИО')),
21
+ ('login', models.CharField(max_length=100, unique=True, verbose_name='Логин')),
22
+ ('password', models.CharField(max_length=100, verbose_name='Пароль')),
23
+ ],
24
+ options={
25
+ 'verbose_name': 'Пользователь',
26
+ 'verbose_name_plural': 'Пользователи',
27
+ 'db_table': 'users',
28
+ },
29
+ ),
30
+ migrations.CreateModel(
31
+ name='PickupPoints',
32
+ fields=[
33
+ ('point_id', models.IntegerField(primary_key=True, serialize=False)),
34
+ ('address', models.TextField(verbose_name='Адрес')),
35
+ ],
36
+ options={
37
+ 'verbose_name': 'Пункт выдачи',
38
+ 'verbose_name_plural': 'Пункты выдачи',
39
+ 'db_table': 'pickup_points',
40
+ },
41
+ ),
42
+ migrations.CreateModel(
43
+ name='Products',
44
+ fields=[
45
+ ('sku', models.CharField(max_length=50, primary_key=True, serialize=False, verbose_name='Артикул')),
46
+ ('product_name', models.TextField(verbose_name='Наименование')),
47
+ ('unit_of_measure', models.CharField(max_length=20, verbose_name='Ед. изм.')),
48
+ ('price', models.DecimalField(decimal_places=2, max_digits=10, verbose_name='Цена')),
49
+ ('supplier', models.CharField(max_length=100, verbose_name='Поставщик')),
50
+ ('manufacturer', models.CharField(max_length=100, verbose_name='Производитель')),
51
+ ('category', models.CharField(max_length=100, verbose_name='Категория')),
52
+ ('discount', models.IntegerField(default=0, verbose_name='Скидка')),
53
+ ('stock_quantity', models.IntegerField(verbose_name='Остаток')),
54
+ ('description', models.TextField(blank=True, null=True, verbose_name='Описание')),
55
+ ('photo', models.CharField(blank=True, max_length=255, null=True, verbose_name='Фото')),
56
+ ],
57
+ options={
58
+ 'verbose_name': 'Товар',
59
+ 'verbose_name_plural': 'Товары',
60
+ 'db_table': 'products',
61
+ },
62
+ ),
63
+ migrations.CreateModel(
64
+ name='Roles',
65
+ fields=[
66
+ ('role_id', models.AutoField(primary_key=True, serialize=False)),
67
+ ('role_name', models.CharField(max_length=50, unique=True, verbose_name='Название роли')),
68
+ ],
69
+ options={
70
+ 'verbose_name': 'Роль',
71
+ 'verbose_name_plural': 'Роли',
72
+ 'db_table': 'roles',
73
+ },
74
+ ),
75
+ migrations.CreateModel(
76
+ name='Orders',
77
+ fields=[
78
+ ('order_id', models.IntegerField(primary_key=True, serialize=False, verbose_name='№ заказа')),
79
+ ('order_date', models.DateField(verbose_name='Дата заказа')),
80
+ ('delivery_date', models.DateField(verbose_name='Дата доставки')),
81
+ ('pickup_code', models.IntegerField(verbose_name='Код получения')),
82
+ ('order_status', models.CharField(max_length=50, verbose_name='Статус')),
83
+ ('client', models.ForeignKey(on_delete=django.db.models.deletion.RESTRICT, to=settings.AUTH_USER_MODEL, verbose_name='Клиент')),
84
+ ('pickup_point', models.ForeignKey(on_delete=django.db.models.deletion.RESTRICT, to='core.pickuppoints', verbose_name='Пункт выдачи')),
85
+ ],
86
+ options={
87
+ 'verbose_name': 'Заказ',
88
+ 'verbose_name_plural': 'Заказы',
89
+ 'db_table': 'orders',
90
+ },
91
+ ),
92
+ migrations.AddField(
93
+ model_name='users',
94
+ name='role',
95
+ field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.RESTRICT, to='core.roles', verbose_name='Роль'),
96
+ ),
97
+ migrations.CreateModel(
98
+ name='OrderItems',
99
+ fields=[
100
+ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
101
+ ('quantity', models.IntegerField(verbose_name='Количество')),
102
+ ('order', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='core.orders', verbose_name='Заказ')),
103
+ ('sku', models.ForeignKey(on_delete=django.db.models.deletion.RESTRICT, to='core.products', verbose_name='Товар')),
104
+ ],
105
+ options={
106
+ 'verbose_name': 'Составной элемент',
107
+ 'verbose_name_plural': 'Состав заказа',
108
+ 'db_table': 'order_items',
109
+ 'unique_together': {('order', 'sku')},
110
+ },
111
+ ),
112
+ ]
File without changes
@@ -0,0 +1,114 @@
1
+ from django.db import models
2
+
3
+ # Create your models here.
4
+ from django.db import models
5
+ from django.contrib.auth.models import AbstractBaseUser
6
+
7
+ class Roles(models.Model):
8
+ role_id = models.AutoField(primary_key=True)
9
+ role_name = models.CharField(unique=True, max_length=50, verbose_name="Название роли")
10
+
11
+ def __str__(self): return self.role_name
12
+ class Meta:
13
+ db_table = 'roles'
14
+ verbose_name = "Роль"
15
+ verbose_name_plural = "Роли"
16
+
17
+ class Users(AbstractBaseUser):
18
+ user_id = models.AutoField(primary_key=True)
19
+ fio = models.CharField(max_length=150, verbose_name="ФИО")
20
+ login = models.CharField(unique=True, max_length=100, verbose_name="Логин")
21
+ password = models.CharField(max_length=100, verbose_name="Пароль")
22
+ role = models.ForeignKey(Roles, models.RESTRICT, blank=True, null=True, verbose_name="Роль")
23
+
24
+ USERNAME_FIELD = 'login'
25
+ REQUIRED_FIELDS = ['fio']
26
+
27
+ @property
28
+ def is_staff(self):
29
+ return self.role_id in [1, 2]
30
+ @property
31
+ def is_superuser(self):
32
+ return self.role_id == 1
33
+ def has_perm(self, perm, obj=None): return True
34
+ def has_module_perms(self, app_label): return True
35
+ @property
36
+ def last_login(self): return None
37
+ @last_login.setter
38
+ def last_login(self, value): pass
39
+
40
+ class Meta:
41
+ db_table = 'users'
42
+ verbose_name = "Пользователь"
43
+ verbose_name_plural = "Пользователи"
44
+
45
+ class PickupPoints(models.Model):
46
+ point_id = models.IntegerField(primary_key=True)
47
+ address = models.TextField(verbose_name="Адрес")
48
+
49
+ def __str__(self): return self.address
50
+ class Meta:
51
+ db_table = 'pickup_points'
52
+ verbose_name = "Пункт выдачи"
53
+ verbose_name_plural = "Пункты выдачи"
54
+
55
+ class Products(models.Model):
56
+ sku = models.CharField(primary_key=True, max_length=50, verbose_name="Артикул")
57
+ product_name = models.TextField(verbose_name="Наименование")
58
+ unit_of_measure = models.CharField(max_length=20, verbose_name="Ед. изм.")
59
+ price = models.DecimalField(max_digits=10, decimal_places=2, verbose_name="Цена")
60
+ supplier = models.CharField(max_length=100, verbose_name="Поставщик")
61
+ manufacturer = models.CharField(max_length=100, verbose_name="Производитель")
62
+ category = models.CharField(max_length=100, verbose_name="Категория")
63
+ discount = models.IntegerField(default=0, verbose_name="Скидка")
64
+ stock_quantity = models.IntegerField(verbose_name="Остаток")
65
+ description = models.TextField(blank=True, null=True, verbose_name="Описание")
66
+ photo = models.CharField(max_length=255, blank=True, null=True, verbose_name="Фото")
67
+
68
+ def __str__(self): return self.product_name
69
+ class Meta:
70
+ db_table = 'products'
71
+ verbose_name = "Товар"
72
+ verbose_name_plural = "Товары"
73
+
74
+ class Orders(models.Model):
75
+ order_id = models.IntegerField(primary_key=True, verbose_name="№ заказа")
76
+ order_date = models.DateField(verbose_name="Дата заказа")
77
+ delivery_date = models.DateField(verbose_name="Дата доставки")
78
+ pickup_point = models.ForeignKey(PickupPoints, models.RESTRICT, verbose_name="Пункт выдачи")
79
+ client = models.ForeignKey(Users, models.RESTRICT, verbose_name="Клиент")
80
+ pickup_code = models.IntegerField(verbose_name="Код получения")
81
+ order_status = models.CharField(max_length=50, verbose_name="Статус")
82
+
83
+ class Meta:
84
+ db_table = 'orders'
85
+ verbose_name = "Заказ"
86
+ verbose_name_plural = "Заказы"
87
+
88
+
89
+ class OrderItems(models.Model):
90
+ # Django автоматически свяжет это поле с новой колонкой id в PostgreSQL
91
+ id = models.AutoField(primary_key=True)
92
+
93
+ order = models.ForeignKey(
94
+ Orders,
95
+ models.CASCADE,
96
+ db_column='order_id',
97
+ verbose_name="Заказ"
98
+ )
99
+ sku = models.ForeignKey(
100
+ Products,
101
+ models.RESTRICT,
102
+ db_column='sku',
103
+ verbose_name="Товар"
104
+ )
105
+ quantity = models.IntegerField(verbose_name="Количество")
106
+
107
+ def __str__(self):
108
+ return f"{self.sku.product_name} ({self.quantity} шт.)"
109
+
110
+ class Meta:
111
+ db_table = 'order_items'
112
+ unique_together = (('order', 'sku'),)
113
+ verbose_name = "Товар в заказе"
114
+ verbose_name_plural = "Состав заказа"
@@ -0,0 +1,30 @@
1
+ body, input, select, textarea, button { font-family: 'Arial', sans-serif !important; }
2
+
3
+ :root {
4
+ --primary: #DEB887; /* Цвет акцентов */
5
+ --secondary: #F5DEB3; /* Дополнительный фон */
6
+ --accent: #DEB887;
7
+ --body-bg: #FFFFFF; /* Основной фон — белый */
8
+ --header-bg: #F5DEB3 !important;
9
+ --header-color: #000000 !important;
10
+ --header-link-color: #000000 !important;
11
+ }
12
+
13
+ /* Кнопки целевого действия (Акцент) */
14
+ .button, input[type=submit], .submit-row input {
15
+ background: #DEB887 !important;
16
+ border-color: #DEB887 !important;
17
+ color: #000000 !important;
18
+ font-weight: bold;
19
+ }
20
+
21
+ /* Окно авторизации */
22
+ body.login { background: #F5DEB3 !important; }
23
+ body.login #container {
24
+ background: #FFFFFF !important;
25
+ border: 1px solid #DEB887 !important;
26
+ border-radius: 6px;
27
+ }
28
+
29
+ /* Покраска строк при скидке > 17% */
30
+ tr.high-discount { background-color: #FFDEAD !important; }
@@ -0,0 +1,11 @@
1
+ document.addEventListener("DOMContentLoaded", function() {
2
+ document.querySelectorAll("#result_list tbody tr").forEach(row => {
3
+ const discountCell = row.querySelector(".field-discount");
4
+ if (discountCell) {
5
+ const discountValue = parseInt(discountCell.textContent.replace('%', '').trim());
6
+ if (!isNaN(discountValue) && discountValue > 17) {
7
+ row.classList.add("high-discount");
8
+ }
9
+ }
10
+ });
11
+ });
@@ -0,0 +1,10 @@
1
+ {% extends "admin/base.html" %}
2
+ {% load static %}
3
+
4
+ {% block extrastyle %}
5
+ <link rel="stylesheet" href="{% static 'core/css/custom_admin.css' %}">
6
+ {% endblock %}
7
+
8
+ {% block branding %}
9
+ <h1 id="site-name"><a href="{% url 'admin:index' %}">ООО «МирИгрушек»</a></h1>
10
+ {% endblock %}
@@ -0,0 +1,8 @@
1
+ {% extends "admin/login.html" %}
2
+
3
+ {% block content %}
4
+ {{ block.super }}
5
+ <div style="margin-top: 15px;">
6
+ <a href="/catalog/" style="background: #F5DEB3; display: block; text-align: center; padding: 10px; color: #000; font-weight: bold; text-decoration: none; border-radius: 4px; border: 1px solid #DEB887;">ВОЙТИ КАК ГОСТЬ</a>
7
+ </div>
8
+ {% endblock %}
@@ -0,0 +1,3 @@
1
+ from django.test import TestCase
2
+
3
+ # Create your tests here.
@@ -0,0 +1,3 @@
1
+ from django.shortcuts import render
2
+
3
+ # Create your views here.
@@ -0,0 +1,8 @@
1
+ Metadata-Version: 2.4
2
+ Name: diehard1207
3
+ Version: 1.0.0
4
+ Summary: Полный слепок проекта МирИгрушек для развертывания
5
+ Requires-Python: >=3.10
6
+ Description-Content-Type: text/markdown
7
+ Requires-Dist: Django>=5.0
8
+ Requires-Dist: psycopg2-binary>=2.9
@@ -0,0 +1,37 @@
1
+ MANIFEST.in
2
+ README.md
3
+ main.py
4
+ manage.py
5
+ pyproject.toml
6
+ core/__init__.py
7
+ core/admin.py
8
+ core/apps.py
9
+ core/backends.py
10
+ core/models.py
11
+ core/tests.py
12
+ core/views.py
13
+ core/csv_import/SQL.txt
14
+ core/csv_import/backup.txt
15
+ core/csv_import/order_items.csv
16
+ core/csv_import/orders.csv
17
+ core/csv_import/pickup_points.csv
18
+ core/csv_import/positions.csv
19
+ core/csv_import/products.csv
20
+ core/csv_import/roles.csv
21
+ core/csv_import/users.csv
22
+ core/migrations/0001_initial.py
23
+ core/migrations/__init__.py
24
+ core/static/core/css/custom_admin.css
25
+ core/static/core/js/admin_discount.js
26
+ core/templates/admin/base_site.html
27
+ core/templates/admin/login.html
28
+ diehard1207.egg-info/PKG-INFO
29
+ diehard1207.egg-info/SOURCES.txt
30
+ diehard1207.egg-info/dependency_links.txt
31
+ diehard1207.egg-info/requires.txt
32
+ diehard1207.egg-info/top_level.txt
33
+ toy_shop_project/__init__.py
34
+ toy_shop_project/asgi.py
35
+ toy_shop_project/settings.py
36
+ toy_shop_project/urls.py
37
+ toy_shop_project/wsgi.py
@@ -0,0 +1,2 @@
1
+ Django>=5.0
2
+ psycopg2-binary>=2.9
@@ -0,0 +1,4 @@
1
+ core
2
+ main
3
+ manage
4
+ toy_shop_project
@@ -0,0 +1,16 @@
1
+ # This is a sample Python script.
2
+
3
+ # Press Shift+F10 to execute it or replace it with your code.
4
+ # Press Double Shift to search everywhere for classes, files, tool windows, actions, and settings.
5
+
6
+
7
+ def print_hi(name):
8
+ # Use a breakpoint in the code line below to debug your script.
9
+ print(f'Hi, {name}') # Press Ctrl+F8 to toggle the breakpoint.
10
+
11
+
12
+ # Press the green button in the gutter to run the script.
13
+ if __name__ == '__main__':
14
+ print_hi('PyCharm')
15
+
16
+ # See PyCharm help at https://www.jetbrains.com/help/pycharm/
@@ -0,0 +1,22 @@
1
+ #!/usr/bin/env python
2
+ """Django's command-line utility for administrative tasks."""
3
+ import os
4
+ import sys
5
+
6
+
7
+ def main():
8
+ """Run administrative tasks."""
9
+ os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'toy_shop_project.settings')
10
+ try:
11
+ from django.core.management import execute_from_command_line
12
+ except ImportError as exc:
13
+ raise ImportError(
14
+ "Couldn't import Django. Are you sure it's installed and "
15
+ "available on your PYTHONPATH environment variable? Did you "
16
+ "forget to activate a virtual environment?"
17
+ ) from exc
18
+ execute_from_command_line(sys.argv)
19
+
20
+
21
+ if __name__ == '__main__':
22
+ main()
@@ -0,0 +1,21 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61.0.0"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "diehard1207" # Имя пакета на PyPI
7
+ version = "1.0.0"
8
+ description = "Полный слепок проекта МирИгрушек для развертывания"
9
+ readme = "README.md"
10
+ requires-python = ">=3.10"
11
+ dependencies = [
12
+ "Django>=5.0",
13
+ "psycopg2-binary>=2.9",
14
+ ]
15
+
16
+ [tool.setuptools]
17
+ # Указываем, какие папки положить в корень архива
18
+ packages = ["core", "toy_shop_project"]
19
+ # Указываем, какие отдельные файлы положить в корень архива
20
+ py-modules = ["manage", "main"]
21
+ include-package-data = true
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
File without changes
@@ -0,0 +1,16 @@
1
+ """
2
+ ASGI config for toy_shop_project project.
3
+
4
+ It exposes the ASGI callable as a module-level variable named ``application``.
5
+
6
+ For more information on this file, see
7
+ https://docs.djangoproject.com/en/6.0/howto/deployment/asgi/
8
+ """
9
+
10
+ import os
11
+
12
+ from django.core.asgi import get_asgi_application
13
+
14
+ os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'toy_shop_project.settings')
15
+
16
+ application = get_asgi_application()
@@ -0,0 +1,132 @@
1
+ """
2
+ Django settings for toy_shop_project project.
3
+
4
+ Generated by 'django-admin startproject' using Django 6.0.6.
5
+
6
+ For more information on this file, see
7
+ https://docs.djangoproject.com/en/6.0/topics/settings/
8
+
9
+ For the full list of settings and their values, see
10
+ https://docs.djangoproject.com/en/6.0/ref/settings/
11
+ """
12
+
13
+ from pathlib import Path
14
+
15
+ # Build paths inside the project like this: BASE_DIR / 'subdir'.
16
+ BASE_DIR = Path(__file__).resolve().parent.parent
17
+
18
+
19
+ # Quick-start development settings - unsuitable for production
20
+ # See https://docs.djangoproject.com/en/6.0/howto/deployment/checklist/
21
+
22
+ # SECURITY WARNING: keep the secret key used in production secret!
23
+ SECRET_KEY = 'django-insecure-gju!wxir==3vn1h%k%fs*uel-ty)0&j*f8%wtu^&)r+g6kxla0'
24
+
25
+ # SECURITY WARNING: don't run with debug turned on in production!
26
+ DEBUG = True
27
+
28
+ ALLOWED_HOSTS = []
29
+
30
+
31
+ # Application definition
32
+
33
+ INSTALLED_APPS = [
34
+ 'django.contrib.admin',
35
+ 'django.contrib.auth',
36
+ 'django.contrib.contenttypes',
37
+ 'django.contrib.sessions',
38
+ 'django.contrib.messages',
39
+ 'django.contrib.staticfiles',
40
+ 'core',
41
+ ]
42
+
43
+ MIDDLEWARE = [
44
+ 'django.middleware.security.SecurityMiddleware',
45
+ 'django.contrib.sessions.middleware.SessionMiddleware',
46
+ 'django.middleware.common.CommonMiddleware',
47
+ 'django.middleware.csrf.CsrfViewMiddleware',
48
+ 'django.contrib.auth.middleware.AuthenticationMiddleware',
49
+ 'django.contrib.messages.middleware.MessageMiddleware',
50
+ 'django.middleware.clickjacking.XFrameOptionsMiddleware',
51
+ ]
52
+
53
+ ROOT_URLCONF = 'toy_shop_project.urls'
54
+ import os
55
+ TEMPLATES = [
56
+ {
57
+ 'BACKEND': 'django.template.backends.django.DjangoTemplates',
58
+
59
+ 'DIRS': [os.path.join(BASE_DIR, 'core', 'templates')],
60
+ 'APP_DIRS': True,
61
+ 'OPTIONS': {
62
+ 'context_processors': [
63
+ 'django.template.context_processors.request',
64
+ 'django.contrib.auth.context_processors.auth',
65
+ 'django.contrib.messages.context_processors.messages',
66
+ ],
67
+ },
68
+ },
69
+ ]
70
+
71
+ WSGI_APPLICATION = 'toy_shop_project.wsgi.application'
72
+
73
+
74
+ # Database
75
+ # https://docs.djangoproject.com/en/6.0/ref/settings/#databases
76
+
77
+ DATABASES = {
78
+ 'default': {
79
+ 'ENGINE': 'django.db.backends.postgresql',
80
+ 'NAME': '381KashutinDE',
81
+ 'USER': 'postgres',
82
+ 'PASSWORD': '12072005',
83
+ 'HOST': 'localhost',
84
+ 'PORT': '5432',
85
+ }
86
+ }
87
+
88
+
89
+ # Password validation
90
+ # https://docs.djangoproject.com/en/6.0/ref/settings/#auth-password-validators
91
+
92
+ AUTH_PASSWORD_VALIDATORS = [
93
+ {
94
+ 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
95
+ },
96
+ {
97
+ 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
98
+ },
99
+ {
100
+ 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
101
+ },
102
+ {
103
+ 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
104
+ },
105
+ ]
106
+
107
+
108
+ # Internationalization
109
+ # https://docs.djangoproject.com/en/6.0/topics/i18n/
110
+
111
+ LANGUAGE_CODE = 'ru-ru'
112
+
113
+ TIME_ZONE = 'UTC'
114
+
115
+ USE_I18N = True
116
+
117
+ USE_TZ = True
118
+
119
+
120
+ # Static files (CSS, JavaScript, Images)
121
+ # https://docs.djangoproject.com/en/6.0/howto/static-files/
122
+
123
+ STATIC_URL = 'static/'
124
+ MEDIA_URL = '/media/'
125
+ MEDIA_ROOT = r"D:\Загрузки\17 июня\17 июня\Прил_В4_КОД 09.02.07-2-2026-ПУ\Модуль 1\import"
126
+
127
+ AUTH_USER_MODEL = 'core.Users'
128
+ AUTHENTICATION_BACKENDS = [
129
+ 'core.backends.ToyShopAuthBackend',
130
+ ]
131
+
132
+
@@ -0,0 +1,39 @@
1
+ """
2
+ URL configuration for toy_shop_project project.
3
+
4
+ The `urlpatterns` list routes URLs to views. For more information please see:
5
+ https://docs.djangoproject.com/en/6.0/topics/http/urls/
6
+ Examples:
7
+ Function views
8
+ 1. Add an import: from my_app import views
9
+ 2. Add a URL to urlpatterns: path('', views.home, name='home')
10
+ Class-based views
11
+ 1. Add an import: from other_app.views import Home
12
+ 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
13
+ Including another URLconf
14
+ 1. Import the include() function: from django.urls import include, path
15
+ 2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
16
+ """
17
+ from django.contrib import admin
18
+ from django.urls import path
19
+ from django.conf import settings
20
+ from django.conf.urls.static import static
21
+ from django.http import HttpResponse
22
+ from core.models import Products
23
+
24
+ # Простейшее гостевое представление прямо в URL-файле (для демонстрации)
25
+ def guest_catalog(request):
26
+ items = Products.objects.all()
27
+ html = f"<h1 style='font-family: Arial; background: #F5DEB3; padding: 20px; margin: 0;'>Каталог товаров (Режим Гостя)</h1><div style='padding: 20px; font-family: Arial;'>"
28
+ for item in items:
29
+ html += f"<p style='border-bottom: 1px solid #DEB887; padding: 10px;'><b>{item.product_name}</b> — Производитель: {item.manufacturer} | Цена: {item.price} руб.</p>"
30
+ html += "</div><p style='padding: 20px;'><a href='/admin/login/' style='color: #DEB887; font-weight: bold;'>Вернуться к авторизации</a></p>"
31
+ return HttpResponse(html)
32
+
33
+ urlpatterns = [
34
+ path('admin/', admin.site.urls),
35
+ path('catalog/', guest_catalog),
36
+ ]
37
+
38
+ if settings.MEDIA_ROOT:
39
+ urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
@@ -0,0 +1,16 @@
1
+ """
2
+ WSGI config for toy_shop_project project.
3
+
4
+ It exposes the WSGI callable as a module-level variable named ``application``.
5
+
6
+ For more information on this file, see
7
+ https://docs.djangoproject.com/en/6.0/howto/deployment/wsgi/
8
+ """
9
+
10
+ import os
11
+
12
+ from django.core.wsgi import get_wsgi_application
13
+
14
+ os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'toy_shop_project.settings')
15
+
16
+ application = get_wsgi_application()