odoo-addon-contract-line-successor 18.0.1.0.0.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. odoo/addons/contract_line_successor/README.rst +168 -0
  2. odoo/addons/contract_line_successor/__init__.py +2 -0
  3. odoo/addons/contract_line_successor/__manifest__.py +24 -0
  4. odoo/addons/contract_line_successor/data/contract_renew_cron.xml +12 -0
  5. odoo/addons/contract_line_successor/i18n/contract_line_successor.pot +532 -0
  6. odoo/addons/contract_line_successor/models/__init__.py +5 -0
  7. odoo/addons/contract_line_successor/models/contract_contract.py +25 -0
  8. odoo/addons/contract_line_successor/models/contract_line.py +835 -0
  9. odoo/addons/contract_line_successor/models/contract_line_constraints.py +429 -0
  10. odoo/addons/contract_line_successor/models/contract_template_line.py +35 -0
  11. odoo/addons/contract_line_successor/models/res_company.py +15 -0
  12. odoo/addons/contract_line_successor/models/res_config_settings.py +18 -0
  13. odoo/addons/contract_line_successor/readme/CONFIGURE.md +10 -0
  14. odoo/addons/contract_line_successor/readme/CONTRIBUTORS.md +1 -0
  15. odoo/addons/contract_line_successor/readme/DESCRIPTION.md +40 -0
  16. odoo/addons/contract_line_successor/readme/USAGE.md +6 -0
  17. odoo/addons/contract_line_successor/security/ir.model.access.csv +2 -0
  18. odoo/addons/contract_line_successor/static/description/icon.png +0 -0
  19. odoo/addons/contract_line_successor/static/description/index.html +523 -0
  20. odoo/addons/contract_line_successor/tests/__init__.py +1 -0
  21. odoo/addons/contract_line_successor/tests/test_contract.py +847 -0
  22. odoo/addons/contract_line_successor/views/contract_contract.xml +73 -0
  23. odoo/addons/contract_line_successor/views/contract_line.xml +134 -0
  24. odoo/addons/contract_line_successor/views/contract_template.xml +19 -0
  25. odoo/addons/contract_line_successor/views/contract_template_line.xml +50 -0
  26. odoo/addons/contract_line_successor/views/res_config_settings.xml +16 -0
  27. odoo/addons/contract_line_successor/wizards/__init__.py +1 -0
  28. odoo/addons/contract_line_successor/wizards/contract_line_wizard.py +53 -0
  29. odoo/addons/contract_line_successor/wizards/contract_line_wizard.xml +110 -0
  30. odoo_addon_contract_line_successor-18.0.1.0.0.2.dist-info/METADATA +185 -0
  31. odoo_addon_contract_line_successor-18.0.1.0.0.2.dist-info/RECORD +33 -0
  32. odoo_addon_contract_line_successor-18.0.1.0.0.2.dist-info/WHEEL +5 -0
  33. odoo_addon_contract_line_successor-18.0.1.0.0.2.dist-info/top_level.txt +1 -0
