From b7d98a999a14db977fadcaab03db67b3929d2c56 Mon Sep 17 00:00:00 2001 From: "matthias.lotz" Date: Sun, 1 Jun 2025 22:13:14 +0200 Subject: [PATCH 1/3] =?UTF-8?q?[POS]=20Einweisungen=20und=20Sicherheitsdat?= =?UTF-8?q?en=20=C3=BCber=20RPC=20geladen?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Erweiterung von get_access_list_grouped() um Sicherheitsdaten (Haftungsausschluss, ID, Geburtstag, RFID) - Rückgabe nun als dict mit access_by_area + user-Feldern - Anpassung der OWL-Komponente OwsMachineAccessList: - updateAccessList() lädt Sicherheitsdaten über jsonrpc - state enthält jetzt getrennte Felder für security_briefing, security_id etc. - Maschinenliste bleibt dauerhaft sichtbar - Fehlerbehandlung bei fehlendem ows.user integriert - alte load_fields()-Logik entfernt --- __manifest__.py | 2 + models/ows_models.py | 28 ++-- static/src/js/machine_access_sidebar.js | 132 ------------------ static/src/js/ows_machine_access_list.js | 63 +++++++++ .../src/js/product_screen_template_patch.js | 2 + static/src/xml/ows_machine_access_list.xml | 76 ++++++++++ static/src/xml/ows_machine_sidebar.xml | 88 ------------ static/src/xml/ows_product_screen.xml | 1 + 8 files changed, 163 insertions(+), 229 deletions(-) delete mode 100644 static/src/js/machine_access_sidebar.js create mode 100644 static/src/js/ows_machine_access_list.js create mode 100644 static/src/xml/ows_machine_access_list.xml delete mode 100644 static/src/xml/ows_machine_sidebar.xml diff --git a/__manifest__.py b/__manifest__.py index 6529af3..e0005ad 100644 --- a/__manifest__.py +++ b/__manifest__.py @@ -23,6 +23,8 @@ 'open_workshop/static/src/css/pos.css', 'open_workshop/static/src/js/ows_pos_customer_sidebar.js', 'open_workshop/static/src/xml/ows_pos_customer_sidebar.xml', + 'open_workshop/static/src/js/ows_machine_access_list.js', + 'open_workshop/static/src/xml/ows_machine_access_list.xml', 'open_workshop/static/src/xml/ows_product_screen.xml', ], }, diff --git a/models/ows_models.py b/models/ows_models.py index 7032fa3..e2fea6e 100644 --- a/models/ows_models.py +++ b/models/ows_models.py @@ -424,10 +424,10 @@ class OwsMachine(models.Model): @api.model def get_access_list_grouped(self, partner_id): + partner = self.env['res.partner'].browse(partner_id) areas = self.env['ows.machine.area'].search([], order="name") - _logger.info("🔍 Maschinenbereiche: %s", areas.mapped('name')) - _logger.info("🔍 Partner_id: %s", partner_id) - res = [] + + access_by_area = [] for area in areas: machines = self.search([('area_id', '=', area.id)], order="name") machine_list = [] @@ -440,12 +440,22 @@ class OwsMachine(models.Model): 'name': machine.name, 'has_access': has_access, }) - res.append({ - 'area': area.name, - 'color_hex': area.color_hex or '#000000', - 'machines': machine_list - }) - return res + if machine_list: + access_by_area.append({ + 'area': area.name, + 'color_hex': area.color_hex or '#000000', + 'machines': machine_list + }) + + user = partner.ows_user_id[:1] + return { + 'access_by_area': access_by_area, + 'security_briefing': user.security_briefing if user else False, + 'security_id': user.security_id if user else '', + 'rfid_card': user.rfid_card if user else '', + 'birthday': user.birthday if user else '', + } + class OwsMachineAccess(models.Model): diff --git a/static/src/js/machine_access_sidebar.js b/static/src/js/machine_access_sidebar.js deleted file mode 100644 index f94cb2b..0000000 --- a/static/src/js/machine_access_sidebar.js +++ /dev/null @@ -1,132 +0,0 @@ -/* This file is based on the original code from the OrderWidget in Odoo Point of Sale module (screen.js). - * It has been modified to create a sidebar that displays machine access information for the selected customer.*/ - -odoo.define('open_workshop.machine_access_sidebar', function (require) { - "use strict"; - - const DUMMY_PARTNER = { - id: 1, - name: "AAAA Max Mustermann", - security_briefing: false, - security_id: null, - create_date: null, - }; - - var rpc = require('web.rpc'); - var screens = require('point_of_sale.screens'); - var chrome = require('point_of_sale.chrome'); - var core = require('web.core'); - var QWeb = core.qweb; - - - - var MachineAccessSidebar = screens.ScreenWidget.extend({ - template: 'MachineAccessSidebar', - - init: function(parent, options) { - this._super(parent, options); - this.partner = null; - this.pos.bind('change:selectedOrder', this.bind_order_events, this); - }, - - show: function() { - this._super(); - this.render_access(); - }, - - update_machine_access: function(partner) { - console.log("🔁 Sidebar aktualisiert Maschinenfreigaben für Partner:", partner); - this.partner = partner; - this.render_access(); - }, - - render_access: function() { - var self = this; - var partner = this.partner || DUMMY_PARTNER; - - - - rpc.query({ - model: 'ows.machine', - method: 'get_access_list_grouped', - args: [partner.id], - }).then(function (result) { - partner.create_date = partner.create_date && partner.create_date.substring(0, 10); - var html = QWeb.render('PartnerMachineAccessList', { - areas: result || [], - partner: partner, - }); - self.$('.access-content').html(html); - }); - }, - - bind_order_events: function () { - var order = this.pos.get_order(); - if (!order) return; - - /*order.unbind('change:client', this); - order.bind('change:client', this, function () { - this.update_machine_access(order.get_client()); - });*/ - - this.update_machine_access(order.get_client()); - } - }); - - // Sidebar aktualisieren bei Klick im ClientListScreenWidget (Vorschau) - screens.ClientListScreenWidget.include({ - show: function () { - this._super(); - $('.order-selector').hide(); // ← wird hier versteckt - }, - hide: function () { - this._super(); - $('.order-selector').show(); // ← beim Verlassen wieder zeigen - }, - display_client_details: function (visibility, partner, clickpos) { - this._super(visibility, partner, clickpos); - - try { - if (partner && typeof partner === 'object' && 'id' in partner) { - var sidebar = this.pos.chrome.sidebar_widget; - if (sidebar) { - console.log("👤 ClientListScreen: Vorschau für", partner.name); - sidebar.update_machine_access(partner); - } - } - } catch (e) { - console.warn("⚠️ Fehler beim Update der Sidebar nach Kundenauswahl:", e); - } - } - }); - - chrome.Chrome.include({ - build_widgets: function () { - this._super(); - - var sidebar = new MachineAccessSidebar(this, {}); - this.sidebar_widget = sidebar; - sidebar.appendTo(this.$el); - - //this.pos.bind('change:selectedOrder', sidebar.bind_order_events, sidebar); - - if (this.pos.get_order()) { - sidebar.bind_order_events(); - } - } - }); -}); - - -odoo.define('open_workshop.models', function (require) { - "use strict"; - var models = require('point_of_sale.models'); - var field_utils = require('web.field_utils'); - models.load_fields('res.partner', 'create_date'); - models.load_fields('res.partner', 'birthday'); - models.load_fields('res.partner', 'security_briefing'); - models.load_fields('res.partner', 'security_id'); - models.load_fields('res.partner', 'rfid_card'); - - -}); \ No newline at end of file diff --git a/static/src/js/ows_machine_access_list.js b/static/src/js/ows_machine_access_list.js new file mode 100644 index 0000000..f209071 --- /dev/null +++ b/static/src/js/ows_machine_access_list.js @@ -0,0 +1,63 @@ +// @odoo-module + +import { Component, useState, onMounted } from "@odoo/owl"; +import { useBus } from "@web/core/utils/hooks"; +import { usePos } from "@point_of_sale/app/store/pos_hook"; +import { jsonrpc } from "@web/core/network/rpc_service"; + +export class OwsMachineAccessList extends Component { + static template = "open_workshop.OwsMachineAccessList"; + + setup() { + this.pos = usePos(); + + this.state = useState({ + client: null, + grouped_accesses: [], + security_briefing: false, + security_id: '', + rfid_card: '', + birthday: '', + }); + + // Initialer Client bei Komponentenerstellung (z. B. bei Seiten-Refresh) + const initial_order = this.pos.get_order?.(); + const initial_client = initial_order?.get_client?.() || null; + this.state.client = initial_client; + + // OWL-idiomatisch: Bus-Events mit useBus() registrieren + useBus(this.env.bus, "order-changed", () => this.updateAccessList()); + useBus(this.env.bus, "client-selected", () => this.updateAccessList()); + + // Beim Initial-Render Zugriffsdaten laden + onMounted(() => { + this.updateAccessList(); + }); + } + + async updateAccessList() { + const partner = this.pos.get_order?.()?.get_client?.() || null; + this.state.client = partner; + + try { + const data = await jsonrpc("/open_workshop/partner_access", { + partner_id: partner?.id || 0, + }); + + this.state.grouped_accesses = data.access_by_area || []; + this.state.security_briefing = data.security_briefing; + this.state.security_id = data.security_id; + this.state.rfid_card = data.rfid_card; + this.state.birthday = data.birthday; + + } catch (error) { + console.error("Fehler beim Laden der Einweisungen:", error); + this.state.grouped_accesses = []; + this.state.security_briefing = false; + this.state.security_id = ''; + this.state.rfid_card = ''; + this.state.birthday = ''; + } + } +} + diff --git a/static/src/js/product_screen_template_patch.js b/static/src/js/product_screen_template_patch.js index c86c55a..d478e56 100644 --- a/static/src/js/product_screen_template_patch.js +++ b/static/src/js/product_screen_template_patch.js @@ -3,12 +3,14 @@ import { ProductScreen } from "@point_of_sale/app/screens/product_screen/product_screen"; import { registry } from "@web/core/registry"; import { OwsPosCustomerSidebar } from "@open_workshop/js/ows_pos_customer_sidebar"; +import { OwsMachineAccessList } from "@open_workshop/js/ows_machine_access_list"; export class OwsProductScreen extends ProductScreen { static template = "open_workshop.ProductScreen"; static components = { ...ProductScreen.components, OwsPosCustomerSidebar, + OwsMachineAccessList, }; } diff --git a/static/src/xml/ows_machine_access_list.xml b/static/src/xml/ows_machine_access_list.xml new file mode 100644 index 0000000..787441b --- /dev/null +++ b/static/src/xml/ows_machine_access_list.xml @@ -0,0 +1,76 @@ + +
+ + + +
+
    +
  • Einweisungen
  • +
+ +
+
    +
  • + + Werkstatt +
  • + +
  • + + + + + + + Haftungsausschluss +
  • + + +
  • +
      + ‼️Bitte Prüfen‼️ +
    +
  • +
    + + +
      +
    • + Id: + + + +
    • +
    • + Geburtstag: + + + +
    • +
    +
    +
+
+
+
+ + + + +
+
    + +
  • + + + + +
  • +
    +
+
+
+
+ +
+
diff --git a/static/src/xml/ows_machine_sidebar.xml b/static/src/xml/ows_machine_sidebar.xml deleted file mode 100644 index 61f19a2..0000000 --- a/static/src/xml/ows_machine_sidebar.xml +++ /dev/null @@ -1,88 +0,0 @@ - - - -
-
-
-
-
- -
-
-
    -
  • Einweisungen
  • -
- -
-
    -
  • - - Werkstatt -
  • - -
  • - - - - - - - Haftungsausschluss -
  • - - - -
  • -
      ‼️Bitte Prüfen‼️
    -
  • -
    - - - -
      -
    • - Id: - - - - - N/A - -
    • -
    • - Erstellt: - - - - - N/A - -
    • -
    -
    - -
-
-
- - -
-
    - -
  • - - - - - - - -
  • -
    -
-
-
-
-
- -
diff --git a/static/src/xml/ows_product_screen.xml b/static/src/xml/ows_product_screen.xml index 287376b..2893a3f 100644 --- a/static/src/xml/ows_product_screen.xml +++ b/static/src/xml/ows_product_screen.xml @@ -4,6 +4,7 @@
+
Date: Mon, 2 Jun 2025 23:35:49 +0200 Subject: [PATCH 2/3] [IMP] open_workshop: trigger-based update of machine access list on partner change - Replaced reactive `effect()` with `env.bus` event handling for partner changes - `OwsPosCustomerSidebar` emits `partner-changed` when order is selected - `OwsMachineAccessList` listens to `partner-changed` and updates access list - Refactored to use `.get_partner()` instead of deprecated `.get_client()` - Improved robustness and consistency of partner-based sidebar refresh --- static/src/js/ows_machine_access_list.js | 38 +++++++++++------------ static/src/js/ows_pos_customer_sidebar.js | 3 ++ 2 files changed, 22 insertions(+), 19 deletions(-) diff --git a/static/src/js/ows_machine_access_list.js b/static/src/js/ows_machine_access_list.js index f209071..fc9e981 100644 --- a/static/src/js/ows_machine_access_list.js +++ b/static/src/js/ows_machine_access_list.js @@ -1,18 +1,17 @@ // @odoo-module -import { Component, useState, onMounted } from "@odoo/owl"; +import { Component, useState } from "@odoo/owl"; import { useBus } from "@web/core/utils/hooks"; import { usePos } from "@point_of_sale/app/store/pos_hook"; import { jsonrpc } from "@web/core/network/rpc_service"; export class OwsMachineAccessList extends Component { - static template = "open_workshop.OwsMachineAccessList"; + static template = 'open_workshop.OwsMachineAccessList'; setup() { this.pos = usePos(); this.state = useState({ - client: null, grouped_accesses: [], security_briefing: false, security_id: '', @@ -20,28 +19,31 @@ export class OwsMachineAccessList extends Component { birthday: '', }); - // Initialer Client bei Komponentenerstellung (z. B. bei Seiten-Refresh) - const initial_order = this.pos.get_order?.(); - const initial_client = initial_order?.get_client?.() || null; - this.state.client = initial_client; - - // OWL-idiomatisch: Bus-Events mit useBus() registrieren - useBus(this.env.bus, "order-changed", () => this.updateAccessList()); - useBus(this.env.bus, "client-selected", () => this.updateAccessList()); - - // Beim Initial-Render Zugriffsdaten laden - onMounted(() => { + // 🔁 Reagiere auf Partnerwechsel über den Odoo-Bus + useBus(this.env.bus, 'partner-changed', () => { this.updateAccessList(); }); + + // 🔃 Beim Mounten initiale Daten laden + this.updateAccessList(); } async updateAccessList() { - const partner = this.pos.get_order?.()?.get_client?.() || null; - this.state.client = partner; + const order = this.pos.get_order(); + const partner = order?.get_partner?.(); + + if (!partner) { + this.state.grouped_accesses = []; + this.state.security_briefing = false; + this.state.security_id = ''; + this.state.rfid_card = ''; + this.state.birthday = ''; + return; + } try { const data = await jsonrpc("/open_workshop/partner_access", { - partner_id: partner?.id || 0, + partner_id: partner.id, }); this.state.grouped_accesses = data.access_by_area || []; @@ -49,7 +51,6 @@ export class OwsMachineAccessList extends Component { this.state.security_id = data.security_id; this.state.rfid_card = data.rfid_card; this.state.birthday = data.birthday; - } catch (error) { console.error("Fehler beim Laden der Einweisungen:", error); this.state.grouped_accesses = []; @@ -60,4 +61,3 @@ export class OwsMachineAccessList extends Component { } } } - diff --git a/static/src/js/ows_pos_customer_sidebar.js b/static/src/js/ows_pos_customer_sidebar.js index dfd95b8..e650f48 100644 --- a/static/src/js/ows_pos_customer_sidebar.js +++ b/static/src/js/ows_pos_customer_sidebar.js @@ -6,12 +6,14 @@ import { usePos } from "@point_of_sale/app/store/pos_hook"; import { _t } from "@web/core/l10n/translation"; import { ConfirmPopup } from "@point_of_sale/app/utils/confirm_popup/confirm_popup"; + export class OwsPosCustomerSidebar extends Component { static template = "open_workshop.OwsPosCustomerSidebar"; setup() { this.pos = usePos(); this.popup = useService("popup"); + } addOrder() { @@ -66,5 +68,6 @@ export class OwsPosCustomerSidebar extends Component { selectOrder(order) { this.pos.set_order(order); + this.env.bus.trigger('partner-changed'); // ✅ korrektes Event feuern } } -- 2.45.1 From a1618af03462c942f41801dc9ac88b151473220b Mon Sep 17 00:00:00 2001 From: "matthias.lotz" Date: Tue, 3 Jun 2025 17:43:44 +0200 Subject: [PATCH 3/3] [FIX] open_workshop: Das Datum vor dem Kunden in der POS Sidebar zeigt nun das Eincheckdatum an MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Datum wird nun im Format 'dd.mm. hh:mm' angezeigt (analog zu Odoo 17 TicketScreen). - Name rechtsbündig dargestellt, Datum linksbündig für bessere Lesbarkeit. - Tooltip bei langen Kundennamen zeigt vollständigen Namen beim Hover. --- static/src/css/pos.css | 21 +++++++++++++++++++++ static/src/js/ows_pos_customer_sidebar.js | 13 ++++++++++--- static/src/xml/ows_pos_customer_sidebar.xml | 5 ++++- 3 files changed, 35 insertions(+), 4 deletions(-) diff --git a/static/src/css/pos.css b/static/src/css/pos.css index c6f6e10..9276402 100644 --- a/static/src/css/pos.css +++ b/static/src/css/pos.css @@ -1,3 +1,24 @@ .ows-sidebar { width: 220px; } .order-entry:hover { cursor: pointer; } .order-entry.selected { background-color: #007bff; color: white; } + +.sidebar-line { + display: flex; + justify-content: space-between; + gap: 0.5em; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + padding: 0.2em 0; +} + +.sidebar-date { + flex-shrink: 0; + } + +.sidebar-name { + flex-shrink: 1; + text-align: right; + overflow: hidden; + text-overflow: ellipsis; +} diff --git a/static/src/js/ows_pos_customer_sidebar.js b/static/src/js/ows_pos_customer_sidebar.js index e650f48..ff69dca 100644 --- a/static/src/js/ows_pos_customer_sidebar.js +++ b/static/src/js/ows_pos_customer_sidebar.js @@ -5,7 +5,7 @@ import { useService } from "@web/core/utils/hooks"; import { usePos } from "@point_of_sale/app/store/pos_hook"; import { _t } from "@web/core/l10n/translation"; import { ConfirmPopup } from "@point_of_sale/app/utils/confirm_popup/confirm_popup"; - +import { deserializeDateTime, formatDateTime, parseDateTime } from "@web/core/l10n/dates"; export class OwsPosCustomerSidebar extends Component { static template = "open_workshop.OwsPosCustomerSidebar"; @@ -19,6 +19,7 @@ export class OwsPosCustomerSidebar extends Component { addOrder() { this.pos.add_new_order(); // neue Order wird aktive Order this.pos.selectPartner(); + this.env.bus.trigger('partner-changed'); // ✅ korrektes Event feuern } async removeCurrentOrder() { @@ -47,6 +48,7 @@ export class OwsPosCustomerSidebar extends Component { } // Hinweis: Weitere Funktionen wie Sync mit Server (siehe ticket_screen.js) können hier ergänzt werden. + this.env.bus.trigger('partner-changed'); // ✅ korrektes Event feuern } openTicketScreen() { @@ -58,10 +60,15 @@ export class OwsPosCustomerSidebar extends Component { } getDate(order) { - const date = new Date(order.creationDate || order.creation_date || Date.now()); - return date.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }); + const date = new Date(order.date_order); + const dd = String(date.getDate()).padStart(2, '0'); + const mm = String(date.getMonth() + 1).padStart(2, '0'); + const hh = String(date.getHours()).padStart(2, '0'); + const mi = String(date.getMinutes()).padStart(2, '0'); + return `${dd}.${mm}. ${hh}:${mi}`; } + getPartner(order) { return order.get_partner()?.name || "Kein Kunde"; } diff --git a/static/src/xml/ows_pos_customer_sidebar.xml b/static/src/xml/ows_pos_customer_sidebar.xml index b530d78..31f5e35 100644 --- a/static/src/xml/ows_pos_customer_sidebar.xml +++ b/static/src/xml/ows_pos_customer_sidebar.xml @@ -15,7 +15,10 @@ t-att-class="order === pos.get_order() ? 'bg-primary text-white' : 'bg-white'" t-on-click="() => selectOrder(order)"-->
-
+
-- 2.45.1