import base64 from odoo import Command from odoo.addons.sale.tests.common import SaleCommon from .common_exports import StudioExportCase, StudioExportSerializer class TestStudioExports(StudioExportCase): def test_export_customizations(self): custom_model = self.create_customization( "ir.model", name="Furnace Types", model="x_furnace_types" ) custom_field = self.create_customization( "ir.model.fields", name="x_studio_max_temp", ttype="integer", model_id=custom_model.id, ) custom_view = self.create_customization( "ir.ui.view", name="Kanban view for x_furnace_types", model="x_furnace_types", type="kanban", arch=""" """, ) custom_action = self.create_customization( "ir.actions.act_window", name="Furnaces", res_model="x_furnace_types", view_mode="list,form,kanban", help="

This is your new action.

", ) custom_menu_1 = self.create_customization( "ir.ui.menu", name="My Furnaces", ) custom_menu_2 = self.create_customization( "ir.ui.menu", name="Furnaces Types", parent_id=custom_menu_1.id, action=f"ir.actions.act_window,{custom_action.id}", ) # Create a record to show that it is not exported # (appears neither in manifest nor in filelist) self.env[custom_model.model].create( {"x_name": "Austenitization", "x_studio_max_temp": 1200} ) self.studio_export() self.assertManifest( data=[ "data/ir_model.xml", "data/ir_model_fields.xml", "data/ir_ui_view.xml", "data/ir_actions_act_window.xml", "data/ir_ui_menu.xml", ], depends=["web_studio"], ) self.assertFileList( "data/ir_model.xml", "data/ir_model_fields.xml", "data/ir_ui_view.xml", "data/ir_actions_act_window.xml", "data/ir_ui_menu.xml", ) self.assertXML( "data/ir_model.xml", f""" x_furnace_types Furnace Types """, ) self.assertXML( "data/ir_model_fields.xml", f""" integer X Studio Max Temp x_furnace_types x_studio_max_temp """, ) self.assertXML( "data/ir_ui_view.xml", f""" x_furnace_types Kanban view for x_furnace_types kanban """, ) self.assertXML( "data/ir_actions_act_window.xml", f""" This is your new action.

]]>
Furnaces x_furnace_types list,form,kanban
""", ) self.assertXML( "data/ir_ui_menu.xml", f""" My Furnaces Furnaces Types """, ) def test_export_customizations_without_export_model(self): custom_model = self.create_customization( "ir.model", name="Furnace Types", model="x_furnace_types" ) self.create_customization( "ir.model.fields", name="x_studio_max_temp", ttype="integer", model_id=custom_model.id, ) CustomModel = self.env[custom_model.model].with_user(2).sudo() CustomModel.create({"x_name": "Austenitization", "x_studio_max_temp": 1200}) # Without export model, the custom model data are not exported self.studio_export() self.assertFileList("data/ir_model.xml", "data/ir_model_fields.xml") def test_export_customizations_with_export_model(self): custom_model = self.create_customization( "ir.model", name="Furnace Types", model="x_furnace_types" ) self.create_customization( "ir.model.fields", name="x_studio_max_temp", ttype="integer", model_id=custom_model.id, ) CustomModel = self.env[custom_model.model].with_user(2).sudo() furnace_type = CustomModel.create( {"x_name": "Austenitization", "x_studio_max_temp": 1200} ) # With the export model, the custom model data is exported self.create_export_model(CustomModel._name) self.studio_export() self.assertFileList( "data/ir_model.xml", "data/ir_model_fields.xml", "data/x_furnace_types.xml", ) self.assertXML( "data/x_furnace_types.xml", f""" 1200 Austenitization """, ) def test_simple_export_model_without_record(self): self.create_export_model(self.TestModel._name) # Without record, the export_model has no effect self.studio_export() self.assertFileList() def test_simple_export_model_with_record(self): self.create_export_model(self.TestModel._name) some_record = self.TestModel.create({"name": "Some record"}) # Simple case self.studio_export() self.assertFileList("data/test_studio_export_model1.xml") self.assertXML("data/test_studio_export_model1.xml", f""" Some record """) def test_simple_export_model_with_record_updatable(self): self.create_export_model(self.TestModel._name, updatable=True) some_record = self.TestModel.create({"name": "Some record"}) # Without updatable mode self.studio_export() self.assertFileList("data/test_studio_export_model1.xml") self.assertXML("data/test_studio_export_model1.xml", f""" Some record """) def test_simple_export_model_with_record_as_demo(self): self.create_export_model(self.TestModel._name, is_demo_data=True) some_record = self.TestModel.create({"name": "Some record"}) # With is_demo_data mode, with updatable self.studio_export() self.assertFileList("demo/test_studio_export_model1.xml") self.assertXML("demo/test_studio_export_model1.xml", f""" Some record """) def test_simple_export_model_with_record_as_demo_updatable(self): self.create_export_model(self.TestModel._name, updatable=True, is_demo_data=True) some_record = self.TestModel.create({"name": "Some record"}) # With is_demo_data mode, without updatable self.studio_export() self.assertFileList("demo/test_studio_export_model1.xml") self.assertXML("demo/test_studio_export_model1.xml", f""" Some record """) def test_export_model_with_demo_data(self): some_record = self.TestModel.create({"name": "Some record"}) other_record = self.TestModel.create({"name": "Some other record"}) self.create_export_model(self.TestModel._name, is_demo_data=True) self.studio_export() self.assertFileList("demo/test_studio_export_model1.xml") self.assertXML( "demo/test_studio_export_model1.xml", f""" Some record Some other record """, ) def test_export_model_with_binary_field_without_include_attachment(self): some_record = self.TestModel.create( { "name": "Some record", "binary_data": base64.b64encode(b"My binary attachment"), } ) self.create_export_model(self.TestModel._name) # Without include_attachment self.studio_export() self.assertFileList( "data/test_studio_export_model1.xml", f"static/src/binary/test_studio_export_model1/{some_record.id}-binary_data", ) self.assertXML( "data/test_studio_export_model1.xml", f""" Some record """, ) def test_export_model_with_binary_field_with_include_attachment(self): some_record = self.TestModel.create( { "name": "Some record", "binary_data": base64.b64encode(b"My binary attachment"), } ) self.create_export_model(self.TestModel._name, include_attachment=True) # With include_attachment we have the same export result self.studio_export() self.assertFileList( "data/test_studio_export_model1.xml", f"static/src/binary/test_studio_export_model1/{some_record.id}-binary_data", ) self.assertXML( "data/test_studio_export_model1.xml", f""" Some record """, ) def test_export_model_non_attachment_binary_field(self): self.create_customization( "ir.model.fields", name="x_test_binary", ttype="binary", model_id=self.env["ir.model"]._get("res.partner").id, state="manual", depends="name", compute="for partner in self: partner['x_test_binary'] = [{'key': 'value'}]", field_description="Test Binary Field", store=False, ) partner = self.env["res.partner"].create({ "name": "Test Partner Binary Field", }) self.assertEqual(partner.x_test_binary, [{'key': 'value'}]) wizard_data = self.env["studio.export.wizard.data"].create( [{"model": partner._name, "res_id": partner.id}] ) wizard = self.env["studio.export.wizard"].create({ "default_export_data": [Command.set(wizard_data.ids)], }) export_info = wizard.get_export_info() studio_module = self.env["ir.module.module"].get_studio_module() self.exporter = StudioExportSerializer(self.env, studio_module, export_info) self._export_cache = {} self._export_iter = iter(self.exporter.serialize()) self.assertFileContains( f"static/src/binary/res_partner/{partner.id}-x_test_binary", "[{'key': 'value'}]" ) def test_export_model_with_many2one_attachment(self): some_record = self.TestModel.create({"name": "Some record"}) attachment = self.env["ir.attachment"].create( { "name": "Some attachment", "datas": base64.b64encode(b"My attachment"), "res_model": self.TestModel._name, "res_id": some_record.id, "res_field": "attachment_id", } ) some_record.attachment_id = attachment self.create_export_model(self.TestModel._name, include_attachment=True) self.studio_export() self.assertFileList( "data/test_studio_export_model1.xml", "data/ir_attachment_pre.xml", f"static/src/binary/ir_attachment/{attachment.id}-Someattachment", ) self.assertManifest( depends=["test_web_studio"], data=["data/ir_attachment_pre.xml", "data/test_studio_export_model1.xml"], ) self.assertXML( "data/ir_attachment_pre.xml", f""" Some attachment """, ) self.assertXML( "data/test_studio_export_model1.xml", f""" Some record """, ) def test_export_model_with_one2many_attachment(self): some_record = self.TestModel.create({"name": "Some record"}) attachment1 = self.env["ir.attachment"].create( { "name": "Some attachment", "datas": base64.b64encode(b"My attachment"), "res_model": self.TestModel._name, "res_id": some_record.id, "res_field": "attachment_ids", } ) attachment2 = self.env["ir.attachment"].create( { "name": "Another attachment", "datas": base64.b64encode(b"My second attachment"), "res_model": self.TestModel._name, "res_id": some_record.id, "res_field": "attachment_ids", } ) some_record.attachment_ids = [Command.set([attachment1.id, attachment2.id])] self.create_export_model(self.TestModel._name, include_attachment=True) self.studio_export() self.assertFileList( "data/test_studio_export_model1.xml", "data/ir_attachment_post.xml", f"static/src/binary/ir_attachment/{attachment1.id}-Someattachment", f"static/src/binary/ir_attachment/{attachment2.id}-Anotherattachment", ) self.assertManifest( depends=["test_web_studio"], data=["data/test_studio_export_model1.xml", "data/ir_attachment_post.xml"], ) self.assertXML( "data/ir_attachment_post.xml", f""" Another attachment test.studio_export.model1 attachment_ids Some attachment test.studio_export.model1 attachment_ids """, ) self.assertXML( "data/test_studio_export_model1.xml", f""" Some record """, ) def test_empty_models_and_fields(self): # Test that models without records do not export any data # and empty fields are not exported model2_record1 = self.TestModel2.create({ "name": "Some Record" }) model2_record2 = self.TestModel2.create({ "name": "", "model2_id": model2_record1.id }) self.create_export_model(self.TestModel2._name) self.create_export_model(self.TestModel3._name) self.studio_export() self.assertManifest( data=[ "data/test_studio_export_model2.xml", ], ) self.assertFileList( "data/test_studio_export_model2.xml", ) self.assertXML( "data/test_studio_export_model2.xml", f""" Some Record """, ) def test_export_property_field(self): model1_record = self.TestModel.create({"name": "Record 1"}) model3_record = self.TestModel3.create({ "name": "Some record", "model1_id": model1_record.id, "properties": [{'name': 'test', 'type': 'char', 'value': 'TEST', 'definition_changed': True}], }) self.create_export_model(self.TestModel._name) self.create_export_model(self.TestModel3._name) self.studio_export() self.assertFileList( "data/test_studio_export_model1.xml", "data/test_studio_export_model3.xml", ) self.assertXML( "data/test_studio_export_model1.xml", f""" Record 1 """, ) self.assertXML( "data/test_studio_export_model3.xml", f""" Some record """, ) def test_export_data_related_to_demo(self): # Test that master data (non demo) does not export fields related # to demo records, but data records related to demo are also exported # as demo with only the fields related to said demo records. model3_record = self.TestModel3.create({"name": "Some record"}) model2_record = self.TestModel2.create({ "name": "Some other record", "model3_id": model3_record.id }) self.create_export_model(self.TestModel2._name, is_demo_data=False) self.create_export_model(self.TestModel3._name, is_demo_data=True) self.studio_export() self.assertFileList( "data/test_studio_export_model2.xml", "demo/test_studio_export_model2.xml", "demo/test_studio_export_model3.xml", ) self.assertXML( "data/test_studio_export_model2.xml", f""" Some other record """, ) self.assertXML( "demo/test_studio_export_model3.xml", f""" Some record """, ) self.assertXML( "demo/test_studio_export_model2.xml", f""" """, ) def test_export_dependencies_order(self): # Test that files and records order respects dependencies model3_record = self.TestModel3.create({"name": "Some record"}) model2_record = self.TestModel2.create({ "name": "Some other record", "model3_id": model3_record.id }) model2b_record = self.TestModel2.create({ "name": "Some other record", "model2_id": model2_record.id, "model3_id": model3_record.id }) model2c_record = self.TestModel2.create({"name": "Yet another record"}) model2_record.write({ "res_model": self.TestModel2._name, "res_id": model2c_record.id, }) self.create_export_model(self.TestModel2._name) self.create_export_model(self.TestModel3._name) self.studio_export() self.assertManifest( data=[ "data/test_studio_export_model3.xml", "data/test_studio_export_model2.xml", ], ) self.assertFileList( "data/test_studio_export_model3.xml", "data/test_studio_export_model2.xml", ) self.assertXML( "data/test_studio_export_model3.xml", f""" Some record """, ) self.assertXML( "data/test_studio_export_model2.xml", f""" Yet another record Some other record test.studio_export.model2 Some other record """, ) def test_export_handles_circular_dependencies(self): # Test that models circular dependencies appear in warning.txt # and only if some records causes it model3_record = self.TestModel3.create({ "name": "Record 3", }) model2_record = self.TestModel2.create({ "name": "Record 2", "model3_id": model3_record.id }) model1_record = self.TestModel.create({ "name": "Record 1", "model2_id": model2_record.id }) model3_record.update({"model1_id": model1_record.id}) self.create_export_model(self.TestModel._name) self.create_export_model(self.TestModel2._name) self.create_export_model(self.TestModel3._name) self.studio_export() self.assertFileList( "warnings.txt", "data/test_studio_export_model1.xml", "data/test_studio_export_model2.xml", "data/test_studio_export_model3.xml", ) self.assertXML( "data/test_studio_export_model1.xml", f""" Record 1 """, ) self.assertXML( "data/test_studio_export_model2.xml", f""" Record 2 """, ) self.assertXML( "data/test_studio_export_model3.xml", f""" Record 3 """, ) self.assertFileContains( "warnings.txt", f"""Found 1 circular dependencies (you may have to change data loading order to avoid issues when importing): (data) {self.TestModel._name} -> {self.TestModel2._name} -> {self.TestModel3._name} -> {self.TestModel._name} """, ) def test_export_abstract_actions_with_proper_types(self): WindowActions = self.env["ir.actions.act_window"].with_user(2) window_action = WindowActions.create({ "name": "Test action", "type": "ir.actions.act_window", "res_model": self.TestModel._name, "view_mode": "form", "target": "new", }) URLActions = self.env["ir.actions.act_url"].with_user(2) url_action = URLActions.create({ "name": "Test action", "type": "ir.actions.act_url", "url": "http://odoo.com", }) self.create_export_model("ir.actions.actions", domain=[("id", "in", [window_action.id, url_action.id])]) self.studio_export() self.assertFileList( "data/ir_actions_act_window.xml", "data/ir_actions_act_url.xml", ) self.assertXML( "data/ir_actions_act_window.xml", f""" Test action {self.TestModel._name} form new """, ) self.assertXML( "data/ir_actions_act_url.xml", f""" Test action Test action """, ) class TestSpecificStudioExports_SaleOrder( SaleCommon, StudioExportCase ): def test_export_demo_sale_orders(self): # SPECIFIC: Confirm demo sale orders self.sale_order.action_confirm() self.create_export_model("sale.order", domain=[("id", "in", [self.sale_order.id])], is_demo_data=True) self.create_export_model("sale.order.line", domain=[("order_id", "in", [self.sale_order.id])], is_demo_data=True) self.studio_export() self.assertFileList( "demo/sale_order.xml", "demo/sale_order_line.xml", "demo/sale_order_confirm.xml", "warnings.txt", # because no products nor partners are exported here, but that's not the point of this test. ) evl_attr_value = f"[[ref('{self.get_xmlid(self.sale_order)}')]]" self.assertXML( "demo/sale_order_confirm.xml", f""" """, ) class TestSpecificStudioExports_Website(StudioExportCase): def test_unlink_default_main_menu(self): # SPECIFIC: unlink the default main menu from the website if needed website = self.env['website'].get_current_website() menus = self.env['website.menu'].search([('website_id', '=', website.id)]) self.create_export_model("website.menu", domain=[("id", "in", [m.id for m in menus])]) self.studio_export() if self.get_xmlid(website) != "website.default_website" or not any(r['url'] == '/default-main-menu' for r in menus): # Only written for the default website and on a fresh install self.skipTest("This test is only written for the default website and on a fresh install") self.assertFileList( "warnings.txt", # because we do not export all the necessary data for website exports, but that's not the point of this test "data/website_menu.xml", ) # should have only function to unlink the default main menu nodes = self.get_xml_nodes("data/website_menu.xml", "/odoo/*") self.assertEqual(len(nodes), 1 + len(menus), "has a child for each menu + 1") self.assertEqual([n.tag for n in nodes], ["function"] + ["record"] * len(menus)) self.assertXML( nodes[0], """ """, )