diff --git a/app.py b/app.py index 1a0759d..2e25587 100644 --- a/app.py +++ b/app.py @@ -14,7 +14,7 @@ DB_CONFIG = dict( dbname="inventory_cartriges", user="inventory_user", password="inventory_password", - host="192.168.100.3", + host="192.168.1.92", port=5433 ) @@ -1269,6 +1269,14 @@ def init_db(): ) """ ) + cur.execute( + """ + CREATE TABLE IF NOT EXISTS computer_manage_icons ( + computer_id INTEGER PRIMARY KEY REFERENCES computers(id) ON DELETE CASCADE, + icon_label TEXT NOT NULL DEFAULT '' + ) + """ + ) cur.execute( """ CREATE TABLE IF NOT EXISTS document_cameras ( @@ -1908,11 +1916,13 @@ def index(): COALESCE(NULLIF(BTRIM(cab.organization), ''), NULLIF(BTRIM(cab.building), ''), 'Без организации') AS organization_name, NULLIF(BTRIM(cab.floor), '') AS floor_name, COALESCE(NULLIF(BTRIM(cab.cabinet), ''), NULLIF(BTRIM(cab.name), ''), 'Без кабинета') AS cabinet_name, + NULLIF(BTRIM(cmi.icon_label), '') AS icon_label, c.inventory_number, c.brand, c.model FROM computers c LEFT JOIN cabinets cab ON cab.id = c.cabinet_id + LEFT JOIN computer_manage_icons cmi ON cmi.computer_id = c.id WHERE COALESCE(BTRIM(c.rustdesk_id), '') <> '' AND COALESCE(BTRIM(c.rustdesk_password), '') <> '' """ @@ -1946,6 +1956,7 @@ def index(): organization_name, floor_name, cabinet_name, + icon_label, inv, brand, model, @@ -1953,6 +1964,7 @@ def index(): org_label = _group_label(organization_name, "Без организации") floor_label = _group_label(floor_name, "Без этажа") cabinet_label = _group_label(cabinet_name, "Без кабинета") + icon_label_value = _group_label(icon_label, cabinet_label) comp_name = " ".join(p for p in [brand or "", model or ""] if p).strip() org_bucket = grouped_org_map.setdefault(org_label, {}) @@ -1964,6 +1976,7 @@ def index(): "rustdesk_id": rustdesk_id or "", "rustdesk_password": rustdesk_password or "", "cabinet_name": cabinet_label, + "icon_label": icon_label_value, "inventory_number": inv or "", "brand": brand or "", "model": model or "", @@ -3447,6 +3460,42 @@ def api_home_notes_delete(note_id): conn.close() return jsonify(ok=True, deleted_id=row[0]) + +@app.route("/computers/manage-icon-label", methods=["POST"]) +@admin_required +def computers_manage_icon_label(): + computer_id_raw = _clean_text(request.form.get("computer_id")) + icon_label = _clean_text(request.form.get("icon_label")) + next_url = _safe_next_url(request.form.get("next"), fallback="index") + if not computer_id_raw.isdigit(): + return redirect(next_url) + + computer_id = int(computer_id_raw) + conn = get_conn() + cur = conn.cursor() + cur.execute("SELECT 1 FROM computers WHERE id=%s", (computer_id,)) + exists = cur.fetchone() is not None + if not exists: + conn.close() + return redirect(next_url) + + if icon_label: + cur.execute( + """ + INSERT INTO computer_manage_icons (computer_id, icon_label) + VALUES (%s, %s) + ON CONFLICT (computer_id) + DO UPDATE SET icon_label = EXCLUDED.icon_label + """, + (computer_id, icon_label), + ) + else: + cur.execute("DELETE FROM computer_manage_icons WHERE computer_id=%s", (computer_id,)) + conn.commit() + conn.close() + return redirect(next_url) + + @app.route("/computers/update_cabinet", methods=["POST"]) @admin_required def computers_update_cabinet(): @@ -3577,6 +3626,12 @@ def cabinets(): query += """ ORDER BY COALESCE(NULLIF(BTRIM(building), ''), NULLIF(BTRIM(SPLIT_PART(name, ' / ', 1)), '')) NULLS LAST, + CASE + WHEN NULLIF(REGEXP_REPLACE(COALESCE(floor, ''), '\\D', '', 'g'), '') IS NULL THEN 1 + ELSE 0 + END, + COALESCE(NULLIF(REGEXP_REPLACE(COALESCE(floor, ''), '\\D', '', 'g'), ''), '0')::INT, + floor NULLS LAST, CASE WHEN NULLIF(REGEXP_REPLACE(COALESCE(cabinet, ''), '\\D', '', 'g'), '') IS NULL THEN 1 ELSE 0 @@ -6712,6 +6767,107 @@ def report_devices_xlsx(): headers={"Content-Disposition": "attachment;filename=devices.xlsx"}, ) +@app.route("/report/computers.xlsx") +@login_required +def report_computers_xlsx(): + conn = get_conn() + cur = conn.cursor() + cur.execute( + """ + SELECT c.inventory_number, c.brand, c.model, c.serial_number, c.type, + c.rustdesk_id, c.rustdesk_password, + c.cpu_brand, c.cpu_model, c.gpu_model, c.memory_size, c.memory_type, + c.storage_size, c.motherboard, c.os, + cab.name AS cabinet_name, c.note, c.date_in_operation, c.date_added + FROM computers c + LEFT JOIN cabinets cab ON cab.id = c.cabinet_id + ORDER BY c.date_added DESC, c.id DESC + """ + ) + rows = cur.fetchall() + conn.close() + + wb = Workbook() + ws = wb.active + ws.title = "Computers" + ws.append( + [ + "Инв. №", + "Бренд", + "Модель", + "Серийный №", + "Тип", + "ID RustDesk", + "Пароль RustDesk", + "CPU бренд", + "CPU модель", + "GPU модель", + "Объём памяти", + "Тип памяти", + "Объём накопителя", + "Материнская плата", + "ОС", + "Кабинет", + "Примечание", + "Дата ввода в эксплуатацию", + "Добавлено", + ] + ) + for ( + inv, + brand, + model, + sn, + ctype, + rustdesk_id, + rustdesk_password, + cpu_brand, + cpu_model, + gpu_model, + memory_size, + memory_type, + storage_size, + motherboard, + os_name, + cab_name, + note, + date_in_operation, + date_added, + ) in rows: + ws.append( + [ + inv, + brand, + model, + sn, + "Ноутбук" if ctype == "laptop" else "ПК" if ctype == "pc" else ctype, + rustdesk_id, + rustdesk_password, + cpu_brand, + cpu_model, + gpu_model, + memory_size, + memory_type, + storage_size, + motherboard, + os_name, + cab_name, + note, + date_in_operation, + date_added, + ] + ) + + bio = BytesIO() + wb.save(bio) + bio.seek(0) + + return Response( + bio.getvalue(), + mimetype="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", + headers={"Content-Disposition": "attachment;filename=computers.xlsx"}, + ) + @app.route("/report/cabinets.xlsx") @login_required def report_cabinets_xlsx():