diff --git a/connector_oxigesti/README.rst b/connector_oxigesti/README.rst index 7c841131c..771a650b1 100644 --- a/connector_oxigesti/README.rst +++ b/connector_oxigesti/README.rst @@ -24,6 +24,7 @@ Contributors * Eric Antones * Kilian Niubo +* Frank Cespedes diff --git a/connector_oxigesti/__manifest__.py b/connector_oxigesti/__manifest__.py index ad8bd862e..1128a3c7c 100644 --- a/connector_oxigesti/__manifest__.py +++ b/connector_oxigesti/__manifest__.py @@ -16,6 +16,7 @@ "sale_line_partner_description", "sale_specific_order_date", "oxigen_stock_alternate_lot", + "oxigen_mrp", ], "external_dependencies": { "python": [ @@ -34,6 +35,7 @@ "views/product_pricelist_item_view.xml", "views/stock_production_lot_view.xml", "views/sale_order_view.xml", + "views/mrp_production_view.xml", "views/connector_oxigesti_menu.xml", "security/connector_oxigesti.xml", "security/ir.model.access.csv", diff --git a/connector_oxigesti/components/adapter.py b/connector_oxigesti/components/adapter.py index a597e189a..5dbc10f6f 100644 --- a/connector_oxigesti/components/adapter.py +++ b/connector_oxigesti/components/adapter.py @@ -121,6 +121,25 @@ class GenericAdapter(AbstractComponent): _name = "oxigesti.adapter" _inherit = "oxigesti.crud.adapter" + @property + def PYTHON_TYPE_MAP(self): + return { + int: ["int"], + float: ["float", "numeric"], + str: ["nvarchar"], + bool: ["bit"], + } + + @property + def MSSQL_TYPE_MAP(self): + mssql_type_map = {} + for k, v in self.PYTHON_TYPE_MAP.items(): + for t in v: + if t in mssql_type_map: + raise ValidationError(_("Duplicated type %s in MSSQL_TYPE_MAP") % t) + mssql_type_map[t] = k + return mssql_type_map + # private methods def _convert_dict(self, data, to_backend=True): if not isinstance(data, dict): @@ -228,6 +247,26 @@ def _exec_query(self, filters=None, fields=None, as_dict=True): return res + def get_headers(self): + # prepare the sql and execute + if not hasattr(self, "_sql_field_type"): + raise ValidationError( + _("The adapter %s doesn't have a _sql_field_type defined") % self._name + ) + conn = self.conn() + cr = conn.cursor() + cr.execute(self._sql_field_type, dict(schema=self.schema)) + res = {} + for field, ttype in cr.fetchall(): + if ttype not in self.MSSQL_TYPE_MAP: + raise ValidationError( + _("Unexpected type %s for field %s") % (ttype, field) + ) + res[field] = self.MSSQL_TYPE_MAP[ttype] + cr.close() + conn.close() + return res + def _check_uniq(self, data): uniq = set() for rec in data: diff --git a/connector_oxigesti/data/ir_cron.xml b/connector_oxigesti/data/ir_cron.xml index 1afbda3e9..a66bc3849 100644 --- a/connector_oxigesti/data/ir_cron.xml +++ b/connector_oxigesti/data/ir_cron.xml @@ -124,7 +124,24 @@ code model._scheduler_import_stock_production_lot() - + + Oxigesti OS - Export Productions + + + + + 1 + days + -1 + + code + model._scheduler_export_mrp_production() + Oxigesti OS - Import services diff --git a/connector_oxigesti/data/queue_job_function_data.xml b/connector_oxigesti/data/queue_job_function_data.xml index 01a51c63b..4e75ad83e 100644 --- a/connector_oxigesti/data/queue_job_function_data.xml +++ b/connector_oxigesti/data/queue_job_function_data.xml @@ -189,7 +189,25 @@ License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl)--> - + + + + export_batch + + + + + + export_record + + + +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) + +from odoo.addons.component.core import Component + + +class MrpProductionAdapter(Component): + _name = "oxigesti.mrp.production.adapter" + _inherit = "oxigesti.adapter" + + _apply_on = "oxigesti.mrp.production" + + _sql = """select a.Id, a.CodigoOrdenProduccion, a.FechaProduccion, + a.CodigoOrdenDeconstruccion, a.CodigoBotellaVacia, + a.LoteBotellaVacia, a.CodigoCilindro, a.LoteCilindro, + a.CodigoValvula, a.LoteValvula + from %(schema)s.Odoo_Orden_Produccion a + """ + + _sql_update = """update s + set %(qset)s + from %(schema)s.Odoo_Orden_Produccion s + where s.CodigoOrdenProduccion = %%(CodigoOrdenProduccion)s + """ + + _sql_insert = """insert into %(schema)s.Odoo_Orden_Produccion + (%(fields)s) + output %(retvalues)s + values (%(phvalues)s) + """ + + _id = ("Id",) diff --git a/connector_oxigesti/models/mrp_production/binder.py b/connector_oxigesti/models/mrp_production/binder.py new file mode 100644 index 000000000..ff737de61 --- /dev/null +++ b/connector_oxigesti/models/mrp_production/binder.py @@ -0,0 +1,21 @@ +# Copyright NuoBiT Solutions - Frank Cespedes +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) + +from odoo.addons.component.core import Component + + +class MrpProductionBinder(Component): + _name = "oxigesti.mrp.production.binder" + _inherit = "oxigesti.binder" + + _apply_on = "oxigesti.mrp.production" + + # def _get_external_id(self, binding): + # if not self._is_binding(binding): + # raise Exception("The source object %s must be a binding" % binding._name) + # + # external_id = None + # if binding.odoo_id.product_id.default_code: + # external_id = [binding.odoo_id.product_id.default_code] + # + # return external_id diff --git a/connector_oxigesti/models/mrp_production/binding.py b/connector_oxigesti/models/mrp_production/binding.py new file mode 100644 index 000000000..61e4ed2a4 --- /dev/null +++ b/connector_oxigesti/models/mrp_production/binding.py @@ -0,0 +1,88 @@ +# Copyright NuoBiT Solutions - Frank Cespedes +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) +from odoo import _, api, fields, models +from odoo.exceptions import ValidationError + + +class MrpProduction(models.Model): + _inherit = "mrp.production" + + oxigesti_bind_ids = fields.One2many( + comodel_name="oxigesti.mrp.production", + inverse_name="odoo_id", + string="Oxigesti Bindings", + ) + + def _get_valid_components(self): + fields = ["cylinder", "valve"] + moves = self.env["stock.move"] + for mrp_type in fields: + move_raw = self.move_raw_ids.filtered( + lambda x: x.product_id.mrp_type == mrp_type + and x.quantity_done > 0 + and x.move_line_ids + ) + if len(move_raw.product_id) == 0: + raise ValidationError( + _("Production of empty cylinder type without %s product: %s") + % (mrp_type, self.name) + ) + if len(move_raw) > 1 or sum(move_raw.mapped("quantity_done")) > 1: + raise ValidationError( + _( + "The empty cylinder (%s) has been created with" + " more than one %s" + ) + % (self.name, mrp_type) + ) + if len(move_raw.move_line_ids) > 1: + raise ValidationError( + _( + "You have a component with more than one serial" + " number to generate: %s" + ) + % self.name + ) + if not move_raw.product_id.default_code: + raise ValidationError( + _("Internal Reference not set in product: %s") + % move_raw.product_id.name + ) + moves |= move_raw + return moves + + +class MrpProductionBinding(models.Model): + _name = "oxigesti.mrp.production" + _inherit = "oxigesti.binding" + _inherits = {"mrp.production": "odoo_id"} + _description = "Product Mrp Production" + + odoo_id = fields.Many2one( + comodel_name="mrp.production", + string="Production", + required=True, + ondelete="cascade", + ) + + @api.model + def export_data(self, backend, since_date): + domain = [ + ("company_id", "=", backend.company_id.id), + ("product_id.mrp_type", "=", "empty_cylinder"), + ("state", "=", "done"), + ] + if since_date: + domain += [("write_date", ">", since_date)] + self.with_delay().export_batch(backend, domain=domain) + + def resync(self): + for record in self: + with record.backend_id.work_on(record._name) as work: + binder = work.component(usage="binder") + relation = binder.unwrap_binding(self) + func = record.export_record + if record.env.context.get("connector_delay"): + func = record.export_record.delay + func(record.backend_id, relation) + return True diff --git a/connector_oxigesti/models/mrp_production/export_mapper.py b/connector_oxigesti/models/mrp_production/export_mapper.py new file mode 100644 index 000000000..3e4b2f6e5 --- /dev/null +++ b/connector_oxigesti/models/mrp_production/export_mapper.py @@ -0,0 +1,70 @@ +# Copyright NuoBiT Solutions - Frank Cespedes +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) +from odoo import _ +from odoo.exceptions import ValidationError + +from odoo.addons.component.core import Component +from odoo.addons.connector.components.mapper import follow_m2o_relations, mapping, none + + +class MrpProductionExportMapper(Component): + _name = "oxigesti.mrp.production.export.mapper" + _inherit = "oxigesti.export.mapper" + + _apply_on = "oxigesti.mrp.production" + + direct = [ + ("name", "CodigoOrdenProduccion"), + (none("date_planned_start"), "FechaProduccion"), + (none(follow_m2o_relations("lot_producing_id.name")), "LoteBotellaVacia"), + ] + + @mapping + def CodigoBotellaVacia(self, record): + if not record.product_id.default_code: + raise ValidationError( + _("Internal Reference not set in product: %s") % record.product_id.name + ) + return {"CodigoBotellaVacia": record.product_id.default_code} + + @mapping + def CodigoOrdenDeconstruccion(self, record): + unbuild = self.env["mrp.unbuild"].search( + [("mo_id", "=", record.odoo_id.id), ("state", "=", "done")] + ) + if len(unbuild) > 1: + raise ValidationError( + _("The production %s has more than one unbuild. %s") + % (record.name, unbuild.mapped("name")) + ) + return {"CodigoOrdenDeconstruccion": unbuild.name or None} + + @mapping + def Componentes(self, record): + binder = self.binder_for("oxigesti.mrp.production") + mrp_production = binder.unwrap_binding(record) + move_raws = mrp_production._get_valid_components() + mrp_type_map = { + "cylinder": ("CodigoCilindro", "LoteCilindro"), + "valve": ("CodigoValvula", "LoteValvula"), + } + res = {} + for move_raw in move_raws: + mrp_type = move_raw.product_id.mrp_type + if mrp_type not in mrp_type_map: + raise ValidationError(_("Invalid product type: %s") % mrp_type) + default_code = move_raw.product_id.default_code + if not default_code: + raise ValidationError( + _("Internal Reference not set in product: %s") + % move_raw.product_id.name + ) + lot = move_raw.move_line_ids.lot_id + if not lot: + raise ValidationError( + _("Serial Number not set in product: %s") % move_raw.product_id.name + ) + code_key, lot_key = mrp_type_map[mrp_type] + res[code_key] = default_code + res[lot_key] = lot.name + return res diff --git a/connector_oxigesti/models/mrp_production/exporter.py b/connector_oxigesti/models/mrp_production/exporter.py new file mode 100644 index 000000000..1e0c8ca2f --- /dev/null +++ b/connector_oxigesti/models/mrp_production/exporter.py @@ -0,0 +1,53 @@ +# Copyright NuoBiT Solutions - Frank Cespedes +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) + +from odoo.addons.component.core import Component + + +class MrpProductionDelayedBatchExporter(Component): + """Export the Oxigesti Productions. + + For every production in the list, a delayed job is created. + """ + + _name = "oxigesti.mrp.production.delayed.batch.exporter" + _inherit = "oxigesti.delayed.batch.exporter" + + _apply_on = "oxigesti.mrp.production" + + +class MrpProductionDirectBatchExporter(Component): + """Export the Oxigesti Productions. + + For every production in the list, execute inmediately. + """ + + _name = "oxigesti.mrp.production.direct.batch.exporter" + _inherit = "oxigesti.direct.batch.exporter" + + _apply_on = "oxigesti.mrp.production" + + +class MrpProductionExporter(Component): + _name = "oxigesti.mrp.production.exporter" + _inherit = "oxigesti.exporter" + + _apply_on = "oxigesti.mrp.production" + + def _export_dependencies(self): + binder = self.binder_for("oxigesti.mrp.production") + mrp_production = binder.unwrap_binding(self.binding) + components = mrp_production._get_valid_components() + items_dict = { + "oxigesti.stock.production.lot": mrp_production.lot_producing_id + | components.move_line_ids.lot_id, + "oxigesti.product.product": mrp_production.product_id + | components.product_id, + } + for binder_name, items in items_dict.items(): + binder = self.binder_for(binder_name) + for item in items: + exporter = self.component( + usage="record.exporter", model_name=binder.model._name + ) + exporter.run(item) diff --git a/connector_oxigesti/models/oxigesti_backend/__init__.py b/connector_oxigesti/models/oxigesti_backend/__init__.py index f72b58532..0cacb9672 100644 --- a/connector_oxigesti/models/oxigesti_backend/__init__.py +++ b/connector_oxigesti/models/oxigesti_backend/__init__.py @@ -1,3 +1,4 @@ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) from . import common +from . import backend_product_attribute_map diff --git a/connector_oxigesti/models/oxigesti_backend/backend_product_attribute_map.py b/connector_oxigesti/models/oxigesti_backend/backend_product_attribute_map.py new file mode 100644 index 000000000..82a551037 --- /dev/null +++ b/connector_oxigesti/models/oxigesti_backend/backend_product_attribute_map.py @@ -0,0 +1,47 @@ +# Copyright NuoBiT Solutions - Frank Cespedes +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) +import logging + +from odoo import fields, models + +_logger = logging.getLogger(__name__) + + +class OxigestiBackendProductAttributeMap(models.Model): + _name = "oxigesti.backend.product.attribute.map" + + backend_id = fields.Many2one( + comodel_name="oxigesti.backend", + string="Oxigesti Backend", + ondelete="cascade", + ) + + attribute_id = fields.Many2one( + comodel_name="product.attribute", + string="Odoo Product Attribute", + required=True, + ondelete="restrict", + ) + + oxigesti_attribute = fields.Char( + string="Oxigesti Attribute", + required=True, + ) + + _sql_constraints = [ + ( + "uniq", + "unique(backend_id, attribute_id, external_id)", + "Attribute mapping line must be unique", + ), + ( + "attribute_uniq", + "unique(backend_id, attribute_id)", + "Odoo Attribute used in another map line", + ), + ( + "external_uniq", + "unique(backend_id, external_id)", + "External ID used in another map line", + ), + ] diff --git a/connector_oxigesti/models/oxigesti_backend/common.py b/connector_oxigesti/models/oxigesti_backend/common.py index da6567146..e5bca0b57 100644 --- a/connector_oxigesti/models/oxigesti_backend/common.py +++ b/connector_oxigesti/models/oxigesti_backend/common.py @@ -77,6 +77,11 @@ def _select_state(self): ) active = fields.Boolean(string="Active", default=True) state = fields.Selection(selection="_select_state", string="State", default="draft") + product_attribute_map_ids = fields.One2many( + comodel_name="oxigesti.backend.product.attribute.map", + inverse_name="backend_id", + string="Product Attribute Map", + ) def button_reset_to_draft(self): self.ensure_one() @@ -108,6 +113,7 @@ def button_check_connection(self): export_stock_production_lot_since_date = fields.Datetime("Export Lots since") import_services_since_date = fields.Datetime("Import Services since") export_services_since_date = fields.Datetime("Export Services since") + export_mrp_production_since_date = fields.Datetime("Export Productions since") sync_offset = fields.Integer( string="Sync Offset", @@ -172,6 +178,12 @@ def export_services_since(self): rec.export_services_since_date = fields.Datetime.now() self.env["oxigesti.sale.order"].export_data(rec, since_date) + def export_mrp_production_since(self): + for rec in self: + since_date = rec.export_mrp_production_since_date + rec.export_mrp_production_since_date = fields.Datetime.now() + self.env["oxigesti.mrp.production"].export_data(rec, since_date) + # Scheduler methods @api.model def get_current_user_company(self): @@ -231,6 +243,11 @@ def _scheduler_export_services(self): domain = [("company_id", "=", company_id.id)] self.search(domain).export_services_since() + def _scheduler_export_mrp_production(self): + company_id = self.get_current_user_company() + domain = [("company_id", "=", company_id.id)] + self.search(domain).export_mrp_production_since() + def tz_to_utc(self, dt): t = pytz.timezone(self.tz).localize(dt) t = t.astimezone(pytz.utc) diff --git a/connector_oxigesti/models/product_product/adapter.py b/connector_oxigesti/models/product_product/adapter.py index af1ef919c..dcdbf68dd 100644 --- a/connector_oxigesti/models/product_product/adapter.py +++ b/connector_oxigesti/models/product_product/adapter.py @@ -11,7 +11,13 @@ class ProductProductAdapter(Component): _apply_on = "oxigesti.product.product" _sql = """select a.CodigoArticulo, a.DescripcionArticulo, - a.Familia, a.CodigoAlternativo, a.Importe + a.Familia, a.CodigoAlternativo, a.Importe, a.Diametro, + a.ColorOjiva, a.AlojamientoValvula, a.Base, + a.CapacidadGeometrica, a.ColorCuerpo, a.PresionPrueba, + a.RoscaCollarinParaTulipa, a.TipoAleacion, + a.AcoplamientoSalida, a.PresionMaximaTrabajo, a.Sonda, + a.TomaRapida, a.Cromada, a.TipoValvula, a.PresionResidual, + a.CaudalPresionMaximo, a.PresionRotura, a.Comunicacion, a.Manometro from %(schema)s.Odoo_Articulos_Generales a """ @@ -31,4 +37,10 @@ class ProductProductAdapter(Component): where CodigoArticulo = %%(CodigoArticulo)s """ + _sql_field_type = """SELECT COLUMN_NAME, DATA_TYPE + FROM INFORMATION_SCHEMA.COLUMNS + WHERE TABLE_NAME = 'Odoo_Articulos_Generales' + AND TABLE_SCHEMA = %(schema)s; + """ + _id = ("CodigoArticulo",) diff --git a/connector_oxigesti/models/product_product/export_mapper.py b/connector_oxigesti/models/product_product/export_mapper.py index 493ffabc9..face1b8ce 100644 --- a/connector_oxigesti/models/product_product/export_mapper.py +++ b/connector_oxigesti/models/product_product/export_mapper.py @@ -1,6 +1,8 @@ # Copyright NuoBiT Solutions - Eric Antones # Copyright NuoBiT Solutions - Kilian Niubo # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) +from odoo import _ +from odoo.exceptions import ValidationError from odoo.addons.component.core import Component from odoo.addons.connector.components.mapper import changed_by, mapping, only_create @@ -46,7 +48,8 @@ def CodigoArticulo(self, record): raise AssertionError( "The Odoo product with ID %i and Name '%s' " "has no Internal reference. " - "Please assign one and requeue the job." % (record.id, record.name) + "Please assign one and requeue the job." + % (record.odoo_id.id, record.name) ) return {"CodigoArticulo": record.default_code} @@ -90,3 +93,41 @@ def Archivado(self, record): @mapping def Eliminado(self, record): return {"Eliminado": 0} + + @mapping + def Atributos(self, record): + adapter = self.component(usage="backend.adapter", model_name=self.model._name) + headers = adapter.get_headers() + attributes = {} + for ptav in record.with_context( + lang=self.backend_record.lang_id.code + ).product_template_attribute_value_ids: + attribute_map = self.backend_record.product_attribute_map_ids.filtered( + lambda x: x.attribute_id == ptav.attribute_id + ) + if attribute_map: + if attribute_map.oxigesti_attribute not in headers: + raise ValidationError( + _("The attribute %s doesn't exists in backend") + % attribute_map.oxigesti_attribute + ) + try: + content = headers[attribute_map.oxigesti_attribute]( + ptav.product_attribute_value_id.name + ) + attributes[attribute_map.oxigesti_attribute] = content + except ValueError: + raise ValidationError( + _( + "The Odoo product with ID %i and Name '%s' " + "has a value '%s' in the attribute '%s' that is not a float. " + "Please correct it and requeue the job." + ) + % ( + record.product_tmpl_id.id, + record.name, + ptav.product_attribute_value_id.name, + attribute_map.oxigesti_attribute, + ) + ) + return attributes or None diff --git a/connector_oxigesti/models/stock_production_lot/adapter.py b/connector_oxigesti/models/stock_production_lot/adapter.py index e4ba789cc..3f332d777 100644 --- a/connector_oxigesti/models/stock_production_lot/adapter.py +++ b/connector_oxigesti/models/stock_production_lot/adapter.py @@ -13,7 +13,10 @@ class StockProductionLotAdapter(Component): _apply_on = "oxigesti.stock.production.lot" _sql = """select l.CodigoArticulo, l.Lote, l.nos, l.nos_unknown, - l.dn, l.dn_unknown, l.write_date + l.dn, l.dn_unknown, l.Fabricante, l.Peso, + l.FechaFabricacion, l.FechaRetimbrado, + l.FechaProximoRetimbrado, l.FechaCaducidad, + l.write_date from %(schema)s.Odoo_Articulos_Lotes l """ diff --git a/connector_oxigesti/models/stock_production_lot/binding.py b/connector_oxigesti/models/stock_production_lot/binding.py index c0c0ca3a0..f14593d25 100644 --- a/connector_oxigesti/models/stock_production_lot/binding.py +++ b/connector_oxigesti/models/stock_production_lot/binding.py @@ -43,6 +43,12 @@ def _compute_oxigesti_readonly(self): "dn", "nos_unknown", "dn_unknown", + "manufacturer_id", + "weight", + "manufacture_date", + "retesting_date", + "next_retesting_date", + "removal_date", ) def _compute_oxigesti_write_date(self): for rec in self: diff --git a/connector_oxigesti/models/stock_production_lot/export_mapper.py b/connector_oxigesti/models/stock_production_lot/export_mapper.py index 5e44a619c..2e57293db 100644 --- a/connector_oxigesti/models/stock_production_lot/export_mapper.py +++ b/connector_oxigesti/models/stock_production_lot/export_mapper.py @@ -3,7 +3,7 @@ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) from odoo.addons.component.core import Component -from odoo.addons.connector.components.mapper import mapping +from odoo.addons.connector.components.mapper import follow_m2o_relations, mapping, none class StockProductionLotExportMapper(Component): @@ -14,6 +14,12 @@ class StockProductionLotExportMapper(Component): direct = [ ("name", "Lote"), + (none(follow_m2o_relations("manufacturer_id.name")), "Fabricante"), + (none("weight"), "Peso"), + (none("manufacture_date"), "FechaFabricacion"), + (none("retesting_date"), "FechaRetimbrado"), + (none("next_retesting_date"), "FechaProximoRetimbrado"), + (none("removal_date"), "FechaCaducidad"), ] @mapping diff --git a/connector_oxigesti/models/stock_production_lot/import_mapper.py b/connector_oxigesti/models/stock_production_lot/import_mapper.py index d29f7b767..12d836b48 100644 --- a/connector_oxigesti/models/stock_production_lot/import_mapper.py +++ b/connector_oxigesti/models/stock_production_lot/import_mapper.py @@ -1,6 +1,7 @@ # Copyright NuoBiT Solutions - Kilian Niubo # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) from odoo import _ +from odoo.exceptions import ValidationError from odoo.addons.component.core import Component from odoo.addons.connector.components.mapper import mapping, only_create @@ -12,6 +13,14 @@ class ResPartnerImportMapper(Component): _apply_on = "oxigesti.stock.production.lot" + direct = [ + ("Peso", "weight"), + ("FechaFabricacion", "manufacture_date"), + ("FechaRetimbrado", "retesting_date"), + ("FechaProximoRetimbrado", "next_retesting_date"), + ("FechaCaducidad", "removal_date"), + ] + @mapping def backend_id(self, record): return {"backend_id": self.backend_record.id} @@ -73,6 +82,34 @@ def dn(self, record): values["dn_unknown"] = record["dn_unknown"] return values + # TODO: REVIEW: improve this mapping, exists the possibility that the + # manufacturer has a different name in Odoo than in Oxigesti + @mapping + def manufacturer_id(self, record): + if record["Fabricante"]: + manufacturer = self.env["res.partner"].search( + [ + ("name", "=", record["Fabricante"]), + ("company_id", "in", (False, self.backend_record.company_id.id)), + ] + ) + if not manufacturer: + raise ValidationError( + _("Manufacturer (%s) does not exist with this name on Odoo") + % (record["Fabricante"],) + ) + if len(manufacturer) > 1: + raise ValidationError( + _( + "Found more than one manufacturer (%i) with " + "the same name (%s) in Odoo" + ) + % (len(manufacturer), record["Fabricante"]) + ) + return {"manufacturer_id": manufacturer.id} + else: + return {"manufacturer_id": None} + @mapping def oxigesti_write_date(self, record): return {"oxigesti_write_date": record["write_date"]} diff --git a/connector_oxigesti/security/ir.model.access.csv b/connector_oxigesti/security/ir.model.access.csv index 1f38e7e33..119e9bbfc 100644 --- a/connector_oxigesti/security/ir.model.access.csv +++ b/connector_oxigesti/security/ir.model.access.csv @@ -16,3 +16,7 @@ id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink "access_oxigesti_stock_production_lot","oxigesti_stock_production_lot connector manager","model_oxigesti_stock_production_lot","connector.group_connector_manager",1,1,1,1 "access_oxigesti_stock_production_lot_group_user","oxigesti_stock_production_lot group_user","model_oxigesti_stock_production_lot","base.group_user",1,0,0,0 "access_oxigesti_backend","oxigesti_backend connector manager","model_oxigesti_backend","connector.group_connector_manager",1,1,1,1 +"access_oxigesti_mrp_production","oxigesti_mrp_production connector manager","model_oxigesti_mrp_production","connector.group_connector_manager",1,1,1,1 +"access_oxigesti_mrp_production_group_user","oxigesti_mrp_production group_user","model_oxigesti_mrp_production","base.group_user",1,0,0,0 +"access_oxigesti_backend_product_attribute_map","oxigesti_backend_product_attribute_map connector manager","model_oxigesti_backend_product_attribute_map","connector.group_connector_manager",1,1,1,1 +"access_oxigesti_backend_product_attribute_map_group_user","oxigesti_backend_product_attribute_map group_user","model_oxigesti_backend_product_attribute_map","base.group_user",1,1,1,1 diff --git a/connector_oxigesti/views/connector_oxigesti_menu.xml b/connector_oxigesti/views/connector_oxigesti_menu.xml index 969ba8076..fba892298 100644 --- a/connector_oxigesti/views/connector_oxigesti_menu.xml +++ b/connector_oxigesti/views/connector_oxigesti_menu.xml @@ -75,6 +75,14 @@ action="oxigesti_stock_production_lot_action" /> + + + + + + mrp.production.connector.form + mrp.production + + + + + + + + + + + + + + oxigesti.mrp.production.form + oxigesti.mrp.production + +
+ + + + + + +
+
+
+ + oxigesti.mrp.production.tree + oxigesti.mrp.production + + + + + + +