@@ -14,6 +14,10 @@ class TestRuleEvaluation(AlertsTestCommon):
1414
1515 Covers threshold rules, date rules, duplicate prevention,
1616 cron evaluation, validation constraints, and error handling.
17+
18+ Uses `color` (integer, base res.partner) for threshold tests and
19+ `write_date` (datetime, always available) for date tests to avoid
20+ dependencies on optional modules like `account`.
1721 """
1822
1923 @classmethod
@@ -23,38 +27,35 @@ def setUpClass(cls):
2327 # Get ir.model for res.partner (always available)
2428 cls .partner_model = cls .env ["ir.model" ].search ([("model" , "=" , "res.partner" )], limit = 1 )
2529
26- # Get numeric field: credit_limit on res.partner
27- cls .field_credit_limit = cls .env ["ir.model.fields" ].search (
28- [("model_id" , "=" , cls .partner_model .id ), ("name" , "=" , "credit_limit " )],
30+ # Get numeric field: color (integer) on res.partner — always available from base
31+ cls .field_color = cls .env ["ir.model.fields" ].search (
32+ [("model_id" , "=" , cls .partner_model .id ), ("name" , "=" , "color " )],
2933 limit = 1 ,
3034 )
3135
32- # Get date field: date on res.partner
33- cls .field_date = cls .env ["ir.model.fields" ].search (
34- [("model_id" , "=" , cls .partner_model .id ), ("name" , "=" , "date " )],
36+ # Get date field: write_date (datetime) on res.partner — always available
37+ cls .field_write_date = cls .env ["ir.model.fields" ].search (
38+ [("model_id" , "=" , cls .partner_model .id ), ("name" , "=" , "write_date " )],
3539 limit = 1 ,
3640 )
3741
38- # Create test partners with known values
42+ # Create test partners with known color values
3943 cls .partner_low = cls .env ["res.partner" ].create (
4044 {
41- "name" : "Test Partner Low Credit" ,
42- "credit_limit" : 10.0 ,
43- "date" : fields .Date .today () + timedelta (days = 5 ),
45+ "name" : "Test Partner Low Color" ,
46+ "color" : 1 ,
4447 }
4548 )
4649 cls .partner_mid = cls .env ["res.partner" ].create (
4750 {
48- "name" : "Test Partner Mid Credit" ,
49- "credit_limit" : 50.0 ,
50- "date" : fields .Date .today () + timedelta (days = 30 ),
51+ "name" : "Test Partner Mid Color" ,
52+ "color" : 5 ,
5153 }
5254 )
5355 cls .partner_high = cls .env ["res.partner" ].create (
5456 {
55- "name" : "Test Partner High Credit" ,
56- "credit_limit" : 200.0 ,
57- "date" : fields .Date .today () + timedelta (days = 90 ),
57+ "name" : "Test Partner High Color" ,
58+ "color" : 10 ,
5859 }
5960 )
6061
@@ -64,29 +65,29 @@ def setUpClass(cls):
6465 )
6566
6667 def _create_threshold_rule (self , ** kwargs ):
67- """Helper to create a threshold rule for res.partner."""
68+ """Helper to create a threshold rule for res.partner using color field ."""
6869 vals = {
6970 "name" : "Test Threshold Rule" ,
7071 "alert_type_id" : self .alert_type_threshold .id ,
7172 "model_id" : self .partner_model .id ,
7273 "rule_type" : "threshold" ,
73- "monitored_field_id" : self .field_credit_limit .id ,
74+ "monitored_field_id" : self .field_color .id ,
7475 "comparison" : "lt" ,
75- "threshold_value" : 100 .0 ,
76+ "threshold_value" : 8 .0 ,
7677 "domain_filter" : self .test_domain ,
7778 "priority" : "medium" ,
7879 }
7980 vals .update (kwargs )
8081 return self .env ["spp.alert.rule" ].create (vals )
8182
8283 def _create_date_rule (self , ** kwargs ):
83- """Helper to create a date rule for res.partner."""
84+ """Helper to create a date rule for res.partner using write_date field ."""
8485 vals = {
8586 "name" : "Test Date Rule" ,
8687 "alert_type_id" : self .alert_type_deadline .id ,
8788 "model_id" : self .partner_model .id ,
8889 "rule_type" : "date" ,
89- "date_field_id" : self .field_date .id ,
90+ "date_field_id" : self .field_write_date .id ,
9091 "days_before" : 14 ,
9192 "domain_filter" : self .test_domain ,
9293 "priority" : "high" ,
@@ -103,10 +104,10 @@ def test_threshold_rule_creates_alerts(self):
103104 if not self .alert_type_threshold :
104105 self .skipTest ("Alert type threshold not found" )
105106
106- rule = self ._create_threshold_rule (threshold_value = 100 .0 , comparison = "lt" )
107+ rule = self ._create_threshold_rule (threshold_value = 8 .0 , comparison = "lt" )
107108 count = rule ._evaluate_rule ()
108109
109- # partner_low (10 ) and partner_mid (50 ) are < 100 , partner_high (200 ) is not
110+ # partner_low (1 ) and partner_mid (5 ) are < 8 , partner_high (10 ) is not
110111 self .assertEqual (count , 2 )
111112
112113 alerts = self .env ["spp.alert" ].search ([("rule_id" , "=" , rule .id )])
@@ -117,60 +118,60 @@ def test_threshold_rule_gt_comparison(self):
117118 if not self .alert_type_threshold :
118119 self .skipTest ("Alert type threshold not found" )
119120
120- rule = self ._create_threshold_rule (threshold_value = 100 .0 , comparison = "gt" )
121+ rule = self ._create_threshold_rule (threshold_value = 8 .0 , comparison = "gt" )
121122 count = rule ._evaluate_rule ()
122123
123- # Only partner_high (200 ) is > 100
124+ # Only partner_high (10 ) is > 8
124125 self .assertEqual (count , 1 )
125126
126127 def test_threshold_rule_eq_comparison (self ):
127128 """Test equal comparison."""
128129 if not self .alert_type_threshold :
129130 self .skipTest ("Alert type threshold not found" )
130131
131- rule = self ._create_threshold_rule (threshold_value = 50 .0 , comparison = "eq" )
132+ rule = self ._create_threshold_rule (threshold_value = 5 .0 , comparison = "eq" )
132133 count = rule ._evaluate_rule ()
133134
134- # Only partner_mid (50 ) equals 50
135+ # Only partner_mid (5 ) equals 5
135136 self .assertEqual (count , 1 )
136137
137138 def test_threshold_rule_lte_comparison (self ):
138139 """Test less-than-or-equal comparison."""
139140 if not self .alert_type_threshold :
140141 self .skipTest ("Alert type threshold not found" )
141142
142- rule = self ._create_threshold_rule (threshold_value = 50 .0 , comparison = "lte" )
143+ rule = self ._create_threshold_rule (threshold_value = 5 .0 , comparison = "lte" )
143144 count = rule ._evaluate_rule ()
144145
145- # partner_low (10 ) and partner_mid (50 ) are <= 50
146+ # partner_low (1 ) and partner_mid (5 ) are <= 5
146147 self .assertEqual (count , 2 )
147148
148149 def test_threshold_rule_gte_comparison (self ):
149150 """Test greater-than-or-equal comparison."""
150151 if not self .alert_type_threshold :
151152 self .skipTest ("Alert type threshold not found" )
152153
153- rule = self ._create_threshold_rule (threshold_value = 50 .0 , comparison = "gte" )
154+ rule = self ._create_threshold_rule (threshold_value = 5 .0 , comparison = "gte" )
154155 count = rule ._evaluate_rule ()
155156
156- # partner_mid (50 ) and partner_high (200 ) are >= 50
157+ # partner_mid (5 ) and partner_high (10 ) are >= 5
157158 self .assertEqual (count , 2 )
158159
159160 def test_threshold_alert_values (self ):
160161 """Test that created alerts have correct field values."""
161162 if not self .alert_type_threshold :
162163 self .skipTest ("Alert type threshold not found" )
163164
164- rule = self ._create_threshold_rule (threshold_value = 15 .0 , comparison = "lt" )
165+ rule = self ._create_threshold_rule (threshold_value = 2 .0 , comparison = "lt" )
165166 rule ._evaluate_rule ()
166167
167168 alert = self .env ["spp.alert" ].search ([("rule_id" , "=" , rule .id )])
168169 self .assertEqual (len (alert ), 1 )
169170 self .assertEqual (alert .rule_id , rule )
170171 self .assertEqual (alert .res_model , "res.partner" )
171172 self .assertEqual (alert .res_id , self .partner_low .id )
172- self .assertEqual (alert .current_value , 10 .0 )
173- self .assertEqual (alert .threshold_value , 15 .0 )
173+ self .assertEqual (alert .current_value , 1 .0 )
174+ self .assertEqual (alert .threshold_value , 2 .0 )
174175 self .assertEqual (alert .priority , "medium" )
175176 self .assertEqual (alert .alert_type_id , self .alert_type_threshold )
176177
@@ -179,45 +180,31 @@ def test_threshold_alert_values(self):
179180 # -------------------------------------------------------------------------
180181
181182 def test_date_rule_creates_alerts (self ):
182- """Test that date rule creates alerts for records within days_before."""
183- if not self .alert_type_deadline or not self .field_date :
183+ """Test that date rule creates alerts for records with write_date within window.
184+
185+ write_date is today (just created), so days_until=0 which is <= any positive days_before.
186+ """
187+ if not self .alert_type_deadline or not self .field_write_date :
184188 self .skipTest ("Required alert type or field not found" )
185189
190+ # All 3 partners were just created, so write_date is today.
191+ # days_until = 0, which is <= 14
186192 rule = self ._create_date_rule (days_before = 14 )
187193 count = rule ._evaluate_rule ()
188194
189- # partner_low date is 5 days out (<= 14), others are 30 and 90 days out
190- self .assertEqual (count , 1 )
195+ self .assertEqual (count , 3 )
191196
192- alert = self .env ["spp.alert" ].search ([("rule_id" , "=" , rule .id )])
193- self .assertEqual (alert .res_id , self .partner_low .id )
194- self .assertEqual (alert .days_until , 5 )
195-
196- def test_date_rule_wide_window (self ):
197- """Test date rule with a wide window that catches more records."""
198- if not self .alert_type_deadline or not self .field_date :
197+ def test_date_rule_negative_window (self ):
198+ """Test date rule with negative days_before only catches past dates."""
199+ if not self .alert_type_deadline or not self .field_write_date :
199200 self .skipTest ("Required alert type or field not found" )
200201
201- rule = self ._create_date_rule (days_before = 60 )
202+ # write_date is today (days_until=0), -1 means only overdue (negative days_until)
203+ rule = self ._create_date_rule (days_before = - 1 )
202204 count = rule ._evaluate_rule ()
203205
204- # partner_low (5 days) and partner_mid (30 days) are <= 60
205- self .assertEqual (count , 2 )
206-
207- def test_date_rule_alert_values (self ):
208- """Test that date alerts have correct days_until value."""
209- if not self .alert_type_deadline or not self .field_date :
210- self .skipTest ("Required alert type or field not found" )
211-
212- rule = self ._create_date_rule (days_before = 100 )
213- rule ._evaluate_rule ()
214-
215- alerts = self .env ["spp.alert" ].search ([("rule_id" , "=" , rule .id )], order = "days_until asc" )
216- # All 3 partners should match (5, 30, 90 days <= 100)
217- self .assertEqual (len (alerts ), 3 )
218- self .assertEqual (alerts [0 ].days_until , 5 )
219- self .assertEqual (alerts [1 ].days_until , 30 )
220- self .assertEqual (alerts [2 ].days_until , 90 )
206+ # days_until=0 is NOT <= -1
207+ self .assertEqual (count , 0 )
221208
222209 # -------------------------------------------------------------------------
223210 # Duplicate Prevention Tests
@@ -228,7 +215,7 @@ def test_duplicate_prevention(self):
228215 if not self .alert_type_threshold :
229216 self .skipTest ("Alert type threshold not found" )
230217
231- rule = self ._create_threshold_rule (threshold_value = 100 .0 , comparison = "lt" )
218+ rule = self ._create_threshold_rule (threshold_value = 8 .0 , comparison = "lt" )
232219
233220 count1 = rule ._evaluate_rule ()
234221 count2 = rule ._evaluate_rule ()
@@ -244,15 +231,15 @@ def test_duplicate_allows_after_resolve(self):
244231 if not self .alert_type_threshold :
245232 self .skipTest ("Alert type threshold not found" )
246233
247- rule = self ._create_threshold_rule (threshold_value = 15 .0 , comparison = "lt" )
234+ rule = self ._create_threshold_rule (threshold_value = 2 .0 , comparison = "lt" )
248235
249- # First run creates 1 alert (partner_low)
236+ # First run creates 1 alert (partner_low, color=1 )
250237 count1 = rule ._evaluate_rule ()
251238 self .assertEqual (count1 , 1 )
252239
253- # Resolve the alert
240+ # Acknowledge then resolve the alert
254241 alert = self .env ["spp.alert" ].search ([("rule_id" , "=" , rule .id )])
255- alert .write ({ "resolution_notes" : "Test resolution" } )
242+ alert .action_acknowledge ( )
256243 alert .action_resolve (notes = "Test resolution" )
257244
258245 # Second run should create a new alert (old one is resolved)
@@ -272,7 +259,7 @@ def test_cron_evaluate_rules(self):
272259 self .skipTest ("Alert type threshold not found" )
273260
274261 # Active rule with type — should be evaluated
275- active_rule = self ._create_threshold_rule (name = "Active Rule" , threshold_value = 15 .0 )
262+ active_rule = self ._create_threshold_rule (name = "Active Rule" , threshold_value = 2 .0 )
276263
277264 # Inactive rule — should be skipped
278265 self ._create_threshold_rule (name = "Inactive Rule" , active = False )
@@ -307,7 +294,7 @@ def test_cron_continues_on_error(self):
307294 # Valid rule — should still succeed
308295 good_rule = self ._create_threshold_rule (
309296 name = "Good Rule" ,
310- threshold_value = 15 .0 ,
297+ threshold_value = 2 .0 ,
311298 )
312299
313300 self .env ["spp.alert.rule" ]._cron_evaluate_rules ()
@@ -362,7 +349,7 @@ def test_validation_rule_type_without_model(self):
362349 "name" : "No Model Rule" ,
363350 "alert_type_id" : self .alert_type_threshold .id ,
364351 "rule_type" : "threshold" ,
365- "monitored_field_id" : self .field_credit_limit .id ,
352+ "monitored_field_id" : self .field_color .id ,
366353 "priority" : "medium" ,
367354 }
368355 )
@@ -376,7 +363,7 @@ def test_action_evaluate_returns_notification(self):
376363 if not self .alert_type_threshold :
377364 self .skipTest ("Alert type threshold not found" )
378365
379- rule = self ._create_threshold_rule (threshold_value = 15 .0 )
366+ rule = self ._create_threshold_rule (threshold_value = 2 .0 )
380367 result = rule .action_evaluate ()
381368
382369 self .assertEqual (result ["type" ], "ir.actions.client" )
@@ -444,7 +431,7 @@ def test_rule_propagates_company(self):
444431 self .skipTest ("Alert type threshold not found" )
445432
446433 rule = self ._create_threshold_rule (
447- threshold_value = 15 .0 ,
434+ threshold_value = 2 .0 ,
448435 company_id = self .company_main .id ,
449436 )
450437 rule ._evaluate_rule ()
0 commit comments