diff --git a/16.0/Dockerfile b/16.0/Dockerfile index 97e93a4..ffb0e1e 100644 --- a/16.0/Dockerfile +++ b/16.0/Dockerfile @@ -87,6 +87,7 @@ RUN chown odoo /etc/odoo/odoo.conf \ && chown -R odoo /mnt/extra-addons VOLUME ["/var/lib/odoo", "/mnt/extra-addons"] +COPY ./extra-addons /mnt/extra-addons # Expose Odoo services EXPOSE 8069 8071 8072 diff --git a/16.0/Odoo.config b/16.0/Odoo.config new file mode 100644 index 0000000..92f59a5 --- /dev/null +++ b/16.0/Odoo.config @@ -0,0 +1,16 @@ +# Place in openems-backend/config.d/Metadata +:org.apache.felix.configadmin.revision:=L"2" +database="postgres" +debugMode="ON" +odooHost="localhost" +odooPassword="admin" +odooPort=I"8069" +odooProtocol="HTTP" +odooUid=I"2" +pgConnectionPoolSize=I"40" +pgHost="localhost" +pgPassword="odoo" +pgPort=I"5432" +pgUser="odoo" +poolSize=I"30" +service.pid="Metadata.Odoo" diff --git a/16.0/add-ons.sh b/16.0/add-ons.sh new file mode 100644 index 0000000..8af4638 --- /dev/null +++ b/16.0/add-ons.sh @@ -0,0 +1,13 @@ + mkdir -p /workspace/odoo + cd /workspace/odoo + mkdir -p addons-available addons-enabled + cd addons-available + git clone --depth=1 -b 16.0 https://github.com/OCA/partner-contact + git clone --depth=1 -b 16.0 https://github.com/OCA/web.git + git clone --depth=1 -b 16.0 https://github.com/OpenEMS/odoo-openems.git + cd ../addons-enabled + ln -s ../addons-available/partner-contact/partner_firstname + ln -s ../addons-available/web/web_m2x_options + ln -s ../addons-available/odoo-openems/openems + odoo -d prod --addons-path=/workspace/odoo/addons-enabled -i base,partner_firstname,web_m2x_options,stock,openems + diff --git a/16.0/docker-compose.yml b/16.0/docker-compose.yml new file mode 100644 index 0000000..56a341a --- /dev/null +++ b/16.0/docker-compose.yml @@ -0,0 +1,34 @@ +version: '3.1' +services: + web: + image: odoo:16.0 + depends_on: + - db + ports: + - "8069:8069" + volumes: + - odoo-web-data:/var/lib/odoo + - ./config:/etc/odoo + - ./addons:/mnt/extra-addons + environment: + - PASSWORD_FILE=/run/secrets/postgresql_password + secrets: + - postgresql_password + db: + image: postgres:15 + environment: + - POSTGRES_DB=postgres + - POSTGRES_PASSWORD_FILE=/run/secrets/postgresql_password + - POSTGRES_USER=odoo + - PGDATA=/var/lib/postgresql/data/pgdata + volumes: + - odoo-db-data:/var/lib/postgresql/data/pgdata + secrets: + - postgresql_password +volumes: + odoo-web-data: + odoo-db-data: + +secrets: + postgresql_password: + file: odoo_pg_pass diff --git a/16.0/extra-addons/openems/.gitignore b/16.0/extra-addons/openems/.gitignore new file mode 100644 index 0000000..e41d5e2 --- /dev/null +++ b/16.0/extra-addons/openems/.gitignore @@ -0,0 +1,3 @@ +.swp +.swo +**/__pycache__ diff --git a/16.0/extra-addons/openems/__init__.py b/16.0/extra-addons/openems/__init__.py new file mode 100644 index 0000000..72d3ea6 --- /dev/null +++ b/16.0/extra-addons/openems/__init__.py @@ -0,0 +1 @@ +from . import controllers, models diff --git a/16.0/extra-addons/openems/__manifest__.py b/16.0/extra-addons/openems/__manifest__.py new file mode 100644 index 0000000..232ebf1 --- /dev/null +++ b/16.0/extra-addons/openems/__manifest__.py @@ -0,0 +1,40 @@ +{ + "name": "OpenEMS", + "summary": "Everything related to OpenEMS (Open Energy Management System)", + "version": "16.0.1.0.1", + "author": "OpenEMS Association e.V.", + "maintainer": "OpenEMS Association e.V.", + "contributors": [ + "Stefan Feilmeier " + "Maximilian Lang " + ], + "website": "https://openems.io", + "license": "AGPL-3", + "category": "Specific Industry Applications", + "depends": ["base", "web", "mail", "crm", "resource", "stock", "web_m2x_options", "partner_firstname"], + "data": [ + "data/ir_config_parameter.xml", + "data/res_partner_category.xml", + "security/openems.xml", + "security/ir.model.access.csv", + "report/setup_protocol.xml", + "views/device.xml", + "views/partner.xml", + "views/setup_protocol.xml", + "views/user.xml", + "views/stock_production_lot_views.xml", + "mail/openems/alerting_offline.xml", + "mail/openems/alerting_sum_state.xml", + "mail/openems/setup_protocol_customer.xml", + "mail/openems/setup_protocol_installer.xml", + "mail/openems/user_registration.xml", + ], + "demo": ["data/demo.xml"], + "js": [], + "css": [], + "qweb": [], + "images": [], + "test": [], + "installable": True, + "application": True, +} diff --git a/16.0/extra-addons/openems/controllers/__init__.py b/16.0/extra-addons/openems/controllers/__init__.py new file mode 100644 index 0000000..3e863cd --- /dev/null +++ b/16.0/extra-addons/openems/controllers/__init__.py @@ -0,0 +1 @@ +from . import openems_backend, setup_protocol, user, alerting diff --git a/16.0/extra-addons/openems/controllers/alerting.py b/16.0/extra-addons/openems/controllers/alerting.py new file mode 100644 index 0000000..7a848f2 --- /dev/null +++ b/16.0/extra-addons/openems/controllers/alerting.py @@ -0,0 +1,86 @@ +import logging +from datetime import datetime +from enum import Enum + +from odoo import http +from odoo.http import request + +class SumState(Enum): + FAULT = 0 + WARNING = 1 + +class Message: + sentAt: datetime + edgeId: str + userIds: list[int] + + def __init__(self, sentAt: datetime, edgeId: str, userIds: list[int]) -> None: + self.sentAt = sentAt + self.edgeId = edgeId + self.userIds = userIds + +class SumStateMessage(Message): + state: SumState + + def __init__(self, sentAt: datetime, edgeId: str, userIds: list[int], state: SumState) -> None: + super().__init__(sentAt, edgeId, userIds) + self.state = state + +class Alerting(http.Controller): + __logger = logging.getLogger("Alerting") + + @http.route("/openems_backend/mail/alerting_sum_state", type="json", auth="user") + def sum_state_alerting(self, sentAt: str, params: list[dict]): + msgs = self.__get_sum_state_params(sentAt, params) + update_func = lambda role, at: { role.write({"sum_state_last_notification": at})} + + if len(msgs) == 0: + self.__logger.error("Scheduled SumState-Alerting-Mail without any recipients!!!") + + template = request.env.ref('openems.alerting_sum_state') + for msg in msgs: + self.__send_mails(template, msg, update_func) + + return {} + + @http.route("/openems_backend/mail/alerting_offline", type="json", auth="user") + def offline_alerting(self, sentAt: str, params: list[dict]): + msgs = self.__get_offline_params(sentAt, params) + update_func = lambda role, at: { role.write({"offline_last_notification": at})} + + template = request.env.ref("openems.alerting_offline") + for msg in msgs: + self.__send_mails(template, msg, update_func) + + return {} + + def __get_offline_params(self, sentAt, params) -> list[Message]: + msgs = list() + sent = datetime.strptime(sentAt, "%Y-%m-%d %H:%M:%S") + for param in params: + edgeId = param["edgeId"] + recipients = param["recipients"] + msgs.append(Message(sent, edgeId, recipients)); + return msgs + + def __get_sum_state_params(self, sentAt, params) -> list[SumStateMessage]: + msgs = list() + sent = datetime.strptime(sentAt, "%Y-%m-%d %H:%M:%S") + for param in params: + edgeId = param["edgeId"] + recipients = param["recipients"] + state = param["state"] + msgs.append(SumStateMessage(sent, edgeId, recipients, state)); + return msgs + + def __send_mails(self, template, msg: Message, update_func): + roles = http.request.env['openems.alerting'].search( + [('user_id','in',msg.userIds),('device_id','=',msg.edgeId)] + ) + + for role in roles: + try: + template.send_mail(res_id=role.id, force_send=True) + update_func(role, msg.sentAt) + except Exception as err: + self.__logger.error("[" + str(err) + "] Unable to send template[" + str(template.name) +"] to edgeUser[user=" + str(role.id) + ", edge=" + str(msg.edgeId)+ "]") \ No newline at end of file diff --git a/16.0/extra-addons/openems/controllers/const.py b/16.0/extra-addons/openems/controllers/const.py new file mode 100644 index 0000000..c6fbc01 --- /dev/null +++ b/16.0/extra-addons/openems/controllers/const.py @@ -0,0 +1,5 @@ +from odoo.modules.module import get_module_resource + +import base64 + +OPENEMS_LOGO_BASE64 = base64.b64encode(open(get_module_resource('openems', 'data', 'OpenEMS-Logo.jpg') , "rb").read()) \ No newline at end of file diff --git a/16.0/extra-addons/openems/controllers/openems_backend.py b/16.0/extra-addons/openems/controllers/openems_backend.py new file mode 100644 index 0000000..d0dcb80 --- /dev/null +++ b/16.0/extra-addons/openems/controllers/openems_backend.py @@ -0,0 +1,276 @@ +from odoo import http + + +class OpenemsBackend(http.Controller): + @http.route("/openems_backend/info", auth="user", type="json") + def index(self): + # Get user + user_id = http.request.env.context.get("uid") + res_users = http.request.env["res.users"].sudo() + user_rec = res_users.search_read( + [("id", "=", user_id)], + ["login", "name", "groups_id", "global_role", "openems_language"], + )[0] + res_users.browse([user_id]) + + # Get res group model + res_groups_model = http.request.env["res.groups"].sudo() + + # Get Manager and Reader group + manager_group = res_groups_model.env.ref("openems.group_openems_manager") + reader_group = res_groups_model.env.ref("openems.group_openems_reader") + + manager_group_id = manager_group["id"] + reader_group_id = reader_group["id"] + + # Get user attributes + global_role = user_rec["global_role"] + if manager_group_id in user_rec["groups_id"]: + # Manager group + global_role = "admin" + + # return empty device (use pagination) list if user is manager or reader + if manager_group_id in user_rec["groups_id"] or reader_group_id in user_rec["groups_id"]: + return { + "user": { + "id": user_rec["id"], + "login": user_rec["login"], + "name": user_rec["name"], + "global_role": global_role, + "language": user_rec["openems_language"], + "has_multiple_edges": True + }, + "devices": [], + } + + # Get specific Device roles + device_user_role_model = http.request.env["openems.device_user_role"] + user_role_ids = device_user_role_model.search_read( + [("user_id", "=", user_id)], ["id", "role"] + ) + + # Get Devices + device_model = http.request.env["openems.device"] + devices = device_model.search_read( + [], ["id", "name", "user_role_ids", "comment", "producttype", + "lastmessage", "first_setup_protocol_date", "openems_sum_state_level"] + ) + + devs = [] + for device_rec in devices: + # Set user role per group + role = "guest" + if manager_group_id in user_rec["groups_id"]: + # Manager group + role = "admin" + elif reader_group_id in user_rec["groups_id"]: + # Reader group + role = "guest" + + # Set specific user role + for device_role_id in device_rec["user_role_ids"]: + for user_role_id in user_role_ids: + if device_role_id == user_role_id["id"]: + role = user_role_id["role"] + + # Prepare result + dev = { + "id": device_rec["id"], + "name": device_rec["name"], + "comment": device_rec["comment"], + "producttype": device_rec["producttype"], + "role": role, + "lastmessage": device_rec["lastmessage"], + "openems_sum_state_level": device_rec["openems_sum_state_level"] + } + + if device_rec["first_setup_protocol_date"]: + dev["first_setup_protocol_date"] = device_rec[ + "first_setup_protocol_date" + ] + + devs.append(dev) + + return { + "user": { + "id": user_rec["id"], + "login": user_rec["login"], + "name": user_rec["name"], + "global_role": global_role, + "language": user_rec["openems_language"], + "has_multiple_edges": len(devs) > 1 + }, + "devices": devs, + } + + @http.route("/openems_backend/get_edge_with_role", auth="user", type="json") + def get_edge_with_role(self, edge_id: str): + user_id = http.request.env.context.get("uid") + res_users = http.request.env["res.users"].sudo() + user_rec = res_users.search_read( + [("id", "=", user_id)], + ["login", "name", "groups_id"], + )[0] + + # Get res group model + res_groups_model = http.request.env["res.groups"].sudo() + + # Get Manager and Reader group + manager_group = res_groups_model.env.ref("openems.group_openems_manager") + reader_group = res_groups_model.env.ref("openems.group_openems_reader") + + manager_group_id = manager_group["id"] + reader_group_id = reader_group["id"] + + # get devices for which the user has permissions + device_model = http.request.env["openems.device"] + devices = device_model.search_read( + [("name", "=", edge_id)], + ["id", "name", "comment", "producttype", "lastmessage", "first_setup_protocol_date", "openems_sum_state_level"]) + + if len(devices) != 1: + return {} + + device = devices[0] + + # Get specific Device roles + device_user_role_model = http.request.env["openems.device_user_role"] + device_user_roles = device_user_role_model.search_read( + [("user_id", "=", user_id), + ("device_id", "=", device["id"])], ["id", "role"] + ) + + # Set user role per group + role = "guest" + if manager_group_id in user_rec["groups_id"]: + # Manager group + role = "admin" + elif reader_group_id in user_rec["groups_id"]: + # Reader group + role = "guest" + + # Set specific user role + if len(device_user_roles) > 0: + role = device_user_roles[0]["role"] + + dev = { + "id": device["id"], + "name": device["name"], + "comment": device["comment"], + "producttype": device["producttype"], + "role": role, + "lastmessage": device["lastmessage"], + "openems_sum_state_level": device["openems_sum_state_level"] + } + if device["first_setup_protocol_date"]: + dev["first_setup_protocol_date"] = device["first_setup_protocol_date"] + + return dev + + @http.route("/openems_backend/get_edges", auth="user", type="json") + def get_edges(self, limit, page, query=None, searchParams=None): + # Get user + user_id = http.request.env.context.get("uid") + res_users = http.request.env["res.users"].sudo() + user_rec = res_users.search_read( + [("id", "=", user_id)], + ["login", "name", "groups_id", "global_role"], + )[0] + + # Get res group model + res_groups_model = http.request.env["res.groups"].sudo() + + # Get Manager and Reader group + manager_group = res_groups_model.env.ref("openems.group_openems_manager") + reader_group = res_groups_model.env.ref("openems.group_openems_reader") + + manager_group_id = manager_group["id"] + reader_group_id = reader_group["id"] + + # Get specific Device roles + device_user_role_model = http.request.env["openems.device_user_role"] + user_role_ids = device_user_role_model.search_read( + [("user_id", "=", user_id)], ["id", "role"] + ) + + domains = [] + logical_operators = [] + additional_domains = [] + if query: + logical_operators.extend(['|', '|']) + domains = [ + ("name", "ilike", query), + ("comment", "ilike", query), + ("producttype", "ilike", query)] + + if searchParams: + if searchParams.get("producttype"): + additional_domains.append( + ("producttype", "in", searchParams.get("producttype"))) + + if searchParams.get("sumState"): + sum_states = list(map(lambda s: s.lower(), searchParams.get("sumState"))) + additional_domains.append( + ("openems_sum_state_level", "in", sum_states)) + + if "isOnline" in searchParams: + additional_domains.append( + ("openems_is_connected", "=", searchParams.get("isOnline"))) + + if len(additional_domains) > 1: + for _ in range(len(additional_domains) - 1): + logical_operators.insert(0, '&') + + # insert 'and' if both are not 'None' + if query and searchParams: + logical_operators.insert(0, '&') + + domains.extend(additional_domains) + logical_operators.extend(domains) + + # Get Devices + device_model = http.request.env["openems.device"] + devices = device_model.search_read( + logical_operators, + ["id", "name", "user_role_ids", "comment", "producttype", + "lastmessage", "first_setup_protocol_date", "openems_sum_state_level"], + limit=limit, offset=(page * limit) + ) + devs = [] + for device_rec in devices: + # Set user role per group + role = "guest" + if manager_group_id in user_rec["groups_id"]: + # Manager group + role = "admin" + elif reader_group_id in user_rec["groups_id"]: + # Reader group + role = "guest" + + # Set specific user role + for device_role_id in device_rec["user_role_ids"]: + for user_role_id in user_role_ids: + if device_role_id == user_role_id["id"]: + role = user_role_id["role"] + + # Prepare result + dev = { + "id": device_rec["id"], + "name": device_rec["name"], + "comment": device_rec["comment"], + "producttype": device_rec["producttype"], + "role": role, + "lastmessage": device_rec["lastmessage"], + "openems_sum_state_level": device_rec["openems_sum_state_level"] + } + + if device_rec["first_setup_protocol_date"]: + dev["first_setup_protocol_date"] = device_rec[ + "first_setup_protocol_date" + ] + + devs.append(dev) + + return { + "devices": devs, + } diff --git a/16.0/extra-addons/openems/controllers/setup_protocol.py b/16.0/extra-addons/openems/controllers/setup_protocol.py new file mode 100644 index 0000000..d64b18e --- /dev/null +++ b/16.0/extra-addons/openems/controllers/setup_protocol.py @@ -0,0 +1,142 @@ +import base64 + +from odoo import http +from odoo.http import request + + +class SetupProtocol(http.Controller): + @http.route("/openems_backend/sendSetupProtocolEmail", type="json", auth="user") + def index(self, setupProtocolId, edgeId): + setup_protocol_model = request.env["openems.setup_protocol"] + setup_protocol_record = setup_protocol_model.search_read( + [("id", "=", setupProtocolId)] + ) + if len(setup_protocol_record) != 1: + raise ValueError("Setup protocol not found for id [" + edgeId + "]") + + device_model = request.env["openems.device"] + device_rec = device_model.search_read([("name", "=", edgeId)]) + if len(device_rec) != 1: + raise ValueError("Device not found for id [" + edgeId + "]") + + name = ( + "IBN-" + + edgeId + + "-" + + setup_protocol_record[0]["create_date"].strftime("%d.%m.%Y") + + ".pdf" + ) + + data = request.env.ref( + "openems.action_openems_setup_protocol_report" + )._render_qweb_pdf([setupProtocolId]) + ibnPdf = request.env["ir.attachment"].create( + { + "res_model": "openems.device", + "res_id": device_rec[0]["id"], + "name": name, + "store_fname": name, + "datas": base64.b64encode(data[0]), + } + ) + + templates = self.getTemplates(device_rec[0]['oem'], ibnPdf) + + templates['installer'].send_mail(setupProtocolId, force_send=True) + templates['customer'].send_mail(setupProtocolId, force_send=True) + + return {} + + def getTemplates(self, oem: str, protocol): + templates = {'customer': None, 'installer': None} + + templates['customer'] = request.env.ref( + "openems.setup_protocol_email_customer") + templates['installer'] = request.env.ref( + "openems.setup_protocol_email_installer") + + logo = request.env.ref("openems.attachment_logo_openems") + + templates['customer'].attachment_ids = [ + (6, 0, [protocol.id, logo.id])] + templates['installer'].attachment_ids = [ + (6, 0, [protocol.id, logo.id])] + + return templates + + @http.route('/openems_backend/get_latest_setup_protocol', type='json', auth='user') + def get_latest_setup_protocol(self, edge_name): + # search for device + device_model = request.env['openems.device'] + device = device_model.search([('name', '=', edge_name)]) + + response = dict() + if not len(device.setup_protocol_ids) > 0: + return response + + latest_protocol = device.setup_protocol_ids[0] + + # build customer object + customer = latest_protocol.customer_id + customer_values = dict({ + "firstname": customer['firstname'], + "lastname": customer['lastname'], + "email": customer['email'], + "phone": customer['phone'], + "address": { + "street": customer['street'], + "city": customer['city'], + "zip": customer['zip'], + "country": customer['country_id']['name'] + } + }) + + # check company for customer + customer_company = customer['commercial_company_name'] + if customer_company: + customer_values.update({ + "company": { + "name": customer['commercial_company_name'] + } + }) + response.update({"customer": customer_values}) + + # check different location is available + location = latest_protocol['different_location_id'] + if location: + location_values = dict({ + "firstname": location['firstname'], + "lastname": location['lastname'], + "email": location['email'], + "phone": location['phone'], + "address": { + "street": location['street'], + "city": location['city'], + "zip": location['zip'], + "country": location['country_id']['name'] + } + }) + + # check company for different location + different_location_company = location['commercial_company_name'] + if different_location_company: + location_values.update({ + "company": { + "name": location['commercial_company_name'] + } + }) + response.update({"location": location_values}) + + # build items object + items = list() + for item in latest_protocol.item_ids: + items.append({ + "view": item['view'], + "field": item['field'], + "category": item['category'], + "name": item['name'], + "value": item['value'] + }) + response.update({"items": items}) + + return response diff --git a/16.0/extra-addons/openems/controllers/user.py b/16.0/extra-addons/openems/controllers/user.py new file mode 100644 index 0000000..75ec137 --- /dev/null +++ b/16.0/extra-addons/openems/controllers/user.py @@ -0,0 +1,32 @@ +from odoo import http +from odoo.http import request + +class User(http.Controller): + @http.route("/openems_backend/sendRegistrationEmail", type="json", auth="user") + def index(self, userId, password=None, oem: str = ''): + user_model = request.env["res.users"] + user_record = user_model.search_read([("id", "=", userId)], ["partner_id"]) + if len(user_record) != 1: + raise ValueError("User not found for id [" + userId + "]") + + partner = user_record[0] + partner_id = partner.get("partner_id") + if partner_id is None: + raise ValueError("User has no partner") + + if password is None: + password = "*****" + # load template + template = self.getTemplate(oem) + # set mail values + email_values = { + 'password': password + } + # send mail + template.with_context(email_values).send_mail( + res_id=partner_id[0], force_send=True) + return {} + + def getTemplate(self, oem: str): + template = request.env.ref("openems.registration_email") + return template diff --git a/16.0/extra-addons/openems/data/OpenEMS-Logo.jpg b/16.0/extra-addons/openems/data/OpenEMS-Logo.jpg new file mode 100644 index 0000000..13ae0fb Binary files /dev/null and b/16.0/extra-addons/openems/data/OpenEMS-Logo.jpg differ diff --git a/16.0/extra-addons/openems/data/demo.xml b/16.0/extra-addons/openems/data/demo.xml new file mode 100644 index 0000000..f4bf18e --- /dev/null +++ b/16.0/extra-addons/openems/data/demo.xml @@ -0,0 +1,12 @@ + + + + + + edge0 + OpenEMS Edge #0 + DEMO_API_KEY + + + + diff --git a/16.0/extra-addons/openems/data/ir_config_parameter.xml b/16.0/extra-addons/openems/data/ir_config_parameter.xml new file mode 100644 index 0000000..b1da5cf --- /dev/null +++ b/16.0/extra-addons/openems/data/ir_config_parameter.xml @@ -0,0 +1,9 @@ + + + + + edge_monitoring_url + http://localhost:8082/device/ + + + diff --git a/16.0/extra-addons/openems/data/res_partner_category.xml b/16.0/extra-addons/openems/data/res_partner_category.xml new file mode 100644 index 0000000..f56c0e0 --- /dev/null +++ b/16.0/extra-addons/openems/data/res_partner_category.xml @@ -0,0 +1,11 @@ + + + + + Created via IBN + + + Customer + + + diff --git a/16.0/extra-addons/openems/i18n/de.po b/16.0/extra-addons/openems/i18n/de.po new file mode 100644 index 0000000..cc4c660 --- /dev/null +++ b/16.0/extra-addons/openems/i18n/de.po @@ -0,0 +1,1666 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * openems +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 16.0-20240218\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2024-02-18 11:57+0000\n" +"PO-Revision-Date: 2024-02-18 11:57+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: openems +#: model:mail.template,body_html:openems.setup_protocol_email_installer +msgid "" +"\n" +"\n" +"\n" +"\n" +"\n" +" OpenEMS setup protocol \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +"\n" +"\n" +"\n" +"
\n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +"
\n" +" \n" +"
\n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +"
\n" +" \n" +"
\n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +"
\n" +"

