From 2d02aa8a6c64179ef0198517571a4b94111a4984 Mon Sep 17 00:00:00 2001 From: FrankC013 Date: Thu, 21 Sep 2023 09:11:23 +0200 Subject: [PATCH 1/9] [ADD] connector_oxigesti: export manufacturing and unbuild orders --- connector_oxigesti/__manifest__.py | 1 + connector_oxigesti/data/ir_cron.xml | 19 +++- .../data/queue_job_function_data.xml | 20 ++++- connector_oxigesti/models/__init__.py | 1 + .../models/mrp_production/__init__.py | 7 ++ .../models/mrp_production/adapter.py | 32 +++++++ .../models/mrp_production/binder.py | 21 +++++ .../models/mrp_production/binding.py | 88 +++++++++++++++++++ .../models/mrp_production/export_mapper.py | 70 +++++++++++++++ .../models/mrp_production/exporter.py | 54 ++++++++++++ .../models/oxigesti_backend/common.py | 12 +++ .../security/ir.model.access.csv | 2 + .../views/connector_oxigesti_menu.xml | 8 ++ .../views/mrp_production_view.xml | 60 +++++++++++++ .../views/oxigesti_backend_view.xml | 20 +++++ 15 files changed, 413 insertions(+), 2 deletions(-) create mode 100644 connector_oxigesti/models/mrp_production/__init__.py create mode 100644 connector_oxigesti/models/mrp_production/adapter.py create mode 100644 connector_oxigesti/models/mrp_production/binder.py create mode 100644 connector_oxigesti/models/mrp_production/binding.py create mode 100644 connector_oxigesti/models/mrp_production/export_mapper.py create mode 100644 connector_oxigesti/models/mrp_production/exporter.py create mode 100644 connector_oxigesti/views/mrp_production_view.xml diff --git a/connector_oxigesti/__manifest__.py b/connector_oxigesti/__manifest__.py index ad8bd862e..ef0c355b0 100644 --- a/connector_oxigesti/__manifest__.py +++ b/connector_oxigesti/__manifest__.py @@ -34,6 +34,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/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.EsMontaje, 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..69f30f5ea --- /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 gas bottle 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 gas bottle (%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_gas_bottle"), + ("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..c5e60262b --- /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 EsMontaje(self, record): + unbuild_count = self.env["mrp.unbuild"].search( + [("mo_id", "=", record.odoo_id.id), ("state", "=", "done")] + ) + if len(unbuild_count) > 1: + raise ValidationError( + _("The production %s has more than one unbuild. %s") + % (record.name, unbuild_count.mapped("name")) + ) + return {"EsMontaje": 0 if len(unbuild_count) > 0 else 1} + + @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..03ef4d3d5 --- /dev/null +++ b/connector_oxigesti/models/mrp_production/exporter.py @@ -0,0 +1,54 @@ +# 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: + if not binder.to_external(item, wrap=True): + exporter = self.component( + usage="record.exporter", model_name=binder.model._name + ) + exporter.run(item) diff --git a/connector_oxigesti/models/oxigesti_backend/common.py b/connector_oxigesti/models/oxigesti_backend/common.py index da6567146..8bfe03817 100644 --- a/connector_oxigesti/models/oxigesti_backend/common.py +++ b/connector_oxigesti/models/oxigesti_backend/common.py @@ -108,6 +108,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 +173,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 +238,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/security/ir.model.access.csv b/connector_oxigesti/security/ir.model.access.csv index 1f38e7e33..92b592d40 100644 --- a/connector_oxigesti/security/ir.model.access.csv +++ b/connector_oxigesti/security/ir.model.access.csv @@ -16,3 +16,5 @@ 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 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 + + + + + + +