Serves Demo Scene 8. Per spec this is glue only, not a custom LMS:
mc.batch gets one new field (channel_id -> slide.channel), and
mc.enrollment's create()/write() calls the stock
slide.channel._action_add_members() when an enrollment becomes active
for a batch that has a channel - that's the entire feature. 40 lines
of model code, well under the spec's own "~200 lines or something has
gone wrong" ceiling.
Demo data matches Scene 8's script exactly - "Mathematics - Algebra
Basics": a video lesson, a PDF handout, a 5-question quiz (verified:
each question has exactly one correct and one incorrect answer, the
minimum website_slides itself requires).
This module took far longer to get right than its size suggests, and
the reason is worth recording. A ForeignKeyViolation on an unrelated
model (mc.batch referencing a channel Postgres said was never
inserted, despite that channel being created earlier in the same
file) sent the investigation looking for an install-time flush-
ordering bug for a long time - checking whether attachment=True
binary fields interact badly with a pending FK write in the same
flush batch, splitting the demo data across multiple files, even
routing the batch-channel link through a post_init_hook to sidestep
it. All of that was chasing a symptom. Bisecting the actual XML down
to a single record eventually surfaced the real, simple cause:
type="base64" on an XML <field> is only valid paired with a file=
attribute pointing to a real file in the addon - inline base64 text
raises a ValueError that Odoo's demo-data loader catches and
downgrades to "installed without demo data", and in an earlier
configuration (batch-link in the same file) that same swallowed
error surfaced instead as the confusing FK violation. Fixed by saving
the handout as a real file (static/demo/algebra_handout.pdf) and
referencing it properly; the post_init_hook and file-splitting were
reverted since the real fix needed neither. Verified byte-for-byte:
the attachment Odoo stores is exactly 604 bytes, detected as
application/pdf, matching the source file's md5sum.
Also caught before it reached git: line-ending conversion on this
binary PDF ("LF will be replaced by CRLF"), which would have silently
corrupted it on checkout for any contributor with Windows's
core.autocrlf on. Added *.pdf (and common image types) as binary to
.gitattributes and confirmed the staged blob's md5sum matches the
source file exactly.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
68 lines
3.0 KiB
Python
68 lines
3.0 KiB
Python
from odoo.tests.common import TransactionCase
|
|
|
|
|
|
class TestLms(TransactionCase):
|
|
|
|
@classmethod
|
|
def setUpClass(cls):
|
|
super().setUpClass()
|
|
cls.year = cls.env["mc.academic.year"].create({
|
|
"name": "TEST-LMS-2026-27",
|
|
"date_start": "2026-06-01", "date_end": "2027-04-30",
|
|
})
|
|
cls.program = cls.env["mc.program"].create({
|
|
"name": "TEST LMS Program", "code": "TEST-LMS-P1", "sequence_no": 1,
|
|
"display_label": "Test Grade",
|
|
})
|
|
cls.channel = cls.env["slide.channel"].create({"name": "TEST LMS Channel"})
|
|
cls.batch = cls.env["mc.batch"].create({
|
|
"name": "TEST LMS Batch", "program_id": cls.program.id, "year_id": cls.year.id,
|
|
"channel_id": cls.channel.id,
|
|
})
|
|
partner = cls.env["res.partner"].create({"name": "LMS Test Student"})
|
|
cls.student = cls.env["mc.student"].create({
|
|
"partner_id": partner.id, "name": "LMS Test Student",
|
|
})
|
|
|
|
def test_active_enrollment_joins_the_batchs_channel(self):
|
|
self.env["mc.enrollment"].create({
|
|
"student_id": self.student.id, "program_id": self.program.id,
|
|
"batch_id": self.batch.id, "year_id": self.year.id, "state": "active",
|
|
})
|
|
member = self.env["slide.channel.partner"].search([
|
|
("channel_id", "=", self.channel.id), ("partner_id", "=", self.student.partner_id.id),
|
|
])
|
|
self.assertTrue(member)
|
|
self.assertEqual(member.member_status, "joined")
|
|
|
|
def test_draft_enrollment_does_not_join_the_channel(self):
|
|
self.env["mc.enrollment"].create({
|
|
"student_id": self.student.id, "program_id": self.program.id,
|
|
"batch_id": self.batch.id, "year_id": self.year.id, "state": "draft",
|
|
})
|
|
member = self.env["slide.channel.partner"].search([
|
|
("channel_id", "=", self.channel.id), ("partner_id", "=", self.student.partner_id.id),
|
|
])
|
|
self.assertFalse(member)
|
|
|
|
def test_activating_a_draft_enrollment_joins_the_channel(self):
|
|
enrollment = self.env["mc.enrollment"].create({
|
|
"student_id": self.student.id, "program_id": self.program.id,
|
|
"batch_id": self.batch.id, "year_id": self.year.id, "state": "draft",
|
|
})
|
|
enrollment.write({"state": "active"})
|
|
member = self.env["slide.channel.partner"].search([
|
|
("channel_id", "=", self.channel.id), ("partner_id", "=", self.student.partner_id.id),
|
|
])
|
|
self.assertTrue(member)
|
|
|
|
def test_batch_without_channel_does_not_error(self):
|
|
batch_no_channel = self.env["mc.batch"].create({
|
|
"name": "TEST LMS Batch No Channel", "program_id": self.program.id, "year_id": self.year.id,
|
|
})
|
|
# Should not raise.
|
|
self.env["mc.enrollment"].create({
|
|
"student_id": self.student.id, "program_id": self.program.id,
|
|
"batch_id": batch_no_channel.id, "year_id": self.year.id, "state": "active",
|
|
})
|