\n" +"

\n" +" \n" +"
\n" +"
Dear \n" +" ,\n" +"
\n" +"
\n" +"
please find the setup protocol for your customer attached.\n" +"
\n" +"
\n" +"
Best Regards
\n" +"
\n" +"
OpenEMS Association e.V.
\n" +"
\n" +"
\n" +" \n" +"
\n" +"
\n" +" \n" +"
\n" +"
\n" +"\n" +"\n" +"\n" +" \n" +" " +msgstr "" + +#. module: openems +#: model:mail.template,body_html:openems.registration_email +msgid "" +"\n" +"\n" +"\n" +"\n" +"\n" +" Registrierung erfolgreich \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +"\n" +"\n" +"\n" +"
\n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +"
\n" +" \n" +"
\n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +"
\n" +" \n" +"
\n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +"
\n" +"

\n" +"

\n" +" \n" +"
\n" +"
Guten Tag \n" +" ,\n" +"
\n" +"
\n" +"
Ihr Zugang zu OpenEMS UI wurde erstellt.
\n" +"
\n" +"
Sie können sich auf OpenEMS UI einloggen, um auf das Online-Monitoring zuzugreifen.
\n" +"
\n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +"
Ihre Zugangsdaten:
E-Mail\n" +" \n" +"
Passwort\n" +" \n" +"
\n" +"
\n" +"
Best Regards
\n" +"
\n" +"
OpenEMS Association e.V.
\n" +"
\n" +"
\n" +" \n" +"
\n" +"
\n" +" \n" +"
\n" +"
\n" +"\n" +"\n" +"\n" +" \n" +" " +msgstr "" + +#. module: openems +#: model:mail.template,body_html:openems.setup_protocol_email_customer +msgid "" +"\n" +"\n" +"\n" +"\n" +"\n" +" Your OpenEMS Edge setup protocol \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +"\n" +"\n" +"\n" +"
\n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +"
\n" +" \n" +"
\n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +"
\n" +" \n" +"
\n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +"
\n" +"

\n" +"

\n" +" \n" +"
\n" +"
Welcome to OpenEMS,
\n" +"
\n" +"
please find your setup protocol for OpenEMS Edge attached.
\n" +"
\n" +"
Best Regards
\n" +"
\n" +"
OpenEMS Association e.V.
\n" +"
\n" +"
\n" +" \n" +"
\n" +"
\n" +" \n" +"
\n" +"
\n" +"\n" +"\n" +"\n" +" \n" +" " +msgstr "" + +#. module: openems +#: model:mail.template,body_html:openems.alerting_email_generic +msgid "" +"\n" +"\n" +"\n" +"\n" +"\n" +" OpenEMS Alert - Edge is offline\n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +"\n" +"\n" +"\n" +"
\n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +"
\n" +" \n" +"
\n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +"
\n" +" \n" +"
\n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +"
\n" +"

\n" +"

