From 7b37ed92a1406007d69a8f5081fce6bedcbdefc1 Mon Sep 17 00:00:00 2001 From: luis Date: Thu, 22 May 2025 13:09:29 +0200 Subject: [PATCH] LaOsaCoop/Odoo16#12 migration pos_balance_multishop + pos_balance_epelsa --- .gitignore | 2 +- pos_balance_epelsa/README.rst | 46 +++ pos_balance_epelsa/__init__.py | 3 + pos_balance_epelsa/__manifest__.py | 23 ++ pos_balance_epelsa/data/balance_data.xml | 9 + pos_balance_epelsa/models/__init__.py | 3 + pos_balance_epelsa/models/product.py | 325 ++++++++++++++++++ pos_balance_multishop/README.rst | 46 +++ pos_balance_multishop/__init__.py | 3 + pos_balance_multishop/__manifest__.py | 24 ++ pos_balance_multishop/i18n/es.po | 245 +++++++++++++ .../i18n/pos_balance_multishop.pot | 245 +++++++++++++ pos_balance_multishop/models/__init__.py | 25 ++ pos_balance_multishop/models/product.py | 151 ++++++++ pos_balance_multishop/models/sale.py | 14 + pos_balance_multishop/models/sale_balance.py | 27 ++ .../security/ir.model.access.csv | 13 + pos_balance_multishop/views/product_view.xml | 53 +++ pos_balance_multishop/views/sale_view.xml | 71 ++++ .../models/product_template.py | 2 +- 20 files changed, 1328 insertions(+), 2 deletions(-) create mode 100644 pos_balance_epelsa/README.rst create mode 100644 pos_balance_epelsa/__init__.py create mode 100644 pos_balance_epelsa/__manifest__.py create mode 100644 pos_balance_epelsa/data/balance_data.xml create mode 100644 pos_balance_epelsa/models/__init__.py create mode 100644 pos_balance_epelsa/models/product.py create mode 100644 pos_balance_multishop/README.rst create mode 100644 pos_balance_multishop/__init__.py create mode 100644 pos_balance_multishop/__manifest__.py create mode 100644 pos_balance_multishop/i18n/es.po create mode 100644 pos_balance_multishop/i18n/pos_balance_multishop.pot create mode 100644 pos_balance_multishop/models/__init__.py create mode 100644 pos_balance_multishop/models/product.py create mode 100644 pos_balance_multishop/models/sale.py create mode 100644 pos_balance_multishop/models/sale_balance.py create mode 100644 pos_balance_multishop/security/ir.model.access.csv create mode 100644 pos_balance_multishop/views/product_view.xml create mode 100644 pos_balance_multishop/views/sale_view.xml diff --git a/.gitignore b/.gitignore index 11335b7..e03074a 100644 --- a/.gitignore +++ b/.gitignore @@ -56,7 +56,7 @@ coverage.xml # Translations *.mo -*.pot +#*.pot # Django stuff: *.log diff --git a/pos_balance_epelsa/README.rst b/pos_balance_epelsa/README.rst new file mode 100644 index 0000000..60c018b --- /dev/null +++ b/pos_balance_epelsa/README.rst @@ -0,0 +1,46 @@ +.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg + :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html + :alt: License: AGPL-3 + +=========================== +POS Balace Multishop Epelsa +=========================== + +Update Euroscale Epelsa Balances + +Installation +============ + +To install this module, you need to: + +* pos_balance_multishop + +TODO +==== + +* Nothing + +Bug Tracker +=========== + +Bugs are tracked on `GitHub Issues +`_. In case of trouble, please +check there if your issue has already been reported. If you spotted it first, +help us smashing it by providing a detailed and welcomed feedback. + +Credits +======= + +Contributors +------------ + +* Ignacio Ibeas +* Iñaki Santos + + +Maintainer +---------- + +.. image:: https://acysos.com/logo.png + :alt: Acysos S.L. + :target: https://www.acysos.com \ No newline at end of file diff --git a/pos_balance_epelsa/__init__.py b/pos_balance_epelsa/__init__.py new file mode 100644 index 0000000..f0bbfa8 --- /dev/null +++ b/pos_balance_epelsa/__init__.py @@ -0,0 +1,3 @@ +# Copyright 2014 Ignacio Ibeas +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +from . import models diff --git a/pos_balance_epelsa/__manifest__.py b/pos_balance_epelsa/__manifest__.py new file mode 100644 index 0000000..d9b0b6a --- /dev/null +++ b/pos_balance_epelsa/__manifest__.py @@ -0,0 +1,23 @@ +# Copyright 2014 Ignacio Ibeas +# Copyright 2020 Santi Noreña +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +{ + "name": "Epelsa Balances", + "version": "16.0.1.0.0", + "category": "Specific Industries", + "summary": "Communication with Epelsa Balances.", + "description": """ + Communication with Epelsa Balances. + Type Euroescale. + Communication Socket TCP/IP. + Protocol Ascii. + """, + "author": "Acysos S.L., Criptomart S.L.L.", + "website": "http://www.acysos.com, https://criptomart.net", + "license": "AGPL-3", + "depends": ["base", "pos_balance_multishop"], + "data": ["data/balance_data.xml"], + "demo": [], + "installable": True, + "auto_install": False, +} diff --git a/pos_balance_epelsa/data/balance_data.xml b/pos_balance_epelsa/data/balance_data.xml new file mode 100644 index 0000000..6ff4186 --- /dev/null +++ b/pos_balance_epelsa/data/balance_data.xml @@ -0,0 +1,9 @@ + + + + + Epelsa Socket + epelsa_socket + + + \ No newline at end of file diff --git a/pos_balance_epelsa/models/__init__.py b/pos_balance_epelsa/models/__init__.py new file mode 100644 index 0000000..e95150b --- /dev/null +++ b/pos_balance_epelsa/models/__init__.py @@ -0,0 +1,3 @@ +# Copyright 2014 Ignacio Ibeas +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +from . import product diff --git a/pos_balance_epelsa/models/product.py b/pos_balance_epelsa/models/product.py new file mode 100644 index 0000000..da813a3 --- /dev/null +++ b/pos_balance_epelsa/models/product.py @@ -0,0 +1,325 @@ +# Copyright 2014 Ignacio Ibeas +# Copyright 2020 Santi Noreña - Criptomart +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +import logging +import time +import socket +from unidecode import unidecode + +from odoo import models, api, _ +from odoo.exceptions import ValidationError, UserError + +_logger = logging.getLogger(__name__) + + +class product_balance_code(models.Model): + _inherit = "product.balance.code" + + @api.onchange("key") + def _onchange_key(self): + for balance in self: + if balance.key: + if not len(balance.key) == 2: + raise ValidationError(_("The key must have 2 digits")) + if not balance.key.isdigit(): + raise ValidationError(_("Key between 0-9")) + + @api.onchange("table") + def _onchange_table(self): + for balance in self: + if balance.table: + if not len(balance.table) == 1: + raise ValidationError(_("The table must have 1 digit")) + if not balance.key.isdigit(): + raise ValidationError(_("Table between 0-9")) + + def get_checksum(self, micade): + check = 0 + longitud_cade = len(micade) + i = 0 + buffercheck = [] + while i < longitud_cade: + buffercheck.append(micade[i : i + 1]) + if buffercheck[i] == chr(126): + buffercheck[i] = chr(0) + check = check ^ ord(buffercheck[i]) + i += 1 + + check = check ^ (longitud_cade + 2) + check = (check & 63) | 64 + + return check + + def recv_timeout(self, the_socket, timeout=2): + # make socket non blocking + the_socket.setblocking(0) + + # total data partwise in an array + total_data = [] + data = "" + + # beginning time + begin = time.time() + while 1: + # if you got some data, then break after timeout + if total_data and time.time() - begin > timeout: + break + + # if you got no data at all, wait a little longer, + # twice the timeout + elif time.time() - begin > timeout * 2: + break + + # recv something + try: + data = the_socket.recv(8192) + for ch in data: + _logger.debug(ord(ch)), + _logger.debug("\n\n") + if data: + total_data.append(data) + # change the beginning time for measurement + begin = time.time() + else: + # sleep for sometime to indicate a gap + time.sleep(0.1) + except: + pass + + # join all parts to make final string + return "".join(total_data) + + def add_balance_epelsa_socket(self, code, balance): + # Product + # create an INET, STREAMing socket + try: + s = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0) + except socket.error: + _logger.debug("Failed to create socket") + return False + + _logger.debug("Socket Created") + + host = balance.ip + port = balance.port + + try: + remote_ip = socket.gethostbyname(host) + + except socket.gaierror: + # could not resolve + raise UserError(_("Error ! Hostname could not be resolved. Exiting")) + + # Connect to remote server + try: + s.connect((remote_ip, port)) + except: + raise UserError(_("Error ! No route to host")) + + _logger.debug("Socket Connected to " + host + " on ip " + remote_ip) + + # Send some data to remote server + operation = "7" + suboperation = "0" + prod_code = code.product_id.balance_name + list_price = round(code.product_id.list_price, 2) + _logger.debug("[epelsa] list_price rounded: %s " % list_price) + price = "%07d" % (round(list_price * 100, 2)) + _logger.debug("[epelsa] price to send rounded: %s " % price) + + if code.product_id.not_weighed: + sale_type = "U" + else: + sale_type = "W" + + name = unidecode(code.product_id.name.ljust(25)) + tare = code.product_id.tare + # Epelsa supports 25 char only name + name = name[:25] + message = chr(2) + operation + suboperation + prod_code + chr(0) + message += ( + "01" + chr(0) + code.product_id.balance_name[2:] + chr(0) + "000" + chr(0) + ) + message += "0000" + chr(0) + price + chr(0) + sale_type + chr(0) + message += "000" + chr(0) + "0" + chr(0) + name + chr(0) + message += str(tare).rjust(5, "0") + chr(0) + chr(3) + + _logger.debug("Message set product: " + message) + checksum = self.get_checksum(message) + _logger.debug("Checksum: chr(" + str(checksum) + ") = " + chr(checksum)) + message += chr(checksum) + chr(13) + + try: + # Set the whole string + s.sendall(bytes(message, "utf-8")) + except socket.error: + # Send failed + _logger.debug("Send failed") + raise UserError(_("Error! Send failed %s") % name) + + _logger.debug("Message send successfully set product : " + message) + + # get reply and print + # _logger.debug('Reply set product : ' + self.recv_timeout(s)) + + # Close the socket + s.close() + + # Key + # create an INET, STREAMing socket + try: + s = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0) + except socket.error: + raise UserError(_("Error! Failed to create socket %s") % name) + + _logger.debug("Socket Created") + + try: + remote_ip = socket.gethostbyname(host) + except socket.gaierror: + # could not resolve + raise UserError( + _("Error! Hostname could not be resolved. Exiting %s") % name + ) + + # Connect to remote server + s.connect((remote_ip, port)) + + _logger.debug("Socket Connected to " + host + " on ip " + remote_ip) + + # default values for key and table to prevent errors when not configured + key = "99" + table = "09" + # Send some data to remote server + operation = "5" + suboperation = "7" + mode = "1" + bal_code = balance.name + if code.table: + table = "0" + code.table + type_key = "1" + prod_code = code.product_id.balance_name + if code.key: + key = code.key + message = chr(2) + operation + suboperation + mode + chr(0) + message += bal_code + chr(0) + table + chr(0) + type_key + chr(0) + message += "01" + chr(0) + "0" + key + chr(0) + prod_code + chr(3) + + _logger.debug("Message set key : " + message) + checksum = self.get_checksum(message) + _logger.debug("Checksum: chr(" + str(checksum) + ") = " + chr(checksum)) + + message += chr(checksum) + chr(13) + + try: + # Set the whole string + s.sendall(bytes(message, "utf-8")) + except socket.error: + # Send failed + raise UserError(_("Error! Send failed %s") % name) + + _logger.debug("Message send successfully set key : " + message) + + # get reply and print + # _logger.debug('Reply set key : ' + self.recv_timeout(s)) + + # Close the socket + s.close() + return True + + def remove_balance_epelsa_socket(self, code, balance): + name = unidecode(code.product_id.name.ljust(25)) + try: + s = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0) + except socket.error: + raise UserError(_("Error! Failed to create socket %s") % name) + + _logger.debug("Socket Created") + + host = balance.ip + port = balance.port + + try: + remote_ip = socket.gethostbyname(host) + + except socket.gaierror: + # could not resolve + raise UserError( + _("Error! Hostname could not be resolved. Exiting %s") % name + ) + + # Connect to remote server + try: + s.connect((remote_ip, port)) + except: + raise UserError(_("Error! No route to host")) + + _logger.debug("Socket Connected to " + host + " on ip " + remote_ip) + + # Send some data to remote server + operation = "7" + suboperation = "2" + mode = "1" + prod_code = code.product_id.balance_name + + message = chr(2) + operation + suboperation + mode + chr(0) + message += prod_code + chr(3) + + _logger.debug("Message remove product : " + message) + checksum = self.get_checksum(message) + _logger.debug("Checksum: chr(" + str(checksum) + ") = " + chr(checksum)) + + message += chr(checksum) + chr(13) + + try: + # Set the whole string + s.sendall(bytes(message, "utf-8")) + except socket.error: + # Send failed + raise UserError(_("Error! Send failed {}").format(name)) + + _logger.debug("Message send successfully remove product : " + message) + + # get reply and print + _logger.debug("Reply remove product : " + self.recv_timeout(s)) + + # Close the socket + s.close() + return True + + def update_balance_epelsa_socket(self, code, balance): + _logger.debug("updating balance") + return self.add_balance_epelsa_socket(code, balance) + + +class ProductTemplate(models.Model): + _inherit = "product.template" + + @api.onchange("balance_name") + def _onchange_name(self): + for product in self: + if product.balance_name and not len(product.balance_name) == 6: + raise ValidationError(_("The code must have 6 digits")) + if product.balance_name and not product.balance_name.isdigit(): + raise ValidationError(_("Code between 0-9")) + + @api.constrains("tare") + def _check_tare(self): + tare = self.tare + if tare > 99999: + raise ValidationError(_("Tare must be maximum 99999")) + if tare % 5 != 0: + raise ValidationError(_("Tare must be multiple of 5")) + + +class ProductProduct(models.Model): + _inherit = "product.product" + + @api.onchange("balance_name") + def _onchange_name(self): + for product in self: + if not len(product.balance_name) == 6: + raise ValidationError(_("The code must have 6 digits")) + if not product.balance_name.isdigit(): + raise ValidationError(_("Code between 0-9")) diff --git a/pos_balance_multishop/README.rst b/pos_balance_multishop/README.rst new file mode 100644 index 0000000..127b5b7 --- /dev/null +++ b/pos_balance_multishop/README.rst @@ -0,0 +1,46 @@ +.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg + :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html + :alt: License: AGPL-3 + +==================== +POS Balace Multishop +==================== + +Add multiple code for balance products for each shop + +Installation +============ + +To install this module, you need to: + +* point_of_sale + +TODO +==== + +* Migrate to new API massive update product + +Bug Tracker +=========== + +Bugs are tracked on `GitHub Issues +`_. In case of trouble, please +check there if your issue has already been reported. If you spotted it first, +help us smashing it by providing a detailed and welcomed feedback. + +Credits +======= + +Contributors +------------ + +* Ignacio Ibeas +* Iñaki Santos + + +Maintainer +---------- + +.. image:: https://acysos.com/logo.png + :alt: Acysos S.L. + :target: https://www.acysos.com \ No newline at end of file diff --git a/pos_balance_multishop/__init__.py b/pos_balance_multishop/__init__.py new file mode 100644 index 0000000..f0bbfa8 --- /dev/null +++ b/pos_balance_multishop/__init__.py @@ -0,0 +1,3 @@ +# Copyright 2014 Ignacio Ibeas +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +from . import models diff --git a/pos_balance_multishop/__manifest__.py b/pos_balance_multishop/__manifest__.py new file mode 100644 index 0000000..18c26ee --- /dev/null +++ b/pos_balance_multishop/__manifest__.py @@ -0,0 +1,24 @@ +# Copyright 2014 Ignacio Ibeas +# Copyright 2020 Santi Noreña +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +{ + "name": "POS Balace Multishop", + "version": "16.0.1.0.0", + "category": "Point of sale", + "summary": "Add multiple code for balance products for each shop.", + "description": "Add multiple code for balance products for each shop.", + "depends": ["base", "point_of_sale"], + "author": "Acysos S.L., Criptomart S.L.L.", + "website": "http://www.acysos.com, https://criptomart.net", + "license": "AGPL-3", + "data": [ + "security/ir.model.access.csv", + "views/product_view.xml", + "views/sale_view.xml", + ], + "demo": [], + "test": [], + "installable": True, + "auto_install": False, +} diff --git a/pos_balance_multishop/i18n/es.po b/pos_balance_multishop/i18n/es.po new file mode 100644 index 0000000..f548979 --- /dev/null +++ b/pos_balance_multishop/i18n/es.po @@ -0,0 +1,245 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * pos_balance_multishop +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 12.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2021-12-02 10:21+0000\n" +"PO-Revision-Date: 2021-12-02 10:21+0000\n" +"Last-Translator: <>\n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: \n" + +#. module: pos_balance_multishop +#: model_terms:ir.ui.view,arch_db:pos_balance_multishop.product_balace_code_form_view +msgid "Balance" +msgstr "Saldo pendiente" + +#. module: pos_balance_multishop +#: model:ir.model.fields,field_description:pos_balance_multishop.field_product_product__balance_code_ids +#: model:ir.model.fields,field_description:pos_balance_multishop.field_product_template__balance_code_ids +msgid "Balance Codes" +msgstr "" + +#. module: pos_balance_multishop +#: model:ir.model,name:pos_balance_multishop.model_balance_model +#: model:ir.model.fields,field_description:pos_balance_multishop.field_sale_balance__model_id +msgid "Balance Model" +msgstr "" + +#. module: pos_balance_multishop +#: model:ir.model,name:pos_balance_multishop.model_balance_sale_shop +msgid "Balance Sale Shop" +msgstr "" + +#. module: pos_balance_multishop +#: model:ir.model.fields,field_description:pos_balance_multishop.field_balance_sale_shop__balance_ids +#: model_terms:ir.ui.view,arch_db:pos_balance_multishop.sale_balance_form_view +#: model_terms:ir.ui.view,arch_db:pos_balance_multishop.sale_balance_tree_view +#: model_terms:ir.ui.view,arch_db:pos_balance_multishop.view_shop_form_balance +msgid "Balances" +msgstr "" + +#. module: pos_balance_multishop +#: model_terms:ir.actions.act_window,help:pos_balance_multishop.action_sale_tree +msgid "Click to create balance shop." +msgstr "" + +#. module: pos_balance_multishop +#: model:ir.model.fields,field_description:pos_balance_multishop.field_balance_model__code +#: model:ir.model.fields,field_description:pos_balance_multishop.field_product_product__balance_name +#: model:ir.model.fields,field_description:pos_balance_multishop.field_product_template__balance_name +msgid "Code" +msgstr "Código" + +#. module: pos_balance_multishop +#: model:ir.model.fields,field_description:pos_balance_multishop.field_balance_model__create_uid +#: model:ir.model.fields,field_description:pos_balance_multishop.field_balance_sale_shop__create_uid +#: model:ir.model.fields,field_description:pos_balance_multishop.field_product_balance_code__create_uid +#: model:ir.model.fields,field_description:pos_balance_multishop.field_sale_balance__create_uid +msgid "Created by" +msgstr "Creado por" + +#. module: pos_balance_multishop +#: model:ir.model.fields,field_description:pos_balance_multishop.field_balance_model__create_date +#: model:ir.model.fields,field_description:pos_balance_multishop.field_balance_sale_shop__create_date +#: model:ir.model.fields,field_description:pos_balance_multishop.field_product_balance_code__create_date +#: model:ir.model.fields,field_description:pos_balance_multishop.field_sale_balance__create_date +msgid "Created on" +msgstr "Creado el" + +#. module: pos_balance_multishop +#: model:ir.model.fields,field_description:pos_balance_multishop.field_balance_model__display_name +#: model:ir.model.fields,field_description:pos_balance_multishop.field_balance_sale_shop__display_name +#: model:ir.model.fields,field_description:pos_balance_multishop.field_product_balance_code__display_name +#: model:ir.model.fields,field_description:pos_balance_multishop.field_sale_balance__display_name +msgid "Display Name" +msgstr "Nombre mostrado" + +#. module: pos_balance_multishop +#: model:ir.model.fields,field_description:pos_balance_multishop.field_balance_model__id +#: model:ir.model.fields,field_description:pos_balance_multishop.field_balance_sale_shop__id +#: model:ir.model.fields,field_description:pos_balance_multishop.field_product_balance_code__id +#: model:ir.model.fields,field_description:pos_balance_multishop.field_sale_balance__id +msgid "ID" +msgstr "" + +#. module: pos_balance_multishop +#: model:ir.model.fields,field_description:pos_balance_multishop.field_sale_balance__ip +msgid "IP" +msgstr "" + +#. module: pos_balance_multishop +#: model:ir.model.fields,field_description:pos_balance_multishop.field_product_balance_code__key +msgid "Key" +msgstr "Clave" + +#. module: pos_balance_multishop +#: model:ir.model.fields,field_description:pos_balance_multishop.field_balance_model____last_update +#: model:ir.model.fields,field_description:pos_balance_multishop.field_balance_sale_shop____last_update +#: model:ir.model.fields,field_description:pos_balance_multishop.field_product_balance_code____last_update +#: model:ir.model.fields,field_description:pos_balance_multishop.field_sale_balance____last_update +msgid "Last Modified on" +msgstr "Última modificación en" + +#. module: pos_balance_multishop +#: model:ir.model.fields,field_description:pos_balance_multishop.field_balance_model__write_uid +#: model:ir.model.fields,field_description:pos_balance_multishop.field_balance_sale_shop__write_uid +#: model:ir.model.fields,field_description:pos_balance_multishop.field_product_balance_code__write_uid +#: model:ir.model.fields,field_description:pos_balance_multishop.field_sale_balance__write_uid +msgid "Last Updated by" +msgstr "Última actualización por" + +#. module: pos_balance_multishop +#: model:ir.model.fields,field_description:pos_balance_multishop.field_balance_model__write_date +#: model:ir.model.fields,field_description:pos_balance_multishop.field_balance_sale_shop__write_date +#: model:ir.model.fields,field_description:pos_balance_multishop.field_product_balance_code__write_date +#: model:ir.model.fields,field_description:pos_balance_multishop.field_sale_balance__write_date +msgid "Last Updated on" +msgstr "Última actualización el" + +#. module: pos_balance_multishop +#: model:ir.model.fields,field_description:pos_balance_multishop.field_balance_model__name +#: model:ir.model.fields,field_description:pos_balance_multishop.field_sale_balance__name +msgid "Name" +msgstr "Nombre" + +#. module: pos_balance_multishop +#: model:ir.model.fields,field_description:pos_balance_multishop.field_sale_balance__network +msgid "Network Connection" +msgstr "" + +#. module: pos_balance_multishop +#: model:ir.model.fields,field_description:pos_balance_multishop.field_product_product__balance_nomenclature +#: model:ir.model.fields,field_description:pos_balance_multishop.field_product_template__balance_nomenclature +msgid "Nomenclature" +msgstr "Denominación" + +#. module: pos_balance_multishop +#: model:ir.model.fields,field_description:pos_balance_multishop.field_product_product__not_weighed +#: model:ir.model.fields,field_description:pos_balance_multishop.field_product_template__not_weighed +msgid "Not weighed" +msgstr "" + +#. module: pos_balance_multishop +#: model:ir.model.fields,field_description:pos_balance_multishop.field_sale_balance__port +msgid "Port" +msgstr "Puerto" + +#. module: pos_balance_multishop +#: selection:product.template,balance_type:0 +msgid "Price" +msgstr "Precio" + +#. module: pos_balance_multishop +#: model:ir.model,name:pos_balance_multishop.model_product_product +#: model:ir.model.fields,field_description:pos_balance_multishop.field_product_balance_code__product_id +msgid "Product" +msgstr "Producto" + +#. module: pos_balance_multishop +#: model:ir.model,name:pos_balance_multishop.model_product_balance_code +msgid "Product Balance Code" +msgstr "" + +#. module: pos_balance_multishop +#: model_terms:ir.ui.view,arch_db:pos_balance_multishop.product_balace_code_form_view +msgid "Product Balance Codes" +msgstr "" + +#. module: pos_balance_multishop +#: model:ir.model,name:pos_balance_multishop.model_product_template +msgid "Product Template" +msgstr "Plantilla de producto" + +#. module: pos_balance_multishop +#: model:ir.model.fields,field_description:pos_balance_multishop.field_product_product__balance_rule +#: model:ir.model.fields,field_description:pos_balance_multishop.field_product_template__balance_rule +msgid "Rule" +msgstr "" + +#. module: pos_balance_multishop +#: model:ir.model,name:pos_balance_multishop.model_sale_balance +msgid "Sale Balance" +msgstr "" + +#. module: pos_balance_multishop +#: model:ir.actions.act_window,name:pos_balance_multishop.action_sale_tree +#: model:ir.ui.menu,name:pos_balance_multishop.menu_sale_tree +msgid "Sale balance shop" +msgstr "Tienda de venta de balanza" + +#. module: pos_balance_multishop +#: model_terms:ir.ui.view,arch_db:pos_balance_multishop.view_shop_form_balance +msgid "Sale_shop" +msgstr "" + +#. module: pos_balance_multishop +#: model:ir.model.fields,field_description:pos_balance_multishop.field_product_balance_code__shop_id +#: model:ir.model.fields,field_description:pos_balance_multishop.field_sale_balance__shop_id +msgid "Shop" +msgstr "" + +#. module: pos_balance_multishop +#: model:ir.model.fields,field_description:pos_balance_multishop.field_product_balance_code__table +msgid "Table" +msgstr "Tabla" + +#. module: pos_balance_multishop +#: model:ir.model.fields,field_description:pos_balance_multishop.field_product_product__tare +#: model:ir.model.fields,field_description:pos_balance_multishop.field_product_template__tare +msgid "Tare" +msgstr "" + +#. module: pos_balance_multishop +#: model:ir.model.fields,help:pos_balance_multishop.field_product_product__tare +#: model:ir.model.fields,help:pos_balance_multishop.field_product_template__tare +msgid "Tare in grams of the product. Maximum 5 digits" +msgstr "" + +#. module: pos_balance_multishop +#: model:ir.model.fields,field_description:pos_balance_multishop.field_product_product__balance_type +#: model:ir.model.fields,field_description:pos_balance_multishop.field_product_template__balance_type +msgid "Type" +msgstr "Tipo" + +#. module: pos_balance_multishop +#: model:ir.actions.server,name:pos_balance_multishop.action_update_balance_codes_menu +msgid "Update Balances" +msgstr "" + +#. module: pos_balance_multishop +#: selection:product.template,balance_type:0 +msgid "Weight" +msgstr "Peso" + +#. module: pos_balance_multishop +#: model:ir.model.fields,field_description:pos_balance_multishop.field_balance_sale_shop__name +msgid "name" +msgstr "nombre" + diff --git a/pos_balance_multishop/i18n/pos_balance_multishop.pot b/pos_balance_multishop/i18n/pos_balance_multishop.pot new file mode 100644 index 0000000..60fd71f --- /dev/null +++ b/pos_balance_multishop/i18n/pos_balance_multishop.pot @@ -0,0 +1,245 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * pos_balance_multishop +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 12.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2021-12-02 10:20+0000\n" +"PO-Revision-Date: 2021-12-02 10:20+0000\n" +"Last-Translator: <>\n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: \n" + +#. module: pos_balance_multishop +#: model_terms:ir.ui.view,arch_db:pos_balance_multishop.product_balace_code_form_view +msgid "Balance" +msgstr "" + +#. module: pos_balance_multishop +#: model:ir.model.fields,field_description:pos_balance_multishop.field_product_product__balance_code_ids +#: model:ir.model.fields,field_description:pos_balance_multishop.field_product_template__balance_code_ids +msgid "Balance Codes" +msgstr "" + +#. module: pos_balance_multishop +#: model:ir.model,name:pos_balance_multishop.model_balance_model +#: model:ir.model.fields,field_description:pos_balance_multishop.field_sale_balance__model_id +msgid "Balance Model" +msgstr "" + +#. module: pos_balance_multishop +#: model:ir.model,name:pos_balance_multishop.model_balance_sale_shop +msgid "Balance Sale Shop" +msgstr "" + +#. module: pos_balance_multishop +#: model:ir.model.fields,field_description:pos_balance_multishop.field_balance_sale_shop__balance_ids +#: model_terms:ir.ui.view,arch_db:pos_balance_multishop.sale_balance_form_view +#: model_terms:ir.ui.view,arch_db:pos_balance_multishop.sale_balance_tree_view +#: model_terms:ir.ui.view,arch_db:pos_balance_multishop.view_shop_form_balance +msgid "Balances" +msgstr "" + +#. module: pos_balance_multishop +#: model_terms:ir.actions.act_window,help:pos_balance_multishop.action_sale_tree +msgid "Click to create balance shop." +msgstr "" + +#. module: pos_balance_multishop +#: model:ir.model.fields,field_description:pos_balance_multishop.field_balance_model__code +#: model:ir.model.fields,field_description:pos_balance_multishop.field_product_product__balance_name +#: model:ir.model.fields,field_description:pos_balance_multishop.field_product_template__balance_name +msgid "Code" +msgstr "" + +#. module: pos_balance_multishop +#: model:ir.model.fields,field_description:pos_balance_multishop.field_balance_model__create_uid +#: model:ir.model.fields,field_description:pos_balance_multishop.field_balance_sale_shop__create_uid +#: model:ir.model.fields,field_description:pos_balance_multishop.field_product_balance_code__create_uid +#: model:ir.model.fields,field_description:pos_balance_multishop.field_sale_balance__create_uid +msgid "Created by" +msgstr "" + +#. module: pos_balance_multishop +#: model:ir.model.fields,field_description:pos_balance_multishop.field_balance_model__create_date +#: model:ir.model.fields,field_description:pos_balance_multishop.field_balance_sale_shop__create_date +#: model:ir.model.fields,field_description:pos_balance_multishop.field_product_balance_code__create_date +#: model:ir.model.fields,field_description:pos_balance_multishop.field_sale_balance__create_date +msgid "Created on" +msgstr "" + +#. module: pos_balance_multishop +#: model:ir.model.fields,field_description:pos_balance_multishop.field_balance_model__display_name +#: model:ir.model.fields,field_description:pos_balance_multishop.field_balance_sale_shop__display_name +#: model:ir.model.fields,field_description:pos_balance_multishop.field_product_balance_code__display_name +#: model:ir.model.fields,field_description:pos_balance_multishop.field_sale_balance__display_name +msgid "Display Name" +msgstr "" + +#. module: pos_balance_multishop +#: model:ir.model.fields,field_description:pos_balance_multishop.field_balance_model__id +#: model:ir.model.fields,field_description:pos_balance_multishop.field_balance_sale_shop__id +#: model:ir.model.fields,field_description:pos_balance_multishop.field_product_balance_code__id +#: model:ir.model.fields,field_description:pos_balance_multishop.field_sale_balance__id +msgid "ID" +msgstr "" + +#. module: pos_balance_multishop +#: model:ir.model.fields,field_description:pos_balance_multishop.field_sale_balance__ip +msgid "IP" +msgstr "" + +#. module: pos_balance_multishop +#: model:ir.model.fields,field_description:pos_balance_multishop.field_product_balance_code__key +msgid "Key" +msgstr "" + +#. module: pos_balance_multishop +#: model:ir.model.fields,field_description:pos_balance_multishop.field_balance_model____last_update +#: model:ir.model.fields,field_description:pos_balance_multishop.field_balance_sale_shop____last_update +#: model:ir.model.fields,field_description:pos_balance_multishop.field_product_balance_code____last_update +#: model:ir.model.fields,field_description:pos_balance_multishop.field_sale_balance____last_update +msgid "Last Modified on" +msgstr "" + +#. module: pos_balance_multishop +#: model:ir.model.fields,field_description:pos_balance_multishop.field_balance_model__write_uid +#: model:ir.model.fields,field_description:pos_balance_multishop.field_balance_sale_shop__write_uid +#: model:ir.model.fields,field_description:pos_balance_multishop.field_product_balance_code__write_uid +#: model:ir.model.fields,field_description:pos_balance_multishop.field_sale_balance__write_uid +msgid "Last Updated by" +msgstr "" + +#. module: pos_balance_multishop +#: model:ir.model.fields,field_description:pos_balance_multishop.field_balance_model__write_date +#: model:ir.model.fields,field_description:pos_balance_multishop.field_balance_sale_shop__write_date +#: model:ir.model.fields,field_description:pos_balance_multishop.field_product_balance_code__write_date +#: model:ir.model.fields,field_description:pos_balance_multishop.field_sale_balance__write_date +msgid "Last Updated on" +msgstr "" + +#. module: pos_balance_multishop +#: model:ir.model.fields,field_description:pos_balance_multishop.field_balance_model__name +#: model:ir.model.fields,field_description:pos_balance_multishop.field_sale_balance__name +msgid "Name" +msgstr "" + +#. module: pos_balance_multishop +#: model:ir.model.fields,field_description:pos_balance_multishop.field_sale_balance__network +msgid "Network Connection" +msgstr "" + +#. module: pos_balance_multishop +#: model:ir.model.fields,field_description:pos_balance_multishop.field_product_product__balance_nomenclature +#: model:ir.model.fields,field_description:pos_balance_multishop.field_product_template__balance_nomenclature +msgid "Nomenclature" +msgstr "" + +#. module: pos_balance_multishop +#: model:ir.model.fields,field_description:pos_balance_multishop.field_product_product__not_weighed +#: model:ir.model.fields,field_description:pos_balance_multishop.field_product_template__not_weighed +msgid "Not weighed" +msgstr "" + +#. module: pos_balance_multishop +#: model:ir.model.fields,field_description:pos_balance_multishop.field_sale_balance__port +msgid "Port" +msgstr "" + +#. module: pos_balance_multishop +#: selection:product.template,balance_type:0 +msgid "Price" +msgstr "" + +#. module: pos_balance_multishop +#: model:ir.model,name:pos_balance_multishop.model_product_product +#: model:ir.model.fields,field_description:pos_balance_multishop.field_product_balance_code__product_id +msgid "Product" +msgstr "" + +#. module: pos_balance_multishop +#: model:ir.model,name:pos_balance_multishop.model_product_balance_code +msgid "Product Balance Code" +msgstr "" + +#. module: pos_balance_multishop +#: model_terms:ir.ui.view,arch_db:pos_balance_multishop.product_balace_code_form_view +msgid "Product Balance Codes" +msgstr "" + +#. module: pos_balance_multishop +#: model:ir.model,name:pos_balance_multishop.model_product_template +msgid "Product Template" +msgstr "" + +#. module: pos_balance_multishop +#: model:ir.model.fields,field_description:pos_balance_multishop.field_product_product__balance_rule +#: model:ir.model.fields,field_description:pos_balance_multishop.field_product_template__balance_rule +msgid "Rule" +msgstr "" + +#. module: pos_balance_multishop +#: model:ir.model,name:pos_balance_multishop.model_sale_balance +msgid "Sale Balance" +msgstr "" + +#. module: pos_balance_multishop +#: model:ir.actions.act_window,name:pos_balance_multishop.action_sale_tree +#: model:ir.ui.menu,name:pos_balance_multishop.menu_sale_tree +msgid "Sale balance shop" +msgstr "" + +#. module: pos_balance_multishop +#: model_terms:ir.ui.view,arch_db:pos_balance_multishop.view_shop_form_balance +msgid "Sale_shop" +msgstr "" + +#. module: pos_balance_multishop +#: model:ir.model.fields,field_description:pos_balance_multishop.field_product_balance_code__shop_id +#: model:ir.model.fields,field_description:pos_balance_multishop.field_sale_balance__shop_id +msgid "Shop" +msgstr "" + +#. module: pos_balance_multishop +#: model:ir.model.fields,field_description:pos_balance_multishop.field_product_balance_code__table +msgid "Table" +msgstr "" + +#. module: pos_balance_multishop +#: model:ir.model.fields,field_description:pos_balance_multishop.field_product_product__tare +#: model:ir.model.fields,field_description:pos_balance_multishop.field_product_template__tare +msgid "Tare" +msgstr "" + +#. module: pos_balance_multishop +#: model:ir.model.fields,help:pos_balance_multishop.field_product_product__tare +#: model:ir.model.fields,help:pos_balance_multishop.field_product_template__tare +msgid "Tare in grams of the product. Maximum 5 digits" +msgstr "" + +#. module: pos_balance_multishop +#: model:ir.model.fields,field_description:pos_balance_multishop.field_product_product__balance_type +#: model:ir.model.fields,field_description:pos_balance_multishop.field_product_template__balance_type +msgid "Type" +msgstr "" + +#. module: pos_balance_multishop +#: model:ir.actions.server,name:pos_balance_multishop.action_update_balance_codes_menu +msgid "Update Balances" +msgstr "" + +#. module: pos_balance_multishop +#: selection:product.template,balance_type:0 +msgid "Weight" +msgstr "" + +#. module: pos_balance_multishop +#: model:ir.model.fields,field_description:pos_balance_multishop.field_balance_sale_shop__name +msgid "name" +msgstr "" + diff --git a/pos_balance_multishop/models/__init__.py b/pos_balance_multishop/models/__init__.py new file mode 100644 index 0000000..1329c55 --- /dev/null +++ b/pos_balance_multishop/models/__init__.py @@ -0,0 +1,25 @@ +######################################################################## +# +# @authors: Ignacio Ibeas +# Copyright (C) 2014 Acysos S.L. +# +#This program is free software: you can redistribute it and/or modify +#it under the terms of the GNU General Public License as published by +#the Free Software Foundation, either version 3 of the License, or +#(at your option) any later version. +# +# This module is GPLv3 or newer and incompatible +# with OpenERP SA "AGPL + Private Use License"! +# +#This program is distributed in the hope that it will be useful, +#but WITHOUT ANY WARRANTY; without even the implied warranty of +#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +#GNU General Public License for more details. +# +#You should have received a copy of the GNU General Public License +#along with this program. If not, see http://www.gnu.org/licenses. +######################################################################## + +from . import product +from . import sale_balance +from . import sale diff --git a/pos_balance_multishop/models/product.py b/pos_balance_multishop/models/product.py new file mode 100644 index 0000000..618f0fc --- /dev/null +++ b/pos_balance_multishop/models/product.py @@ -0,0 +1,151 @@ +# Copyright 2014 Ignacio Ibeas +# Copyright 2020 Santi Noreña +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +import logging + +from odoo import api, models, fields, _ + +_logger = logging.getLogger(__name__) + + +class ProductTemplate(models.Model): + _inherit = "product.template" + + balance_code_ids = fields.One2many( + comodel_name="product.balance.code", + inverse_name="product_id", + string="Balance Codes", + ) + balance_type = fields.Selection( + [("price", "Price"), ("weight", "Weight")], "Type Balance" + ) + balance_name = fields.Char(string="Code", readonly=False, related="default_code") + balance_nomenclature = fields.Many2one( + comodel_name="barcode.nomenclature", string="Nomenclature" + ) + balance_rule = fields.Many2one(comodel_name="barcode.rule", string="Rule") + not_weighed = fields.Boolean("Not weighed", required=False) + + tare = fields.Integer( + string="Tare", + help="Tare in grams of the product. Maximum 5 digits", + default=0, + readonly=False, + ) + + @api.onchange("balance_name", "balance_rule") + def get_ean_code(self): + barcode = False + for product in self: + if self.balance_rule and self.balance_name: + ean = product.balance_rule.pattern[:1] + ean += product.balance_name + ean += "00000" + code = list(ean) + oddsum = evensum = total = control_digit = 0 + for i in range(len(code)): + if i % 2 == 0: + # even calculation + evensum += int(code[i]) + else: + # odd calculation + oddsum += int(code[i]) + total = oddsum * 3 + evensum + control_digit = int((10 - total % 10) % 10) + barcode = ean + str(control_digit) + product.barcode = barcode + + def update_codes(self): + # _logger.debug("updating codes") + for product in self: + if len(product.balance_code_ids) > 0: + for code in product.balance_code_ids: + if len(code.shop_id.balance_ids) > 0: + code.remove_balance() + code.update_balance() + + +class product_balance_code(models.Model): + _name = "product.balance.code" + _description = "Product Balance Code" + + def add_balance(self): + for code in self: + for balance in code.shop_id.balance_ids: + if balance.network: + balance_con = getattr(self, "add_balance_" + balance.model_id.code) + balance_con(code, balance) + + return True + + def update_balance(self): + for code in self: + # _logger.debug("code : " + code ) + for balance in code.shop_id.balance_ids: + _logger.debug("balance con ") + if balance.network: + # _logger.debug("balance con " + balance) + balance_con = getattr( + self, "update_balance_" + balance.model_id.code + ) + # _logger.debug('update_balance_' + balance.model_id.code)) + balance_con(code, balance) + return True + + def remove_balance(self): + for code in self: + for balance in code.shop_id.balance_ids: + if balance.network: + balance_con = getattr( + self, "remove_balance_" + balance.model_id.code + ) + balance_con(code, balance) + return True + + product_id = fields.Many2one( + comodel_name="product.template", string="Product", required=True + ) + key = fields.Char(string="Key") + table = fields.Char(string="Table") + shop_id = fields.Many2one(comodel_name="balance.sale.shop", string="Shop") + + +class ProductProduct(models.Model): + _inherit = "product.product" + + @api.onchange("balance_name", "balance_rule") + def get_ean_code(self): + barcode = False + for product in self: + if self.balance_rule and self.balance_name: + ean = product.balance_rule.pattern[:1] + ean += product.balance_name + ean += "00000" + code = list(ean) + + oddsum = evensum = total = control_digit = 0 + for i in range(len(code)): + if i % 2 == 0: + # even calculation + evensum += int(code[i]) + else: + # odd calculation + oddsum += int(code[i]) + total = oddsum * 3 + evensum + + control_digit = int((10 - total % 10) % 10) + barcode = ean + str(control_digit) + product.barcode = barcode + + def unlink(self): + self.balance_code_ids.remove_balance() + res = super(ProductProduct, self).unlink() + return res + + def update_codes(self): + for product in self: + if len(product.balance_code_ids) > 0: + for code in product.balance_code_ids: + if len(code.shop_id.balance_ids) > 0: + code.remove_balance() diff --git a/pos_balance_multishop/models/sale.py b/pos_balance_multishop/models/sale.py new file mode 100644 index 0000000..a139af1 --- /dev/null +++ b/pos_balance_multishop/models/sale.py @@ -0,0 +1,14 @@ +# Copyright 2014 Ignacio Ibeas +# Copyright 2020 Santi Noreña +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +from odoo import models, fields, _ + + +class balance_sale_shop(models.Model): + _name = "balance.sale.shop" + _description = "Balance Sale Shop" + + name = fields.Char("Name") + balance_ids = fields.One2many( + comodel_name="sale.balance", inverse_name="shop_id", string="Balances" + ) diff --git a/pos_balance_multishop/models/sale_balance.py b/pos_balance_multishop/models/sale_balance.py new file mode 100644 index 0000000..dfc9dd5 --- /dev/null +++ b/pos_balance_multishop/models/sale_balance.py @@ -0,0 +1,27 @@ +# Copyright 2014 Ignacio Ibeas +# Copyright 2020 Santi Noreña +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import models, fields, _ + + +class balance_model(models.Model): + _name = "balance.model" + _description = "Balance Model" + + name = fields.Char(string="Name", required=True, readonly=True) + code = fields.Char(string="Code", required=True, readonly=True) + + +class sale_balance(models.Model): + _name = "sale.balance" + _description = "Sale Balance" + + name = fields.Char("Name") + shop_id = fields.Many2one(comodel_name="balance.sale.shop", string="Shop") + network = fields.Boolean("Network Connection", required=False) + model_id = fields.Many2one( + comodel_name="balance.model", string="Balance Model", required=False + ) + ip = fields.Char("IP", required=False, readonly=False) + port = fields.Integer("Port") diff --git a/pos_balance_multishop/security/ir.model.access.csv b/pos_balance_multishop/security/ir.model.access.csv new file mode 100644 index 0000000..42853c9 --- /dev/null +++ b/pos_balance_multishop/security/ir.model.access.csv @@ -0,0 +1,13 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +access_product_balance_code_all,product.balance.code,model_product_balance_code,base.group_user,1,0,0,0 +access_product_balance_code_user,product.balance.code,model_product_balance_code,point_of_sale.group_pos_user,1,1,1,0 +access_product_balance_code_manager,product.balance.code manager,model_product_balance_code,point_of_sale.group_pos_manager,1,1,1,1 +access_product_balance_shop_all,balance.sale.shop user,model_balance_sale_shop,base.group_user,1,0,0,0 +access_product_balance_shop_user,balance.sale.shop user,model_balance_sale_shop,point_of_sale.group_pos_user,1,1,1,0 +access_product_balance_shop_manager,balance.sale.shop manager,model_balance_sale_shop,point_of_sale.group_pos_manager,1,1,1,1 +access_sale_balance_all,sale.balance user,model_sale_balance,base.group_user,1,0,0,0 +access_sale_balance_user,sale.balance user,model_sale_balance,point_of_sale.group_pos_user,1,1,1,0 +access_sale_balance_manager,sale.balance manager,model_sale_balance,point_of_sale.group_pos_manager,1,1,1,1 +access_balance_model_all,balance.model user,model_balance_model,base.group_user,1,0,0,0 +access_balance_model_user,balance.model user,model_balance_model,point_of_sale.group_pos_user,1,1,1,0 +access_balance_model_manager,balance.model manager,model_balance_model,point_of_sale.group_pos_manager,1,1,1,1 diff --git a/pos_balance_multishop/views/product_view.xml b/pos_balance_multishop/views/product_view.xml new file mode 100644 index 0000000..03384e0 --- /dev/null +++ b/pos_balance_multishop/views/product_view.xml @@ -0,0 +1,53 @@ + + + + + + product.balance.code.form + product.template + form + + + + + + + + + + + + + +
+ + + + + +
+ + + + + +
+
+
+
+
+ + + Update Balances + ir.actions.server + + + code + + if records: + records.update_codes() + + + +
+
diff --git a/pos_balance_multishop/views/sale_view.xml b/pos_balance_multishop/views/sale_view.xml new file mode 100644 index 0000000..014d21d --- /dev/null +++ b/pos_balance_multishop/views/sale_view.xml @@ -0,0 +1,71 @@ + + + + + balance.sale.shop.form + balance.sale.shop + form + + + + + + + + + + + + sale.balance.form + sale.balance + form + + +
+ + + + + + + + +
+
+
+
+ + + sale.balance.form + sale.balance + tree + + + + + + + + + + + + + Sale balance shop + ir.actions.act_window + balance.sale.shop + tree,form + + +

+ Click to create balance shop. +

+
+
+ + + +
+
diff --git a/product_update_price_last_purchase/models/product_template.py b/product_update_price_last_purchase/models/product_template.py index af93c48..ceefac2 100644 --- a/product_update_price_last_purchase/models/product_template.py +++ b/product_update_price_last_purchase/models/product_template.py @@ -14,7 +14,7 @@ class ProductTemplate(models.Model): string="Last purchase price", help="The last price at which the product was purchased. " "It is used as the base price field for calculating the product sale price.", - readonly=True, + # readonly=True, digits="Product Price", )