I'm building a custom module for email templates for HR. My template file (hr_mail_template_mail_templates.xml) is this one:
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data>
<record id="mail_template_work_permit_expiry" model="mail.template">
<field name="name">Work Permit Expiry Reminder</field>
<field name="model_id" ref="hr.model_hr_employee"/>
<field name="subject">Your Work Permit Expires Soon</field>
<field name="body_html" type="html">
<![CDATA[
<p>Dear ${object.name},</p>
<p>This is a reminder that your work permit is set to expire on <strong>${object.work_permit_expiration}</strong>.</p>
<p>Please update your documents as soon as possible.</p>
<p>Regards,<br/>HR Department</p>
]]>
</field>
<field name="email_to">${object.work_email}</field>
<field name="email_from">${user.email}</field>
</record>
<record id="mail_template_performance_review" model="mail.template">
<field name="name">Performance Review Notification</field>
<field name="model_id" ref="hr.model_hr_employee"/>
<field name="subject">Upcoming Performance Review</field>
<field name="body_html" type="html">
<![CDATA[
<p>Hello ${object.parent_id.name},</p>
<p>Please be reminded that <strong>${object.name}</strong>'s performance review is due soon.</p>
<p>Kindly schedule the evaluation accordingly.</p>
<p>Best regards,<br/>HR Department</p>
]]>
</field>
<field name="email_to">${object.parent_id.work_email}</field>
<field name="email_from">${user.email}</field>
</record>
</data>
</odoo>
When I try to activate the custom, show this error:
2025-07-02 16:56:09,236 4 ERROR nocllc-costex-migr18-20984680 odoo.tools.convert: The XML file '/home/odoo/src/user/hr_mail_template_control/data/hr_mail_template_mail_templates.xml' does not fit the required schema!
Traceback (most recent call last):
File "/home/odoo/src/odoo/odoo/tools/convert.py", line 669, in convert_xml_import
relaxng.assert_(doc)
File "src/lxml/etree.pyx", line 3682, in lxml.etree._Validator.assert_
AssertionError: Element odoo has extra content: data, line 3
2025-07-02 16:56:09,237 4 WARNING nocllc-costex-migr18-20984680 odoo.tools.convert: /home/odoo/src/user/hr_mail_template_control/data/hr_mail_template_mail_templates.xml:3:0:ERROR:RELAXNGV:RELAXNG_ERR_EXTRACONTENT: Element odoo has extra content: data
2025-07-02 16:56:09,237 4 INFO nocllc-costex-migr18-20984680 odoo.tools.convert: Install 'jingtrang' for more precise and useful validation messages.
2025-07-02 16:56:09,242 4 WARNING nocllc-costex-migr18-20984680 odoo.modules.loading: Transient module states were reset
2025-07-02 16:56:09,243 4 ERROR nocllc-costex-migr18-20984680 odoo.modules.registry: Failed to load registry
2025-07-02 16:56:09,245 4 INFO nocllc-costex-migr18-20984680 odoo.modules.loading: loading 1 modules...
2025-07-02 16:56:09,249 4 INFO nocllc-costex-migr18-20984680 odoo.modules.loading: 1 modules loaded in 0.00s, 0 queries (+0 extra)
2025-07-02 16:56:09,266 4 INFO nocllc-costex-migr18-20984680 odoo.modules.loading: loading 246 modules...
2025-07-02 16:56:09,365 4 INFO nocllc-costex-migr18-20984680 odoo.modules.loading: 246 modules loaded in 0.10s, 0 queries (+0 extra)
2025-07-02 16:56:09,616 4 INFO nocllc-costex-migr18-20984680 odoo.modules.loading: Modules loaded.
2025-07-02 16:56:09,623 4 INFO nocllc-costex-migr18-20984680 odoo.modules.registry: Registry loaded in 0.380s
2025-07-02 16:56:09,625 4 ERROR nocllc-costex-migr18-20984680 odoo.http: Exception during request handling.
Traceback (most recent call last):
File "/home/odoo/src/odoo/odoo/http.py", line 2532, in __call__
response = request._serve_db()
^^^^^^^^^^^^^^^^^^^
File "/home/odoo/src/odoo/odoo/http.py", line 2059, in _serve_db
return self._transactioning(
^^^^^^^^^^^^^^^^^^^^^
File "/home/odoo/src/odoo/odoo/http.py", line 2122, in _transactioning
return service_model.retrying(func, env=self.env)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/odoo/src/odoo/odoo/service/model.py", line 156, in retrying
result = func()
^^^^^^
File "/home/odoo/src/odoo/odoo/http.py", line 2089, in _serve_ir_http
response = self.dispatcher.dispatch(rule.endpoint, args)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/odoo/src/odoo/odoo/http.py", line 2337, in dispatch
result = self.request.registry['ir.http']._dispatch(endpoint)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/odoo/src/odoo/odoo/addons/base/models/ir_http.py", line 333, in _dispatch
result = endpoint(**request.params)
^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/odoo/src/odoo/odoo/http.py", line 754, in route_wrapper
result = endpoint(self, *args, **params_ok)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/odoo/src/odoo/addons/web/controllers/dataset.py", line 42, in call_button
action = call_kw(request.env[model], method, args, kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/odoo/src/odoo/odoo/api.py", line 533, in call_kw
result = getattr(recs, name)(*args, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3/dist-packages/decorator.py", line 232, in fun
return caller(func, *(extras + args), **kw)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/odoo/src/odoo/odoo/addons/base/models/ir_module.py", line 75, in check_and_log
return method(self, *args, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/odoo/src/odoo/odoo/addons/base/models/ir_module.py", line 480, in button_immediate_install
return self._button_immediate_function(self.env.registry[self._name].button_install)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/odoo/src/odoo/odoo/addons/base/models/ir_module.py", line 604, in _button_immediate_function
registry = modules.registry.Registry.new(self._cr.dbname, update_module=True)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3/dist-packages/decorator.py", line 232, in fun
return caller(func, *(extras + args), **kw)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/odoo/src/odoo/odoo/tools/func.py", line 97, in locked
return func(inst, *args, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/odoo/src/odoo/odoo/modules/registry.py", line 129, in new
odoo.modules.load_modules(registry, force_demo, status, update_module)
File "/home/odoo/src/odoo/odoo/modules/loading.py", line 484, in load_modules
processed_modules += load_marked_modules(env, graph,
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/odoo/src/odoo/odoo/modules/loading.py", line 365, in load_marked_modules
loaded, processed = load_module_graph(
^^^^^^^^^^^^^^^^^^
File "/home/odoo/src/odoo/odoo/modules/loading.py", line 228, in load_module_graph
load_data(env, idref, mode, kind='data', package=package)
File "/home/odoo/src/odoo/odoo/modules/loading.py", line 72, in load_data
tools.convert_file(env, package.name, filename, idref, mode, noupdate, kind)
File "/home/odoo/src/odoo/odoo/tools/convert.py", line 615, in convert_file
convert_xml_import(env, module, fp, idref, mode, noupdate)
File "/home/odoo/src/odoo/odoo/tools/convert.py", line 669, in convert_xml_import
relaxng.assert_(doc)
File "src/lxml/etree.pyx", line 3682, in lxml.etree._Validator.assert_
AssertionError: Element odoo has extra content: data, line 3
Any suggestions it's appreciated,
Thanks for the instructions, it's working now