\n" +" \n" +"
\n" +"
Dear \n" +" ,\n" +"
\n" +"
\n" +"
Your OpenEMS Edge with number is offline since:
\n" +"
\n" +"
\n" +" \n" +" \n" +" \n" +" \n" +" \n" +" (UTC)\n" +" \n" +"
\n" +"
\n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +"
Info
\n" +" Edge-Version\n" +" \n" +" \n" +"
\n" +" Type\n" +" \n" +" \n" +" \n" +"
\n" +"
\n" +"
Best Regards
\n" +"
\n" +"
OpenEMS Association e.V.
\n" +"
\n" +"
\n" +" \n" +"
\n" +"
\n" +" \n" +"
\n" +" \n" +"
\n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +"
\n" +" \n" +"
\n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +"
\n" +"
Wenn Sie keine E-Mail Benachrichtigung mehr wünschen, können Sie die Funktion hier deaktivieren.
\n" +"
\n" +"
Dies ist eine automatisch generierte Nachricht. Bitte antworten Sie nicht auf diese Nachricht!
\n" +"
\n" +"
\n" +" \n" +"
\n" +"
\n" +" \n" +"
\n" +"\n" +"\n" +"\n" +" \n" +" " +msgstr "" + +#. module: openems +#: model:ir.actions.report,print_report_name:openems.action_openems_setup_protocol_report +msgid "" +"('IBN-' + object.openems_device_id.name + '-' + " +"object.create_date.strftime('%d.%m.%Y'))" +msgstr "" + +#. module: openems +#: model:ir.model.fields,field_description:openems.field_openems_device__apikey +msgid "API-Key" +msgstr "" + +#. module: openems +#: model_terms:ir.ui.view,arch_db:openems.openems_device_form +msgid "Access Role in Online-Monitoring" +msgstr "Zugriffsrollen im Online-Monitoring" + +#. module: openems +#: model:ir.model.fields,field_description:openems.field_openems_device__message_needaction +msgid "Action Needed" +msgstr "" + +#. module: openems +#: model:ir.model.fields,field_description:openems.field_openems_device__active +msgid "Active" +msgstr "" + +#. module: openems +#: model:ir.model.fields.selection,name:openems.selection__openems_device_user_role__role__admin +#: model:ir.model.fields.selection,name:openems.selection__res_users__global_role__admin +msgid "Admin" +msgstr "" + +#. module: openems +#: model:ir.ui.menu,name:openems.menu_openems_admin +msgid "Administration" +msgstr "" + +#. module: openems +#: model_terms:ir.ui.view,arch_db:openems.device_search_view +msgid "Archived" +msgstr "" + +#. module: openems +#: model:ir.model.fields,field_description:openems.field_openems_device__message_attachment_count +msgid "Attachment Count" +msgstr "" + +#. module: openems +#: model:ir.model.fields,field_description:openems.field_res_users__branding_partner_id +msgid "Branding Partner" +msgstr "" + +#. module: openems +#: model:ir.model.fields,field_description:openems.field_openems_setup_protocol_item__category +#: model:ir.model.fields,field_description:openems.field_openems_setup_protocol_production_lot__category +msgid "Category" +msgstr "" + +#. module: openems +#: model:ir.model.fields,field_description:openems.field_openems_device__comment +msgid "Comment" +msgstr "" + +#. module: openems +#: model_terms:ir.ui.view,arch_db:openems.openems_device_form +msgid "Configuration" +msgstr "Konfiguration" + +#. module: openems +#: model_terms:ir.ui.view,arch_db:openems.openems_device_form +msgid "Configuration Updates" +msgstr "Konfigurationsänderungen" + +#. module: openems +#: model:ir.model,name:openems.model_res_partner +msgid "Contact" +msgstr "Kontakt" + +#. module: openems +#: model:ir.model.fields,field_description:openems.field_openems_device__create_uid +#: model:ir.model.fields,field_description:openems.field_openems_device_tag__create_uid +#: model:ir.model.fields,field_description:openems.field_openems_device_user_role__create_uid +#: model:ir.model.fields,field_description:openems.field_openems_openemsconfigupdate__create_uid +#: model:ir.model.fields,field_description:openems.field_openems_setup_protocol__create_uid +#: model:ir.model.fields,field_description:openems.field_openems_setup_protocol_item__create_uid +#: model:ir.model.fields,field_description:openems.field_openems_setup_protocol_production_lot__create_uid +#: model:ir.model.fields,field_description:openems.field_openems_systemmessage__create_uid +msgid "Created by" +msgstr "" + +#. module: openems +#: model:ir.model.fields,field_description:openems.field_openems_device__create_date +#: model:ir.model.fields,field_description:openems.field_openems_device_tag__create_date +#: model:ir.model.fields,field_description:openems.field_openems_device_user_role__create_date +#: model:ir.model.fields,field_description:openems.field_openems_openemsconfigupdate__create_date +#: model:ir.model.fields,field_description:openems.field_openems_setup_protocol__create_date +#: model:ir.model.fields,field_description:openems.field_openems_setup_protocol_item__create_date +#: model:ir.model.fields,field_description:openems.field_openems_setup_protocol_production_lot__create_date +#: model:ir.model.fields,field_description:openems.field_openems_systemmessage__create_date +msgid "Created on" +msgstr "" + +#. module: openems +#: model:res.partner.category,name:openems.res_partner_category_created_via_ibn +msgid "Created via IBN" +msgstr "" + +#. module: openems +#: model:ir.model.fields,field_description:openems.field_openems_systemmessage__timestamp +msgid "Creation date" +msgstr "" + +#. module: openems +#: model:ir.model.fields,field_description:openems.field_openems_setup_protocol__customer_id +#: model:res.partner.category,name:openems.res_partner_category_customer +msgid "Customer" +msgstr "" + +#. module: openems +#: model:ir.model.fields.selection,name:openems.selection__res_users__openems_language__cz +msgid "Czech" +msgstr "" + +#. module: openems +#: model_terms:ir.ui.view,arch_db:openems.report_openems_setup_protocol_template +msgid "Datum:" +msgstr "" + +#. module: openems +#: model_terms:ir.ui.view,arch_db:openems.device_search_view +#: model_terms:ir.ui.view,arch_db:openems.openems_device_tree +msgid "Description" +msgstr "Bezeichnung" + +#. module: openems +#: model_terms:ir.ui.view,arch_db:openems.openems_openemsconfigupdate_tree +msgid "Details" +msgstr "" + +#. module: openems +#: model:ir.model.fields,field_description:openems.field_stock_lot__device_id +#: model:ir.model.fields,field_description:openems.field_stock_lot__device_ids +msgid "Device" +msgstr "" + +#. module: openems +#: model:ir.actions.act_window,name:openems.action_openems_openemsconfigupdate +#: model:ir.ui.menu,name:openems.menu_openems_admin_openemsconfigupdate +msgid "Device Configuration Updates" +msgstr "" + +#. module: openems +#: model:ir.actions.act_window,name:openems.action_openems_device +#: model:ir.ui.menu,name:openems.menu_openems_content_devices +msgid "Devices" +msgstr "" + +#. module: openems +#: model:ir.model.fields,field_description:openems.field_openems_setup_protocol__different_location_id +msgid "Different Location" +msgstr "" + +#. module: openems +#: model:ir.model.fields,field_description:openems.field_openems_device__display_name +#: model:ir.model.fields,field_description:openems.field_openems_device_tag__display_name +#: model:ir.model.fields,field_description:openems.field_openems_device_user_role__display_name +#: model:ir.model.fields,field_description:openems.field_openems_openemsconfigupdate__display_name +#: model:ir.model.fields,field_description:openems.field_openems_setup_protocol__display_name +#: model:ir.model.fields,field_description:openems.field_openems_setup_protocol_item__display_name +#: model:ir.model.fields,field_description:openems.field_openems_setup_protocol_production_lot__display_name +#: model:ir.model.fields,field_description:openems.field_openems_systemmessage__display_name +msgid "Display Name" +msgstr "" + +#. module: openems +#: model:ir.model.fields.selection,name:openems.selection__res_users__openems_language__nl +msgid "Dutch" +msgstr "" + +#. module: openems +#: model:mail.template,name:openems.alerting_email_generic +msgid "E-Mail Alerting" +msgstr "" + +#. module: openems +#: model:mail.template,name:openems.registration_email +msgid "E-Mail Kunden Registrierung" +msgstr "" + +#. module: openems +#: model:mail.template,name:openems.setup_protocol_email_customer +msgid "E-Mail setup protocol for customer" +msgstr "" + +#. module: openems +#: model:mail.template,name:openems.setup_protocol_email_installer +msgid "E-Mail setup protocol for installer" +msgstr "" + +#. module: openems +#: model_terms:ir.ui.view,arch_db:openems.report_openems_setup_protocol_template +msgid "E-Mail-Adresse" +msgstr "" + +#. module: openems +#: model:ir.model.fields,field_description:openems.field_openems_device__emshardware +msgid "EMS Hardware" +msgstr "" + +#. module: openems +#: model:ir.model.fields.selection,name:openems.selection__res_users__openems_language__en +msgid "English" +msgstr "" + +#. module: openems +#: model:ir.model.fields,field_description:openems.field_openems_setup_protocol__item_ids +msgid "Entry Items" +msgstr "" + +#. module: openems +#: model:ir.model.fields.selection,name:openems.selection__openems_device__openems_sum_state_level__fault +msgid "Fault" +msgstr "" + +#. module: openems +#: model:ir.model.fields,field_description:openems.field_openems_setup_protocol_item__field +msgid "Field Identifier" +msgstr "" + +#. module: openems +#: model_terms:ir.ui.view,arch_db:openems.report_openems_setup_protocol_template +msgid "Firmenname" +msgstr "" + +#. module: openems +#: model:ir.model.fields,field_description:openems.field_openems_device__first_setup_protocol_date +msgid "First Setup Protocol Date" +msgstr "" + +#. module: openems +#: model:ir.model.fields,field_description:openems.field_openems_device__message_follower_ids +msgid "Followers" +msgstr "" + +#. module: openems +#: model:ir.model.fields,field_description:openems.field_openems_device__message_partner_ids +msgid "Followers (Partners)" +msgstr "" + +#. module: openems +#: model:ir.model.fields.selection,name:openems.selection__res_users__openems_language__fr +msgid "French" +msgstr "" + +#. module: openems +#: model_terms:ir.ui.view,arch_db:openems.openems_device_form +msgid "General" +msgstr "" + +#. module: openems +#: model:ir.model.fields.selection,name:openems.selection__res_users__openems_language__de +msgid "German" +msgstr "" + +#. module: openems +#: model:ir.model.fields,field_description:openems.field_res_users__global_role +msgid "Global Role" +msgstr "" + +#. module: openems +#: model:ir.model.fields.selection,name:openems.selection__openems_device_user_role__role__guest +#: model:ir.model.fields.selection,name:openems.selection__res_users__global_role__guest +msgid "Guest" +msgstr "" + +#. module: openems +#: model_terms:ir.ui.view,arch_db:openems.openems_device_form +msgid "Hardware" +msgstr "" + +#. module: openems +#: model:ir.model.fields,field_description:openems.field_openems_device__has_message +msgid "Has Message" +msgstr "" + +#. module: openems +#: model:ir.model.fields.selection,name:openems.selection__res_users__openems_language__hu +msgid "Hungarian" +msgstr "" + +#. module: openems +#: model:ir.model.fields,field_description:openems.field_openems_device__id +#: model:ir.model.fields,field_description:openems.field_openems_device_tag__id +#: model:ir.model.fields,field_description:openems.field_openems_device_user_role__id +#: model:ir.model.fields,field_description:openems.field_openems_openemsconfigupdate__id +#: model:ir.model.fields,field_description:openems.field_openems_setup_protocol__id +#: model:ir.model.fields,field_description:openems.field_openems_setup_protocol_item__id +#: model:ir.model.fields,field_description:openems.field_openems_setup_protocol_production_lot__id +#: model:ir.model.fields,field_description:openems.field_openems_systemmessage__id +msgid "ID" +msgstr "" + +#. module: openems +#: model:ir.model.fields,help:openems.field_openems_device__message_needaction +msgid "If checked, new messages require your attention." +msgstr "" + +#. module: openems +#: model:ir.model.fields,help:openems.field_openems_device__message_has_error +#: model:ir.model.fields,help:openems.field_openems_device__message_has_sms_error +msgid "If checked, some messages have a delivery error." +msgstr "" + +#. module: openems +#: model:ir.model.fields.selection,name:openems.selection__openems_device__openems_sum_state_level__info +msgid "Info" +msgstr "" + +#. module: openems +#: model_terms:ir.ui.view,arch_db:openems.report_openems_setup_protocol_template +msgid "Installateur" +msgstr "" + +#. module: openems +#: model_terms:ir.ui.view,arch_db:openems.openems_device_form +msgid "Installation" +msgstr "Inbetriebnahme" + +#. module: openems +#: model_terms:ir.ui.view,arch_db:openems.openems_device_form +msgid "Installation Log" +msgstr "Inbetriebnahme Protokolle" + +#. module: openems +#: model:ir.model.fields,field_description:openems.field_openems_device__setup_password +msgid "Installation key" +msgstr "Installateursschlüssel" + +#. module: openems +#: model:ir.model.fields,field_description:openems.field_res_partner__installer_setup_protocols_ids +#: model:ir.model.fields,field_description:openems.field_res_users__installer_setup_protocols_ids +msgid "Installed OpenEMS Edge" +msgstr "" + +#. module: openems +#: model:ir.model.fields,field_description:openems.field_openems_setup_protocol__installer_id +#: model:ir.model.fields.selection,name:openems.selection__openems_device_user_role__role__installer +#: model:ir.model.fields.selection,name:openems.selection__res_users__global_role__installer +msgid "Installer" +msgstr "" + +#. module: openems +#: model:ir.model.fields,field_description:openems.field_openems_device__internalnote +msgid "Internal note" +msgstr "" + +#. module: openems +#: model:ir.model.fields,field_description:openems.field_openems_device__message_is_follower +msgid "Is Follower" +msgstr "" + +#. module: openems +#: model_terms:ir.ui.view,arch_db:openems.openems_device_form +msgid "Is connected" +msgstr "" + +#. module: openems +#: model_terms:ir.ui.view,arch_db:openems.report_openems_setup_protocol_template +msgid "Kontaktdaten Endkunde" +msgstr "" + +#. module: openems +#: model_terms:ir.ui.view,arch_db:openems.report_openems_setup_protocol_template +msgid "Land" +msgstr "" + +#. module: openems +#: model:ir.model.fields,field_description:openems.field_openems_device____last_update +#: model:ir.model.fields,field_description:openems.field_openems_device_tag____last_update +#: model:ir.model.fields,field_description:openems.field_openems_device_user_role____last_update +#: model:ir.model.fields,field_description:openems.field_openems_openemsconfigupdate____last_update +#: model:ir.model.fields,field_description:openems.field_openems_setup_protocol____last_update +#: model:ir.model.fields,field_description:openems.field_openems_setup_protocol_item____last_update +#: model:ir.model.fields,field_description:openems.field_openems_setup_protocol_production_lot____last_update +#: model:ir.model.fields,field_description:openems.field_openems_systemmessage____last_update +msgid "Last Modified on" +msgstr "" + +#. module: openems +#: model:ir.model.fields,field_description:openems.field_openems_device__write_uid +#: model:ir.model.fields,field_description:openems.field_openems_device_tag__write_uid +#: model:ir.model.fields,field_description:openems.field_openems_device_user_role__write_uid +#: model:ir.model.fields,field_description:openems.field_openems_openemsconfigupdate__write_uid +#: model:ir.model.fields,field_description:openems.field_openems_setup_protocol__write_uid +#: model:ir.model.fields,field_description:openems.field_openems_setup_protocol_item__write_uid +#: model:ir.model.fields,field_description:openems.field_openems_setup_protocol_production_lot__write_uid +#: model:ir.model.fields,field_description:openems.field_openems_systemmessage__write_uid +msgid "Last Updated by" +msgstr "" + +#. module: openems +#: model:ir.model.fields,field_description:openems.field_openems_device__write_date +#: model:ir.model.fields,field_description:openems.field_openems_device_tag__write_date +#: model:ir.model.fields,field_description:openems.field_openems_device_user_role__write_date +#: model:ir.model.fields,field_description:openems.field_openems_openemsconfigupdate__write_date +#: model:ir.model.fields,field_description:openems.field_openems_setup_protocol__write_date +#: model:ir.model.fields,field_description:openems.field_openems_setup_protocol_item__write_date +#: model:ir.model.fields,field_description:openems.field_openems_setup_protocol_production_lot__write_date +#: model:ir.model.fields,field_description:openems.field_openems_systemmessage__write_date +msgid "Last Updated on" +msgstr "" + +#. module: openems +#: model:ir.model.fields,field_description:openems.field_openems_device__lastupdate +msgid "Last data update" +msgstr "" + +#. module: openems +#: model:ir.model.fields,field_description:openems.field_openems_device__lastmessage +msgid "Last message" +msgstr "" + +#. module: openems +#: model:ir.model.fields,field_description:openems.field_openems_device_user_role__last_notification +msgid "Last notification sent" +msgstr "" + +#. module: openems +#: model:ir.model,name:openems.model_stock_lot +msgid "Lot/Serial" +msgstr "Los/Serie" + +#. module: openems +#: model:ir.model.fields,field_description:openems.field_openems_device__message_main_attachment_id +msgid "Main Attachment" +msgstr "" + +#. module: openems +#: model:res.groups,name:openems.group_openems_manager +msgid "Manager" +msgstr "" + +#. module: openems +#: model:ir.model.fields,field_description:openems.field_openems_device__manual_setup_date +msgid "Manual Setup Date" +msgstr "" + +#. module: openems +#: model:res.groups,comment:openems.group_openems_manager +msgid "Members of this group can manage all devices" +msgstr "" + +#. module: openems +#: model:res.groups,comment:openems.group_openems_reader +msgid "Members of this group have reading access to all devices" +msgstr "" + +#. module: openems +#: model:ir.model.fields,field_description:openems.field_openems_systemmessage__text +msgid "Message" +msgstr "" + +#. module: openems +#: model:ir.model.fields,field_description:openems.field_openems_device__message_has_error +msgid "Message Delivery error" +msgstr "" + +#. module: openems +#: model:ir.model.fields,field_description:openems.field_openems_device__message_ids +msgid "Messages" +msgstr "" + +#. module: openems +#: model:ir.model.fields,field_description:openems.field_openems_device__name +#: model:ir.model.fields,field_description:openems.field_openems_device_tag__name +#: model:ir.model.fields,field_description:openems.field_openems_setup_protocol_item__name +#: model:ir.model.fields,field_description:openems.field_openems_setup_protocol_production_lot__name +msgid "Name" +msgstr "" + +#. module: openems +#: model_terms:ir.ui.view,arch_db:openems.report_openems_setup_protocol_template +msgid "Name Installateur" +msgstr "" + +#. module: openems +#: model_terms:ir.ui.view,arch_db:openems.report_openems_setup_protocol_template +msgid "Name Kontaktperson" +msgstr "" + +#. module: openems +#: model:ir.model.fields,field_description:openems.field_openems_device__name_number +msgid "Name Number" +msgstr "" + +#. module: openems +#: model:ir.model.constraint,message:openems.constraint_openems_device_unique_name +msgid "Name needs to be unique" +msgstr "" + +#. module: openems +#: model:ir.model.fields,field_description:openems.field_openems_device_user_role__time_to_wait +msgid "Notification" +msgstr "" + +#. module: openems +#: model:ir.model.fields,field_description:openems.field_openems_device__message_needaction_counter +msgid "Number of Actions" +msgstr "" + +#. module: openems +#: model:ir.model.fields,field_description:openems.field_openems_device__message_has_error_counter +msgid "Number of errors" +msgstr "" + +#. module: openems +#: model:ir.model.fields,help:openems.field_openems_device__message_needaction_counter +msgid "Number of messages requiring action" +msgstr "" + +#. module: openems +#: model:ir.model.fields,help:openems.field_openems_device__message_has_error_counter +msgid "Number of messages with delivery error" +msgstr "" + +#. module: openems +#: model_terms:ir.ui.view,arch_db:openems.openems_device_tree +msgid "OE Connected" +msgstr "" + +#. module: openems +#: model_terms:ir.ui.view,arch_db:openems.openems_device_tree +msgid "OE State" +msgstr "" + +#. module: openems +#: model_terms:ir.ui.view,arch_db:openems.openems_device_tree +msgid "OE Version" +msgstr "" + +#. module: openems +#: model:ir.model.fields,field_description:openems.field_openems_device__oem +msgid "OEM Branding" +msgstr "" + +#. module: openems +#: model:ir.model.fields.selection,name:openems.selection__openems_device__openems_sum_state_level__ok +msgid "Ok" +msgstr "" + +#. module: openems +#: model:ir.model.fields,field_description:openems.field_openems_device__monitoring_url +msgid "Online-Monitoring" +msgstr "" + +#. module: openems +#: model:ir.model.fields.selection,name:openems.selection__openems_device__oem__openems +#: model:ir.module.category,name:openems.module_category_openems +#: model:ir.ui.menu,name:openems.menu_openems +#: model_terms:ir.ui.view,arch_db:openems.openems_users_form +msgid "OpenEMS" +msgstr "" + +#. module: openems +#: model:mail.template,subject:openems.alerting_email_generic +msgid "OpenEMS Alert - Edge is offline" +msgstr "" + +#. module: openems +#: model_terms:ir.ui.view,arch_db:openems.report_openems_setup_protocol_template +msgid "OpenEMS Association e.V." +msgstr "" + +#. module: openems +#: model:ir.model.fields,field_description:openems.field_openems_device__openems_config_components +msgid "OpenEMS Config" +msgstr "" + +#. module: openems +#: model:ir.model.fields,field_description:openems.field_openems_device__openems_config +msgid "OpenEMS Config Full" +msgstr "" + +#. module: openems +#: model:ir.model.fields,field_description:openems.field_openems_device__openems_config_update_ids +msgid "OpenEMS Config Updates" +msgstr "" + +#. module: openems +#: model_terms:ir.ui.view,arch_db:openems.openems_openemsconfigupdate_tree +msgid "OpenEMS Configuration Updates" +msgstr "" + +#. module: openems +#: model:ir.model.fields,field_description:openems.field_openems_device_user_role__device_id +#: model:ir.model.fields,field_description:openems.field_openems_openemsconfigupdate__device_id +#: model:ir.model.fields,field_description:openems.field_openems_setup_protocol__device_id +#: model:ir.model.fields,field_description:openems.field_openems_systemmessage__device_id +#: model:ir.model.fields.selection,name:openems.selection__openems_device__producttype__openems-edge +#: model:ir.ui.menu,name:openems.menu_openems_content +msgid "OpenEMS Edge" +msgstr "" + +#. module: openems +#: model:ir.model,name:openems.model_openems_device +msgid "OpenEMS Edge Device" +msgstr "" + +#. module: openems +#: model:ir.model,name:openems.model_openems_openemsconfigupdate +msgid "OpenEMS Edge Device Configuration Update" +msgstr "" + +#. module: openems +#: model:ir.model,name:openems.model_openems_device_tag +msgid "OpenEMS Edge Device Tag" +msgstr "" + +#. module: openems +#: model:ir.model,name:openems.model_openems_device_user_role +msgid "OpenEMS Edge Device User Role" +msgstr "" + +#. module: openems +#: model:ir.model,name:openems.model_openems_setup_protocol_item +msgid "OpenEMS Edge Setup Protocol Entry Item" +msgstr "" + +#. module: openems +#: model:ir.model,name:openems.model_openems_setup_protocol_production_lot +msgid "OpenEMS Edge Setup Protocol Serial Number" +msgstr "" + +#. module: openems +#: model:ir.model,name:openems.model_openems_setup_protocol +msgid "OpenEMS Edge Setup Protocols (IBN)" +msgstr "" + +#. module: openems +#: model:ir.model,name:openems.model_openems_systemmessage +msgid "OpenEMS Edge Systemmessage" +msgstr "" + +#. module: openems +#: model:ir.model.fields,field_description:openems.field_openems_device__openems_is_connected +msgid "OpenEMS Is connected" +msgstr "" + +#. module: openems +#: model:ir.actions.report,name:openems.action_openems_setup_protocol_report +msgid "OpenEMS Setup Protocol" +msgstr "" + +#. module: openems +#: model:ir.model.fields,field_description:openems.field_openems_device__openems_sum_state_level +msgid "OpenEMS State" +msgstr "" + +#. module: openems +#: model:ir.model.fields,field_description:openems.field_openems_device__openems_version +#: model_terms:ir.ui.view,arch_db:openems.device_search_view +msgid "OpenEMS Version" +msgstr "" + +#. module: openems +#: model_terms:ir.ui.view,arch_db:openems.device_search_view +#: model_terms:ir.ui.view,arch_db:openems.openems_device_tree +msgid "OpenEMS-Number" +msgstr "" + +#. module: openems +#: model:ir.model.fields,field_description:openems.field_res_users__openems_language +msgid "Openems Language" +msgstr "" + +#. module: openems +#: model_terms:ir.ui.view,arch_db:openems.report_openems_setup_protocol_template +msgid "Ort" +msgstr "" + +#. module: openems +#: model:ir.model.fields.selection,name:openems.selection__openems_device_user_role__role__owner +#: model:ir.model.fields.selection,name:openems.selection__res_users__global_role__owner +msgid "Owner" +msgstr "" + +#. module: openems +#: model:ir.model.fields,field_description:openems.field_res_partner__customer_setup_protocols_ids +#: model:ir.model.fields,field_description:openems.field_res_users__customer_setup_protocols_ids +msgid "Owner of OpenEMS Edge" +msgstr "" + +#. module: openems +#: model_terms:ir.ui.view,arch_db:openems.report_openems_setup_protocol_template +msgid "PLZ" +msgstr "" + +#. module: openems +#: model:ir.model.fields,help:openems.field_openems_device__setup_password +msgid "Password for commissioning by the installer" +msgstr "Passwort für die Inbetriebnahme durch den Installateur" + +#. module: openems +#: model_terms:ir.ui.view,arch_db:openems.openems_setup_protocol_form +msgid "Print" +msgstr "" + +#. module: openems +#: model_terms:ir.ui.view,arch_db:openems.openems_device_form +msgid "Product" +msgstr "Produkt" + +#. module: openems +#: model:ir.model.fields,field_description:openems.field_openems_device__producttype +msgid "Product type" +msgstr "" + +#. module: openems +#: model:res.groups,name:openems.group_openems_reader +msgid "Read access" +msgstr "" + +#. module: openems +#: model:mail.template,subject:openems.registration_email +msgid "Registrierung erfolgreich" +msgstr "" + +#. module: openems +#: model:ir.model.fields,field_description:openems.field_openems_device_user_role__role +msgid "Role" +msgstr "" + +#. module: openems +#: model:ir.model.fields,field_description:openems.field_openems_device__user_role_ids +#: model:ir.model.fields,field_description:openems.field_res_users__device_role_ids +msgid "Roles" +msgstr "" + +#. module: openems +#: model:ir.model.fields,field_description:openems.field_openems_device__message_has_sms_error +msgid "SMS Delivery error" +msgstr "" + +#. module: openems +#: model_terms:ir.ui.view,arch_db:openems.openems_device_form +msgid "Security" +msgstr "Sicherheit" + +#. module: openems +#: model:ir.model.fields,field_description:openems.field_openems_setup_protocol_production_lot__lot_id +msgid "Serial Number" +msgstr "" + +#. module: openems +#: model:ir.model.fields,field_description:openems.field_openems_setup_protocol__productionlot_ids +msgid "Serial Numbers" +msgstr "" + +#. module: openems +#: model:ir.model.constraint,message:openems.constraint_openems_device_unique_stock_production_lot_id +msgid "Serial number needs to be unique" +msgstr "" + +#. module: openems +#: model:ir.model.fields,field_description:openems.field_openems_setup_protocol_item__setup_protocol_id +#: model:ir.model.fields,field_description:openems.field_openems_setup_protocol_production_lot__setup_protocol_id +#: model_terms:ir.ui.view,arch_db:openems.report_openems_setup_protocol_template +msgid "Setup Protocol" +msgstr "" + +#. module: openems +#: model:ir.actions.act_window,name:openems.action_openems_admin_setup_protocol +#: model:ir.model.fields,field_description:openems.field_openems_device__setup_protocol_ids +#: model:ir.ui.menu,name:openems.menu_openems_admin_setup_protocol +#: model_terms:ir.ui.view,arch_db:openems.res_partner_form +msgid "Setup Protocols" +msgstr "" + +#. module: openems +#: model:ir.model.fields,field_description:openems.field_openems_setup_protocol_item__sequence +#: model:ir.model.fields,field_description:openems.field_openems_setup_protocol_production_lot__sequence +msgid "Sort" +msgstr "" + +#. module: openems +#: model:ir.model.fields.selection,name:openems.selection__res_users__openems_language__es +msgid "Spanish" +msgstr "" + +#. module: openems +#: model_terms:ir.ui.view,arch_db:openems.report_openems_setup_protocol_template +msgid "Speicherstandort" +msgstr "" + +#. module: openems +#: model_terms:ir.ui.view,arch_db:openems.openems_device_form +msgid "Status" +msgstr "" + +#. module: openems +#: model:ir.model.fields,field_description:openems.field_openems_device__stock_production_lot_id +msgid "Stock Production Lot" +msgstr "" + +#. module: openems +#: model_terms:ir.ui.view,arch_db:openems.report_openems_setup_protocol_template +msgid "Straße / Hausnummer" +msgstr "" + +#. module: openems +#: model_terms:ir.ui.view,arch_db:openems.openems_device_form +msgid "System Messages" +msgstr "" + +#. module: openems +#: model:ir.actions.act_window,name:openems.action_openems_systemmessage +#: model:ir.model.fields,field_description:openems.field_openems_device__systemmessage_ids +#: model:ir.ui.menu,name:openems.menu_openems_admin_systemmessages +msgid "Systemmessages" +msgstr "" + +#. module: openems +#: model_terms:ir.ui.view,arch_db:openems.openems_device_form +msgid "Systemstatus" +msgstr "" + +#. module: openems +#: model:ir.model.fields,field_description:openems.field_openems_device__tag_ids +msgid "Tags" +msgstr "" + +#. module: openems +#: model_terms:ir.ui.view,arch_db:openems.report_openems_setup_protocol_template +msgid "Telefonnummer" +msgstr "" + +#. module: openems +#: model:ir.model.fields,field_description:openems.field_openems_systemmessage__text_teaser +msgid "Text Teaser" +msgstr "" + +#. module: openems +#: model:ir.model.fields,field_description:openems.field_openems_openemsconfigupdate__details +msgid "Update Details" +msgstr "" + +#. module: openems +#: model:ir.model.fields,field_description:openems.field_openems_openemsconfigupdate__teaser +msgid "Update Details Teaser" +msgstr "" + +#. module: openems +#: model:ir.model,name:openems.model_res_users +#: model:ir.model.fields,field_description:openems.field_openems_device_user_role__user_id +msgid "User" +msgstr "Benutzer" + +#. module: openems +#: model:ir.model.constraint,message:openems.constraint_openems_device_user_role_device_user_uniq +msgid "User already exists for this device." +msgstr "" + +#. module: openems +#: model_terms:ir.ui.view,arch_db:openems.openems_systemmessage_tree +msgid "Users" +msgstr "" + +#. module: openems +#: model:ir.model.fields,field_description:openems.field_openems_setup_protocol_item__value +msgid "Value" +msgstr "" + +#. module: openems +#: model:ir.model.fields,field_description:openems.field_openems_setup_protocol_item__view +msgid "View Identifier" +msgstr "" + +#. module: openems +#: model_terms:ir.ui.view,arch_db:openems.report_openems_setup_protocol_template +msgid "Vor- Nachname" +msgstr "" + +#. module: openems +#: model:ir.model.fields.selection,name:openems.selection__openems_device__openems_sum_state_level__warning +msgid "Warning" +msgstr "" + +#. module: openems +#: model:ir.model.fields,field_description:openems.field_openems_device__website_message_ids +msgid "Website Messages" +msgstr "" + +#. module: openems +#: model:ir.model.fields,help:openems.field_openems_device__website_message_ids +msgid "Website communication history" +msgstr "" + +#. module: openems +#: model:mail.template,subject:openems.setup_protocol_email_customer +#: model:mail.template,subject:openems.setup_protocol_email_installer +msgid "Your OpenEMS setup protocol for {{object.openems_device_id.name}}" +msgstr "" diff --git a/16.0/extra-addons/openems/i18n/openems.pot b/16.0/extra-addons/openems/i18n/openems.pot new file mode 100644 index 0000000..c02fb93 --- /dev/null +++ b/16.0/extra-addons/openems/i18n/openems.pot @@ -0,0 +1,1666 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * openems +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 16.0-20240218\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2024-02-18 11:56+0000\n" +"PO-Revision-Date: 2024-02-18 11:56+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: openems +#: model:mail.template,body_html:openems.setup_protocol_email_installer +msgid "" +"\n" +"\n" +"\n" +"\n" +"\n" +" OpenEMS setup protocol \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +"\n" +"\n" +"\n" +"
\n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +"
\n" +" \n" +"
\n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +"
\n" +" \n" +"
\n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +"
\n" +"