@@ -0,0 +1,847 @@
1
+ # Copyright 2018 ACSONE SA/NV
2
+ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
3
+
4
+ from datetime import timedelta
5
+
6
+ from dateutil.relativedelta import relativedelta
7
+
8
+ from odoo.exceptions import ValidationError
9
+
10
+ from odoo.addons.contract.tests.test_contract import (
11
+ TestContract,
12
+ to_date,
13
+ )
14
+
15
+
16
+ class TestContractSuccessor(TestContract):
17
+ @classmethod
18
+ def setUpClass(cls):
19
+ super().setUpClass()
20
+ cls.contract.company_id.create_new_line_at_contract_line_renew = True
21
+ cls.line_vals.update({"is_auto_renew": False})
22
+
23
+ def test_date_end(self):
24
+ """recurring next date for a contract is the min for all lines"""
25
+ self.acct_line.date_end = "2018-01-01"
26
+ self.acct_line.copy()
27
+ self.acct_line.write({"date_end": False, "is_auto_renew": False})
28
+ self.assertFalse(self.contract.date_end)
29
+
30
+ def test_cancel_contract_line(self):
31
+ """It should raise a validation error"""
32
+ self.acct_line.cancel()
33
+ with self.assertRaises(ValidationError):
34
+ self.acct_line.stop(self.today)
35
+
36
+ def test_stop_contract_line(self):
37
+ """It should put end to the contract line"""
38
+ self.acct_line.write(
39
+ {
40
+ "date_start": self.today,
41
+ "recurring_next_date": self.today,
42
+ "date_end": self.today + relativedelta(months=7),
43
+ "is_auto_renew": True,
44
+ }
45
+ )
46
+ self.acct_line.stop(self.today + relativedelta(months=5))
47
+ self.assertEqual(self.acct_line.date_end, self.today + relativedelta(months=5))
48
+
49
+ def test_stop_upcoming_contract_line(self):
50
+ """It should put end to the contract line"""
51
+ self.acct_line.write(
52
+ {
53
+ "date_start": self.today + relativedelta(months=3),
54
+ "recurring_next_date": self.today + relativedelta(months=3),
55
+ "date_end": self.today + relativedelta(months=7),
56
+ "is_auto_renew": True,
57
+ }
58
+ )
59
+ self.acct_line.stop(self.today)
60
+ self.assertEqual(self.acct_line.date_end, self.today + relativedelta(months=7))
61
+ self.assertTrue(self.acct_line.is_canceled)
62
+
63
+ def test_stop_past_contract_line(self):
64
+ """Past contract line are ignored on stop"""
65
+ self.acct_line.write(
66
+ {"date_end": self.today + relativedelta(months=5), "is_auto_renew": True}
67
+ )
68
+ self.acct_line.stop(self.today + relativedelta(months=7))
69
+ self.assertEqual(self.acct_line.date_end, self.today + relativedelta(months=5))
70
+
71
+ def test_stop_contract_line_without_date_end(self):
72
+ """Past contract line are ignored on stop"""
73
+ self.acct_line.write({"date_end": False, "is_auto_renew": False})
74
+ self.acct_line.stop(self.today + relativedelta(months=7))
75
+ self.assertEqual(self.acct_line.date_end, self.today + relativedelta(months=7))
76
+
77
+ def test_stop_wizard(self):
78
+ self.acct_line.write(
79
+ {
80
+ "date_start": self.today,
81
+ "recurring_next_date": self.today,
82
+ "date_end": self.today + relativedelta(months=5),
83
+ "is_auto_renew": True,
84
+ }
85
+ )
86
+ wizard = self.env["contract.line.wizard"].create(
87
+ {
88
+ "date_end": self.today + relativedelta(months=3),
89
+ "contract_line_id": self.acct_line.id,
90
+ }
91
+ )
92
+ wizard.stop()
93
+ self.assertEqual(self.acct_line.date_end, self.today + relativedelta(months=3))
94
+ self.assertFalse(self.acct_line.is_auto_renew)
95
+
96
+ def test_stop_plan_successor_contract_line_0(self):
97
+ successor_contract_line = self.acct_line.copy(
98
+ {
99
+ "date_start": self.today + relativedelta(months=5),
100
+ "recurring_next_date": self.today + relativedelta(months=5),
101
+ }
102
+ )
103
+ self.acct_line.write(
104
+ {
105
+ "successor_contract_line_id": successor_contract_line.id,
106
+ "is_auto_renew": False,
107
+ "date_end": self.today,
108
+ }
109
+ )
110
+ suspension_start = self.today + relativedelta(months=5)
111
+ suspension_end = self.today + relativedelta(months=6)
112
+ with self.assertRaises(ValidationError):
113
+ self.acct_line.stop_plan_successor(suspension_start, suspension_end, True)
114
+
115
+ def test_stop_plan_successor_contract_line_1(self):
116
+ """
117
+ * contract line end's before the suspension period:
118
+ -> apply stop
119
+ """
120
+ suspension_start = self.today + relativedelta(months=5)
121
+ suspension_end = self.today + relativedelta(months=6)
122
+ start_date = self.today
123
+ end_date = self.today + relativedelta(months=4)
124
+ self.acct_line.write(
125
+ {
126
+ "date_start": start_date,
127
+ "recurring_next_date": start_date,
128
+ "date_end": end_date,
129
+ }
130
+ )
131
+ self.acct_line.stop_plan_successor(suspension_start, suspension_end, True)
132
+ self.assertEqual(self.acct_line.date_end, end_date)
133
+ new_line = self.env["contract.line"].search(
134
+ [("predecessor_contract_line_id", "=", self.acct_line.id)]
135
+ )
136
+ self.assertFalse(new_line)
137
+
138
+ def test_stop_plan_successor_contract_line_2(self):
139
+ """
140
+ * contract line start before the suspension period and end in it
141
+ -> apply stop at suspension start date
142
+ -> apply plan successor:
143
+ - date_start: suspension.date_end
144
+ - date_end: suspension.date_end + (contract_line.date_end
145
+ - suspension.date_start)
146
+ """
147
+ suspension_start = self.today + relativedelta(months=3)
148
+ suspension_end = self.today + relativedelta(months=5)
149
+ start_date = self.today
150
+ end_date = self.today + relativedelta(months=4)
151
+ self.acct_line.write(
152
+ {
153
+ "date_start": start_date,
154
+ "recurring_next_date": start_date,
155
+ "date_end": end_date,
156
+ }
157
+ )
158
+ self.acct_line.stop_plan_successor(suspension_start, suspension_end, True)
159
+ self.assertEqual(
160
+ self.acct_line.date_end, suspension_start - relativedelta(days=1)
161
+ )
162
+ new_line = self.env["contract.line"].search(
163
+ [("predecessor_contract_line_id", "=", self.acct_line.id)]
164
+ )
165
+ self.assertTrue(new_line)
166
+ new_date_end = (
167
+ suspension_end + (end_date - suspension_start) + relativedelta(days=1)
168
+ )
169
+ self.assertEqual(new_line.date_start, suspension_end + relativedelta(days=1))
170
+ self.assertEqual(new_line.date_end, new_date_end)
171
+ self.assertTrue(self.acct_line.manual_renew_needed)
172
+
173
+ def test_stop_plan_successor_contract_line_3(self):
174
+ """
175
+ * contract line start before the suspension period and end after it
176
+ -> apply stop at suspension start date
177
+ -> apply plan successor:
178
+ - date_start: suspension.date_end
179
+ - date_end: suspension.date_end + (suspension.date_end
180
+ - suspension.date_start)
181
+ """
182
+ suspension_start = self.today + relativedelta(months=3)
183
+ suspension_end = self.today + relativedelta(months=5)
184
+ start_date = self.today
185
+ end_date = self.today + relativedelta(months=6)
186
+ self.acct_line.write(
187
+ {
188
+ "date_start": start_date,
189
+ "recurring_next_date": start_date,
190
+ "date_end": end_date,
191
+ }
192
+ )
193
+ self.acct_line.stop_plan_successor(suspension_start, suspension_end, True)
194
+ self.assertEqual(
195
+ self.acct_line.date_end, suspension_start - relativedelta(days=1)
196
+ )
197
+ new_line = self.env["contract.line"].search(
198
+ [("predecessor_contract_line_id", "=", self.acct_line.id)]
199
+ )
200
+ self.assertTrue(new_line)
201
+ new_date_end = (
202
+ end_date + (suspension_end - suspension_start) + relativedelta(days=1)
203
+ )
204
+ self.assertEqual(new_line.date_start, suspension_end + relativedelta(days=1))
205
+ self.assertEqual(new_line.date_end, new_date_end)
206
+ self.assertTrue(self.acct_line.manual_renew_needed)
207
+
208
+ def test_stop_plan_successor_contract_line_3_without_end_date(self):
209
+ """
210
+ * contract line start before the suspension period and end after it
211
+ -> apply stop at suspension start date
212
+ -> apply plan successor:
213
+ - date_start: suspension.date_end
214
+ - date_end: suspension.date_end + (suspension.date_end
215
+ - suspension.date_start)
216
+ """
217
+ suspension_start = self.today + relativedelta(months=3)
218
+ suspension_end = self.today + relativedelta(months=5)
219
+ start_date = self.today
220
+ end_date = False
221
+ self.acct_line.write(
222
+ {
223
+ "date_start": start_date,
224
+ "recurring_next_date": start_date,
225
+ "date_end": end_date,
226
+ "is_auto_renew": False,
227
+ }
228
+ )
229
+ self.acct_line.stop_plan_successor(suspension_start, suspension_end, False)
230
+ self.assertEqual(
231
+ self.acct_line.date_end, suspension_start - relativedelta(days=1)
232
+ )
233
+ new_line = self.env["contract.line"].search(
234
+ [("predecessor_contract_line_id", "=", self.acct_line.id)]
235
+ )
236
+ self.assertTrue(new_line)
237
+ self.assertEqual(new_line.date_start, suspension_end + relativedelta(days=1))
238
+ self.assertFalse(new_line.date_end)
239
+ self.assertTrue(self.acct_line.manual_renew_needed)
240
+
241
+ def test_stop_plan_successor_contract_line_4(self):
242
+ """
243
+ * contract line start and end's in the suspension period
244
+ -> apply delay
245
+ - delay: suspension.date_end - contract_line.end_date
246
+ """
247
+ suspension_start = self.today + relativedelta(months=2)
248
+ suspension_end = self.today + relativedelta(months=5)
249
+ start_date = self.today + relativedelta(months=3)
250
+ end_date = self.today + relativedelta(months=4)
251
+ self.acct_line.write(
252
+ {
253
+ "date_start": start_date,
254
+ "recurring_next_date": start_date,
255
+ "date_end": end_date,
256
+ }
257
+ )
258
+ self.acct_line.stop_plan_successor(suspension_start, suspension_end, True)
259
+ self.assertEqual(
260
+ self.acct_line.date_start,
261
+ start_date + (suspension_end - start_date) + timedelta(days=1),
262
+ )
263
+ self.assertEqual(
264
+ self.acct_line.date_end,
265
+ end_date + (suspension_end - start_date) + timedelta(days=1),
266
+ )
267
+ new_line = self.env["contract.line"].search(
268
+ [("predecessor_contract_line_id", "=", self.acct_line.id)]
269
+ )
270
+ self.assertFalse(new_line)
271
+
272
+ def test_stop_plan_successor_contract_line_5(self):
273
+ """
274
+ * contract line start in the suspension period and end after it
275
+ -> apply delay
276
+ - delay: suspension.date_end - contract_line.date_start
277
+ """
278
+ suspension_start = self.today + relativedelta(months=2)
279
+ suspension_end = self.today + relativedelta(months=5)
280
+ start_date = self.today + relativedelta(months=3)
281
+ end_date = self.today + relativedelta(months=6)
282
+ self.acct_line.write(
283
+ {
284
+ "date_start": start_date,
285
+ "recurring_next_date": start_date,
286
+ "date_end": end_date,
287
+ }
288
+ )
289
+ self.acct_line.stop_plan_successor(suspension_start, suspension_end, True)
290
+ self.assertEqual(
291
+ self.acct_line.date_start,
292
+ start_date + (suspension_end - start_date) + timedelta(days=1),
293
+ )
294
+ self.assertEqual(
295
+ self.acct_line.date_end,
296
+ end_date + (suspension_end - start_date) + timedelta(days=1),
297
+ )
298
+ new_line = self.env["contract.line"].search(
299
+ [("predecessor_contract_line_id", "=", self.acct_line.id)]
300
+ )
301
+ self.assertFalse(new_line)
302
+
303
+ def test_stop_plan_successor_contract_line_5_without_date_end(self):
304
+ """
305
+ * contract line start in the suspension period and end after it
306
+ -> apply delay
307
+ - delay: suspension.date_end - contract_line.date_start
308
+ """
309
+ suspension_start = self.today + relativedelta(months=2)
310
+ suspension_end = self.today + relativedelta(months=5)
311
+ start_date = self.today + relativedelta(months=3)
312
+ end_date = False
313
+ self.acct_line.write(
314
+ {
315
+ "date_start": start_date,
316
+ "recurring_next_date": start_date,
317
+ "date_end": end_date,
318
+ "is_auto_renew": False,
319
+ }
320
+ )
321
+ self.acct_line.stop_plan_successor(suspension_start, suspension_end, True)
322
+ self.assertEqual(
323
+ self.acct_line.date_start,
324
+ start_date + (suspension_end - start_date) + timedelta(days=1),
325
+ )
326
+ self.assertFalse(self.acct_line.date_end)
327
+ new_line = self.env["contract.line"].search(
328
+ [("predecessor_contract_line_id", "=", self.acct_line.id)]
329
+ )
330
+ self.assertFalse(new_line)
331
+
332
+ def test_stop_plan_successor_contract_line_6(self):
333
+ """
334
+ * contract line start and end after the suspension period
335
+ -> apply delay
336
+ - delay: suspension.date_end - suspension.start_date
337
+ """
338
+ suspension_start = self.today + relativedelta(months=2)
339
+ suspension_end = self.today + relativedelta(months=3)
340
+ start_date = self.today + relativedelta(months=4)
341
+ end_date = self.today + relativedelta(months=6)
342
+ self.acct_line.write(
343
+ {
344
+ "date_start": start_date,
345
+ "recurring_next_date": start_date,
346
+ "date_end": end_date,
347
+ }
348
+ )
349
+ self.acct_line.stop_plan_successor(suspension_start, suspension_end, True)
350
+ self.assertEqual(
351
+ self.acct_line.date_start,
352
+ start_date + (suspension_end - suspension_start) + timedelta(days=1),
353
+ )
354
+ self.assertEqual(
355
+ self.acct_line.date_end,
356
+ end_date + (suspension_end - suspension_start) + timedelta(days=1),
357
+ )
358
+ new_line = self.env["contract.line"].search(
359
+ [("predecessor_contract_line_id", "=", self.acct_line.id)]
360
+ )
361
+ self.assertFalse(new_line)
362
+
363
+ def test_stop_plan_successor_contract_line_6_without_date_end(self):
364
+ """
365
+ * contract line start and end after the suspension period
366
+ -> apply delay
367
+ - delay: suspension.date_end - suspension.start_date
368
+ """
369
+ suspension_start = self.today + relativedelta(months=2)
370
+ suspension_end = self.today + relativedelta(months=3)
371
+ start_date = self.today + relativedelta(months=4)
372
+ end_date = False
373
+ self.acct_line.write(
374
+ {
375
+ "date_start": start_date,
376
+ "recurring_next_date": start_date,
377
+ "date_end": end_date,
378
+ "is_auto_renew": False,
379
+ }
380
+ )
381
+ self.acct_line.stop_plan_successor(suspension_start, suspension_end, True)
382
+ self.assertEqual(
383
+ self.acct_line.date_start,
384
+ start_date + (suspension_end - suspension_start) + timedelta(days=1),
385
+ )
386
+ self.assertFalse(self.acct_line.date_end)
387
+ new_line = self.env["contract.line"].search(
388
+ [("predecessor_contract_line_id", "=", self.acct_line.id)]
389
+ )
390
+ self.assertFalse(new_line)
391
+
392
+ def test_stop_plan_successor_wizard(self):
393
+ suspension_start = self.today + relativedelta(months=2)
394
+ suspension_end = self.today + relativedelta(months=3)
395
+ start_date = self.today + relativedelta(months=4)
396
+ end_date = self.today + relativedelta(months=6)
397
+ self.acct_line.write(
398
+ {
399
+ "date_start": start_date,
400
+ "recurring_next_date": start_date,
401
+ "date_end": end_date,
402
+ }
403
+ )
404
+ wizard = self.env["contract.line.wizard"].create(
405
+ {
406
+ "date_start": suspension_start,
407
+ "date_end": suspension_end,
408
+ "is_auto_renew": False,
409
+ "contract_line_id": self.acct_line.id,
410
+ }
411
+ )
412
+ wizard.stop_plan_successor()
413
+ self.assertEqual(
414
+ self.acct_line.date_start,
415
+ start_date + (suspension_end - suspension_start) + timedelta(days=1),
416
+ )
417
+ self.assertEqual(
418
+ self.acct_line.date_end,
419
+ end_date + (suspension_end - suspension_start) + timedelta(days=1),
420
+ )
421
+ new_line = self.env["contract.line"].search(
422
+ [("predecessor_contract_line_id", "=", self.acct_line.id)]
423
+ )
424
+ self.assertFalse(new_line)
425
+
426
+ def test_plan_successor_contract_line(self):
427
+ self.acct_line.write(
428
+ {
429
+ "date_start": self.today,
430
+ "recurring_next_date": self.today,
431
+ "date_end": self.today + relativedelta(months=3),
432
+ "is_auto_renew": False,
433
+ }
434
+ )
435
+ self.acct_line.plan_successor(
436
+ self.today + relativedelta(months=5),
437
+ self.today + relativedelta(months=7),
438
+ True,
439
+ )
440
+ new_line = self.env["contract.line"].search(
441
+ [("predecessor_contract_line_id", "=", self.acct_line.id)]
442
+ )
443
+ self.assertFalse(self.acct_line.is_auto_renew)
444
+ self.assertTrue(new_line.is_auto_renew)
445
+ self.assertTrue(new_line, "should create a new contract line")
446
+ self.assertEqual(new_line.date_start, self.today + relativedelta(months=5))
447
+ self.assertEqual(new_line.date_end, self.today + relativedelta(months=7))
448
+
449
+ def test_overlap(self):
450
+ self.acct_line.write(
451
+ {
452
+ "date_start": self.today,
453
+ "recurring_next_date": self.today,
454
+ "date_end": self.today + relativedelta(months=3),
455
+ "is_auto_renew": False,
456
+ }
457
+ )
458
+ self.acct_line.plan_successor(
459
+ self.today + relativedelta(months=5),
460
+ self.today + relativedelta(months=7),
461
+ True,
462
+ )
463
+ new_line = self.env["contract.line"].search(
464
+ [("predecessor_contract_line_id", "=", self.acct_line.id)]
465
+ )
466
+ with self.assertRaises(ValidationError):
467
+ new_line.date_start = self.today + relativedelta(months=2)
468
+ with self.assertRaises(ValidationError):
469
+ self.acct_line.date_end = self.today + relativedelta(months=6)
470
+
471
+ def test_plan_successor_wizard(self):
472
+ self.acct_line.write(
473
+ {
474
+ "date_start": self.today,
475
+ "recurring_next_date": self.today,
476
+ "date_end": self.today + relativedelta(months=2),
477
+ "is_auto_renew": False,
478
+ }
479
+ )
480
+ wizard = self.env["contract.line.wizard"].create(
481
+ {
482
+ "date_start": self.today + relativedelta(months=3),
483
+ "date_end": self.today + relativedelta(months=5),
484
+ "is_auto_renew": True,
485
+ "contract_line_id": self.acct_line.id,
486
+ }
487
+ )
488
+ wizard.plan_successor()
489
+ new_line = self.env["contract.line"].search(
490
+ [("predecessor_contract_line_id", "=", self.acct_line.id)]
491
+ )
492
+ self.assertFalse(self.acct_line.is_auto_renew)
493
+ self.assertTrue(new_line.is_auto_renew)
494
+ self.assertTrue(new_line, "should create a new contract line")
495
+ self.assertEqual(new_line.date_start, self.today + relativedelta(months=3))
496
+ self.assertEqual(new_line.date_end, self.today + relativedelta(months=5))
497
+
498
+ def test_cancel(self):
499
+ self.acct_line.write(
500
+ {"date_end": self.today + relativedelta(months=5), "is_auto_renew": True}
501
+ )
502
+ self.acct_line.cancel()
503
+ self.assertTrue(self.acct_line.is_canceled)
504
+ self.assertFalse(self.acct_line.is_auto_renew)
505
+ with self.assertRaises(ValidationError):
506
+ self.acct_line.is_auto_renew = True
507
+ self.acct_line.uncancel(self.today)
508
+ self.assertFalse(self.acct_line.is_canceled)
509
+
510
+ def test_uncancel_wizard(self):
511
+ self.acct_line.cancel()
512
+ self.assertTrue(self.acct_line.is_canceled)
513
+ wizard = self.env["contract.line.wizard"].create(
514
+ {"recurring_next_date": self.today, "contract_line_id": self.acct_line.id}
515
+ )
516
+ wizard.uncancel()
517
+ self.assertFalse(self.acct_line.is_canceled)
518
+
519
+ def test_cancel_uncancel_with_predecessor(self):
520
+ suspension_start = self.today + relativedelta(months=3)
521
+ suspension_end = self.today + relativedelta(months=5)
522
+ start_date = self.today
523
+ end_date = self.today + relativedelta(months=4)
524
+ self.acct_line.write(
525
+ {
526
+ "date_start": start_date,
527
+ "recurring_next_date": start_date,
528
+ "date_end": end_date,
529
+ }
530
+ )
531
+ self.acct_line.stop_plan_successor(suspension_start, suspension_end, True)
532
+ self.assertEqual(
533
+ self.acct_line.date_end, suspension_start - relativedelta(days=1)
534
+ )
535
+ new_line = self.env["contract.line"].search(
536
+ [("predecessor_contract_line_id", "=", self.acct_line.id)]
537
+ )
538
+ self.assertEqual(self.acct_line.successor_contract_line_id, new_line)
539
+ new_line.cancel()
540
+ self.assertTrue(new_line.is_canceled)
541
+ self.assertFalse(self.acct_line.successor_contract_line_id)
542
+ self.assertEqual(new_line.predecessor_contract_line_id, self.acct_line)
543
+ new_line.uncancel(suspension_end + relativedelta(days=1))
544
+ self.assertFalse(new_line.is_canceled)
545
+ self.assertEqual(self.acct_line.successor_contract_line_id, new_line)
546
+ self.assertEqual(
547
+ new_line.recurring_next_date,
548
+ suspension_end + relativedelta(days=1),
549
+ )
550
+
551
+ def test_cancel_uncancel_with_predecessor_has_successor(self):
552
+ suspension_start = self.today + relativedelta(months=6)
553
+ suspension_end = self.today + relativedelta(months=7)
554
+ start_date = self.today
555
+ end_date = self.today + relativedelta(months=8)
556
+ self.acct_line.write(
557
+ {
558
+ "date_start": start_date,
559
+ "recurring_next_date": start_date,
560
+ "date_end": end_date,
561
+ }
562
+ )
563
+ self.acct_line.stop_plan_successor(suspension_start, suspension_end, True)
564
+ new_line = self.env["contract.line"].search(
565
+ [("predecessor_contract_line_id", "=", self.acct_line.id)]
566
+ )
567
+ new_line.cancel()
568
+ suspension_start = self.today + relativedelta(months=4)
569
+ suspension_end = self.today + relativedelta(months=5)
570
+ self.acct_line.stop_plan_successor(suspension_start, suspension_end, True)
571
+ with self.assertRaises(ValidationError):
572
+ new_line.uncancel(suspension_end)
573
+
574
+ def test_check_has_not_date_end_has_successor(self):
575
+ self.acct_line.write({"date_end": False, "is_auto_renew": False})
576
+ with self.assertRaises(ValidationError):
577
+ self.acct_line.plan_successor(
578
+ to_date("2016-03-01"), to_date("2016-09-01"), False
579
+ )
580
+
581
+ def test_check_has_not_date_end_is_auto_renew(self):
582
+ with self.assertRaises(ValidationError):
583
+ self.acct_line.write({"date_end": False, "is_auto_renew": True})
584
+
585
+ def test_check_has_successor_is_auto_renew(self):
586
+ with self.assertRaises(ValidationError):
587
+ self.acct_line.plan_successor(
588
+ to_date("2016-03-01"), to_date("2018-09-01"), False
589
+ )
590
+
591
+ def test_search_contract_line_to_renew(self):
592
+ self.acct_line.write({"date_end": self.today, "is_auto_renew": True})
593
+ line_1 = self.acct_line.copy({"date_end": self.today + relativedelta(months=1)})
594
+ line_2 = self.acct_line.copy({"date_end": self.today - relativedelta(months=1)})
595
+ line_3 = self.acct_line.copy({"date_end": self.today - relativedelta(months=2)})
596
+ line_4 = self.acct_line.copy({"date_end": self.today + relativedelta(months=2)})
597
+ to_renew = self.acct_line.search(
598
+ self.acct_line._contract_line_to_renew_domain()
599
+ )
600
+ self.assertEqual(set(to_renew), {self.acct_line, line_1, line_2, line_3})
601
+ self.acct_line.cron_renew_contract_line()
602
+ self.assertTrue(self.acct_line.successor_contract_line_id)
603
+ self.assertTrue(line_1.successor_contract_line_id)
604
+ self.assertTrue(line_2.successor_contract_line_id)
605
+ self.assertTrue(line_3.successor_contract_line_id)
606
+ self.assertFalse(line_4.successor_contract_line_id)
607
+
608
+ def test_renew_create_new_line(self):
609
+ date_start = self.today - relativedelta(months=9)
610
+ date_end = date_start + relativedelta(months=12) - relativedelta(days=1)
611
+ self.acct_line.write(
612
+ {
613
+ "is_auto_renew": True,
614
+ "date_start": date_start,
615
+ "recurring_next_date": date_start,
616
+ "date_end": self.today,
617
+ }
618
+ )
619
+ self.acct_line._onchange_is_auto_renew()
620
+ self.assertEqual(self.acct_line.date_end, date_end)
621
+ new_line = self.acct_line.renew()
622
+ self.assertFalse(self.acct_line.is_auto_renew)
623
+ self.assertTrue(new_line.is_auto_renew)
624
+ self.assertEqual(new_line.date_start, date_start + relativedelta(months=12))
625
+ self.assertEqual(new_line.date_end, date_end + relativedelta(months=12))
626
+
627
+ def test_renew_extend_original_line(self):
628
+ self.contract.company_id.create_new_line_at_contract_line_renew = False
629
+ date_start = self.today - relativedelta(months=9)
630
+ date_end = date_start + relativedelta(months=12) - relativedelta(days=1)
631
+ self.acct_line.write(
632
+ {
633
+ "is_auto_renew": True,
634
+ "date_start": date_start,
635
+ "recurring_next_date": date_start,
636
+ "date_end": self.today,
637
+ }
638
+ )
639
+ self.acct_line._onchange_is_auto_renew()
640
+ self.assertEqual(self.acct_line.date_end, date_end)
641
+ self.acct_line.renew()
642
+ self.assertTrue(self.acct_line.is_auto_renew)
643
+ self.assertEqual(self.acct_line.date_start, date_start)
644
+ self.assertEqual(self.acct_line.date_end, date_end + relativedelta(months=12))
645
+
646
+ def test_unlink(self):
647
+ with self.assertRaises(ValidationError):
648
+ self.acct_line.unlink()
649
+
650
+ def test_contract_line_state(self):
651
+ lines = self.env["contract.line"]
652
+ # upcoming
653
+ lines |= self.acct_line.copy(
654
+ {
655
+ "date_start": self.today + relativedelta(months=3),
656
+ "recurring_next_date": self.today + relativedelta(months=3),
657
+ "date_end": self.today + relativedelta(months=5),
658
+ }
659
+ )
660
+ # in-progress
661
+ lines |= self.acct_line.copy(
662
+ {
663
+ "date_start": self.today,
664
+ "recurring_next_date": self.today,
665
+ "date_end": self.today + relativedelta(months=5),
666
+ }
667
+ )
668
+ # in-progress
669
+ lines |= self.acct_line.copy(
670
+ {
671
+ "date_start": self.today,
672
+ "recurring_next_date": self.today,
673
+ "date_end": self.today + relativedelta(months=5),
674
+ "manual_renew_needed": True,
675
+ }
676
+ )
677
+ # to-renew
678
+ lines |= self.acct_line.copy(
679
+ {
680
+ "date_start": self.today - relativedelta(months=5),
681
+ "recurring_next_date": self.today - relativedelta(months=5),
682
+ "date_end": self.today - relativedelta(months=2),
683
+ "manual_renew_needed": True,
684
+ }
685
+ )
686
+ # upcoming-close
687
+ lines |= self.acct_line.copy(
688
+ {
689
+ "date_start": self.today - relativedelta(months=5),
690
+ "recurring_next_date": self.today - relativedelta(months=5),
691
+ "date_end": self.today + relativedelta(days=20),
692
+ "is_auto_renew": False,
693
+ }
694
+ )
695
+ # closed
696
+ lines |= self.acct_line.copy(
697
+ {
698
+ "date_start": self.today - relativedelta(months=5),
699
+ "recurring_next_date": self.today - relativedelta(months=5),
700
+ "date_end": self.today - relativedelta(months=2),
701
+ "is_auto_renew": False,
702
+ }
703
+ )
704
+ # canceled
705
+ lines |= self.acct_line.copy(
706
+ {
707
+ "date_start": self.today - relativedelta(months=5),
708
+ "recurring_next_date": self.today - relativedelta(months=5),
709
+ "date_end": self.today - relativedelta(months=2),
710
+ "is_canceled": True,
711
+ }
712
+ )
713
+ # section
714
+ lines |= self.env["contract.line"].create(
715
+ {
716
+ "contract_id": self.contract.id,
717
+ "display_type": "line_section",
718
+ "name": "Test section",
719
+ }
720
+ )
721
+ states = [
722
+ "upcoming",
723
+ "in-progress",
724
+ "to-renew",
725
+ "upcoming-close",
726
+ "closed",
727
+ "canceled",
728
+ False,
729
+ ]
730
+ self.assertEqual(set(lines.mapped("state")), set(states))
731
+ # Test search method
732
+ lines.flush_recordset() # Needed for computed stored fields
733
+ # like termination_notice_date
734
+ for state in states:
735
+ lines = self.env["contract.line"].search([("state", "=", state)])
736
+ self.assertTrue(lines, state)
737
+ self.assertTrue(state in lines.mapped("state"), state)
738
+ lines = self.env["contract.line"].search([("state", "!=", state)])
739
+ self.assertFalse(state in lines.mapped("state"), state)
740
+ lines = self.env["contract.line"].search([("state", "in", states)])
741
+ self.assertEqual(set(lines.mapped("state")), set(states))
742
+ lines = self.env["contract.line"].search([("state", "in", [])])
743
+ self.assertFalse(lines.mapped("state"))
744
+ with self.assertRaises(TypeError):
745
+ self.env["contract.line"].search([("state", "in", "upcoming")])
746
+ lines = self.env["contract.line"].search([("state", "not in", [])])
747
+ self.assertEqual(set(lines.mapped("state")), set(states))
748
+ lines = self.env["contract.line"].search([("state", "not in", states)])
749
+ self.assertFalse(lines.mapped("state"))
750
+ state2 = ["upcoming", "in-progress"]
751
+ lines = self.env["contract.line"].search([("state", "not in", state2)])
752
+ self.assertEqual(set(lines.mapped("state")), set(states) - set(state2))
753
+
754
+ def test_check_auto_renew_contract_line_with_successor(self):
755
+ """
756
+ A contract line with a successor can't be set to auto-renew
757
+ """
758
+ successor_contract_line = self.acct_line.copy()
759
+ with self.assertRaises(ValidationError):
760
+ self.acct_line.write(
761
+ {
762
+ "is_auto_renew": True,
763
+ "successor_contract_line_id": successor_contract_line.id,
764
+ }
765
+ )
766
+
767
+ def test_check_no_date_end_contract_line_with_successor(self):
768
+ """
769
+ A contract line with a successor must have a end date
770
+ """
771
+ successor_contract_line = self.acct_line.copy()
772
+ with self.assertRaises(ValidationError):
773
+ self.acct_line.write(
774
+ {
775
+ "date_end": False,
776
+ "successor_contract_line_id": successor_contract_line.id,
777
+ }
778
+ )
779
+
780
+ def test_delay_invoiced_contract_line(self):
781
+ self.acct_line.write(
782
+ {"last_date_invoiced": self.acct_line.date_start + relativedelta(days=1)}
783
+ )
784
+ with self.assertRaises(ValidationError):
785
+ self.acct_line._delay(relativedelta(months=1))
786
+
787
+ def test_cancel_invoiced_contract_line(self):
788
+ self.acct_line.write(
789
+ {"last_date_invoiced": self.acct_line.date_start + relativedelta(days=1)}
790
+ )
791
+ with self.assertRaises(ValidationError):
792
+ self.acct_line.cancel()
793
+
794
+ def test_action_uncancel(self):
795
+ action = self.acct_line.action_uncancel()
796
+ self.assertEqual(
797
+ action["context"]["default_contract_line_id"], self.acct_line.id
798
+ )
799
+
800
+ def test_action_plan_successor(self):
801
+ action = self.acct_line.action_plan_successor()
802
+ self.assertEqual(
803
+ action["context"]["default_contract_line_id"], self.acct_line.id
804
+ )
805
+
806
+ def test_action_stop(self):
807
+ action = self.acct_line.action_stop()
808
+ self.assertEqual(
809
+ action["context"]["default_contract_line_id"], self.acct_line.id
810
+ )
811
+
812
+ def test_action_stop_plan_successor(self):
813
+ action = self.acct_line.action_stop_plan_successor()
814
+ self.assertEqual(
815
+ action["context"]["default_contract_line_id"], self.acct_line.id
816
+ )
817
+
818
+ def test_stop_at_last_date_invoiced(self):
819
+ self.contract.recurring_create_invoice()
820
+ self.assertTrue(self.acct_line.recurring_next_date)
821
+ self.acct_line.stop(self.acct_line.last_date_invoiced)
822
+ self.assertFalse(self.acct_line.recurring_next_date)
823
+
824
+ def test_check_last_date_invoiced_before_next_invoice_date(self):
825
+ with self.assertRaises(ValidationError):
826
+ self.acct_line.write(
827
+ {
828
+ "date_start": "2019-01-01",
829
+ "date_end": "2019-12-01",
830
+ "recurring_next_date": "2019-01-01",
831
+ "last_date_invoiced": "2019-06-01",
832
+ }
833
+ )
834
+
835
+ def test_stop_and_update_recurring_invoice_date(self):
836
+ self.acct_line.write(
837
+ {
838
+ "date_start": "2019-01-01",
839
+ "date_end": "2019-12-31",
840
+ "recurring_next_date": "2020-01-01",
841
+ "recurring_invoicing_type": "post-paid",
842
+ "recurring_rule_type": "yearly",
843
+ }
844
+ )
845
+ self.acct_line.stop(to_date("2019-05-31"))
846
+ self.assertEqual(self.acct_line.date_end, to_date("2019-05-31"))
847
+ self.assertEqual(self.acct_line.recurring_next_date, to_date("2019-06-01"))