@@ -922,3 +922,138 @@ def test_normalize_value_includes_code_for_vocabulary(self):
922922
923923 # Restore registrant
924924 self .registrant .write ({"gender_id" : False })
925+
926+ # ──────────────────────────────────────────────────────────────────────────
927+ # TEST 17 – _normalize_value_for_cel covers all field types
928+ # ──────────────────────────────────────────────────────────────────────────
929+
930+ def test_normalize_value_char_field (self ):
931+ """_normalize_value_for_cel returns string for char/text/selection fields."""
932+ cr = self ._create_cr ()
933+ detail = cr .get_detail ()
934+ # given_name is a Char field on the detail model
935+ result = cr ._normalize_value_for_cel ("hello" , detail , "given_name" )
936+ self .assertEqual (result , "hello" )
937+
938+ def test_normalize_value_date_field (self ):
939+ """_normalize_value_for_cel passes through date values."""
940+ cr = self ._create_cr ()
941+ detail = cr .get_detail ()
942+ # birthdate is a Date field on the detail model
943+ from datetime import date
944+
945+ test_date = date (2000 , 1 , 15 )
946+ result = cr ._normalize_value_for_cel (test_date , detail , "birthdate" )
947+ self .assertEqual (result , test_date )
948+
949+ def test_normalize_value_falsy_boolean_returns_false (self ):
950+ """_normalize_value_for_cel returns False (not None) for falsy boolean fields."""
951+ cr = self ._create_cr ()
952+ # Use a known boolean field from the detail base model
953+ detail = cr .get_detail ()
954+ result = cr ._normalize_value_for_cel (False , detail , "is_applied" )
955+ self .assertIs (result , False , "Falsy boolean must return False, not None." )
956+
957+ def test_normalize_value_falsy_integer_returns_zero (self ):
958+ """_normalize_value_for_cel returns 0 for falsy integer/float/monetary fields."""
959+ cr = self ._create_cr ()
960+ registrant = cr .registrant_id
961+ # res.partner has 'color' as an Integer field
962+ result = cr ._normalize_value_for_cel (False , registrant , "color" )
963+ self .assertEqual (result , 0 , "Falsy integer must return 0, not None." )
964+
965+ def test_normalize_value_truthy_boolean (self ):
966+ """_normalize_value_for_cel returns bool for boolean fields."""
967+ cr = self ._create_cr ()
968+ detail = cr .get_detail ()
969+ result = cr ._normalize_value_for_cel (True , detail , "is_applied" )
970+ self .assertIs (result , True )
971+
972+ def test_normalize_value_truthy_integer (self ):
973+ """_normalize_value_for_cel returns numeric value for integer fields."""
974+ cr = self ._create_cr ()
975+ registrant = cr .registrant_id
976+ result = cr ._normalize_value_for_cel (42 , registrant , "color" )
977+ self .assertEqual (result , 42 )
978+
979+ def test_normalize_value_many2many_field (self ):
980+ """_normalize_value_for_cel returns dict with ids and count for x2many fields."""
981+ cr = self ._create_cr ()
982+ registrant = cr .registrant_id
983+ # category_id is a Many2many field on res.partner
984+ result = cr ._normalize_value_for_cel (registrant .category_id , registrant , "category_id" )
985+ self .assertIsInstance (result , dict )
986+ self .assertIn ("ids" , result )
987+ self .assertIn ("count" , result )
988+ self .assertIsInstance (result ["ids" ], list )
989+
990+ def test_normalize_value_none_without_record (self ):
991+ """_normalize_value_for_cel returns None when record is None."""
992+ cr = self ._create_cr ()
993+ result = cr ._normalize_value_for_cel (None , None , None )
994+ self .assertIsNone (result )
995+
996+ # ──────────────────────────────────────────────────────────────────────────
997+ # TEST 18 – _compute_field_values_for_cel with no field selected
998+ # ──────────────────────────────────────────────────────────────────────────
999+
1000+ def test_compute_field_values_no_field_selected (self ):
1001+ """_compute_field_values_for_cel returns None values when no field is selected."""
1002+ cr = self ._create_cr ()
1003+ # Don't set field_to_modify — selected_field_name stays empty
1004+ result = cr ._compute_field_values_for_cel ()
1005+ self .assertIsNone (result ["old_value" ])
1006+ self .assertIsNone (result ["new_value" ])
1007+
1008+ # ──────────────────────────────────────────────────────────────────────────
1009+ # TEST 19 – _resolve_dynamic_approval with no selected field
1010+ # ──────────────────────────────────────────────────────────────────────────
1011+
1012+ def test_resolve_dynamic_approval_no_field_returns_none (self ):
1013+ """_resolve_dynamic_approval returns None when selected_field_name is not set."""
1014+ cr = self ._create_cr ()
1015+ result = cr ._resolve_dynamic_approval ()
1016+ self .assertIsNone (result , "Must return None when no field is selected." )
1017+
1018+ # ──────────────────────────────────────────────────────────────────────────
1019+ # TEST 20 – _check_can_submit validates dynamic field selection
1020+ # ──────────────────────────────────────────────────────────────────────────
1021+
1022+ def test_check_can_submit_rejects_without_field_selection (self ):
1023+ """_check_can_submit raises ValidationError for dynamic CR without field selection."""
1024+ cr = self ._create_cr ()
1025+ # CR is dynamic but no field_to_modify set
1026+ with self .assertRaises (ValidationError ):
1027+ cr ._check_can_submit ()
1028+
1029+ # ──────────────────────────────────────────────────────────────────────────
1030+ # TEST 21 – Many2one normalization with hierarchical parent
1031+ # ──────────────────────────────────────────────────────────────────────────
1032+
1033+ def test_normalize_many2one_with_parent (self ):
1034+ """_normalize_value_for_cel includes parent dict for hierarchical records."""
1035+ # Find a vocabulary code with a parent, or create one
1036+ VocabCode = self .env ["spp.vocabulary.code" ]
1037+ parent = VocabCode .search ([("parent_id" , "!=" , False )], limit = 1 )
1038+ if not parent :
1039+ # Try to find any vocab code and check if the model supports parent
1040+ any_code = VocabCode .search ([], limit = 1 )
1041+ if not any_code or "parent_id" not in any_code ._fields :
1042+ self .skipTest ("Need hierarchical vocabulary codes with parent_id." )
1043+ # Create parent-child pair
1044+ vocab = any_code .vocabulary_id
1045+ parent_rec = VocabCode .create ({"name" : "Test Parent" , "code" : "TEST_PARENT" , "vocabulary_id" : vocab .id })
1046+ child_rec = VocabCode .create (
1047+ {"name" : "Test Child" , "code" : "TEST_CHILD" , "vocabulary_id" : vocab .id , "parent_id" : parent_rec .id }
1048+ )
1049+ parent = child_rec
1050+
1051+ cr = self ._create_cr ()
1052+ detail = cr .get_detail ()
1053+ normalized = cr ._normalize_value_for_cel (parent , detail , "gender_id" )
1054+
1055+ self .assertIsInstance (normalized , dict )
1056+ self .assertIn ("parent" , normalized , "Hierarchical vocab must include parent dict." )
1057+ self .assertIn ("id" , normalized ["parent" ])
1058+ self .assertIn ("name" , normalized ["parent" ])
1059+ self .assertIn ("code" , normalized ["parent" ])
0 commit comments