\n" +"

\n" +" \n" +"
\n" +"
Dear \n" +" ,\n" +"
\n" +"
\n" +"
please find the setup protocol for your customer attached.\n" +"
\n" +"
\n" +"
Best Regards
\n" +"
\n" +"
OpenEMS Association e.V.
\n" +"
\n" +"
\n" +" \n" +"
\n" +"
\n" +" \n" +"
\n" +"
\n" +"\n" +"\n" +"\n" +" \n" +" " +msgstr "" + +#. module: openems +#: model:mail.template,body_html:openems.registration_email +msgid "" +"\n" +"\n" +"\n" +"\n" +"\n" +" Registrierung erfolgreich \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +"\n" +"\n" +"\n" +"
\n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +"
\n" +" \n" +"
\n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +"
\n" +" \n" +"
\n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +"
\n" +"

\n" +"

\n" +" \n" +"
\n" +"
Guten Tag \n" +" ,\n" +"
\n" +"
\n" +"
Ihr Zugang zu OpenEMS UI wurde erstellt.
\n" +"
\n" +"
Sie können sich auf OpenEMS UI einloggen, um auf das Online-Monitoring zuzugreifen.
\n" +"
\n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +"
Ihre Zugangsdaten:
E-Mail\n" +" \n" +"
Passwort\n" +" \n" +"
\n" +"
\n" +"
Best Regards
\n" +"
\n" +"
OpenEMS Association e.V.
\n" +"
\n" +"
\n" +" \n" +"
\n" +"
\n" +" \n" +"
\n" +"
\n" +"\n" +"\n" +"\n" +" \n" +" " +msgstr "" + +#. module: openems +#: model:mail.template,body_html:openems.setup_protocol_email_customer +msgid "" +"\n" +"\n" +"\n" +"\n" +"\n" +" Your OpenEMS Edge setup protocol \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +"\n" +"\n" +"\n" +"
\n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +"
\n" +" \n" +"
\n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +"
\n" +" \n" +"
\n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +"
\n" +"

\n" +"

\n" +" \n" +"
\n" +"
Welcome to OpenEMS,
\n" +"
\n" +"
please find your setup protocol for OpenEMS Edge attached.
\n" +"
\n" +"
Best Regards
\n" +"
\n" +"
OpenEMS Association e.V.
\n" +"
\n" +"
\n" +" \n" +"
\n" +"
\n" +" \n" +"
\n" +"
\n" +"\n" +"\n" +"\n" +" \n" +" " +msgstr "" + +#. module: openems +#: model:mail.template,body_html:openems.alerting_email_generic +msgid "" +"\n" +"\n" +"\n" +"\n" +"\n" +" OpenEMS Alert - Edge is offline\n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +"\n" +"\n" +"\n" +"
\n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +"
\n" +" \n" +"
\n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +"
\n" +" \n" +"
\n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +"
\n" +"

\n" +"

