feat(receiving): technicians can count+close receivings from the tablet
ACL: grant group_fp_technician write+create on fp.receiving / line / damage. sudo the internal sale.order x_fc_receiving_status write so a non-privileged technician isn't blocked inside action_mark_counted / action_close. Tests deferred to entech (local Docker unavailable this session). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from . import test_carrier_fields
|
||||
from . import test_technician_receiving_acl
|
||||
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2026 Nexa Systems Inc.
|
||||
# License OPL-1 (Odoo Proprietary License v1.0)
|
||||
"""Technician can receive (count + close) from the tablet.
|
||||
|
||||
Spec: docs/superpowers/specs/2026-05-29-technician-receiving-shipping-tablet-design.md
|
||||
"""
|
||||
from odoo.tests.common import TransactionCase
|
||||
from odoo.exceptions import AccessError
|
||||
|
||||
|
||||
class TestTechnicianReceivingAcl(TransactionCase):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super().setUpClass()
|
||||
cls.partner = cls.env['res.partner'].create({'name': 'AclCust'})
|
||||
cls.product = cls.env['product.product'].create({'name': 'AclWidget'})
|
||||
cls.so = cls.env['sale.order'].create({
|
||||
'partner_id': cls.partner.id,
|
||||
'order_line': [(0, 0, {
|
||||
'product_id': cls.product.id,
|
||||
'product_uom_qty': 1,
|
||||
})],
|
||||
})
|
||||
cls.tech = cls.env['res.users'].create({
|
||||
'name': 'Tech ACL',
|
||||
'login': 'tech_acl_recv',
|
||||
# Odoo 19: group_ids (NOT groups_id) — CLAUDE.md rule 13c.
|
||||
'group_ids': [(6, 0, [
|
||||
cls.env.ref('fusion_plating.group_fp_technician').id,
|
||||
])],
|
||||
})
|
||||
|
||||
def test_technician_can_count_and_close_receiving(self):
|
||||
# Created as admin; the technician must be able to count + close.
|
||||
rec = self.env['fp.receiving'].create({
|
||||
'sale_order_id': self.so.id,
|
||||
'box_count_in': 3,
|
||||
})
|
||||
rec_as_tech = rec.with_user(self.tech)
|
||||
try:
|
||||
rec_as_tech.action_mark_counted()
|
||||
except AccessError as e:
|
||||
self.fail("Technician blocked marking counted: %s" % e)
|
||||
self.assertEqual(rec.state, 'counted')
|
||||
rec_as_tech.action_close()
|
||||
self.assertEqual(rec.state, 'closed')
|
||||
# The SO status write inside _update_so_receiving_status must have
|
||||
# gone through (it is sudo'd) — proves no AccessError on sale.order.
|
||||
self.assertEqual(self.so.x_fc_receiving_status, 'received')
|
||||
|
||||
def test_technician_can_create_damage(self):
|
||||
rec = self.env['fp.receiving'].create({'sale_order_id': self.so.id})
|
||||
dmg = self.env['fp.receiving.damage'].with_user(self.tech).create({
|
||||
'receiving_id': rec.id,
|
||||
'description': 'scratch on flange',
|
||||
})
|
||||
self.assertTrue(dmg.id)
|
||||
Reference in New Issue
Block a user