diff --git a/DEVELOP.rst b/DEVELOP.rst index 01fa29f..f4d104c 100644 --- a/DEVELOP.rst +++ b/DEVELOP.rst @@ -162,6 +162,22 @@ The list of the operations are written in the subfolder .. code-block:: yaml - ['account.account', 'user_type_id', 'account_type', 'Commit https://github.com/odoo/odoo/commit/26b2472f4977ccedbb0b5ed5f'] +* ``removed_models/migrate_FROM_TO/NAME.yaml`` — removed models rule. Display errors / warnings if files contains a given partern: + * errors: "old_model_name", 'old_model_name', old_table_name["',] + * warnings: old.model.name, old_model_name + + For example, for migration from version 15.0 to 16.0: + .. code-block:: yaml + - ["account.account.type", "Commit https://github.com/odoo/odoo/commit/26b2472f4977ccedbb0b5ed5f"] + +* ``renamed_models/migrate_FROM_TO/NAME.yaml`` — renamed models rule. Display errors / warnings if files contains a given partern: + * errors: "old_model_name", 'old_model_name', old_table_name["',] + * warnings: old.model.name, old_model_name + + For example, for migration from version 15.0 to 16.0: + .. code-block:: yaml + - ["stock.production.lot", "stock.lot", None] + How to improve the library ========================== diff --git a/odoo_module_migrate/base_migration_script.py b/odoo_module_migrate/base_migration_script.py index 787cbbd..ad71e4e 100644 --- a/odoo_module_migrate/base_migration_script.py +++ b/odoo_module_migrate/base_migration_script.py @@ -20,6 +20,8 @@ class BaseMigrationScript(object): _FILE_RENAMES = {} _REMOVED_FIELDS = [] _RENAMED_FIELDS = [] + _RENAMED_MODELS = [] + _REMOVED_MODELS = [] _GLOBAL_FUNCTIONS = [] # [function_object] _module_path = '' @@ -57,13 +59,13 @@ def parse_rules(self): "type": TYPE_DICT, "doc": {}, }, - # [(model_name, field_name, more_info), ...)] - "_REMOVED_FIELDS": { + # [(old.model.name, new.model.name, more_info)] + "_RENAMED_MODELS": { "type": TYPE_ARRAY, "doc": [], }, - # [(model_name, old_field_name, new_field_name, more_info), ...)] - "_RENAMED_FIELDS": { + # [(old.model.name, more_info)] + "_REMOVED_MODELS": { "type": TYPE_ARRAY, "doc": [], }, @@ -188,12 +190,12 @@ def process_file(self, ) absolute_file_path = os.path.join(root, new_name) - removed_fields = self.handle_removed_fields(self._REMOVED_FIELDS) - renamed_fields = self.handle_renamed_fields(self._RENAMED_FIELDS) - + renamed_models = self.handle_renamed_models(self._RENAMED_MODELS) # Operate changes in the file (replacements, removals) replaces = self._TEXT_REPLACES.get("*", {}) replaces.update(self._TEXT_REPLACES.get(extension, {})) + replaces.update(renamed_models.get('replaces')) + replaces.update(removed_models.get('replaces')) new_text = tools._replace_in_file( absolute_file_path, replaces, @@ -203,14 +205,15 @@ def process_file(self, # pattern errors = self._TEXT_ERRORS.get("*", {}) errors.update(self._TEXT_ERRORS.get(extension, {})) + errors.update(renamed_models.get('errors')) + errors.update(removed_models.get('errors')) for pattern, error_message in errors.items(): if re.findall(pattern, new_text): logger.error(error_message) warnings = self._TEXT_WARNINGS.get("*", {}) warnings.update(self._TEXT_WARNINGS.get(extension, {})) - warnings.update(removed_fields.get('warnings')) - warnings.update(renamed_fields.get('warnings')) + warnings.update(renamed_models.get('warnings')) for pattern, warning_message in warnings.items(): if re.findall(pattern, new_text): logger.warning( @@ -309,6 +312,85 @@ def handle_deprecated_modules(self, manifest_path, deprecated_modules): if current_manifest_text != new_manifest_text: tools._write_content(manifest_path, new_manifest_text) + def handle_renamed_models(self, renamed_models): + ''' renamed_models = [(old.model, new.model, msg)] + returns dictionary of all replaces / warnings / errors produced + by a model renamed + { + 'replaces': + { + "old_model_name", 'old_model_name': new_model_name + old_table_name["',]: new_table_name["',] + }, + 'warnings': + { + old.model.name: warning msg + old_model_name: warning msg + } + } + ''' + res = {'replaces': {}, 'warnings': {}, 'errors': {}} + for old_model_name, new_model_name, more_info in renamed_models: + old_table_name = old_model_name.replace('.', '_') + new_table_name = new_model_name.replace('.', '_') + old_name_esc = re.escape(old_model_name) + res['replaces'].update({ + r"\"%s\"" % old_name_esc: '"%s"' % new_model_name, + r"\'%s\'" % old_name_esc: "'%s'" % new_model_name, + r"\"%s\"" % old_table_name: "\"%s\"" % new_table_name, + r"\'%s\'" % old_table_name: "'%s'" % new_table_name, + r"model_%s\"" % old_table_name: "model_%s\"" % new_table_name, + r"model_%s\'" % old_table_name: "model_%s'" % new_table_name, + r"model_%s," % old_table_name: "model_%s," % new_table_name, + }) + msg = "The model %s has been renamed to %s.%s" % ( + old_model_name, new_model_name, (" %s" % more_info) or "") + res['warnings'].update({ + old_name_esc: msg, + old_table_name: msg, + }) + return res + + def handle_removed_models(self, removed_models): + ''' removed_models = [(old.model, msg)] + returns dictionary of all replaces / warnings / errors produced + by a model renamed + { + 'error': + { + "old_model_name", 'old_model_name': new_model_name + old_table_name["',]: new_table_name["',] + }, + 'warnings': + { + old.model.name: warning msg + old_model_name: warning msg + } + } + ''' + res = {'replaces': {}, 'warnings': {}, 'errors': {}} + for model_name, more_info in removed_models: + table_name = model_name.replace('.', '_') + model_name_esc = re.escape(model_name) + + msg = "The model %s has been .%s" % ( + model_name, (" %s" % more_info) or "") + + res['errors'].update({ + r"\"%s\"" % model_name_esc: msg, + r"\'%s\'" % model_name_esc: msg, + r"\"%s\"" % table_name: msg, + r"\'%s\'" % table_name: msg, + r"model_%s\"" % table_name: msg, + r"model_%s\'" % table_name: msg, + r"model_%s," % table_name: msg, + }) + res['warnings'].update({ + model_name_esc: msg, + table_name: msg, + }) + return res + def _get_correct_manifest_path(self, manifest_path, file_renames): current_manifest_file_name = manifest_path.as_posix().split('/')[-1] if current_manifest_file_name in file_renames: diff --git a/odoo_module_migrate/config.py b/odoo_module_migrate/config.py index 6f04e64..061c82f 100644 --- a/odoo_module_migrate/config.py +++ b/odoo_module_migrate/config.py @@ -52,6 +52,6 @@ }, ] -_ALLOWED_EXTENSIONS = [".py", ".xml", ".js"] +_ALLOWED_EXTENSIONS = [".py", ".xml", ".js", ".csv"] _MANIFEST_NAMES = ["__openerp__.py", "__manifest__.py"] diff --git a/odoo_module_migrate/migration_scripts/migrate_150_160.py b/odoo_module_migrate/migration_scripts/migrate_150_160.py index c416779..25a41e0 100644 --- a/odoo_module_migrate/migration_scripts/migrate_150_160.py +++ b/odoo_module_migrate/migration_scripts/migrate_150_160.py @@ -2,13 +2,6 @@ from odoo_module_migrate.base_migration_script import BaseMigrationScript -_TEXT_REPLACES = { - ".py": { - r"\.get_xml_id\(": ".get_external_id(", - r"\.fields_get_keys\(\)": "._fields", - }, -} - class MigrationScript(BaseMigrationScript): - _TEXT_REPLACES = _TEXT_REPLACES + pass diff --git a/odoo_module_migrate/migration_scripts/removed_models/migrate_150_160/removed_models.yaml b/odoo_module_migrate/migration_scripts/removed_models/migrate_150_160/removed_models.yaml new file mode 100644 index 0000000..9161d71 --- /dev/null +++ b/odoo_module_migrate/migration_scripts/removed_models/migrate_150_160/removed_models.yaml @@ -0,0 +1 @@ +- ["account.account.type", "Commit https://github.com/odoo/odoo/commit/26b2472f4977ccedbb0b5ed5f"] diff --git a/odoo_module_migrate/migration_scripts/renamed_models/migrate_150_160/renamed_models.yaml b/odoo_module_migrate/migration_scripts/renamed_models/migrate_150_160/renamed_models.yaml new file mode 100644 index 0000000..eba0e19 --- /dev/null +++ b/odoo_module_migrate/migration_scripts/renamed_models/migrate_150_160/renamed_models.yaml @@ -0,0 +1 @@ +- ["stock.production.lot", "stock.lot", None] diff --git a/odoo_module_migrate/migration_scripts/text_replaces/migrate_150_160/orm_methods.yaml b/odoo_module_migrate/migration_scripts/text_replaces/migrate_150_160/orm_methods.yaml new file mode 100644 index 0000000..1911e48 --- /dev/null +++ b/odoo_module_migrate/migration_scripts/text_replaces/migrate_150_160/orm_methods.yaml @@ -0,0 +1,3 @@ +.py: + \.get_xml_id\(: ".get_external_id(" + \.fields_get_keys\(\): "._fields" diff --git a/tests/data_result/module_150_160/models/account_account_type.py b/tests/data_result/module_150_160/models/account_account_type.py new file mode 100644 index 0000000..3d8ec51 --- /dev/null +++ b/tests/data_result/module_150_160/models/account_account_type.py @@ -0,0 +1,16 @@ +from odoo import fields, models + + +class AccountAccountType(models.Model): + _inherit = "account.account.type" + + analytic_account_required = fields.Boolean( + string='Analytic Account Required?', + help="If True, then an analytic account will be required when posting " + "journal entries with this type of account.", + ) + analytic_tag_required = fields.Boolean( + string='Analytic Tag Required?', + help="If True, then analytic tags will be required when posting " + "journal entries with this type of account.", + ) diff --git a/tests/data_result/module_150_160/models/stock_production_lot.py b/tests/data_result/module_150_160/models/stock_production_lot.py new file mode 100644 index 0000000..f4f54cb --- /dev/null +++ b/tests/data_result/module_150_160/models/stock_production_lot.py @@ -0,0 +1,22 @@ +from odoo import models, _ +from odoo.exceptions import UserError + + +class StockProductionLot(models.Model): + _inherit = 'stock.lot' + _barcode_field = 'name' + + def _get_stock_barcode_specific_data(self): + products = self.product_id + return { + 'product.product': products.read(self.env['product.product']._get_fields_stock_barcode(), load=False), + 'uom.uom': products.uom_id.read(self.env['uom.uom']._get_fields_stock_barcode(), load=False) + } + + def _check_create(self): + active_mo_id = self.env.context.get('active_mo_id') + if active_mo_id: + active_mo = self.env['mrp.production'].browse(active_mo_id) + if not active_mo.picking_type_id.use_create_components_lots: + raise UserError(_('You are not allowed to create or edit a lot or serial number for the components with the operation type "Manufacturing". To change this, go on the operation type and tick the box "Create New Lots/Serial Numbers for Components".')) + return super()._check_create() diff --git a/tests/data_result/module_150_160/security/ir.model.access.csv b/tests/data_result/module_150_160/security/ir.model.access.csv new file mode 100644 index 0000000..4022a97 --- /dev/null +++ b/tests/data_result/module_150_160/security/ir.model.access.csv @@ -0,0 +1,2 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +access_stock_production_user,stock.production.lot,stock.model_stock_lot,quality.group_quality_user,1,0,0,0 diff --git a/tests/data_result/module_150_160/views/stock_report_views.xml b/tests/data_result/module_150_160/views/stock_report_views.xml new file mode 100644 index 0000000..5162c7a --- /dev/null +++ b/tests/data_result/module_150_160/views/stock_report_views.xml @@ -0,0 +1,33 @@ + + + + + Lot/Serial Number (PDF) + stock.production.lot + qweb-pdf + stock.report_lot_label + stock.report_lot_label + 'Lot-Serial - %s' % object.name + + report + + + + Lot/Serial Number (PDF) + stock.production.lot + qweb-pdf + stock.report_lot_label + stock.report_lot_label + 'Lot-Serial - %s' % object.name + + report + + + + 0000000000003 + + 1 + + + + diff --git a/tests/data_template/module_150/models/account_account_type.py b/tests/data_template/module_150/models/account_account_type.py new file mode 100644 index 0000000..3d8ec51 --- /dev/null +++ b/tests/data_template/module_150/models/account_account_type.py @@ -0,0 +1,16 @@ +from odoo import fields, models + + +class AccountAccountType(models.Model): + _inherit = "account.account.type" + + analytic_account_required = fields.Boolean( + string='Analytic Account Required?', + help="If True, then an analytic account will be required when posting " + "journal entries with this type of account.", + ) + analytic_tag_required = fields.Boolean( + string='Analytic Tag Required?', + help="If True, then analytic tags will be required when posting " + "journal entries with this type of account.", + ) diff --git a/tests/data_template/module_150/models/stock_production_lot.py b/tests/data_template/module_150/models/stock_production_lot.py new file mode 100644 index 0000000..15253ed --- /dev/null +++ b/tests/data_template/module_150/models/stock_production_lot.py @@ -0,0 +1,22 @@ +from odoo import models, _ +from odoo.exceptions import UserError + + +class StockProductionLot(models.Model): + _inherit = 'stock.production.lot' + _barcode_field = 'name' + + def _get_stock_barcode_specific_data(self): + products = self.product_id + return { + 'product.product': products.read(self.env['product.product']._get_fields_stock_barcode(), load=False), + 'uom.uom': products.uom_id.read(self.env['uom.uom']._get_fields_stock_barcode(), load=False) + } + + def _check_create(self): + active_mo_id = self.env.context.get('active_mo_id') + if active_mo_id: + active_mo = self.env['mrp.production'].browse(active_mo_id) + if not active_mo.picking_type_id.use_create_components_lots: + raise UserError(_('You are not allowed to create or edit a lot or serial number for the components with the operation type "Manufacturing". To change this, go on the operation type and tick the box "Create New Lots/Serial Numbers for Components".')) + return super()._check_create() diff --git a/tests/data_template/module_150/security/ir.model.access.csv b/tests/data_template/module_150/security/ir.model.access.csv new file mode 100644 index 0000000..9f8360d --- /dev/null +++ b/tests/data_template/module_150/security/ir.model.access.csv @@ -0,0 +1,2 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +access_stock_production_user,stock.production.lot,stock.model_stock_production_lot,quality.group_quality_user,1,0,0,0 diff --git a/tests/data_template/module_150/views/stock_report_views.xml b/tests/data_template/module_150/views/stock_report_views.xml new file mode 100644 index 0000000..01c05f9 --- /dev/null +++ b/tests/data_template/module_150/views/stock_report_views.xml @@ -0,0 +1,33 @@ + + + + + Lot/Serial Number (PDF) + stock.production.lot + qweb-pdf + stock.report_lot_label + stock.report_lot_label + 'Lot-Serial - %s' % object.name + + report + + + + Lot/Serial Number (PDF) + stock.production.lot + qweb-pdf + stock.report_lot_label + stock.report_lot_label + 'Lot-Serial - %s' % object.name + + report + + + + 0000000000003 + + 1 + + + +