\n" +" \n" +"
\n" +"
Dear \n" +" ,\n" +"
\n" +"
\n" +"
Your OpenEMS Edge with number is offline since:
\n" +"
\n" +"
\n" +" \n" +" \n" +" \n" +" \n" +" \n" +" (UTC)\n" +" \n" +"
\n" +"
\n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +"
Info
\n" +" Edge-Version\n" +" \n" +" \n" +"
\n" +" Type\n" +" \n" +" \n" +" \n" +"
\n" +"
\n" +"
Best Regards
\n" +"
\n" +"
OpenEMS Association e.V.
\n" +"
\n" +"
\n" +" \n" +"
\n" +"
\n" +" \n" +"
\n" +" \n" +"
\n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +"
\n" +" \n" +"
\n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +"
\n" +"
Wenn Sie keine E-Mail Benachrichtigung mehr wünschen, können Sie die Funktion hier deaktivieren.
\n" +"
\n" +"
Dies ist eine automatisch generierte Nachricht. Bitte antworten Sie nicht auf diese Nachricht!
\n" +"
\n" +"
\n" +" \n" +"
\n" +"
\n" +" \n" +"
\n" +"\n" +"\n" +"\n" +" \n" +" " +msgstr "" + +#. module: openems +#: model:ir.actions.report,print_report_name:openems.action_openems_setup_protocol_report +msgid "" +"('IBN-' + object.openems_device_id.name + '-' + " +"object.create_date.strftime('%d.%m.%Y'))" +msgstr "" + +#. module: openems +#: model:ir.model.fields,field_description:openems.field_openems_device__apikey +msgid "API-Key" +msgstr "" + +#. module: openems +#: model_terms:ir.ui.view,arch_db:openems.openems_device_form +msgid "Access Role in Online-Monitoring" +msgstr "" + +#. module: openems +#: model:ir.model.fields,field_description:openems.field_openems_device__message_needaction +msgid "Action Needed" +msgstr "" + +#. module: openems +#: model:ir.model.fields,field_description:openems.field_openems_device__active +msgid "Active" +msgstr "" + +#. module: openems +#: model:ir.model.fields.selection,name:openems.selection__openems_device_user_role__role__admin +#: model:ir.model.fields.selection,name:openems.selection__res_users__global_role__admin +msgid "Admin" +msgstr "" + +#. module: openems +#: model:ir.ui.menu,name:openems.menu_openems_admin +msgid "Administration" +msgstr "" + +#. module: openems +#: model_terms:ir.ui.view,arch_db:openems.device_search_view +msgid "Archived" +msgstr "" + +#. module: openems +#: model:ir.model.fields,field_description:openems.field_openems_device__message_attachment_count +msgid "Attachment Count" +msgstr "" + +#. module: openems +#: model:ir.model.fields,field_description:openems.field_res_users__branding_partner_id +msgid "Branding Partner" +msgstr "" + +#. module: openems +#: model:ir.model.fields,field_description:openems.field_openems_setup_protocol_item__category +#: model:ir.model.fields,field_description:openems.field_openems_setup_protocol_production_lot__category +msgid "Category" +msgstr "" + +#. module: openems +#: model:ir.model.fields,field_description:openems.field_openems_device__comment +msgid "Comment" +msgstr "" + +#. module: openems +#: model_terms:ir.ui.view,arch_db:openems.openems_device_form +msgid "Configuration" +msgstr "" + +#. module: openems +#: model_terms:ir.ui.view,arch_db:openems.openems_device_form +msgid "Configuration Updates" +msgstr "" + +#. module: openems +#: model:ir.model,name:openems.model_res_partner +msgid "Contact" +msgstr "" + +#. module: openems +#: model:ir.model.fields,field_description:openems.field_openems_device__create_uid +#: model:ir.model.fields,field_description:openems.field_openems_device_tag__create_uid +#: model:ir.model.fields,field_description:openems.field_openems_device_user_role__create_uid +#: model:ir.model.fields,field_description:openems.field_openems_openemsconfigupdate__create_uid +#: model:ir.model.fields,field_description:openems.field_openems_setup_protocol__create_uid +#: model:ir.model.fields,field_description:openems.field_openems_setup_protocol_item__create_uid +#: model:ir.model.fields,field_description:openems.field_openems_setup_protocol_production_lot__create_uid +#: model:ir.model.fields,field_description:openems.field_openems_systemmessage__create_uid +msgid "Created by" +msgstr "" + +#. module: openems +#: model:ir.model.fields,field_description:openems.field_openems_device__create_date +#: model:ir.model.fields,field_description:openems.field_openems_device_tag__create_date +#: model:ir.model.fields,field_description:openems.field_openems_device_user_role__create_date +#: model:ir.model.fields,field_description:openems.field_openems_openemsconfigupdate__create_date +#: model:ir.model.fields,field_description:openems.field_openems_setup_protocol__create_date +#: model:ir.model.fields,field_description:openems.field_openems_setup_protocol_item__create_date +#: model:ir.model.fields,field_description:openems.field_openems_setup_protocol_production_lot__create_date +#: model:ir.model.fields,field_description:openems.field_openems_systemmessage__create_date +msgid "Created on" +msgstr "" + +#. module: openems +#: model:res.partner.category,name:openems.res_partner_category_created_via_ibn +msgid "Created via IBN" +msgstr "" + +#. module: openems +#: model:ir.model.fields,field_description:openems.field_openems_systemmessage__timestamp +msgid "Creation date" +msgstr "" + +#. module: openems +#: model:ir.model.fields,field_description:openems.field_openems_setup_protocol__customer_id +#: model:res.partner.category,name:openems.res_partner_category_customer +msgid "Customer" +msgstr "" + +#. module: openems +#: model:ir.model.fields.selection,name:openems.selection__res_users__openems_language__cz +msgid "Czech" +msgstr "" + +#. module: openems +#: model_terms:ir.ui.view,arch_db:openems.report_openems_setup_protocol_template +msgid "Datum:" +msgstr "" + +#. module: openems +#: model_terms:ir.ui.view,arch_db:openems.device_search_view +#: model_terms:ir.ui.view,arch_db:openems.openems_device_tree +msgid "Description" +msgstr "" + +#. module: openems +#: model_terms:ir.ui.view,arch_db:openems.openems_openemsconfigupdate_tree +msgid "Details" +msgstr "" + +#. module: openems +#: model:ir.model.fields,field_description:openems.field_stock_lot__device_id +#: model:ir.model.fields,field_description:openems.field_stock_lot__device_ids +msgid "Device" +msgstr "" + +#. module: openems +#: model:ir.actions.act_window,name:openems.action_openems_openemsconfigupdate +#: model:ir.ui.menu,name:openems.menu_openems_admin_openemsconfigupdate +msgid "Device Configuration Updates" +msgstr "" + +#. module: openems +#: model:ir.actions.act_window,name:openems.action_openems_device +#: model:ir.ui.menu,name:openems.menu_openems_content_devices +msgid "Devices" +msgstr "" + +#. module: openems +#: model:ir.model.fields,field_description:openems.field_openems_setup_protocol__different_location_id +msgid "Different Location" +msgstr "" + +#. module: openems +#: model:ir.model.fields,field_description:openems.field_openems_device__display_name +#: model:ir.model.fields,field_description:openems.field_openems_device_tag__display_name +#: model:ir.model.fields,field_description:openems.field_openems_device_user_role__display_name +#: model:ir.model.fields,field_description:openems.field_openems_openemsconfigupdate__display_name +#: model:ir.model.fields,field_description:openems.field_openems_setup_protocol__display_name +#: model:ir.model.fields,field_description:openems.field_openems_setup_protocol_item__display_name +#: model:ir.model.fields,field_description:openems.field_openems_setup_protocol_production_lot__display_name +#: model:ir.model.fields,field_description:openems.field_openems_systemmessage__display_name +msgid "Display Name" +msgstr "" + +#. module: openems +#: model:ir.model.fields.selection,name:openems.selection__res_users__openems_language__nl +msgid "Dutch" +msgstr "" + +#. module: openems +#: model:mail.template,name:openems.alerting_email_generic +msgid "E-Mail Alerting" +msgstr "" + +#. module: openems +#: model:mail.template,name:openems.registration_email +msgid "E-Mail Kunden Registrierung" +msgstr "" + +#. module: openems +#: model:mail.template,name:openems.setup_protocol_email_customer +msgid "E-Mail setup protocol for customer" +msgstr "" + +#. module: openems +#: model:mail.template,name:openems.setup_protocol_email_installer +msgid "E-Mail setup protocol for installer" +msgstr "" + +#. module: openems +#: model_terms:ir.ui.view,arch_db:openems.report_openems_setup_protocol_template +msgid "E-Mail-Adresse" +msgstr "" + +#. module: openems +#: model:ir.model.fields,field_description:openems.field_openems_device__emshardware +msgid "EMS Hardware" +msgstr "" + +#. module: openems +#: model:ir.model.fields.selection,name:openems.selection__res_users__openems_language__en +msgid "English" +msgstr "" + +#. module: openems +#: model:ir.model.fields,field_description:openems.field_openems_setup_protocol__item_ids +msgid "Entry Items" +msgstr "" + +#. module: openems +#: model:ir.model.fields.selection,name:openems.selection__openems_device__openems_sum_state_level__fault +msgid "Fault" +msgstr "" + +#. module: openems +#: model:ir.model.fields,field_description:openems.field_openems_setup_protocol_item__field +msgid "Field Identifier" +msgstr "" + +#. module: openems +#: model_terms:ir.ui.view,arch_db:openems.report_openems_setup_protocol_template +msgid "Firmenname" +msgstr "" + +#. module: openems +#: model:ir.model.fields,field_description:openems.field_openems_device__first_setup_protocol_date +msgid "First Setup Protocol Date" +msgstr "" + +#. module: openems +#: model:ir.model.fields,field_description:openems.field_openems_device__message_follower_ids +msgid "Followers" +msgstr "" + +#. module: openems +#: model:ir.model.fields,field_description:openems.field_openems_device__message_partner_ids +msgid "Followers (Partners)" +msgstr "" + +#. module: openems +#: model:ir.model.fields.selection,name:openems.selection__res_users__openems_language__fr +msgid "French" +msgstr "" + +#. module: openems +#: model_terms:ir.ui.view,arch_db:openems.openems_device_form +msgid "General" +msgstr "" + +#. module: openems +#: model:ir.model.fields.selection,name:openems.selection__res_users__openems_language__de +msgid "German" +msgstr "" + +#. module: openems +#: model:ir.model.fields,field_description:openems.field_res_users__global_role +msgid "Global Role" +msgstr "" + +#. module: openems +#: model:ir.model.fields.selection,name:openems.selection__openems_device_user_role__role__guest +#: model:ir.model.fields.selection,name:openems.selection__res_users__global_role__guest +msgid "Guest" +msgstr "" + +#. module: openems +#: model_terms:ir.ui.view,arch_db:openems.openems_device_form +msgid "Hardware" +msgstr "" + +#. module: openems +#: model:ir.model.fields,field_description:openems.field_openems_device__has_message +msgid "Has Message" +msgstr "" + +#. module: openems +#: model:ir.model.fields.selection,name:openems.selection__res_users__openems_language__hu +msgid "Hungarian" +msgstr "" + +#. module: openems +#: model:ir.model.fields,field_description:openems.field_openems_device__id +#: model:ir.model.fields,field_description:openems.field_openems_device_tag__id +#: model:ir.model.fields,field_description:openems.field_openems_device_user_role__id +#: model:ir.model.fields,field_description:openems.field_openems_openemsconfigupdate__id +#: model:ir.model.fields,field_description:openems.field_openems_setup_protocol__id +#: model:ir.model.fields,field_description:openems.field_openems_setup_protocol_item__id +#: model:ir.model.fields,field_description:openems.field_openems_setup_protocol_production_lot__id +#: model:ir.model.fields,field_description:openems.field_openems_systemmessage__id +msgid "ID" +msgstr "" + +#. module: openems +#: model:ir.model.fields,help:openems.field_openems_device__message_needaction +msgid "If checked, new messages require your attention." +msgstr "" + +#. module: openems +#: model:ir.model.fields,help:openems.field_openems_device__message_has_error +#: model:ir.model.fields,help:openems.field_openems_device__message_has_sms_error +msgid "If checked, some messages have a delivery error." +msgstr "" + +#. module: openems +#: model:ir.model.fields.selection,name:openems.selection__openems_device__openems_sum_state_level__info +msgid "Info" +msgstr "" + +#. module: openems +#: model_terms:ir.ui.view,arch_db:openems.report_openems_setup_protocol_template +msgid "Installateur" +msgstr "" + +#. module: openems +#: model_terms:ir.ui.view,arch_db:openems.openems_device_form +msgid "Installation" +msgstr "" + +#. module: openems +#: model_terms:ir.ui.view,arch_db:openems.openems_device_form +msgid "Installation Log" +msgstr "" + +#. module: openems +#: model:ir.model.fields,field_description:openems.field_openems_device__setup_password +msgid "Installation key" +msgstr "" + +#. module: openems +#: model:ir.model.fields,field_description:openems.field_res_partner__installer_setup_protocols_ids +#: model:ir.model.fields,field_description:openems.field_res_users__installer_setup_protocols_ids +msgid "Installed OpenEMS Edge" +msgstr "" + +#. module: openems +#: model:ir.model.fields,field_description:openems.field_openems_setup_protocol__installer_id +#: model:ir.model.fields.selection,name:openems.selection__openems_device_user_role__role__installer +#: model:ir.model.fields.selection,name:openems.selection__res_users__global_role__installer +msgid "Installer" +msgstr "" + +#. module: openems +#: model:ir.model.fields,field_description:openems.field_openems_device__internalnote +msgid "Internal note" +msgstr "" + +#. module: openems +#: model:ir.model.fields,field_description:openems.field_openems_device__message_is_follower +msgid "Is Follower" +msgstr "" + +#. module: openems +#: model_terms:ir.ui.view,arch_db:openems.openems_device_form +msgid "Is connected" +msgstr "" + +#. module: openems +#: model_terms:ir.ui.view,arch_db:openems.report_openems_setup_protocol_template +msgid "Kontaktdaten Endkunde" +msgstr "" + +#. module: openems +#: model_terms:ir.ui.view,arch_db:openems.report_openems_setup_protocol_template +msgid "Land" +msgstr "" + +#. module: openems +#: model:ir.model.fields,field_description:openems.field_openems_device____last_update +#: model:ir.model.fields,field_description:openems.field_openems_device_tag____last_update +#: model:ir.model.fields,field_description:openems.field_openems_device_user_role____last_update +#: model:ir.model.fields,field_description:openems.field_openems_openemsconfigupdate____last_update +#: model:ir.model.fields,field_description:openems.field_openems_setup_protocol____last_update +#: model:ir.model.fields,field_description:openems.field_openems_setup_protocol_item____last_update +#: model:ir.model.fields,field_description:openems.field_openems_setup_protocol_production_lot____last_update +#: model:ir.model.fields,field_description:openems.field_openems_systemmessage____last_update +msgid "Last Modified on" +msgstr "" + +#. module: openems +#: model:ir.model.fields,field_description:openems.field_openems_device__write_uid +#: model:ir.model.fields,field_description:openems.field_openems_device_tag__write_uid +#: model:ir.model.fields,field_description:openems.field_openems_device_user_role__write_uid +#: model:ir.model.fields,field_description:openems.field_openems_openemsconfigupdate__write_uid +#: model:ir.model.fields,field_description:openems.field_openems_setup_protocol__write_uid +#: model:ir.model.fields,field_description:openems.field_openems_setup_protocol_item__write_uid +#: model:ir.model.fields,field_description:openems.field_openems_setup_protocol_production_lot__write_uid +#: model:ir.model.fields,field_description:openems.field_openems_systemmessage__write_uid +msgid "Last Updated by" +msgstr "" + +#. module: openems +#: model:ir.model.fields,field_description:openems.field_openems_device__write_date +#: model:ir.model.fields,field_description:openems.field_openems_device_tag__write_date +#: model:ir.model.fields,field_description:openems.field_openems_device_user_role__write_date +#: model:ir.model.fields,field_description:openems.field_openems_openemsconfigupdate__write_date +#: model:ir.model.fields,field_description:openems.field_openems_setup_protocol__write_date +#: model:ir.model.fields,field_description:openems.field_openems_setup_protocol_item__write_date +#: model:ir.model.fields,field_description:openems.field_openems_setup_protocol_production_lot__write_date +#: model:ir.model.fields,field_description:openems.field_openems_systemmessage__write_date +msgid "Last Updated on" +msgstr "" + +#. module: openems +#: model:ir.model.fields,field_description:openems.field_openems_device__lastupdate +msgid "Last data update" +msgstr "" + +#. module: openems +#: model:ir.model.fields,field_description:openems.field_openems_device__lastmessage +msgid "Last message" +msgstr "" + +#. module: openems +#: model:ir.model.fields,field_description:openems.field_openems_device_user_role__last_notification +msgid "Last notification sent" +msgstr "" + +#. module: openems +#: model:ir.model,name:openems.model_stock_lot +msgid "Lot/Serial" +msgstr "" + +#. module: openems +#: model:ir.model.fields,field_description:openems.field_openems_device__message_main_attachment_id +msgid "Main Attachment" +msgstr "" + +#. module: openems +#: model:res.groups,name:openems.group_openems_manager +msgid "Manager" +msgstr "" + +#. module: openems +#: model:ir.model.fields,field_description:openems.field_openems_device__manual_setup_date +msgid "Manual Setup Date" +msgstr "" + +#. module: openems +#: model:res.groups,comment:openems.group_openems_manager +msgid "Members of this group can manage all devices" +msgstr "" + +#. module: openems +#: model:res.groups,comment:openems.group_openems_reader +msgid "Members of this group have reading access to all devices" +msgstr "" + +#. module: openems +#: model:ir.model.fields,field_description:openems.field_openems_systemmessage__text +msgid "Message" +msgstr "" + +#. module: openems +#: model:ir.model.fields,field_description:openems.field_openems_device__message_has_error +msgid "Message Delivery error" +msgstr "" + +#. module: openems +#: model:ir.model.fields,field_description:openems.field_openems_device__message_ids +msgid "Messages" +msgstr "" + +#. module: openems +#: model:ir.model.fields,field_description:openems.field_openems_device__name +#: model:ir.model.fields,field_description:openems.field_openems_device_tag__name +#: model:ir.model.fields,field_description:openems.field_openems_setup_protocol_item__name +#: model:ir.model.fields,field_description:openems.field_openems_setup_protocol_production_lot__name +msgid "Name" +msgstr "" + +#. module: openems +#: model_terms:ir.ui.view,arch_db:openems.report_openems_setup_protocol_template +msgid "Name Installateur" +msgstr "" + +#. module: openems +#: model_terms:ir.ui.view,arch_db:openems.report_openems_setup_protocol_template +msgid "Name Kontaktperson" +msgstr "" + +#. module: openems +#: model:ir.model.fields,field_description:openems.field_openems_device__name_number +msgid "Name Number" +msgstr "" + +#. module: openems +#: model:ir.model.constraint,message:openems.constraint_openems_device_unique_name +msgid "Name needs to be unique" +msgstr "" + +#. module: openems +#: model:ir.model.fields,field_description:openems.field_openems_device_user_role__time_to_wait +msgid "Notification" +msgstr "" + +#. module: openems +#: model:ir.model.fields,field_description:openems.field_openems_device__message_needaction_counter +msgid "Number of Actions" +msgstr "" + +#. module: openems +#: model:ir.model.fields,field_description:openems.field_openems_device__message_has_error_counter +msgid "Number of errors" +msgstr "" + +#. module: openems +#: model:ir.model.fields,help:openems.field_openems_device__message_needaction_counter +msgid "Number of messages requiring action" +msgstr "" + +#. module: openems +#: model:ir.model.fields,help:openems.field_openems_device__message_has_error_counter +msgid "Number of messages with delivery error" +msgstr "" + +#. module: openems +#: model_terms:ir.ui.view,arch_db:openems.openems_device_tree +msgid "OE Connected" +msgstr "" + +#. module: openems +#: model_terms:ir.ui.view,arch_db:openems.openems_device_tree +msgid "OE State" +msgstr "" + +#. module: openems +#: model_terms:ir.ui.view,arch_db:openems.openems_device_tree +msgid "OE Version" +msgstr "" + +#. module: openems +#: model:ir.model.fields,field_description:openems.field_openems_device__oem +msgid "OEM Branding" +msgstr "" + +#. module: openems +#: model:ir.model.fields.selection,name:openems.selection__openems_device__openems_sum_state_level__ok +msgid "Ok" +msgstr "" + +#. module: openems +#: model:ir.model.fields,field_description:openems.field_openems_device__monitoring_url +msgid "Online-Monitoring" +msgstr "" + +#. module: openems +#: model:ir.model.fields.selection,name:openems.selection__openems_device__oem__openems +#: model:ir.module.category,name:openems.module_category_openems +#: model:ir.ui.menu,name:openems.menu_openems +#: model_terms:ir.ui.view,arch_db:openems.openems_users_form +msgid "OpenEMS" +msgstr "" + +#. module: openems +#: model:mail.template,subject:openems.alerting_email_generic +msgid "OpenEMS Alert - Edge is offline" +msgstr "" + +#. module: openems +#: model_terms:ir.ui.view,arch_db:openems.report_openems_setup_protocol_template +msgid "OpenEMS Association e.V." +msgstr "" + +#. module: openems +#: model:ir.model.fields,field_description:openems.field_openems_device__openems_config_components +msgid "OpenEMS Config" +msgstr "" + +#. module: openems +#: model:ir.model.fields,field_description:openems.field_openems_device__openems_config +msgid "OpenEMS Config Full" +msgstr "" + +#. module: openems +#: model:ir.model.fields,field_description:openems.field_openems_device__openems_config_update_ids +msgid "OpenEMS Config Updates" +msgstr "" + +#. module: openems +#: model_terms:ir.ui.view,arch_db:openems.openems_openemsconfigupdate_tree +msgid "OpenEMS Configuration Updates" +msgstr "" + +#. module: openems +#: model:ir.model.fields,field_description:openems.field_openems_device_user_role__device_id +#: model:ir.model.fields,field_description:openems.field_openems_openemsconfigupdate__device_id +#: model:ir.model.fields,field_description:openems.field_openems_setup_protocol__device_id +#: model:ir.model.fields,field_description:openems.field_openems_systemmessage__device_id +#: model:ir.model.fields.selection,name:openems.selection__openems_device__producttype__openems-edge +#: model:ir.ui.menu,name:openems.menu_openems_content +msgid "OpenEMS Edge" +msgstr "" + +#. module: openems +#: model:ir.model,name:openems.model_openems_device +msgid "OpenEMS Edge Device" +msgstr "" + +#. module: openems +#: model:ir.model,name:openems.model_openems_openemsconfigupdate +msgid "OpenEMS Edge Device Configuration Update" +msgstr "" + +#. module: openems +#: model:ir.model,name:openems.model_openems_device_tag +msgid "OpenEMS Edge Device Tag" +msgstr "" + +#. module: openems +#: model:ir.model,name:openems.model_openems_device_user_role +msgid "OpenEMS Edge Device User Role" +msgstr "" + +#. module: openems +#: model:ir.model,name:openems.model_openems_setup_protocol_item +msgid "OpenEMS Edge Setup Protocol Entry Item" +msgstr "" + +#. module: openems +#: model:ir.model,name:openems.model_openems_setup_protocol_production_lot +msgid "OpenEMS Edge Setup Protocol Serial Number" +msgstr "" + +#. module: openems +#: model:ir.model,name:openems.model_openems_setup_protocol +msgid "OpenEMS Edge Setup Protocols (IBN)" +msgstr "" + +#. module: openems +#: model:ir.model,name:openems.model_openems_systemmessage +msgid "OpenEMS Edge Systemmessage" +msgstr "" + +#. module: openems +#: model:ir.model.fields,field_description:openems.field_openems_device__openems_is_connected +msgid "OpenEMS Is connected" +msgstr "" + +#. module: openems +#: model:ir.actions.report,name:openems.action_openems_setup_protocol_report +msgid "OpenEMS Setup Protocol" +msgstr "" + +#. module: openems +#: model:ir.model.fields,field_description:openems.field_openems_device__openems_sum_state_level +msgid "OpenEMS State" +msgstr "" + +#. module: openems +#: model:ir.model.fields,field_description:openems.field_openems_device__openems_version +#: model_terms:ir.ui.view,arch_db:openems.device_search_view +msgid "OpenEMS Version" +msgstr "" + +#. module: openems +#: model_terms:ir.ui.view,arch_db:openems.device_search_view +#: model_terms:ir.ui.view,arch_db:openems.openems_device_tree +msgid "OpenEMS-Number" +msgstr "" + +#. module: openems +#: model:ir.model.fields,field_description:openems.field_res_users__openems_language +msgid "Openems Language" +msgstr "" + +#. module: openems +#: model_terms:ir.ui.view,arch_db:openems.report_openems_setup_protocol_template +msgid "Ort" +msgstr "" + +#. module: openems +#: model:ir.model.fields.selection,name:openems.selection__openems_device_user_role__role__owner +#: model:ir.model.fields.selection,name:openems.selection__res_users__global_role__owner +msgid "Owner" +msgstr "" + +#. module: openems +#: model:ir.model.fields,field_description:openems.field_res_partner__customer_setup_protocols_ids +#: model:ir.model.fields,field_description:openems.field_res_users__customer_setup_protocols_ids +msgid "Owner of OpenEMS Edge" +msgstr "" + +#. module: openems +#: model_terms:ir.ui.view,arch_db:openems.report_openems_setup_protocol_template +msgid "PLZ" +msgstr "" + +#. module: openems +#: model:ir.model.fields,help:openems.field_openems_device__setup_password +msgid "Password for commissioning by the installer" +msgstr "" + +#. module: openems +#: model_terms:ir.ui.view,arch_db:openems.openems_setup_protocol_form +msgid "Print" +msgstr "" + +#. module: openems +#: model_terms:ir.ui.view,arch_db:openems.openems_device_form +msgid "Product" +msgstr "" + +#. module: openems +#: model:ir.model.fields,field_description:openems.field_openems_device__producttype +msgid "Product type" +msgstr "" + +#. module: openems +#: model:res.groups,name:openems.group_openems_reader +msgid "Read access" +msgstr "" + +#. module: openems +#: model:mail.template,subject:openems.registration_email +msgid "Registrierung erfolgreich" +msgstr "" + +#. module: openems +#: model:ir.model.fields,field_description:openems.field_openems_device_user_role__role +msgid "Role" +msgstr "" + +#. module: openems +#: model:ir.model.fields,field_description:openems.field_openems_device__user_role_ids +#: model:ir.model.fields,field_description:openems.field_res_users__device_role_ids +msgid "Roles" +msgstr "" + +#. module: openems +#: model:ir.model.fields,field_description:openems.field_openems_device__message_has_sms_error +msgid "SMS Delivery error" +msgstr "" + +#. module: openems +#: model_terms:ir.ui.view,arch_db:openems.openems_device_form +msgid "Security" +msgstr "" + +#. module: openems +#: model:ir.model.fields,field_description:openems.field_openems_setup_protocol_production_lot__lot_id +msgid "Serial Number" +msgstr "" + +#. module: openems +#: model:ir.model.fields,field_description:openems.field_openems_setup_protocol__productionlot_ids +msgid "Serial Numbers" +msgstr "" + +#. module: openems +#: model:ir.model.constraint,message:openems.constraint_openems_device_unique_stock_production_lot_id +msgid "Serial number needs to be unique" +msgstr "" + +#. module: openems +#: model:ir.model.fields,field_description:openems.field_openems_setup_protocol_item__setup_protocol_id +#: model:ir.model.fields,field_description:openems.field_openems_setup_protocol_production_lot__setup_protocol_id +#: model_terms:ir.ui.view,arch_db:openems.report_openems_setup_protocol_template +msgid "Setup Protocol" +msgstr "" + +#. module: openems +#: model:ir.actions.act_window,name:openems.action_openems_admin_setup_protocol +#: model:ir.model.fields,field_description:openems.field_openems_device__setup_protocol_ids +#: model:ir.ui.menu,name:openems.menu_openems_admin_setup_protocol +#: model_terms:ir.ui.view,arch_db:openems.res_partner_form +msgid "Setup Protocols" +msgstr "" + +#. module: openems +#: model:ir.model.fields,field_description:openems.field_openems_setup_protocol_item__sequence +#: model:ir.model.fields,field_description:openems.field_openems_setup_protocol_production_lot__sequence +msgid "Sort" +msgstr "" + +#. module: openems +#: model:ir.model.fields.selection,name:openems.selection__res_users__openems_language__es +msgid "Spanish" +msgstr "" + +#. module: openems +#: model_terms:ir.ui.view,arch_db:openems.report_openems_setup_protocol_template +msgid "Speicherstandort" +msgstr "" + +#. module: openems +#: model_terms:ir.ui.view,arch_db:openems.openems_device_form +msgid "Status" +msgstr "" + +#. module: openems +#: model:ir.model.fields,field_description:openems.field_openems_device__stock_production_lot_id +msgid "Stock Production Lot" +msgstr "" + +#. module: openems +#: model_terms:ir.ui.view,arch_db:openems.report_openems_setup_protocol_template +msgid "Straße / Hausnummer" +msgstr "" + +#. module: openems +#: model_terms:ir.ui.view,arch_db:openems.openems_device_form +msgid "System Messages" +msgstr "" + +#. module: openems +#: model:ir.actions.act_window,name:openems.action_openems_systemmessage +#: model:ir.model.fields,field_description:openems.field_openems_device__systemmessage_ids +#: model:ir.ui.menu,name:openems.menu_openems_admin_systemmessages +msgid "Systemmessages" +msgstr "" + +#. module: openems +#: model_terms:ir.ui.view,arch_db:openems.openems_device_form +msgid "Systemstatus" +msgstr "" + +#. module: openems +#: model:ir.model.fields,field_description:openems.field_openems_device__tag_ids +msgid "Tags" +msgstr "" + +#. module: openems +#: model_terms:ir.ui.view,arch_db:openems.report_openems_setup_protocol_template +msgid "Telefonnummer" +msgstr "" + +#. module: openems +#: model:ir.model.fields,field_description:openems.field_openems_systemmessage__text_teaser +msgid "Text Teaser" +msgstr "" + +#. module: openems +#: model:ir.model.fields,field_description:openems.field_openems_openemsconfigupdate__details +msgid "Update Details" +msgstr "" + +#. module: openems +#: model:ir.model.fields,field_description:openems.field_openems_openemsconfigupdate__teaser +msgid "Update Details Teaser" +msgstr "" + +#. module: openems +#: model:ir.model,name:openems.model_res_users +#: model:ir.model.fields,field_description:openems.field_openems_device_user_role__user_id +msgid "User" +msgstr "" + +#. module: openems +#: model:ir.model.constraint,message:openems.constraint_openems_device_user_role_device_user_uniq +msgid "User already exists for this device." +msgstr "" + +#. module: openems +#: model_terms:ir.ui.view,arch_db:openems.openems_systemmessage_tree +msgid "Users" +msgstr "" + +#. module: openems +#: model:ir.model.fields,field_description:openems.field_openems_setup_protocol_item__value +msgid "Value" +msgstr "" + +#. module: openems +#: model:ir.model.fields,field_description:openems.field_openems_setup_protocol_item__view +msgid "View Identifier" +msgstr "" + +#. module: openems +#: model_terms:ir.ui.view,arch_db:openems.report_openems_setup_protocol_template +msgid "Vor- Nachname" +msgstr "" + +#. module: openems +#: model:ir.model.fields.selection,name:openems.selection__openems_device__openems_sum_state_level__warning +msgid "Warning" +msgstr "" + +#. module: openems +#: model:ir.model.fields,field_description:openems.field_openems_device__website_message_ids +msgid "Website Messages" +msgstr "" + +#. module: openems +#: model:ir.model.fields,help:openems.field_openems_device__website_message_ids +msgid "Website communication history" +msgstr "" + +#. module: openems +#: model:mail.template,subject:openems.setup_protocol_email_customer +#: model:mail.template,subject:openems.setup_protocol_email_installer +msgid "Your OpenEMS setup protocol for {{object.openems_device_id.name}}" +msgstr "" diff --git a/16.0/extra-addons/openems/mail/openems/alerting_offline.xml b/16.0/extra-addons/openems/mail/openems/alerting_offline.xml new file mode 100644 index 0000000..2d2a38d --- /dev/null +++ b/16.0/extra-addons/openems/mail/openems/alerting_offline.xml @@ -0,0 +1,275 @@ + + + + + E-Mail Offline-Alerting + + ]]> + {{object.user_id.partner_id.id}} + OpenEMS Alert - Edge is offline + + + + + OpenEMS Alert - Edge is offline + + + + + + + + + + + + + + + +
+ + + + + + +
+ +
+ + + + + + +
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + +
+

