From 9a28d7dfebd8306422e90cd685bbbfe87df52588 Mon Sep 17 00:00:00 2001 From: Juliana <juliana@le-filament.com> Date: Mon, 6 Dec 2021 15:04:26 +0100 Subject: [PATCH] Clean code --- __manifest__.py | 2 +- models/__init__.py | 3 +- models/__pycache__/__init__.cpython-37.pyc | Bin 211 -> 0 bytes models/__pycache__/pos_config.cpython-37.pyc | Bin 576 -> 0 bytes .../pos_transaction.cpython-37.pyc | Bin 1875 -> 0 bytes models/pos_config.py | 13 +- models/pos_transaction.py | 49 --- static/src/js/custo_header.js | 69 ---- static/src/js/models_and_db.js | 320 ------------------ views/pos_transaction.xml | 61 ---- 10 files changed, 3 insertions(+), 514 deletions(-) delete mode 100644 models/__pycache__/__init__.cpython-37.pyc delete mode 100644 models/__pycache__/pos_config.cpython-37.pyc delete mode 100644 models/__pycache__/pos_transaction.cpython-37.pyc delete mode 100644 models/pos_transaction.py delete mode 100755 static/src/js/custo_header.js delete mode 100644 static/src/js/models_and_db.js delete mode 100755 views/pos_transaction.xml diff --git a/__manifest__.py b/__manifest__.py index 2a6afba..102f113 100644 --- a/__manifest__.py +++ b/__manifest__.py @@ -1,7 +1,7 @@ { "name": "VRACOOP - POS Balance Container QRCode", "summary": "VRACOOP - POS Balance Container QRCode", - "version": "12.0.0.0.0", + "version": "12.0.0.1.0", "development_status": "Beta", "author": "Le Filament", "maintainers": ["remi-filament"], diff --git a/models/__init__.py b/models/__init__.py index 7428ce5..c424d6d 100755 --- a/models/__init__.py +++ b/models/__init__.py @@ -1,5 +1,4 @@ # © 2019 Le Filament (<http://www.le-filament.com>) # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). -from . import pos_config -# from . import pos_transaction \ No newline at end of file +from . import pos_config \ No newline at end of file diff --git a/models/__pycache__/__init__.cpython-37.pyc b/models/__pycache__/__init__.cpython-37.pyc deleted file mode 100644 index 0a1c27b63d97e8415c08ed621c252052fb8f136b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 211 zcmZ?b<>g{vU|`@7SeLk&fq~&Mhy%k+3=9ko3=9m#0t^fcDGVu$ISjdsQH+cXDNMl( zn#?a585kHe8E<hF<QK;$=jWwmru%6!-C`&L>08NA#KOP;A$~>bhZd(673*ge<d>%8 zm+I%I<mZD4LnHm<(&Cc*T>Y}5#N_<^0vH_+HM1l!GcUC$zOX1cKP6Q^H$Npcr&vEe eJ~J<~BtBlRpz;=nO>TZlX-=vg$hOZQmjeJZd^rLD diff --git a/models/__pycache__/pos_config.cpython-37.pyc b/models/__pycache__/pos_config.cpython-37.pyc deleted file mode 100644 index a595e8c0c89059d79ece45ff55eaad57dd346075..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 576 zcmZ?b<>g{vU|>-Awl;AcGXuk85C?`?7#J8F7#J9ewHO!}QW#Pga~N_NqZo6UqL^}- zqnH^P+!<1sQkYv9QkYYjvsjv$qgYc|f*CYfUxG}~WWL3mSde*(EiE%OC#ConTW)?z zYEE%7NDhXXVK%5SFfgPtL@}l?L@}i>Mlq+dWU+$G?q!T(OJ!NWp2D(_F^VIFHJCw@ z?G|T1ez9|YURq{)6<0xiv0gHSE#sM6P?TC+oSC1e5E!K3oS%}a;GCaVl9-v7TBON% zi><h%C^Ij;O3X7QHLoNyEi*B%L?NYAAvZBSu{bl&Pm}W&M|^x<Vs2`D{4K8d_}u)I z(i{+*CqBNgG%*J%!x5jEmyueOSyBXY{VjH<{QR8M#Jnic%;NZhqRhOK_+*%+@r6aV zSe!Ewi=x<a6Vu}}Q&ut*@iH(#h+onAp~b01#rjzV`K2lOrTY0P`T1bN&`3YIw74Wc zSHG+%F*!fK07l0b<QJouo}8bOst*ZQeW+YoX1ZQMWf3a_0|PrK?l>427&sVNm{=G& z7{Q``nrydNKz0=|ft&*JL=h;aZ?XD<A_%Mo;YtXLkAZ>V7KaTaMC?GSi$R)2m;qMX BoaO)k diff --git a/models/__pycache__/pos_transaction.cpython-37.pyc b/models/__pycache__/pos_transaction.cpython-37.pyc deleted file mode 100644 index 5ef25d93b64c09db3718f5ce028e57458c5fc417..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1875 zcmZ?b<>g{vU|_h|QJ6T7oq^#ohy%kc3=9ko3=9m#UJMKjDGVu$ISjdsQH;4vQB1ka zQOt}WF{T`rD3%n46y_Y(T-GR7uoz1YOD<a!8zX}|LkepOTMI)9TPkxFdoyzsM+#Fg zgC_e+kPVv5x0n+PGH<b^Wv1q&6yIXY%}+_qDfZK3yv31{Sdv<jnVSk?gXpUKywqfn zCK!f0?F0h@Ln=cQV+unQQwn1ga|%-wODgLEwiM=tj8W`h7Ry4$C=M`-bs=LEXDVwJ zTMAnWdkRM{Qxq4N#|h<egLzyj+$lW0Oi?^w9xqgmH<dMuFNH6KKSiLIDT*J=6NK^v zQdt-9r3fu#j1mO1gcmYK38k`T38#pph^C13GDV4`ie`zWh^I)jFf=npiKmJ#kVuhS z$e1FPA`KRiOpys@(3HK!7m!~ZQk0lioS0manV(n1Uyxs{SArxU7Lu5hld6!CTC9Mi zuDFUjC^bzXpeR43G_#~i+%rWXB~>9OQ6VWYCowNMRX;f~v$#0*7F%GDbAC!{m8h$u zpP{irKz?RQu>wq6@k?F?1_n*WTP($yRjH+t3=9kqWnqU`7G<VoCMSZN1kqH*;gVRA zs^A&ye@ievIX}0cv?Mhi6l?LB#rc|yx7eX#Rs0Ymob&Ta5;OBsi>d@6mincZC}^bS zDP*T>YBIg#VPIf*$qccbEhRHOv!u962%<0~u_zU$vWhF9D6>MLB)=pvr-}#6PApZ( z&dkZL;x7RCJ-I{=N`sVw14F^nMU(LsTX9KIW?p(S6Ep#_GB7ZJurnxm`!O&uq%+hq zRk+kJx-i7r)H2sFFJMSvOkrBcRLfGsQe;rWuz<0KWg+7NrW)pjjEoF<JT(jpm_h6; zmRgn?rUk4ZSw@CJu5gALP!ebMyTz25;-<-Vi@6}b;1*j_QGR)G>MhptqRf)iTWrZi zsfi`2MS=_r47b=43kp*6QZ!j_u@tA~q}}2{itgfD{7Ag`%#`9=+(-hq_>eg9nJFt7 ziewlV7=Fd;hZd(673*ge<d>%8m+I%I<mZD4LnHm<(&Cc*T>Y}5#N_<^0vH`%kY601 zR+O3=4^1)gWk&jt45<&2Lb6A%pz;<U!~^kZMfth$rJ1szP!s?KEi(%f7b6cN3!@Md z4<jFAl`Lv{)<e~Iiwhhc;DnRR2-45MzyL}HYzzzx;HdFoU|^_lt6^|qh_$O_EMcf& z%wkMoY-TEAD`CoFUcdt4FJxT6TEet|Ern?z<3h$-rW&RN?4azQ!jQt!$|T89!x+y| z$QI6!!Vt`$$?A8DGqX59Ex#x?v80N}wX`U|AXPsgu_&|n7GuauVNe?6yv3f8Se#f= zQdA@liXoPQ%95&E%%vsCw>Wb0lM{0?t5R=qB^JX<rXrAYixfag!6g?s4=@*rGcYg| zNr4Cr5CMuAaP$;`f~$y|fq_9A6mA?Ke=~70@i0m;Rw<x`7e?Ol)0Dc!5g(tIn420O ze~T+VJ~uz5GzY}yiH|QVP0WGHu!5D`;)zd5Elw`VEC6NQB2duYVsXw$EV{)4Qg@3B z7TcLAQEY`p$@wX%w^&mX^9+rnxWQC>S!z*cS`o+tQT)lUOdJoEyT$63lb=`;#Z{h~ znVwM+pO;z^#SLYbBo?JcaTXM1CZ~eMc_B<_=Dfw>o0wN=l%JP+iwi0gpP5nw%AB`Y z!KtoD4iuW~DXE~WQ49_&NZ6r;B0B>E11KbmL7~sV#KH(le_V`QjC@EC%xB``D*~z3 z<h;cK$`4@Mz#&ous!WPhKyd^r62P%}i`5rY8-aCTv-lQ=4J6?0K$?p|aVWyb!NkMJ G!wdk{%MKI( diff --git a/models/pos_config.py b/models/pos_config.py index 59f6a6d..01717d9 100644 --- a/models/pos_config.py +++ b/models/pos_config.py @@ -9,15 +9,4 @@ class PosConfig(models.Model): is_print_container_qr = fields.Boolean( string='Impression QR Code Container') - mag_id = fields.Char("Identifiant du magasin") - # logo_balance = fields.Binary( - # string='Logo Balance', - # attachment=True) - # explication_header = fields.Char(string='Explication') - # weight_default = fields.Char(string='Poids par défault (kg)') - # balance_id = fields.Char("Identifiant de la balance") - # caisse_id = fields.Char("Identifiant de la caisse") - # logo_path = fields.Char( - # "URL Logo", compute="_compute_image_path") - # is_comptoir = fields.Boolean("Est une balance comptoir") - # allow_without_container = fields.Boolean("Autoriser les pesées sans contenant") \ No newline at end of file + mag_id = fields.Char("Identifiant du magasin") \ No newline at end of file diff --git a/models/pos_transaction.py b/models/pos_transaction.py deleted file mode 100644 index b0fab9d..0000000 --- a/models/pos_transaction.py +++ /dev/null @@ -1,49 +0,0 @@ -# © 2020 Le Filament (<http://www.le-filament.com>) -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). - -from odoo import api, fields, models -import pytz - -from datetime import datetime -from pytz import timezone - - -class PosTransaction(models.Model): - - _name = 'pos.transaction' - _description = 'Table des Transactions' - - name = fields.Char('Ref Produit') - balance_id = fields.Char('Id de la balance/caisse') - qrcode = fields.Char("QRCode") - ean13 = fields.Char('EAN13 Poids Produits', size=13) - ean13_verif = fields.Char('EAN13 Vérification Poids', size=13) - date_iso = fields.Char('Date ISO', compute="_compute_date_iso") - container_ean13 = fields.Char('EAN13 Container', size=13) - weight_net = fields.Float('Poids Net (en kg)', digits=(12,3)) - weight_tare = fields.Float('Poids Tare (en kg)', digits=(12,3)) - price_net = fields.Float('Prix total') - price_product = fields.Float('Prix au kilo') - product_id = fields.Many2one('product.product', string='Produit ID') - - @api.model - def create_from_ui(self, transactions): - # retourne la liste des ids dans le même ordre que la liste fournie - transaction_ids = [] - - for transaction in transactions: - transaction_id = transaction.pop('id', False) - if transaction_id: - self.browse(transaction_id).write(transaction) - else: - transaction_id = self.create(transaction).id - transaction_ids.append(transaction_id) - return transaction_ids - - @api.depends('write_date') - def _compute_date_iso(self): - for transaction in self: - # print("---date_iso ---", str(date_iso)) - if hasattr(transaction.write_date, 'isoformat'): - date_iso = pytz.utc.localize(transaction.write_date).astimezone(pytz.timezone('Europe/Paris')) - transaction.date_iso = date_iso.isoformat('T')[:19] diff --git a/static/src/js/custo_header.js b/static/src/js/custo_header.js deleted file mode 100755 index 6e85367..0000000 --- a/static/src/js/custo_header.js +++ /dev/null @@ -1,69 +0,0 @@ -// © 2020 Le Filament (<http://www.le-filament.com>) -// License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). - -odoo.define('vracoop_pos_free_balance_v2.custo_header', function (require) { -"use strict"; - -var chrome = require("point_of_sale.chrome"); -var PosBaseWidget = require('point_of_sale.BaseWidget'); - -chrome.Chrome.include({ - build_widgets: function(){ - this.widgets.push({ - 'name': 'shopname', - 'widget': ShopnameWidget, - 'replace': '.placeholder-ShopnameWidget'}); - - this.widgets.push({ - 'name': 'shopimage', - 'widget': ShopimageWidget, - 'replace': '.placeholder-ShopimageWidget'}); - this._super(); - }, - -}); - -var ShopnameWidget = PosBaseWidget.extend({ - template: 'ShopnameWidget', - init: function(parent, options){ - options = options || {}; - this._super(parent,options); - }, - renderElement: function(){ - var self = this; - this._super(); - }, - get_shopname: function(){ - var header_text = this.pos.get_name_header(); - if(header_text){ - return header_text; - }else{ - return ""; - } - }, -}); - - -var ShopimageWidget = PosBaseWidget.extend({ - template: 'ShopimageWidget', - init: function(parent, options){ - options = options || {}; - this._super(parent,options); - }, - renderElement: function(){ - var self = this; - this._super(); - }, - get_shopimage: function(){ - var header_text = this.pos.get_name_header(); - if(header_text){ - return header_text; - }else{ - return ""; - } - }, -}); -return { - 'ShopnameWidget': ShopnameWidget, - }; -}); diff --git a/static/src/js/models_and_db.js b/static/src/js/models_and_db.js deleted file mode 100644 index e9282bc..0000000 --- a/static/src/js/models_and_db.js +++ /dev/null @@ -1,320 +0,0 @@ -/* - © 2020 Le Filament (<http://www.le-filament.com>) - License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). -*/ - -odoo.define('vracoop_pos_free_balance_v2.models_and_db_balance', function (require) { - "use strict"; - - var PosDB = require('point_of_sale.DB'); - var models = require('point_of_sale.models'); - var rpc = require('web.rpc'); - var config = require('web.config'); - var config = require('web.config'); - - var core = require('web.core'); - var QWeb = core.qweb; - - var utils = require('web.utils'); - var field_utils = require('web.field_utils'); - var round_di = utils.round_decimals; - var round_pr = utils.round_precision; - - - // include not available => extend - models.PosModel = models.PosModel.extend({ - - scan_container_check: function(parsed_code){ - var transactions = this.db.get_transactions_sorted(1000); - var container = this.db.get_container_by_barcode( - parsed_code.base_code); - - // var selected_order = this.get_order(); - - // selected_order.add_container(container); - - var today = new Date(); - var date = today.getFullYear() + '-' + (today.getMonth()+1) + '-' + today.getDate(); - - // var isostring = today.toISOString(); - // var localestring = today.toLocaleString(); - // var dateUTC = today.toUTCString(); - var min = today.getMinutes(); - if (min < 10) { - min = "0" + today.getMinutes(); - } - - var hour = today.getHours()-1; - if (hour < 10) { - hour = "0" + hour; - } - var time = (hour) + ":" + min + ":" + today.getSeconds(); - var date_time = date + ' ' + time; - - for(var i = 0, len = transactions.length; i < len; i++) { - var transaction = transactions[i]; - if ( - (transaction.container_ean13 == parsed_code.base_code) && - transaction.write_date > date_time ) { - return transaction; - } - } - - return false; - }, - - // saves the transaction locally and try to send it to the backend. - // it returns a deferred that succeeds after having tried to send - // the container and all the other pending containers. - push_transaction: function(transaction, opts) { - opts = opts || {}; - var self = this; - - if(transaction){ - this.db.add_transactions([transaction]); - } - - var pushed = new $.Deferred(); - - this.flush_mutex.exec(function(){ - var flushed = self._save_transactions_to_server(self.db.get_transactions_sorted(), opts); - - flushed.always(function(ids){ - pushed.resolve(); - }); - - return flushed; - }); - return pushed; - }, - - // send an array of containers to the server - // available options: - // - timeout: timeout for the rpc call in ms - // returns a deferred that resolves with the list of - // server generated ids for the sent containers - _save_transactions_to_server: function (transactions, options) { - var self = this; - var transactions= transactions.filter(transaction => !( "id" in transaction)) - if (!transactions || !transactions.length) { - var result = $.Deferred(); - result.resolve([]); - return result; - } - - options = options || {}; - var timeout = typeof options.timeout === 'number' ? options.timeout : 7500 * transactions.length; - - return rpc.query({ - model: 'pos.transaction', - method: 'create_from_ui', - args: [transactions], - }, { - timeout: timeout, - }) - .then(function (server_ids) { - _.each(transactions, function(transaction, key){ - transaction["id"] = server_ids[key] - }); - self.set('failed',false); - return server_ids; - }).fail(function (type, error){ - if(error.code === 200 ){ // Business Logic Error, not a connection problem - //if warning do not need to display traceback!! - if (error.data.exception_type == 'warning') { - delete error.data.debug; - } - - // Hide error if already shown before ... - if ((!self.get('failed') || options.show_error) && !options.to_invoice) { - self.gui.show_popup('error-traceback',{ - 'title': error.data.message, - 'body': error.data.debug - }); - } - self.set('failed',error); - } - console.error('Failed to send transactions:', transactions); - }); - }, - - // returns the header text from config - get_name_header: function(){ - if (this.config.explication_header) { - return this.config.explication_header; - } - }, - - // returns the id of caisse from config - get_balance_id: function(){ - if (this.config.balance_id) { - return this.config.balance_id; - } - }, - - // returns the id of balance from config - get_caisse_id: function(){ - if (this.config.caisse_id) { - return this.config.caisse_id; - } - }, - - // returns if the pos is balance from config - get_is_balance_free: function(){ - if (this.config.is_balance_free) { - return this.config.is_balance_free; - } - }, - - get_is_comptoir: function(){ - if (this.config.is_comptoir) { - return this.config.is_comptoir; - } - }, - - }); - - - PosDB.include({ - init: function(parent, options) { - this._super(parent, options); - - this.transaction_sorted = []; - this.transaction_by_barcode = {}; - this.transaction_by_id = {}; - this.transaction_write_date = null; - - this.header_text = ""; - }, - - // returns the header text from config - get_name_header: function(){ - return this.db.get_name_header(); - }, - - add_transactions: function(transactions) { - var updated_count = 0; - var new_write_date = ''; - for(var i = 0, len = transactions.length; i < len; i++) { - var transaction = transactions[i]; - - if (this.transaction_write_date && - new Date(this.transaction_write_date).getTime() + 1000 >= - new Date(transaction.write_date).getTime() ) { - continue; - } else if ( new_write_date < transaction.write_date ) { - new_write_date = transaction.write_date; - } - - if (!this.transaction_by_barcode[transaction.container_ean13]) { - this.transaction_sorted.push(transaction.container_ean13); - } - this.transaction_by_barcode[transaction.container_ean13] = transaction; - - updated_count += 1; - } - - this.transaction_write_date = new_write_date || this.transaction_write_date; - - if (updated_count) { - // If there were updates, we need to completely - // rebuild the search string and the id indexing - - // this.container_search_string = ""; - this.transaction_by_id = {}; - - for (var barcode in this.transaction_by_barcode) { - var transaction = this.transaction_by_barcode[barcode]; - - if(transaction.id){ - this.transaction_by_id[transaction.id] = transaction; - } - // this.container_search_string += this._container_search_string(container); - } - } - - return updated_count; - }, - - get_transaction_by_id: function(id){ - return this.transaction_by_id[id]; - }, - - get_transactions_sorted: function(max_count){ - max_count = max_count ? Math.min(this.transaction_sorted.length, max_count) : this.transaction_sorted.length; - var transactions = []; - for (var i = 0; i < max_count; i++) { - transactions.push(this.transaction_by_barcode[this.transaction_sorted[i]]); - } - - return transactions; - }, - - remove_transactions: function(barcodes){ - for(var i = 0; i < barcodes.length; i++) { - var transaction = this.transaction_by_barcode[barcodes[i]]; - if (transaction){ - var index_s = this.transaction_sorted.indexOf(transaction.container_ean13); - this.transaction_sorted.splice(index_s, 1); - delete this.transaction_by_id[transaction.id]; - delete this.transaction_by_barcode[transaction.container_ean13]; - } - } - }, - - transaction_by_barcode: function(barcode){ - return this.transaction_by_barcode[barcode]; - }, - - get_product_name: function(transaction){ - return transaction.name; - }, - - get_today_date: function(){ - var today = new Date(); - var date = today.getFullYear() + '-' + (today.getMonth()+1) + '-' + today.getDate(); - return date; - }, - - }); - - // Add container to order line - models.Orderline = models.Orderline.extend({ - - export_as_JSON: function(){ - var pack_lot_ids = []; - if (this.has_product_lot){ - this.pack_lot_lines.each(_.bind( function(item) { - return pack_lot_ids.push([0, 0, item.export_as_JSON()]); - }, this)); - } - return { - qty: this.get_quantity(), - price_unit: this.get_unit_price(), - price_subtotal: this.get_price_without_tax(), - price_subtotal_incl: this.get_price_with_tax(), - discount: this.get_discount(), - product_id: this.get_product().id, - tax_ids: [[6, false, _.map(this.get_applicable_taxes(), function(tax){ return tax.id; })]], - id: this.id, - pack_lot_ids: pack_lot_ids, - //custom starts here - tare: this.get_tare(), - container_id: this.get_container() ? this.get_container().id : null, - container_barcode: this.get_container() ? this.get_container().barcode : null, - container_weight: this.get_container() ? this.get_container().weight : null, - caisse_id: this.pos.config.caisse_id, - }; - }, - }), - - models.load_models({ - model: 'pos.transaction', - fields: ['product_id', 'name', 'balance_id', 'ean13', 'write_date', 'container_ean13', 'weight_net', 'price_product', 'price_net', 'weight_tare'], - domain: function(self){ return [['write_date','>',self.db.get_today_date()]]; }, - loaded: function(self, transactions){ - self.db.add_transactions(transactions); - }, - }); - -}); \ No newline at end of file diff --git a/views/pos_transaction.xml b/views/pos_transaction.xml deleted file mode 100755 index 032fd37..0000000 --- a/views/pos_transaction.xml +++ /dev/null @@ -1,61 +0,0 @@ -<odoo> - <record id="pos_transaction_tree_view" model="ir.ui.view"> - <field name="name">pos.transaction.tree</field> - <field name="model">pos.transaction</field> - <field name="arch" type="xml"> - <tree> - <field name="balance_id"/> - <field name="name"/> - <field name="qrcode"/> - <field name="ean13"/> - <field name="ean13_verif"/> - <field name="write_date"/> - <field name="date_iso"/> - <!-- <field name="weight"/> --> - </tree> - </field> - </record> - - <record id="pos_transaction_form_view" model="ir.ui.view"> - <field name="name">pos.transaction.form</field> - <field name="model">pos.transaction</field> - <field name="arch" type="xml"> - <form> - <sheet> - <group> - <group> - <field name="balance_id" readonly="1"/> - <field name="name" readonly="1"/> - <field name="product_id" readonly="1"/> - <field name="write_date" readonly="1"/> - <field name="date_iso" readonly="1"/> - </group> - <group> - <field name="ean13" readonly="1"/> - <field name="ean13_verif" readonly="1"/> - <field name="qrcode" readonly="1"/> - <field name="weight_net" readonly="1"/> - <field name="weight_tare" readonly="1"/> - <field name="price_product" readonly="1"/> - <field name="price_net" readonly="1"/> - </group> - </group> - </sheet> - </form> - </field> - </record> - - <record id="pos_transaction_action_window" model="ir.actions.act_window" > - <field name="name">transactions</field> - <field name="res_model">pos.transaction</field> - <field name="view_mode">tree,form</field> - </record> - - <menuitem id="pos_transaction_menu" - name="transactions" - parent="point_of_sale.pos_config_menu_catalog" - sequence="13" - action="pos_transaction_action_window" - groups="point_of_sale.group_pos_manager,point_of_sale.group_pos_user" - /> -</odoo> -- GitLab