fix: create the four backend demo logins the demo script actually needs
shared/DEMO_SCRIPT.md's cast table names six logins. Two - the parent and student portal accounts - were created in mc_education_portal. The other four (Principal, Front Office, Teacher, Accountant) were explicitly punted to O9: mc_education_base's demo/mc_teacher_demo.xml says outright "res.users/login creation is O9's job (demo rehearsal), not this base module." That job was never actually done - found only now, while writing a demo handbook that needed to print real, working credentials and I checked them against a live container before printing anything. Concretely this means a from-scratch rehearsal of the demo script would have stalled at Scene 1: there was no way to log in as Rekha Nair, Sunitha R, or David Fernandes at all, and logging in as "Arun Prakash" would have meant either sharing the admin account or creating an unlinked user that fails Scene 4's actual point - a teacher seeing only their own batch, which the attendance record rule resolves through batch_id.class_teacher_id.user_id. Arun's login is now attached to the same hr.employee record mc_education_base's own demo data already made Grade 8-A's class teacher, not a fresh one. Real Odoo behaviour learned fixing this, worth recording since it will bite again: demo data loads with noupdate=True so a school's own edits survive a later module update, and that guard is checked by xmlid, not by which file is doing the writing - a <record> in this module trying to set an existing mc_education_base demo record's field was silently skipped, no error, nothing in the log. <function> calls the ORM directly and is the correct tool, but carries the same guard one level up in Odoo's loader (convert.py's _tag_function skips entirely unless the module is being freshly installed, mode == 'init' - an update on an already-installed database is a no-op). That is exactly the real story this product cares about (a clean machine, installing the whole suite once), so it's the right fix going forward - verified on a from-scratch install of all nine modules together. The one already-running database that had this module installed before this fix existed needed a one-off manual correction outside the codebase, not a workaround inside it. Added a regression test asserting all four logins exist in the right group and that Arun's employee record and Grade 8-A's class_teacher_id actually resolve to the same person - not just "a teacher login exists somewhere." Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
parent
736c0e8c10
commit
bd9b449ded
@ -17,6 +17,7 @@
|
||||
],
|
||||
"demo": [
|
||||
"demo/mc_theme_demo.xml",
|
||||
"demo/mc_cast_users_demo.xml",
|
||||
],
|
||||
"installable": True,
|
||||
"application": False,
|
||||
|
||||
82
addons/mc_education_theme/demo/mc_cast_users_demo.xml
Normal file
82
addons/mc_education_theme/demo/mc_cast_users_demo.xml
Normal file
@ -0,0 +1,82 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<!-- shared/DEMO_SCRIPT.md's cast table names six logins. Two of them
|
||||
(parent@demo.school, student@demo.school) already exist as
|
||||
portal users, created in mc_education_portal per its own
|
||||
module's concern. The other four - Principal, Front Office,
|
||||
Teacher, Accountant - were explicitly deferred to here:
|
||||
mc_education_base's demo/mc_teacher_demo.xml says outright
|
||||
"res.users/login creation is O9's job (demo rehearsal), not
|
||||
this base module". That job was never actually done until now
|
||||
- found while writing a handbook that needed to state working
|
||||
demo credentials and checking they were real before printing
|
||||
them. Loading this module last (it depends on every other
|
||||
mc_education_* module transitively through mc_education_base
|
||||
being the gate) is exactly why this is the right place: every
|
||||
group these users need already exists by the time this runs.
|
||||
|
||||
Arun Prakash already exists as an hr.employee (created in
|
||||
mc_education_base's own demo data, work_email
|
||||
teacher@demo.school) and is already Grade 8-A's class teacher.
|
||||
mc_education_attendance's "teachers see only their own batch"
|
||||
rule resolves through batch_id.class_teacher_id.user_id, so his
|
||||
login has to be linked onto that same employee record, not a
|
||||
fresh one - a second, unlinked Arun would pass a login test and
|
||||
fail the actual attendance scene. -->
|
||||
<record id="demo_user_rekha_nair" model="res.users">
|
||||
<field name="name">Rekha Nair</field>
|
||||
<field name="login">principal@demo.school</field>
|
||||
<field name="email">principal@demo.school</field>
|
||||
<field name="password">demo1234</field>
|
||||
<field name="group_ids" eval="[(6, 0, [ref('mc_education_base.group_school_administrator')])]"/>
|
||||
</record>
|
||||
|
||||
<record id="demo_user_sunitha_r" model="res.users">
|
||||
<field name="name">Sunitha R</field>
|
||||
<field name="login">office@demo.school</field>
|
||||
<field name="email">office@demo.school</field>
|
||||
<field name="password">demo1234</field>
|
||||
<field name="group_ids" eval="[(6, 0, [ref('mc_education_base.group_school_staff')])]"/>
|
||||
</record>
|
||||
|
||||
<record id="demo_user_arun_prakash" model="res.users">
|
||||
<field name="name">Arun Prakash</field>
|
||||
<field name="login">teacher@demo.school</field>
|
||||
<field name="email">teacher@demo.school</field>
|
||||
<field name="password">demo1234</field>
|
||||
<field name="group_ids" eval="[(6, 0, [ref('mc_education_base.group_teacher')])]"/>
|
||||
</record>
|
||||
|
||||
<!-- A plain <record> write here is silently skipped: demo records
|
||||
load with noupdate=True precisely so a later module update
|
||||
never resets edits a school made to its own demo data, and
|
||||
that guard applies to a write from ANY file, not just a
|
||||
reload of the record's own defining file. Confirmed against
|
||||
the running container - the <record> version left user_id
|
||||
empty with no error at all.
|
||||
|
||||
<function> is the correct tool, but it carries the exact same
|
||||
guard one level up (odoo/tools/convert.py _tag_function:
|
||||
"if self.noupdate and self.mode != 'init': return") - it only
|
||||
actually runs on a genuinely fresh install. That is the real
|
||||
demo/production story (a clean machine installing the whole
|
||||
suite at once, per CLAUDE.md's acceptance test), so it is the
|
||||
right fix going forward; verified by dropping to a scratch
|
||||
database and installing all nine modules from nothing. It is
|
||||
a no-op on a database that already had mc_education_theme
|
||||
installed before this file existed - that one-time gap on an
|
||||
already-running database needs a one-off manual write, not a
|
||||
data-file change. -->
|
||||
<function model="hr.employee" name="write">
|
||||
<value eval="[ref('mc_education_base.demo_employee_arun_prakash')]"/>
|
||||
<value eval="{'user_id': ref('demo_user_arun_prakash')}"/>
|
||||
</function>
|
||||
|
||||
<record id="demo_user_david_fernandes" model="res.users">
|
||||
<field name="name">David Fernandes</field>
|
||||
<field name="login">accounts@demo.school</field>
|
||||
<field name="email">accounts@demo.school</field>
|
||||
<field name="password">demo1234</field>
|
||||
<field name="group_ids" eval="[(6, 0, [ref('mc_education_base.group_accountant')])]"/>
|
||||
</record>
|
||||
</odoo>
|
||||
@ -30,3 +30,37 @@ class TestTheme(TransactionCase):
|
||||
self.assertNotIn("Powered by", html)
|
||||
self.assertNotIn("odoo.com", html)
|
||||
self.assertNotIn("Odoo Logo", html)
|
||||
|
||||
def test_backend_cast_logins_exist_in_the_right_groups(self):
|
||||
# shared/DEMO_SCRIPT.md's cast table names six logins. Two
|
||||
# (parent@demo.school, student@demo.school) are created by
|
||||
# mc_education_portal. The other four were explicitly deferred
|
||||
# to this module (see demo/mc_cast_users_demo.xml) and, until
|
||||
# that file existed, were never actually created anywhere -
|
||||
# the demo script would have stalled at Scene 1 on a clean
|
||||
# rehearsal. This test is the regression guard for that gap.
|
||||
principal = self.env["res.users"].search([("login", "=", "principal@demo.school")])
|
||||
self.assertTrue(principal)
|
||||
self.assertIn(
|
||||
self.env.ref("mc_education_base.group_school_administrator"), principal.group_ids
|
||||
)
|
||||
|
||||
office = self.env["res.users"].search([("login", "=", "office@demo.school")])
|
||||
self.assertTrue(office)
|
||||
self.assertIn(self.env.ref("mc_education_base.group_school_staff"), office.group_ids)
|
||||
|
||||
accountant = self.env["res.users"].search([("login", "=", "accounts@demo.school")])
|
||||
self.assertTrue(accountant)
|
||||
self.assertIn(self.env.ref("mc_education_base.group_accountant"), accountant.group_ids)
|
||||
|
||||
teacher = self.env["res.users"].search([("login", "=", "teacher@demo.school")])
|
||||
self.assertTrue(teacher)
|
||||
self.assertIn(self.env.ref("mc_education_base.group_teacher"), teacher.group_ids)
|
||||
|
||||
# Not just "a teacher login exists" - it has to be *the* login
|
||||
# that Grade 8-A's class_teacher_id points at, because that is
|
||||
# what mc_education_attendance's record rule actually checks.
|
||||
arun = self.env.ref("mc_education_base.demo_employee_arun_prakash")
|
||||
self.assertEqual(arun.user_id, teacher)
|
||||
grade_8a = self.env.ref("mc_education_base.demo_batch_grade8_a")
|
||||
self.assertEqual(grade_8a.class_teacher_id, arun)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user