+

+ +
+
+ + + , + + Ms/Mr , + Customer, +
+
+
Your OpenEMS Edge with number is offline since: +
+
+
+ + + + + + + (UTC) + + +
+
+ + + + + + + + + + + + +
Info
+ OpenEMS-Version + + + + + UNKNOWN +
+ Type + + + + + + + +
+
+
Best Regards
+
+
OpenEMS Association e.V.
+
+
+ +
+
+ +
+ +
+ + + + + + +
+ +
+ + + + + + + + + + + + + + + +
+
If you do no longer wish to receive email notifications, you can disable the feature here .
+
+
This is an automatically generated message. Please do not reply to this message!
+
+ + + + + + +
+ OpenEMS Logo +
+
+

+

+ +
+
+ +
+
+ +
+ + + + ]]> +
+
+
+
diff --git a/16.0/extra-addons/openems/mail/openems/alerting_sum_state.xml b/16.0/extra-addons/openems/mail/openems/alerting_sum_state.xml new file mode 100644 index 0000000..5ac49f6 --- /dev/null +++ b/16.0/extra-addons/openems/mail/openems/alerting_sum_state.xml @@ -0,0 +1,268 @@ + + + + + E-Mail SumState Alerting + + ]]> + {{object.user_id.partner_id.id}} + OpenEMS Alert - Edge is in {{object.device_id.openems_sum_state_level}} State + + + + + OpenEMS Alert - Edge is in fault + + + + + + + + + + + + + + + +
+ + + + + + +
+ +
+ + + + + + +
+ +
+ + + + + + + + + + + + + + + + + + + + + +
+

+

+ +
+
+ + + , + + Ms/Mr , + Customer, +
+
+
Your OpenEMS Edge with number is in a continuous + + + + + + + UNKNOWN + state! +
+
+ + + + + + + + + + + + +
Info
+ OpenEMS-Version + + + + + UNKNOWN +
+ Type + + + + + + + +
+
+
Best Regards
+
+
OpenEMS Association e.V.
+
+
+ +
+
+ +
+ +
+ + + + + + +
+ +
+ + + + + + + + + + + + + + + +
+
If you do no longer wish to receive email notifications, you can disable the feature here .
+
+
This is an automatically generated message. Please do not reply to this message!
+
+ + + + + + +
+ OpenEMS Logo +
+
+

+

+ +
+
+ +
+
+ +
+ + + + ]]> +
+
+
+
diff --git a/16.0/extra-addons/openems/mail/openems/setup_protocol_customer.xml b/16.0/extra-addons/openems/mail/openems/setup_protocol_customer.xml new file mode 100644 index 0000000..3b1e781 --- /dev/null +++ b/16.0/extra-addons/openems/mail/openems/setup_protocol_customer.xml @@ -0,0 +1,170 @@ + + + + + E-Mail setup protocol for customer + + ]]> + {{object.customer_id.id}} + Your OpenEMS setup protocol for {{object.openems_device_id.name}} + false + + + + + Your OpenEMS Edge setup protocol + + + + + + + + + + + + + + + +
+ + + + + + +
+ +
+ + + + + + +
+ +
+ + + + + + + + + + + + + + + + + + +
+

+

+ +
+
Welcome to OpenEMS,
+
+
please find your setup protocol for OpenEMS Edge attached.
+
+
Best Regards
+
+
OpenEMS Association e.V.
+
+
+ +
+
+ +
+
+ + + + ]]> +
+
+
+
diff --git a/16.0/extra-addons/openems/mail/openems/setup_protocol_installer.xml b/16.0/extra-addons/openems/mail/openems/setup_protocol_installer.xml new file mode 100644 index 0000000..13afd9d --- /dev/null +++ b/16.0/extra-addons/openems/mail/openems/setup_protocol_installer.xml @@ -0,0 +1,173 @@ + + + + + E-Mail setup protocol for installer + + ]]> + {{object.installer_id.id}} + Your OpenEMS setup protocol for {{object.openems_device_id.name}} + false + + + + + OpenEMS setup protocol + + + + + + + + + + + + + + + +
+ + + + + + +
+ +
+ + + + + + +
+ +
+ + + + + + + + + + + + + + + + + + +
+

+

+ +
+
Dear + , +
+
+
please find the setup protocol for your customer attached. +
+
+
Best Regards
+
+
OpenEMS Association e.V.
+
+
+ +
+
+ +
+
+ + + + ]]> +
+
+
+
diff --git a/16.0/extra-addons/openems/mail/openems/user_registration.xml b/16.0/extra-addons/openems/mail/openems/user_registration.xml new file mode 100644 index 0000000..ee01934 --- /dev/null +++ b/16.0/extra-addons/openems/mail/openems/user_registration.xml @@ -0,0 +1,197 @@ + + + + + E-Mail Kunden Registrierung + + ]]> + {{object.id}} + Registrierung erfolgreich + false + + + + + Registrierung erfolgreich + + + + + + + + + + + + + + + +
+ + + + + + +
+ +
+ + + + + + +
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + +
+

+

+ +
+
Guten Tag + , +
+
+
Ihr Zugang zu OpenEMS UI wurde erstellt.
+
+
Sie können sich auf OpenEMS UI einloggen, um auf das Online-Monitoring zuzugreifen.
+
+ + + + + + + + + + + + +
Ihre Zugangsdaten:
E-Mail + +
Passwort + +
+
+
Best Regards
+
+
OpenEMS Association e.V.
+
+
+ +
+
+ +
+
+ + + + ]]> +
+
+
+
diff --git a/16.0/extra-addons/openems/migrations/16.0.1.0.1/post-migrate.py b/16.0/extra-addons/openems/migrations/16.0.1.0.1/post-migrate.py new file mode 100644 index 0000000..1eab6fe --- /dev/null +++ b/16.0/extra-addons/openems/migrations/16.0.1.0.1/post-migrate.py @@ -0,0 +1,9 @@ +def migrate(cr, version): + cr.execute(""" + INSERT INTO openems_alerting (device_id, device_name, user_id, user_login, offline_delay, warning_delay, fault_delay, offline_last_notification) + SELECT device_id, dev.name, user_id, usr.login, time_to_wait, 0, 0, last_notification + FROM openems_alerting_migrate AS migrate + LEFT JOIN openems_device AS dev ON dev.id = migrate.device_id + LEFT JOIN res_users AS usr ON usr.id = migrate.user_id + """) + cr.execute('DROP TABLE openems_alerting_migrate') diff --git a/16.0/extra-addons/openems/migrations/16.0.1.0.1/pre-migrate.py b/16.0/extra-addons/openems/migrations/16.0.1.0.1/pre-migrate.py new file mode 100644 index 0000000..341e4a5 --- /dev/null +++ b/16.0/extra-addons/openems/migrations/16.0.1.0.1/pre-migrate.py @@ -0,0 +1,7 @@ +def migrate(cr, version): + cr.execute(""" + SELECT device_id, user_id, time_to_wait, last_notification + INTO openems_alerting_migrate + FROM openems_device_user_role + WHERE time_to_wait > 0; + """) diff --git a/16.0/extra-addons/openems/models/__init__.py b/16.0/extra-addons/openems/models/__init__.py new file mode 100644 index 0000000..c4a7e59 --- /dev/null +++ b/16.0/extra-addons/openems/models/__init__.py @@ -0,0 +1 @@ +from . import device, partner, setup_protocol, user, stock_production_lot diff --git a/16.0/extra-addons/openems/models/device.py b/16.0/extra-addons/openems/models/device.py new file mode 100644 index 0000000..bdbda80 --- /dev/null +++ b/16.0/extra-addons/openems/models/device.py @@ -0,0 +1,321 @@ +from odoo import api, fields, models, exceptions, _ +from datetime import datetime +from odoo.exceptions import ValidationError +import random +import re +import string + +class Device(models.Model): + _name = "openems.device" + _description = "OpenEMS Edge Device" + _inherit = "mail.thread" + _order = "name_number asc" + _sql_constraints = [ + ("unique_name", "unique(name)", "Name needs to be unique"), + ("unique_stock_production_lot_id", "unique(stock_production_lot_id)", + "Serial number needs to be unique") + ] + + name = fields.Char(required=True) + active = fields.Boolean("Active", default=True, tracking=True) + comment = fields.Char(tracking=True) + internalnote = fields.Text("Internal note", tracking=True) + tag_ids = fields.Many2many("openems.device_tag", string="Tags", tracking=True) + monitoring_url = fields.Char( + "Online-Monitoring", compute="_compute_monitoring_url", store=False + ) + stock_production_lot_id = fields.Many2one("stock.lot") + first_setup_protocol_date = fields.Datetime( + "First Setup Protocol Date", compute="_compute_first_setup_protocol" + ) + manual_setup_date = fields.Datetime("Manual Setup Date") + + @api.depends("setup_protocol_ids", "manual_setup_date") + def _compute_first_setup_protocol(self): + for rec in self: + if rec.manual_setup_date: + rec.first_setup_protocol_date = rec.manual_setup_date + elif len(rec.setup_protocol_ids) > 0: + rec.first_setup_protocol_date = rec.setup_protocol_ids[ + (len(rec.setup_protocol_ids) - 1) + ]["create_date"] + else: + rec.first_setup_protocol_date = None + + @api.depends("name") + def _compute_monitoring_url(self): + # Corrected the parameter key to 'edge_monitoring_url' + base_url = self.env["ir.config_parameter"].sudo().get_param("edge_monitoring_url", default='#') + for rec in self: + if isinstance(rec.name, str) and rec.name: + # Ensuring there is a '/' between base_url and rec.name if it's not already present + separator = '' if base_url.endswith('/') else '/' + rec.monitoring_url = base_url + separator + rec.name + "/live" + else: + rec.monitoring_url = base_url + + producttype = fields.Selection( + [ + ("openems-edge", "OpenEMS Edge"), + ], + "Product type", + tracking=True, + ) + emshardware = fields.Selection([], "EMS Hardware", tracking=True) + oem = fields.Selection( + [ + ("openems", "OpenEMS"), + ], + "OEM Branding", + default="openems", + ) + + # Settings + openems_config = fields.Text("OpenEMS Config Full") + openems_config_components = fields.Text("OpenEMS Config") + openems_version = fields.Char("OpenEMS Version", tracking=True) + + # Security + setup_password = fields.Char( + "Installation Key", + help="Password for commissioning by the installer", + ) + apikey = fields.Char("API-Key", required=True, tracking=True) + + # 'openems_sum_state_level' is updated by OpenEMS Backend + openems_sum_state_level = fields.Selection( + [("ok", "Ok"), ("info", "Info"), ("warning", "Warning"), ("fault", "Fault")], + "OpenEMS State", + ) + # 'openems_is_connected' is updated by OpenEMS Backend + openems_is_connected = fields.Boolean("OpenEMS Is connected") + + # System Status + lastmessage = fields.Datetime("Last message") + lastupdate = fields.Datetime("Last data update") + + # Verknüpfungen + systemmessage_ids = fields.One2many( + "openems.systemmessage", "device_id", string="System Messages" + ) + user_role_ids = fields.One2many( + "openems.device_user_role", "device_id", string="Roles", tracking=True + ) + alerting_settings = fields.One2many( + "openems.alerting", "device_id", string="Alerting", tracking=True + ) + openems_config_update_ids = fields.One2many( + "openems.openemsconfigupdate", "device_id", string="OpenEMS Config Updates" + ) + setup_protocol_ids = fields.One2many( + "openems.setup_protocol", "device_id", "Setup Protocols" + ) + + # Helper fields + name_number = fields.Integer(compute="_compute_name_number", store="True") + + @api.depends("name") + def _compute_name_number(self): + for rec in self: + rec.name_number = int(rec.name[4:]) if rec.name.startswith("edge") else -1 + + def _get_openems_state_number(self, string): + state = 0 + if string == "info": + state = 1 + elif string == "warning": + state = 2 + elif string == "fault": + state = 3 + return state + + def write(self, vals): + """Prohibit to change name field after creation.""" + if 'name' in vals: + for record in self: + if record.id and record.name != vals['name']: + self.env.cr.execute(""" + SELECT EXISTS ( + SELECT 1 FROM openems_device + WHERE name = %s AND id != %s + ) + """, (vals['name'], record.id)) + exists = self.env.cr.fetchone()[0] + if exists: + # This means there's already a device with the intended new name + raise exceptions.UserError( + "The name '{}' is already in use or does not follow the required pattern.".format( + vals['name'])) + + # If you simply want to prevent name changes, the following UserError suffices + raise exceptions.UserError("The name of the device cannot be changed after creation.") + return super(Device, self).write(vals) + + @api.model + def create(self, vals): + + # Generate setup password if not provided + if 'setup_password' not in vals or not vals['setup_password']: + vals['setup_password'] = self._generate_unique_setup_password() + + # Generate API key if not provided + if 'apikey' not in vals or not vals['apikey']: + vals['apikey'] = self._generate_api_key() + + return super(Device, self).create(vals) + + def _generate_unique_setup_password(self): + is_unique = False + setup_password = '' + while not is_unique: + # Generate a random setup password + raw_password = ''.join(random.choices(string.ascii_uppercase + string.digits, k=16)) + setup_password = '-'.join([raw_password[i:i + 4] for i in range(0, len(raw_password), 4)]) + # Check if the generated setup password already exists + existing = self.search_count([('setup_password', '=', setup_password)]) + # If the password does not exist, it is unique, and we can exit the loop + if existing == 0: + is_unique = True + return setup_password + + def _generate_api_key(self): + # Initialize a flag to indicate whether the generated key is unique + is_unique = False + api_key = '' + while not is_unique: + # Generate a random API key + api_key = ''.join(random.choices(string.ascii_letters + string.digits, k=20)) + # Check if the generated API key already exists + existing = self.search_count([('apikey', '=', api_key)]) + # If the key does not exist, it is unique, and we can exit the loop + if existing == 0: + is_unique = True + return api_key + + @api.onchange('setup_password') + def _check_setup_password_format(self): + for record in self: + if not record.setup_password: + continue + if not re.match(r"^[A-Z0-9]{4}-[A-Z0-9]{4}-[A-Z0-9]{4}-[A-Z0-9]{4}$", record.setup_password): + raise ValidationError("The device ID must be formatted as XXXX-XXXX-XXXX-XXXX") + + @api.onchange('apikey') + def _check_api_key_uniqueness(self): + for record in self: + if record.apikey: + # Prepare the domain for searching duplicates + domain = [('apikey', '=', record.apikey)] + # If the record is already saved (has a valid database ID), exclude it from the search + if record.id and isinstance(record.id, (int,)): + domain.append(('id', '!=', record.id)) + # Check if any other records with the same API key exist + existing = self.search_count(domain) + # If there are duplicates, raise a ValidationError + if existing: + raise ValidationError( + _("The API key already exists and must be unique. Please choose a different API key.")) + + +class DeviceTag(models.Model): + _name = "openems.device_tag" + _description = "OpenEMS Edge Device Tag" + name = fields.Char(required=True) + + +class DeviceUserRole(models.Model): + _name = "openems.device_user_role" + _description = "OpenEMS Edge Device User Role" + _sql_constraints = [ + ( + "device_user_uniq", + "unique(device_id, user_id)", + "User already exists for this device.", + ), + ] + device_id = fields.Many2one("openems.device", string="OpenEMS Edge") + user_id = fields.Many2one("res.users", string="User") + role = fields.Selection( + [ + ("admin", "Admin"), + ("installer", "Installer"), + ("owner", "Owner"), + ("guest", "Guest"), + ], + default="guest", + required=True, + ) + + +class OpenemsConfigUpdate(models.Model): + _name = "openems.openemsconfigupdate" + _description = "OpenEMS Edge Device Configuration Update" + _order = "create_date desc" + + device_id = fields.Many2one("openems.device", string="OpenEMS Edge") + teaser = fields.Text("Update Details Teaser") + details = fields.Html("Update Details") + + +class Systemmessage(models.Model): + _name = "openems.systemmessage" + _description = "OpenEMS Edge Systemmessage" + _order = "create_date desc" + + timestamp = fields.Datetime("Creation date") + device_id = fields.Many2one("openems.device", string="OpenEMS Edge") + text = fields.Text("Message") + text_teaser = fields.Char(compute="_compute_text_teaser") + + @api.depends("text") + def _compute_text_teaser(self): + for rec in self: + # get up to 100 characters from first line + rec.text_teaser = rec.text.splitlines()[0][0:100] if rec.text else False + +class Alerting(models.Model): + _name = "openems.alerting" + _description = "OpenEMS Edge AlertingSettings" + _sql_constraints = [ + ( + "device_user_uniq", + "unique(device_id, user_id)", + "User already has Alerting Settings.", + ), + ] + + device_id = fields.Many2one("openems.device", string="OpenEMS Edge") + user_id = fields.Many2one("res.users", string="User") + + offline_delay = fields.Integer(string="Offline Notification", default=1440) + warning_delay = fields.Integer(string="Warning Notification", default=1440) + fault_delay = fields.Integer(string="Fault Notification", default=1440) + + offline_last_notification = fields.Datetime(string="Last Offline notification sent") + sum_state_last_notification = fields.Datetime(string="Last SumState notification sent") + + device_name = fields.Text(compute="_compute_device_name", store="True") + user_login = fields.Text(compute="_compute_user_login", store="True") + + user_role = fields.Selection( + [("admin", "Admin"), ("installer", "Installer"), ("owner", "Owner"), ("guest", "Guest"),], + compute="_compute_user_role", store="False") + + @api.depends("device_id") + def _compute_device_name(self): + for rec in self: + rec.device_name = rec.device_id.name; + + @api.depends("user_id") + def _compute_user_login(self): + for rec in self: + rec.user_login = rec.user_id.login; + + @api.depends("user_id", "device_id") + def _compute_user_role(self): + for rec in self: + user_role: DeviceUserRole = rec.user_id.device_role_ids.search([('device_id','=',rec.device_id.id)]) + if user_role: + return user_role.role + else: + return rec.user_id.global_role diff --git a/16.0/extra-addons/openems/models/partner.py b/16.0/extra-addons/openems/models/partner.py new file mode 100644 index 0000000..37ab8e5 --- /dev/null +++ b/16.0/extra-addons/openems/models/partner.py @@ -0,0 +1,12 @@ +from odoo import fields, models + + +class ResPartner(models.Model): + _inherit = "res.partner" + + installer_setup_protocols_ids = fields.One2many( + "openems.setup_protocol", "installer_id", "Installed OpenEMS Edge" + ) + customer_setup_protocols_ids = fields.One2many( + "openems.setup_protocol", "customer_id", "Owner of OpenEMS Edge" + ) diff --git a/16.0/extra-addons/openems/models/setup_protocol.py b/16.0/extra-addons/openems/models/setup_protocol.py new file mode 100644 index 0000000..548576e --- /dev/null +++ b/16.0/extra-addons/openems/models/setup_protocol.py @@ -0,0 +1,49 @@ +from odoo import fields, models + + +class SetupProtocol(models.Model): + _name = "openems.setup_protocol" + _description = "OpenEMS Edge Setup Protocols (IBN)" + _order = "create_date desc" + + customer_id = fields.Many2one("res.partner", "Customer", required=True) + different_location_id = fields.Many2one("res.partner", "Different Location") + installer_id = fields.Many2one("res.partner", "Installer", required=True) + device_id = fields.Many2one("openems.device", "OpenEMS Edge", required=True) + productionlot_ids = fields.One2many( + "openems.setup_protocol_production_lot", "setup_protocol_id", "Serial Numbers" + ) + item_ids = fields.One2many( + "openems.setup_protocol_item", "setup_protocol_id", "Entry Items" + ) + + +class SetupProtocolProductionLot(models.Model): + _name = "openems.setup_protocol_production_lot" + _description = "OpenEMS Edge Setup Protocol Serial Number" + _order = "setup_protocol_id, category, sequence asc" + + sequence = fields.Integer("Sort") + category = fields.Char("Category") + name = fields.Char("Name") + lot_id = fields.Many2one("stock.production.lot", "Serial Number") + setup_protocol_id = fields.Many2one( + "openems.setup_protocol", "Setup Protocol", ondelete="cascade" + ) + + +class SetupProtocolItem(models.Model): + _name = "openems.setup_protocol_item" + _description = "OpenEMS Edge Setup Protocol Entry Item" + _order = "setup_protocol_id, category, sequence asc" + + sequence = fields.Integer("Sort") + category = fields.Char("Category") + name = fields.Char("Name") + value = fields.Char("Value") + setup_protocol_id = fields.Many2one( + "openems.setup_protocol", "Setup Protocol", ondelete="cascade" + ) + view = fields.Char("View Identifier") + field = fields.Char("Field Identifier") + diff --git a/16.0/extra-addons/openems/models/stock_production_lot.py b/16.0/extra-addons/openems/models/stock_production_lot.py new file mode 100644 index 0000000..37ebb22 --- /dev/null +++ b/16.0/extra-addons/openems/models/stock_production_lot.py @@ -0,0 +1,23 @@ +from odoo import fields, models, api + + +class ProductionLot(models.Model): + _inherit = "stock.lot" + + device_id = fields.Many2one( + 'openems.device', compute='compute_device_id', inverse='device_inverse') + device_ids = fields.One2many('openems.device', 'stock_production_lot_id') + + @api.depends('device_ids') + def compute_device_id(self): + if len(self.device_ids) > 0: + self.device_id = self.device_ids[0] + + def device_inverse(self): + if len(self.device_ids) > 0: + if len(self.device_id.stock_production_lot_id) > 0: + raise ValueError("A serial number has already been assigned to the device") + + device = self.env['openems.device'].browse(self.device_ids[0].id) + device.stock_production_lot_id = False + self.device_id.stock_production_lot_id = self diff --git a/16.0/extra-addons/openems/models/user.py b/16.0/extra-addons/openems/models/user.py new file mode 100644 index 0000000..321dd87 --- /dev/null +++ b/16.0/extra-addons/openems/models/user.py @@ -0,0 +1,37 @@ +from odoo import fields, models + + +class ResUsers(models.Model): + _inherit = "res.users" + + branding_partner_id = fields.Many2one("res.partner", string="Branding Partner") + global_role = fields.Selection( + [ + ("admin", "Admin"), + ("installer", "Installer"), + ("owner", "Owner"), + ("guest", "Guest"), + ], + default="guest", + required=True, + ) + device_role_ids = fields.One2many( + "openems.device_user_role", "user_id", string="Roles" + ) + alerting_settings = fields.One2many( + "openems.alerting", "user_id", string="Alerting" + ) + openems_language = fields.Selection( + [ + ("EN", "English"), + ("DE", "German"), + ("CZ", "Czech"), + ("NL", "Dutch"), + ("ES", "Spanish"), + ("FR", "French"), + ("HU", "Hungarian"), + ("JA", "Japanese"), + ], + default="DE", + required=True, + ) diff --git a/16.0/extra-addons/openems/report/setup_protocol.xml b/16.0/extra-addons/openems/report/setup_protocol.xml new file mode 100644 index 0000000..c9db4ec --- /dev/null +++ b/16.0/extra-addons/openems/report/setup_protocol.xml @@ -0,0 +1,311 @@ + + + + OpenEMS Setup Protocol + openems.setup_protocol + qweb-pdf + openems.report_openems_setup_protocol_template + ('IBN-' + object.openems_device_id.name + '-' + object.create_date.strftime('%d.%m.%Y')) + + + diff --git a/16.0/extra-addons/openems/security/ir.model.access.csv b/16.0/extra-addons/openems/security/ir.model.access.csv new file mode 100644 index 0000000..1ccc0f1 --- /dev/null +++ b/16.0/extra-addons/openems/security/ir.model.access.csv @@ -0,0 +1,31 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +access_openems_device_portal,access_openems_device_portal,openems.model_openems_device,base.group_portal,1,0,0,0 +access_openems_device_user,access_openems_device_user,openems.model_openems_device,base.group_user,1,0,0,0 +access_openems_device_manager,access_openems_device_manager,openems.model_openems_device,openems.group_openems_manager,1,1,1,0 +access_openems_device_tag_portal,access_openems_device_tag_portal,openems.model_openems_device_tag,base.group_portal,1,0,0,0 +access_openems_device_tag_user,access_openems_device_tag_user,openems.model_openems_device_tag,base.group_user,1,0,0,0 +access_openems_device_tag_manager,access_openems_device_tag_manager,openems.model_openems_device_tag,openems.group_openems_manager,1,1,1,1 +access_openems_device_user_role_portal,access_openems_device_user_role_portal,openems.model_openems_device_user_role,base.group_portal,1,0,0,0 +access_openems_device_user_role_user,access_openems_device_user_role_user,openems.model_openems_device_user_role,base.group_user,1,0,0,0 +access_openems_device_user_role_manager,access_openems_device_user_role_manager,openems.model_openems_device_user_role,openems.group_openems_manager,1,1,1,1 +access_openems_alerting_portal,access_openems_alerting_portal,openems.model_openems_alerting,base.group_portal,1,0,0,0 +access_openems_alerting_user,access_openems_alerting_user,openems.model_openems_alerting,base.group_user,1,0,0,0 +access_openems_alerting_manager,access_openems_alerting_manager,openems.model_openems_alerting,openems.group_openems_manager,1,1,1,1 +access_openems_systemmessage_portal,access_openems_systemmessage_portal,openems.model_openems_systemmessage,base.group_portal,1,0,0,0 +access_openems_systemmessage_user,access_openems_systemmessage_user,openems.model_openems_systemmessage,base.group_user,1,0,0,0 +access_openems_systemmessage_manager,access_openems_systemmessage_manager,openems.model_openems_systemmessage,openems.group_openems_manager,1,1,1,1 +access_openems_openemsconfigupdate_portal,access_openems_openemsconfigupdate_portal,openems.model_openems_openemsconfigupdate,base.group_portal,1,0,0,0 +access_openems_openemsconfigupdate_user,access_openems_openemsconfigupdate_user,openems.model_openems_openemsconfigupdate,base.group_user,1,0,0,0 +access_openems_openemsconfigupdate_manager,access_openems_openemsconfigupdate_manager,openems.model_openems_openemsconfigupdate,openems.group_openems_manager,1,1,1,1 +access_openems_setup_protocol_portal,access_openems_setup_protocol_portal,openems.model_openems_setup_protocol,base.group_portal,1,0,0,0 +access_openems_setup_protocol_user,access_openems_setup_protocol_user,openems.model_openems_setup_protocol,base.group_user,1,0,0,0 +access_openems_setup_protocol_manager,access_openems_setup_protocol_manager,openems.model_openems_setup_protocol,openems.group_openems_manager,1,1,1,1 +access_openems_setup_protocol_production_lot_portal,access_openems_setup_protocol_production_lot_portal,openems.model_openems_setup_protocol_production_lot,base.group_portal,1,0,0,0 +access_openems_setup_protocol_production_lot_user,access_openems_setup_protocol_production_lot_user,openems.model_openems_setup_protocol_production_lot,base.group_user,1,0,0,0 +access_openems_setup_protocol_production_lot_manager,access_openems_setup_protocol_production_lot_manager,openems.model_openems_setup_protocol_production_lot,openems.group_openems_manager,1,1,1,1 +access_openems_setup_protocol_item_portal,access_openems_setup_protocol_item_portal,openems.model_openems_setup_protocol_item,base.group_portal,1,0,0,0 +access_openems_setup_protocol_item_user,access_openems_setup_protocol_item_user,openems.model_openems_setup_protocol_item,base.group_user,1,0,0,0 +access_openems_setup_protocol_item_manager,access_openems_setup_protocol_item_manager,openems.model_openems_setup_protocol_item,openems.group_openems_manager,1,1,1,1 +access_openems_production_lot_portal,access_openems_production_lot_portal,stock.model_stock_lot,base.group_portal,1,0,0,0 +access_openems_production_lot_user,access_openems_production_lot_user,stock.model_stock_lot,base.group_user,1,0,0,0 +access_openems_production_lot_manager,access_openems_production_lot_manager,stock.model_stock_lot,openems.group_openems_manager,1,0,0,0 diff --git a/16.0/extra-addons/openems/security/openems.xml b/16.0/extra-addons/openems/security/openems.xml new file mode 100644 index 0000000..ef87c2f --- /dev/null +++ b/16.0/extra-addons/openems/security/openems.xml @@ -0,0 +1,54 @@ + + + + OpenEMS + 30 + + + + Read access + Members of this group have reading access to all devices + + + + + Manager + Members of this group can manage all devices + + + + + + + Website: Show only approved devices to Portal and User + + ['|', ('user_role_ids.user_id','in',[user.id]), ('alerting_settings.user_id','in',[user.id])] + + + + + + + + + Website: Show all devices to readers group + + [(1,'=',1)] + + + + + + + + diff --git a/16.0/extra-addons/openems/setup/.setuptools-odoo-make-default-ignore b/16.0/extra-addons/openems/setup/.setuptools-odoo-make-default-ignore new file mode 100644 index 0000000..207e615 --- /dev/null +++ b/16.0/extra-addons/openems/setup/.setuptools-odoo-make-default-ignore @@ -0,0 +1,2 @@ +# addons listed in this file are ignored by +# setuptools-odoo-make-default (one addon per line) diff --git a/16.0/extra-addons/openems/setup/README b/16.0/extra-addons/openems/setup/README new file mode 100644 index 0000000..a63d633 --- /dev/null +++ b/16.0/extra-addons/openems/setup/README @@ -0,0 +1,2 @@ +To learn more about this directory, please visit +https://pypi.python.org/pypi/setuptools-odoo diff --git a/16.0/extra-addons/openems/static/description/icon.png b/16.0/extra-addons/openems/static/description/icon.png new file mode 100755 index 0000000..ee0aed1 Binary files /dev/null and b/16.0/extra-addons/openems/static/description/icon.png differ diff --git a/16.0/extra-addons/openems/static/mail/OpenEMS-Logo.jpg b/16.0/extra-addons/openems/static/mail/OpenEMS-Logo.jpg new file mode 100644 index 0000000..13ae0fb Binary files /dev/null and b/16.0/extra-addons/openems/static/mail/OpenEMS-Logo.jpg differ diff --git a/16.0/extra-addons/openems/views/device.xml b/16.0/extra-addons/openems/views/device.xml new file mode 100644 index 0000000..e74b1cb --- /dev/null +++ b/16.0/extra-addons/openems/views/device.xml @@ -0,0 +1,320 @@ + + + + + Device: Tree + openems.device + + + + + + + + + + + + + + + + + Device: Search + openems.device + + + + + + + + + + + + + + + + + + + + + Device: Form + openems.device + +
+ +
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + +
+
+
+
+ + + Device DeviceUserRole: Tree + openems.device_user_role + + + + + + + + + + Devices + ir.actions.act_window + openems.device + tree,form + + + + + + Device Configuration Updates + openems.openemsconfigupdate + + + + Device Configuration Updates: Tree + openems.openemsconfigupdate + + + + + + + + + + + Device Configuration Updates: Form + openems.openemsconfigupdate + +
+ + + + + + + +
+
+
+ + + + Systemmessages + openems.systemmessage + + + + Device Systemmessage: Tree + openems.systemmessage + + + + + + + + + + + Device Systemmessage: Form + openems.systemmessage + +
+ + + + + + + +
+
+
+ + + + + + + + +
diff --git a/16.0/extra-addons/openems/views/partner.xml b/16.0/extra-addons/openems/views/partner.xml new file mode 100644 index 0000000..874530e --- /dev/null +++ b/16.0/extra-addons/openems/views/partner.xml @@ -0,0 +1,28 @@ + + + + OpenEMS Partner: Form + res.partner + + + + + + + + + + + + + + + + + + + + + + + diff --git a/16.0/extra-addons/openems/views/setup_protocol.xml b/16.0/extra-addons/openems/views/setup_protocol.xml new file mode 100644 index 0000000..bf2eee2 --- /dev/null +++ b/16.0/extra-addons/openems/views/setup_protocol.xml @@ -0,0 +1,82 @@ + + + + SetupProtocol: Form + openems.setup_protocol + +
+ + + + + + + + + diff --git a/16.0/extra-addons/web_m2x_options/static/src/components/form.esm.js b/16.0/extra-addons/web_m2x_options/static/src/components/form.esm.js new file mode 100644 index 0000000..ecb37d2 --- /dev/null +++ b/16.0/extra-addons/web_m2x_options/static/src/components/form.esm.js @@ -0,0 +1,404 @@ +/** @odoo-module **/ + +import { + Many2ManyTagsField, + Many2ManyTagsFieldColorEditable, +} from "@web/views/fields/many2many_tags/many2many_tags_field"; + +import {Dialog} from "@web/core/dialog/dialog"; +import {FormController} from "@web/views/form/form_controller"; +import {FormViewDialog} from "@web/views/view_dialogs/form_view_dialog"; +import {Many2OneAvatarField} from "@web/views/fields/many2one_avatar/many2one_avatar_field"; +import {Many2OneBarcodeField} from "@web/views/fields/many2one_barcode/many2one_barcode_field"; +import {Many2OneField} from "@web/views/fields/many2one/many2one_field"; +import {ReferenceField} from "@web/views/fields/reference/reference_field"; +import {X2ManyField} from "@web/views/fields/x2many/x2many_field"; +import {isX2Many} from "@web/views/utils"; +import {is_option_set} from "@web_m2x_options/components/relational_utils.esm"; +import {patch} from "@web/core/utils/patch"; +import {sprintf} from "@web/core/utils/strings"; +import {useService} from "@web/core/utils/hooks"; + +const {Component} = owl; + +/** + * Patch Many2ManyTagsField + **/ +patch(Many2ManyTagsField.prototype, "web_m2x_options.Many2ManyTagsField", { + setup() { + this._super(...arguments); + this.actionService = useService("action"); + }, + /** + * @override + */ + getTagProps(record) { + const props = this._super(...arguments); + props.onClick = (ev) => this.onMany2ManyBadgeClick(ev, record); + return props; + }, + async onMany2ManyBadgeClick(event, record) { + var self = this; + if (self.props.open) { + var context = self.context; + var id = record.data.id; + if (self.props.readonly) { + event.preventDefault(); + event.stopPropagation(); + const action = await self.orm.call( + self.props.relation, + "get_formview_action", + [[id]], + {context: context} + ); + self.actionService.doAction(action); + } else { + const view_id = await self.orm.call( + self.props.relation, + "get_formview_id", + [[id]], + {context: context} + ); + + const write_access = await self.orm.call( + self.props.relation, + "check_access_rights", + [], + {operation: "write", raise_exception: false} + ); + var can_write = self.props.canWrite; + self.dialog.add(FormViewDialog, { + resModel: self.props.relation, + resId: id, + context: context, + title: self.env._t("Open: ") + self.string, + viewId: view_id, + mode: !can_write || !write_access ? "readonly" : "edit", + onRecordSaved: () => self.props.value.model.load(), + }); + } + } + }, +}); + +Many2ManyTagsField.props = { + ...Many2ManyTagsField.props, + open: {type: Boolean, optional: true}, + canWrite: {type: Boolean, optional: true}, + nodeOptions: {type: Object, optional: true}, +}; + +const Many2ManyTagsFieldExtractProps = Many2ManyTagsField.extractProps; +Many2ManyTagsField.extractProps = ({attrs, field}) => { + const canOpen = Boolean(attrs.options.open); + const canWrite = attrs.can_write && Boolean(JSON.parse(attrs.can_write)); + return Object.assign(Many2ManyTagsFieldExtractProps({attrs, field}), { + open: canOpen, + canWrite: canWrite, + nodeOptions: attrs.options, + }); +}; + +/** + * Many2ManyTagsFieldColorEditable + **/ +patch( + Many2ManyTagsFieldColorEditable.prototype, + "web_m2x_options.Many2ManyTagsFieldColorEditable", + { + async onBadgeClick(event, record) { + if (this.props.canEditColor && !this.props.open) { + this._super(...arguments); + } + if (this.props.open) { + Many2ManyTagsField.prototype.onMany2ManyBadgeClick.bind(this)( + event, + record + ); + } + }, + } +); + +Many2ManyTagsFieldColorEditable.props = { + ...Many2ManyTagsFieldColorEditable.props, + open: {type: Boolean, optional: true}, + canWrite: {type: Boolean, optional: true}, + nodeOptions: {type: Object, optional: true}, +}; + +/** + * CreateConfirmationDialog + * New customized component for Many2One Field + **/ + +class CreateConfirmationDialog extends Component { + get title() { + return sprintf(this.env._t("New: %s"), this.props.name); + } + + async onCreate() { + await this.props.create(); + this.props.close(); + } + async onCreateEdit() { + await this.props.createEdit(); + this.props.close(); + } +} +CreateConfirmationDialog.components = {Dialog}; +CreateConfirmationDialog.template = + "web_m2x_options.Many2OneField.CreateConfirmationDialog"; + +/** + * Many2OneField + **/ + +patch(Many2OneField.prototype, "web_m2x_options.Many2OneField", { + setup() { + this._super(...arguments); + this.ir_options = Component.env.session.web_m2x_options; + }, + /** + * @override + */ + get Many2XAutocompleteProps() { + const props = this._super(...arguments); + return { + ...props, + searchLimit: this.props.searchLimit, + searchMore: this.props.searchMore, + canCreate: this.props.canCreate, + nodeOptions: this.props.nodeOptions, + }; + }, + + async openConfirmationDialog(request) { + var m2o_dialog_opt = + is_option_set(this.props.nodeOptions.m2o_dialog) || + (_.isUndefined(this.props.nodeOptions.m2o_dialog) && + is_option_set(this.ir_options["web_m2x_options.m2o_dialog"])) || + (_.isUndefined(this.props.nodeOptions.m2o_dialog) && + _.isUndefined(this.ir_options["web_m2x_options.m2o_dialog"])); + if (this.props.canCreate && this.state.isFloating && m2o_dialog_opt) { + return new Promise((resolve, reject) => { + this.addDialog(CreateConfirmationDialog, { + value: request, + name: this.props.string, + create: async () => { + try { + await this.quickCreate(request); + resolve(); + } catch (e) { + reject(e); + } + }, + createEdit: async () => { + try { + await this.quickCreate(request); + await this.props.record.model.load(); + this.openMany2X({ + resId: this.props.value[0], + context: this.user_context, + }); + resolve(); + } catch (e) { + reject(e); + } + }, + }); + }); + } + }, +}); + +const Many2OneFieldExtractProps = Many2OneField.extractProps; +Many2OneField.extractProps = ({attrs, field}) => { + return Object.assign(Many2OneFieldExtractProps({attrs, field}), { + searchLimit: attrs.options.limit, + searchMore: attrs.options.search_more, + nodeOptions: attrs.options, + }); +}; + +Many2OneField.props = { + ...Many2OneField.props, + searchMore: {type: Boolean, optional: true}, + nodeOptions: {type: Object, optional: true}, +}; + +/** + * FIXME: find better way to extend props in Many2OneField + * Override ReferenceField + * Since extracted/added props: nodeOptions and searchMore into Many2OneField props + * and this component inherited props from Many2OneField + * So, must override props here to avoid constraint validateProps (props schema) in owl core + */ + +ReferenceField.props = { + ...ReferenceField.props, + searchMore: {type: Boolean, optional: true}, + nodeOptions: {type: Object, optional: true}, +}; + +/** + * FIXME: find better way to extend props in Many2OneField + * Override Many2OneBarcodeField + * Since extracted/added props: nodeOptions and searchMore into Many2OneField props + * and this component inherited props from Many2OneField + * So, must override props here to avoid constraint validateProps (props schema) in owl core + */ + +Many2OneBarcodeField.props = { + ...Many2OneBarcodeField.props, + searchMore: {type: Boolean, optional: true}, + nodeOptions: {type: Object, optional: true}, +}; + +/** + * FIXME: find better way to extend props in Many2OneField + * Override Many2OneAvatarField + * Since extracted/added props: nodeOptions and searchMore into Many2OneField props + * and this component inherited props from Many2OneField + * So, must override props here to avoid constraint validateProps (props schema) in owl core + */ +Many2OneAvatarField.props = { + ...Many2OneAvatarField.props, + searchMore: {type: Boolean, optional: true}, + nodeOptions: {type: Object, optional: true}, +}; + +/** + * FIXME: find better way to extend props in Many2OneField + * Override mailing_m2o_filter + * Since extracted/added props: nodeOptions and searchMore into Many2OneField props + * and this component inherited props from Many2OneField + * So, must override props here to avoid constraint validateProps (props schema) in owl core + * This component is in module mass_mailing as optional module, + * So need to import dynamic way + */ +try { + (async () => { + // Make sure component mailing_m2o_filter in mass mailing module loaded + const installed_mass_mailing = await odoo.ready( + "@mass_mailing/js/mailing_m2o_filter" + ); + if (installed_mass_mailing) { + const {FieldMany2OneMailingFilter} = await odoo.runtimeImport( + "@mass_mailing/js/mailing_m2o_filter" + ); + FieldMany2OneMailingFilter.props = { + ...FieldMany2OneMailingFilter.props, + searchMore: {type: Boolean, optional: true}, + nodeOptions: {type: Object, optional: true}, + }; + } + })(); +} catch { + console.log( + "Ignore overriding props of component mailing_m2o_filter since the module is not installed" + ); +} + +/** + * X2ManyField + **/ +patch(X2ManyField.prototype, "web_m2x_options.X2ManyField", { + /** + * @override + */ + async openRecord(record) { + var self = this; + var open = this.props.open; + if (open && self.props.readonly) { + var res_id = record.data.id; + const action = await self.env.model.orm.call( + self.props.value.resModel, + "get_formview_action", + [[res_id]] + ); + return self.env.model.actionService.doAction(action); + } + return this._super.apply(this, arguments); + }, +}); + +const X2ManyFieldExtractProps = X2ManyField.extractProps; +X2ManyField.extractProps = ({attrs}) => { + const canOpen = Boolean(attrs.options.open); + return Object.assign(X2ManyFieldExtractProps({attrs}), { + open: canOpen, + }); +}; + +X2ManyField.props = { + ...X2ManyField.props, + open: {type: Boolean, optional: true}, +}; + +/** + * FormController + **/ +patch(FormController.prototype, "web_m2x_options.FormController", { + /** + * @override + */ + setup() { + var self = this; + this._super(...arguments); + + /** Due to problem of 2 onWillStart in native web core + * (see: https://github.com/odoo/odoo/blob/16.0/addons/web/static/src/views/model.js#L142) + * do the trick to override beforeLoadResolver here to customize viewLimit + */ + this.superBeforeLoadResolver = this.beforeLoadResolver; + this.beforeLoadResolver = async () => { + await self._setSubViewLimit(); + self.superBeforeLoadResolver(); + }; + }, + /** + * @override + * add more method to add subview limit on formview + */ + async _setSubViewLimit() { + const ir_options = Component.env.session.web_m2x_options; + + const activeFields = this.archInfo.activeFields, + fields = this.props.fields, + isSmall = this.user; + + var limit = ir_options["web_m2x_options.field_limit_entries"]; + if (!_.isUndefined(limit)) { + limit = parseInt(limit, 10); + } + + for (const fieldName in activeFields) { + const field = fields[fieldName]; + if (!isX2Many(field)) { + // What follows only concerns x2many fields + continue; + } + const fieldInfo = activeFields[fieldName]; + if (fieldInfo.modifiers.invisible === true) { + // No need to fetch the sub view if the field is always invisible + continue; + } + + if (!fieldInfo.FieldComponent.useSubView) { + // The FieldComponent used to render the field doesn't need a sub view + continue; + } + + let viewType = fieldInfo.viewMode || "list,kanban"; + viewType = viewType.replace("tree", "list"); + if (viewType.includes(",")) { + viewType = isSmall ? "kanban" : "list"; + } + fieldInfo.viewMode = viewType; + if (fieldInfo.views[viewType] && limit) { + fieldInfo.views[viewType].limit = limit; + } + } + }, +}); diff --git a/16.0/extra-addons/web_m2x_options/static/src/components/relational_utils.esm.js b/16.0/extra-addons/web_m2x_options/static/src/components/relational_utils.esm.js new file mode 100644 index 0000000..1fbe39e --- /dev/null +++ b/16.0/extra-addons/web_m2x_options/static/src/components/relational_utils.esm.js @@ -0,0 +1,221 @@ +/** @odoo-module **/ + +import {Many2XAutocomplete} from "@web/views/fields/relational_utils"; +import {patch} from "@web/core/utils/patch"; +import {sprintf} from "@web/core/utils/strings"; +const {Component} = owl; + +export function is_option_set(option) { + if (_.isUndefined(option)) return false; + if (typeof option === "string") return option === "true" || option === "True"; + if (typeof option === "boolean") return option; + return false; +} + +patch(Many2XAutocomplete.prototype, "web_m2x_options.Many2XAutocomplete", { + setup() { + this._super(...arguments); + this.ir_options = Component.env.session.web_m2x_options; + }, + + async loadOptionsSource(request) { + if (this.lastProm) { + this.lastProm.abort(false); + } + // Add options limit used to change number of selections record + // returned. + if (!_.isUndefined(this.ir_options["web_m2x_options.limit"])) { + this.props.searchLimit = parseInt( + this.ir_options["web_m2x_options.limit"], + 10 + ); + this.limit = this.props.searchLimit; + } + + if (typeof this.props.nodeOptions.limit === "number") { + this.props.searchLimit = this.props.nodeOptions.limit; + this.limit = this.props.searchLimit; + } + + // Add options field_color and colors to color item(s) depending on field_color value + this.field_color = this.props.nodeOptions.field_color; + this.colors = this.props.nodeOptions.colors; + + this.lastProm = this.orm.call(this.props.resModel, "name_search", [], { + name: request, + operator: "ilike", + args: this.props.getDomain(), + limit: this.props.searchLimit + 1, + context: this.props.context, + }); + const records = await this.lastProm; + + var options = records.map((result) => ({ + value: result[0], + id: result[0], + label: result[1].split("\n")[0], + })); + + // Limit results if there is a custom limit options + if (this.limit) { + options = options.slice(0, this.props.searchLimit); + } + + // Search result value colors + if (this.colors && this.field_color) { + var value_ids = options.map((result) => result.value); + const objects = await this.orm.call( + this.props.resModel, + "search_read", + [], + { + domain: [["id", "in", value_ids]], + fields: [this.field_color], + } + ); + for (var index in objects) { + for (var index_value in options) { + if (options[index_value].id === objects[index].id) { + // Find value in values by comparing ids + var option = options[index_value]; + // Find color with field value as key + var color = + this.colors[objects[index][this.field_color]] || "black"; + option.style = "color:" + color; + break; + } + } + } + } + + // Quick create + // Note: Create should be before `search_more` (reserve native order) + // One more reason: when calling `onInputBlur`, native select the first option (activeSourceOption) + // which triggers m2o_dialog if m2o_dialog=true + var create_enabled = + this.props.quickCreate && !this.props.nodeOptions.no_create; + + var raw_result = _.map(records, function (x) { + return x[1]; + }); + var quick_create = is_option_set(this.props.nodeOptions.create), + quick_create_undef = _.isUndefined(this.props.nodeOptions.create), + m2x_create_undef = _.isUndefined(this.ir_options["web_m2x_options.create"]), + m2x_create = is_option_set(this.ir_options["web_m2x_options.create"]); + var show_create = + (!this.props.nodeOptions && (m2x_create_undef || m2x_create)) || + (this.props.nodeOptions && + (quick_create || + (quick_create_undef && (m2x_create_undef || m2x_create)))); + if ( + create_enabled && + !this.props.nodeOptions.no_quick_create && + request.length > 0 && + !_.contains(raw_result, request) && + show_create + ) { + options.push({ + label: sprintf(this.env._t(`Create "%s"`), request), + classList: "o_m2o_dropdown_option o_m2o_dropdown_option_create", + action: async (params) => { + try { + await this.props.quickCreate(request, params); + } catch { + const context = this.getCreationContext(request); + return this.openMany2X({context}); + } + }, + }); + } + + // Search more... + // Resolution order: + // 1- check if "search_more" is set locally in node's options + // 2- if set locally, apply its value + // 3- if not set locally, check if it's set globally via ir.config_parameter + // 4- if set globally, apply its value + // 5- if not set globally either, check if returned values are more than node's limit + var search_more = false; + if (!_.isUndefined(this.props.nodeOptions.search_more)) { + search_more = is_option_set(this.props.nodeOptions.search_more); + } else if (!_.isUndefined(this.ir_options["web_m2x_options.search_more"])) { + search_more = is_option_set(this.ir_options["web_m2x_options.search_more"]); + } else { + search_more = + !this.props.noSearchMore && this.props.searchLimit < records.length; + } + if (search_more) { + options.push({ + label: this.env._t("Search More..."), + action: this.onSearchMore.bind(this, request), + classList: "o_m2o_dropdown_option o_m2o_dropdown_option_search_more", + }); + } + + // Create and Edit + const canCreateEdit = + "createEdit" in this.activeActions + ? this.activeActions.createEdit + : this.activeActions.create; + if ( + !request.length && + !this.props.value && + (this.props.quickCreate || canCreateEdit) + ) { + options.push({ + label: this.env._t("Start typing..."), + classList: "o_m2o_start_typing", + unselectable: true, + }); + } + + // Create and edit ... + var create_edit = + is_option_set(this.props.nodeOptions.create) || + is_option_set(this.props.nodeOptions.create_edit), + create_edit_undef = + _.isUndefined(this.props.nodeOptions.create) && + _.isUndefined(this.props.nodeOptions.create_edit), + m2x_create_edit_undef = _.isUndefined( + this.ir_options["web_m2x_options.create_edit"] + ), + m2x_create_edit = is_option_set( + this.ir_options["web_m2x_options.create_edit"] + ); + var show_create_edit = + (!this.props.nodeOptions && (m2x_create_edit_undef || m2x_create_edit)) || + (this.props.nodeOptions && + (create_edit || + (create_edit_undef && (m2x_create_edit_undef || m2x_create_edit)))); + if ( + create_enabled && + !this.props.nodeOptions.no_create_edit && + show_create_edit && + request.length && + canCreateEdit + ) { + const context = this.getCreationContext(request); + options.push({ + label: this.env._t("Create and edit..."), + classList: "o_m2o_dropdown_option o_m2o_dropdown_option_create_edit", + action: () => this.openMany2X({context}), + }); + } + + // No records + if (!records.length && !this.activeActions.create) { + options.push({ + label: this.env._t("No records"), + classList: "o_m2o_no_result", + unselectable: true, + }); + } + + return options; + }, +}); + +Many2XAutocomplete.defaultProps = { + ...Many2XAutocomplete.defaultProps, + nodeOptions: {}, +}; diff --git a/16.0/extra-addons/web_m2x_options/tests/__init__.py b/16.0/extra-addons/web_m2x_options/tests/__init__.py new file mode 100644 index 0000000..b472ff3 --- /dev/null +++ b/16.0/extra-addons/web_m2x_options/tests/__init__.py @@ -0,0 +1,2 @@ +# Copyright 2020 initOS GmbH. +from . import test_ir_config_parameter diff --git a/16.0/extra-addons/web_m2x_options/tests/test_ir_config_parameter.py b/16.0/extra-addons/web_m2x_options/tests/test_ir_config_parameter.py new file mode 100644 index 0000000..eae00c7 --- /dev/null +++ b/16.0/extra-addons/web_m2x_options/tests/test_ir_config_parameter.py @@ -0,0 +1,28 @@ +# Copyright 2020 initOS GmbH. +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from odoo.tests import common + + +class TestIrConfigParameter(common.TransactionCase): + @classmethod + def setUpClass(cls): + super(TestIrConfigParameter, cls).setUpClass() + cls.env["ir.config_parameter"].set_param("web_m2x_options.limit", 10) + cls.env["ir.config_parameter"].set_param("web_m2x_options.create_edit", "True") + cls.env["ir.config_parameter"].set_param("web_m2x_options.create", "True") + cls.env["ir.config_parameter"].set_param("web_m2x_options.search_more", "False") + cls.env["ir.config_parameter"].set_param("web_m2x_options.m2o_dialog", "True") + + def test_web_m2x_options_key(self): + web_m2x_options = self.env["ir.config_parameter"].get_web_m2x_options() + self.assertIn("web_m2x_options.limit", web_m2x_options) + self.assertNotIn("web_m2x_options.m2o_dialog_test", web_m2x_options) + + def test_web_m2x_options_value(self): + web_m2x_options = self.env["ir.config_parameter"].get_web_m2x_options() + self.assertEqual(web_m2x_options["web_m2x_options.limit"], "10") + self.assertTrue(bool(web_m2x_options["web_m2x_options.create_edit"])) + self.assertTrue(bool(web_m2x_options["web_m2x_options.create"])) + self.assertEqual(web_m2x_options["web_m2x_options.search_more"], "False") + self.assertTrue(bool(web_m2x_options["web_m2x_options.m2o_dialog"])) diff --git a/16.0/openems-16.0.1.0.1.zip b/16.0/openems-16.0.1.0.1.zip new file mode 100644 index 0000000..5f29e22 Binary files /dev/null and b/16.0/openems-16.0.1.0.1.zip differ