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.
- odoo/addons/contract_line_successor/README.rst +168 -0
- odoo/addons/contract_line_successor/__init__.py +2 -0
- odoo/addons/contract_line_successor/__manifest__.py +24 -0
- odoo/addons/contract_line_successor/data/contract_renew_cron.xml +12 -0
- odoo/addons/contract_line_successor/i18n/contract_line_successor.pot +532 -0
- odoo/addons/contract_line_successor/models/__init__.py +5 -0
- odoo/addons/contract_line_successor/models/contract_contract.py +25 -0
- odoo/addons/contract_line_successor/models/contract_line.py +835 -0
- odoo/addons/contract_line_successor/models/contract_line_constraints.py +429 -0
- odoo/addons/contract_line_successor/models/contract_template_line.py +35 -0
- odoo/addons/contract_line_successor/models/res_company.py +15 -0
- odoo/addons/contract_line_successor/models/res_config_settings.py +18 -0
- odoo/addons/contract_line_successor/readme/CONFIGURE.md +10 -0
- odoo/addons/contract_line_successor/readme/CONTRIBUTORS.md +1 -0
- odoo/addons/contract_line_successor/readme/DESCRIPTION.md +40 -0
- odoo/addons/contract_line_successor/readme/USAGE.md +6 -0
- odoo/addons/contract_line_successor/security/ir.model.access.csv +2 -0
- odoo/addons/contract_line_successor/static/description/icon.png +0 -0
- odoo/addons/contract_line_successor/static/description/index.html +523 -0
- odoo/addons/contract_line_successor/tests/__init__.py +1 -0
- odoo/addons/contract_line_successor/tests/test_contract.py +847 -0
- odoo/addons/contract_line_successor/views/contract_contract.xml +73 -0
- odoo/addons/contract_line_successor/views/contract_line.xml +134 -0
- odoo/addons/contract_line_successor/views/contract_template.xml +19 -0
- odoo/addons/contract_line_successor/views/contract_template_line.xml +50 -0
- odoo/addons/contract_line_successor/views/res_config_settings.xml +16 -0
- odoo/addons/contract_line_successor/wizards/__init__.py +1 -0
- odoo/addons/contract_line_successor/wizards/contract_line_wizard.py +53 -0
- odoo/addons/contract_line_successor/wizards/contract_line_wizard.xml +110 -0
- odoo_addon_contract_line_successor-18.0.1.0.0.2.dist-info/METADATA +185 -0
- odoo_addon_contract_line_successor-18.0.1.0.0.2.dist-info/RECORD +33 -0
- odoo_addon_contract_line_successor-18.0.1.0.0.2.dist-info/WHEEL +5 -0
- odoo_addon_contract_line_successor-18.0.1.0.0.2.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,429 @@
|
|
|
1
|
+
# Copyright 2018 ACSONE SA/NV.
|
|
2
|
+
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
|
3
|
+
|
|
4
|
+
import itertools
|
|
5
|
+
from collections import namedtuple
|
|
6
|
+
|
|
7
|
+
from odoo.fields import Date
|
|
8
|
+
|
|
9
|
+
Criteria = namedtuple(
|
|
10
|
+
"Criteria",
|
|
11
|
+
[
|
|
12
|
+
"when", # Contract line relatively to today (BEFORE, IN, AFTER)
|
|
13
|
+
"has_date_end", # Is date_end set on contract line (bool)
|
|
14
|
+
"has_last_date_invoiced", # Is last_date_invoiced set on contract line
|
|
15
|
+
"is_auto_renew", # Is is_auto_renew set on contract line (bool)
|
|
16
|
+
"has_successor", # Is contract line has_successor (bool)
|
|
17
|
+
"predecessor_has_successor",
|
|
18
|
+
# Is contract line predecessor has successor (bool)
|
|
19
|
+
# In almost of the cases
|
|
20
|
+
# contract_line.predecessor.successor == contract_line
|
|
21
|
+
# But at cancel action,
|
|
22
|
+
# contract_line.predecessor.successor == False
|
|
23
|
+
# This is to permit plan_successor on predecessor
|
|
24
|
+
# If contract_line.predecessor.successor != False
|
|
25
|
+
# and contract_line is canceled, we don't allow uncancel
|
|
26
|
+
# else we re-link contract_line and its predecessor
|
|
27
|
+
"canceled", # Is contract line canceled (bool)
|
|
28
|
+
],
|
|
29
|
+
)
|
|
30
|
+
Allowed = namedtuple(
|
|
31
|
+
"Allowed",
|
|
32
|
+
["plan_successor", "stop_plan_successor", "stop", "cancel", "uncancel"],
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def _expand_none(criteria):
|
|
37
|
+
variations = []
|
|
38
|
+
for attribute, value in criteria._asdict().items():
|
|
39
|
+
if value is None:
|
|
40
|
+
if attribute == "when":
|
|
41
|
+
variations.append(["BEFORE", "IN", "AFTER"])
|
|
42
|
+
else:
|
|
43
|
+
variations.append([True, False])
|
|
44
|
+
else:
|
|
45
|
+
variations.append([value])
|
|
46
|
+
return itertools.product(*variations)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def _add(matrix, criteria, allowed):
|
|
50
|
+
"""Expand None values to True/False combination"""
|
|
51
|
+
for c in _expand_none(criteria):
|
|
52
|
+
matrix[c] = allowed
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
CRITERIA_ALLOWED_DICT = {
|
|
56
|
+
Criteria(
|
|
57
|
+
when="BEFORE",
|
|
58
|
+
has_date_end=True,
|
|
59
|
+
has_last_date_invoiced=False,
|
|
60
|
+
is_auto_renew=True,
|
|
61
|
+
has_successor=False,
|
|
62
|
+
predecessor_has_successor=None,
|
|
63
|
+
canceled=False,
|
|
64
|
+
): Allowed(
|
|
65
|
+
plan_successor=False,
|
|
66
|
+
stop_plan_successor=True,
|
|
67
|
+
stop=True,
|
|
68
|
+
cancel=True,
|
|
69
|
+
uncancel=False,
|
|
70
|
+
),
|
|
71
|
+
Criteria(
|
|
72
|
+
when="BEFORE",
|
|
73
|
+
has_date_end=True,
|
|
74
|
+
has_last_date_invoiced=False,
|
|
75
|
+
is_auto_renew=False,
|
|
76
|
+
has_successor=True,
|
|
77
|
+
predecessor_has_successor=None,
|
|
78
|
+
canceled=False,
|
|
79
|
+
): Allowed(
|
|
80
|
+
plan_successor=False,
|
|
81
|
+
stop_plan_successor=False,
|
|
82
|
+
stop=True,
|
|
83
|
+
cancel=True,
|
|
84
|
+
uncancel=False,
|
|
85
|
+
),
|
|
86
|
+
Criteria(
|
|
87
|
+
when="BEFORE",
|
|
88
|
+
has_date_end=True,
|
|
89
|
+
has_last_date_invoiced=False,
|
|
90
|
+
is_auto_renew=False,
|
|
91
|
+
has_successor=False,
|
|
92
|
+
predecessor_has_successor=None,
|
|
93
|
+
canceled=False,
|
|
94
|
+
): Allowed(
|
|
95
|
+
plan_successor=True,
|
|
96
|
+
stop_plan_successor=True,
|
|
97
|
+
stop=True,
|
|
98
|
+
cancel=True,
|
|
99
|
+
uncancel=False,
|
|
100
|
+
),
|
|
101
|
+
Criteria(
|
|
102
|
+
when="BEFORE",
|
|
103
|
+
has_date_end=False,
|
|
104
|
+
has_last_date_invoiced=False,
|
|
105
|
+
is_auto_renew=False,
|
|
106
|
+
has_successor=False,
|
|
107
|
+
predecessor_has_successor=None,
|
|
108
|
+
canceled=False,
|
|
109
|
+
): Allowed(
|
|
110
|
+
plan_successor=False,
|
|
111
|
+
stop_plan_successor=True,
|
|
112
|
+
stop=True,
|
|
113
|
+
cancel=True,
|
|
114
|
+
uncancel=False,
|
|
115
|
+
),
|
|
116
|
+
Criteria(
|
|
117
|
+
when="IN",
|
|
118
|
+
has_date_end=True,
|
|
119
|
+
has_last_date_invoiced=False,
|
|
120
|
+
is_auto_renew=True,
|
|
121
|
+
has_successor=False,
|
|
122
|
+
predecessor_has_successor=None,
|
|
123
|
+
canceled=False,
|
|
124
|
+
): Allowed(
|
|
125
|
+
plan_successor=False,
|
|
126
|
+
stop_plan_successor=True,
|
|
127
|
+
stop=True,
|
|
128
|
+
cancel=True,
|
|
129
|
+
uncancel=False,
|
|
130
|
+
),
|
|
131
|
+
Criteria(
|
|
132
|
+
when="IN",
|
|
133
|
+
has_date_end=True,
|
|
134
|
+
has_last_date_invoiced=False,
|
|
135
|
+
is_auto_renew=False,
|
|
136
|
+
has_successor=True,
|
|
137
|
+
predecessor_has_successor=None,
|
|
138
|
+
canceled=False,
|
|
139
|
+
): Allowed(
|
|
140
|
+
plan_successor=False,
|
|
141
|
+
stop_plan_successor=False,
|
|
142
|
+
stop=True,
|
|
143
|
+
cancel=True,
|
|
144
|
+
uncancel=False,
|
|
145
|
+
),
|
|
146
|
+
Criteria(
|
|
147
|
+
when="IN",
|
|
148
|
+
has_date_end=True,
|
|
149
|
+
has_last_date_invoiced=False,
|
|
150
|
+
is_auto_renew=False,
|
|
151
|
+
has_successor=False,
|
|
152
|
+
predecessor_has_successor=None,
|
|
153
|
+
canceled=False,
|
|
154
|
+
): Allowed(
|
|
155
|
+
plan_successor=True,
|
|
156
|
+
stop_plan_successor=True,
|
|
157
|
+
stop=True,
|
|
158
|
+
cancel=True,
|
|
159
|
+
uncancel=False,
|
|
160
|
+
),
|
|
161
|
+
Criteria(
|
|
162
|
+
when="IN",
|
|
163
|
+
has_date_end=False,
|
|
164
|
+
has_last_date_invoiced=False,
|
|
165
|
+
is_auto_renew=False,
|
|
166
|
+
has_successor=False,
|
|
167
|
+
predecessor_has_successor=None,
|
|
168
|
+
canceled=False,
|
|
169
|
+
): Allowed(
|
|
170
|
+
plan_successor=False,
|
|
171
|
+
stop_plan_successor=True,
|
|
172
|
+
stop=True,
|
|
173
|
+
cancel=True,
|
|
174
|
+
uncancel=False,
|
|
175
|
+
),
|
|
176
|
+
Criteria(
|
|
177
|
+
when="BEFORE",
|
|
178
|
+
has_date_end=True,
|
|
179
|
+
has_last_date_invoiced=True,
|
|
180
|
+
is_auto_renew=True,
|
|
181
|
+
has_successor=False,
|
|
182
|
+
predecessor_has_successor=None,
|
|
183
|
+
canceled=False,
|
|
184
|
+
): Allowed(
|
|
185
|
+
plan_successor=False,
|
|
186
|
+
stop_plan_successor=True,
|
|
187
|
+
stop=True,
|
|
188
|
+
cancel=False,
|
|
189
|
+
uncancel=False,
|
|
190
|
+
),
|
|
191
|
+
Criteria(
|
|
192
|
+
when="BEFORE",
|
|
193
|
+
has_date_end=True,
|
|
194
|
+
has_last_date_invoiced=True,
|
|
195
|
+
is_auto_renew=False,
|
|
196
|
+
has_successor=True,
|
|
197
|
+
predecessor_has_successor=None,
|
|
198
|
+
canceled=False,
|
|
199
|
+
): Allowed(
|
|
200
|
+
plan_successor=False,
|
|
201
|
+
stop_plan_successor=False,
|
|
202
|
+
stop=True,
|
|
203
|
+
cancel=False,
|
|
204
|
+
uncancel=False,
|
|
205
|
+
),
|
|
206
|
+
Criteria(
|
|
207
|
+
when="BEFORE",
|
|
208
|
+
has_date_end=True,
|
|
209
|
+
has_last_date_invoiced=True,
|
|
210
|
+
is_auto_renew=False,
|
|
211
|
+
has_successor=False,
|
|
212
|
+
predecessor_has_successor=None,
|
|
213
|
+
canceled=False,
|
|
214
|
+
): Allowed(
|
|
215
|
+
plan_successor=True,
|
|
216
|
+
stop_plan_successor=True,
|
|
217
|
+
stop=True,
|
|
218
|
+
cancel=False,
|
|
219
|
+
uncancel=False,
|
|
220
|
+
),
|
|
221
|
+
Criteria(
|
|
222
|
+
when="BEFORE",
|
|
223
|
+
has_date_end=False,
|
|
224
|
+
has_last_date_invoiced=True,
|
|
225
|
+
is_auto_renew=False,
|
|
226
|
+
has_successor=False,
|
|
227
|
+
predecessor_has_successor=None,
|
|
228
|
+
canceled=False,
|
|
229
|
+
): Allowed(
|
|
230
|
+
plan_successor=False,
|
|
231
|
+
stop_plan_successor=True,
|
|
232
|
+
stop=True,
|
|
233
|
+
cancel=False,
|
|
234
|
+
uncancel=False,
|
|
235
|
+
),
|
|
236
|
+
Criteria(
|
|
237
|
+
when="IN",
|
|
238
|
+
has_date_end=True,
|
|
239
|
+
has_last_date_invoiced=True,
|
|
240
|
+
is_auto_renew=True,
|
|
241
|
+
has_successor=False,
|
|
242
|
+
predecessor_has_successor=None,
|
|
243
|
+
canceled=False,
|
|
244
|
+
): Allowed(
|
|
245
|
+
plan_successor=False,
|
|
246
|
+
stop_plan_successor=True,
|
|
247
|
+
stop=True,
|
|
248
|
+
cancel=False,
|
|
249
|
+
uncancel=False,
|
|
250
|
+
),
|
|
251
|
+
Criteria(
|
|
252
|
+
when="IN",
|
|
253
|
+
has_date_end=True,
|
|
254
|
+
has_last_date_invoiced=True,
|
|
255
|
+
is_auto_renew=False,
|
|
256
|
+
has_successor=True,
|
|
257
|
+
predecessor_has_successor=None,
|
|
258
|
+
canceled=False,
|
|
259
|
+
): Allowed(
|
|
260
|
+
plan_successor=False,
|
|
261
|
+
stop_plan_successor=False,
|
|
262
|
+
stop=True,
|
|
263
|
+
cancel=False,
|
|
264
|
+
uncancel=False,
|
|
265
|
+
),
|
|
266
|
+
Criteria(
|
|
267
|
+
when="IN",
|
|
268
|
+
has_date_end=True,
|
|
269
|
+
has_last_date_invoiced=True,
|
|
270
|
+
is_auto_renew=False,
|
|
271
|
+
has_successor=False,
|
|
272
|
+
predecessor_has_successor=None,
|
|
273
|
+
canceled=False,
|
|
274
|
+
): Allowed(
|
|
275
|
+
plan_successor=True,
|
|
276
|
+
stop_plan_successor=True,
|
|
277
|
+
stop=True,
|
|
278
|
+
cancel=False,
|
|
279
|
+
uncancel=False,
|
|
280
|
+
),
|
|
281
|
+
Criteria(
|
|
282
|
+
when="IN",
|
|
283
|
+
has_date_end=False,
|
|
284
|
+
has_last_date_invoiced=True,
|
|
285
|
+
is_auto_renew=False,
|
|
286
|
+
has_successor=False,
|
|
287
|
+
predecessor_has_successor=None,
|
|
288
|
+
canceled=False,
|
|
289
|
+
): Allowed(
|
|
290
|
+
plan_successor=False,
|
|
291
|
+
stop_plan_successor=True,
|
|
292
|
+
stop=True,
|
|
293
|
+
cancel=False,
|
|
294
|
+
uncancel=False,
|
|
295
|
+
),
|
|
296
|
+
Criteria(
|
|
297
|
+
when="AFTER",
|
|
298
|
+
has_date_end=True,
|
|
299
|
+
has_last_date_invoiced=None,
|
|
300
|
+
is_auto_renew=True,
|
|
301
|
+
has_successor=False,
|
|
302
|
+
predecessor_has_successor=None,
|
|
303
|
+
canceled=False,
|
|
304
|
+
): Allowed(
|
|
305
|
+
plan_successor=False,
|
|
306
|
+
stop_plan_successor=False,
|
|
307
|
+
stop=True,
|
|
308
|
+
cancel=False,
|
|
309
|
+
uncancel=False,
|
|
310
|
+
),
|
|
311
|
+
Criteria(
|
|
312
|
+
when="AFTER",
|
|
313
|
+
has_date_end=True,
|
|
314
|
+
has_last_date_invoiced=None,
|
|
315
|
+
is_auto_renew=False,
|
|
316
|
+
has_successor=True,
|
|
317
|
+
predecessor_has_successor=None,
|
|
318
|
+
canceled=False,
|
|
319
|
+
): Allowed(
|
|
320
|
+
plan_successor=False,
|
|
321
|
+
stop_plan_successor=False,
|
|
322
|
+
stop=False,
|
|
323
|
+
cancel=False,
|
|
324
|
+
uncancel=False,
|
|
325
|
+
),
|
|
326
|
+
Criteria(
|
|
327
|
+
when="AFTER",
|
|
328
|
+
has_date_end=True,
|
|
329
|
+
has_last_date_invoiced=None,
|
|
330
|
+
is_auto_renew=False,
|
|
331
|
+
has_successor=False,
|
|
332
|
+
predecessor_has_successor=None,
|
|
333
|
+
canceled=False,
|
|
334
|
+
): Allowed(
|
|
335
|
+
plan_successor=True,
|
|
336
|
+
stop_plan_successor=False,
|
|
337
|
+
stop=True,
|
|
338
|
+
cancel=False,
|
|
339
|
+
uncancel=False,
|
|
340
|
+
),
|
|
341
|
+
Criteria(
|
|
342
|
+
when=None,
|
|
343
|
+
has_date_end=None,
|
|
344
|
+
has_last_date_invoiced=None,
|
|
345
|
+
is_auto_renew=None,
|
|
346
|
+
has_successor=None,
|
|
347
|
+
predecessor_has_successor=False,
|
|
348
|
+
canceled=True,
|
|
349
|
+
): Allowed(
|
|
350
|
+
plan_successor=False,
|
|
351
|
+
stop_plan_successor=False,
|
|
352
|
+
stop=False,
|
|
353
|
+
cancel=False,
|
|
354
|
+
uncancel=True,
|
|
355
|
+
),
|
|
356
|
+
Criteria(
|
|
357
|
+
when=None,
|
|
358
|
+
has_date_end=None,
|
|
359
|
+
has_last_date_invoiced=None,
|
|
360
|
+
is_auto_renew=None,
|
|
361
|
+
has_successor=None,
|
|
362
|
+
predecessor_has_successor=True,
|
|
363
|
+
canceled=True,
|
|
364
|
+
): Allowed(
|
|
365
|
+
plan_successor=False,
|
|
366
|
+
stop_plan_successor=False,
|
|
367
|
+
stop=False,
|
|
368
|
+
cancel=False,
|
|
369
|
+
uncancel=False,
|
|
370
|
+
),
|
|
371
|
+
}
|
|
372
|
+
criteria_allowed_dict = {}
|
|
373
|
+
|
|
374
|
+
for c in CRITERIA_ALLOWED_DICT:
|
|
375
|
+
_add(criteria_allowed_dict, c, CRITERIA_ALLOWED_DICT[c])
|
|
376
|
+
|
|
377
|
+
|
|
378
|
+
def compute_when(date_start, date_end):
|
|
379
|
+
today = Date.today()
|
|
380
|
+
if today < date_start:
|
|
381
|
+
return "BEFORE"
|
|
382
|
+
if date_end and today > date_end:
|
|
383
|
+
return "AFTER"
|
|
384
|
+
return "IN"
|
|
385
|
+
|
|
386
|
+
|
|
387
|
+
def compute_criteria(
|
|
388
|
+
date_start,
|
|
389
|
+
date_end,
|
|
390
|
+
has_last_date_invoiced,
|
|
391
|
+
is_auto_renew,
|
|
392
|
+
successor_contract_line_id,
|
|
393
|
+
predecessor_contract_line_id,
|
|
394
|
+
is_canceled,
|
|
395
|
+
):
|
|
396
|
+
return Criteria(
|
|
397
|
+
when=compute_when(date_start, date_end),
|
|
398
|
+
has_date_end=bool(date_end),
|
|
399
|
+
has_last_date_invoiced=bool(has_last_date_invoiced),
|
|
400
|
+
is_auto_renew=is_auto_renew,
|
|
401
|
+
has_successor=bool(successor_contract_line_id),
|
|
402
|
+
predecessor_has_successor=bool(
|
|
403
|
+
predecessor_contract_line_id.successor_contract_line_id
|
|
404
|
+
),
|
|
405
|
+
canceled=is_canceled,
|
|
406
|
+
)
|
|
407
|
+
|
|
408
|
+
|
|
409
|
+
def get_allowed(
|
|
410
|
+
date_start,
|
|
411
|
+
date_end,
|
|
412
|
+
has_last_date_invoiced,
|
|
413
|
+
is_auto_renew,
|
|
414
|
+
successor_contract_line_id,
|
|
415
|
+
predecessor_contract_line_id,
|
|
416
|
+
is_canceled,
|
|
417
|
+
):
|
|
418
|
+
criteria = compute_criteria(
|
|
419
|
+
date_start,
|
|
420
|
+
date_end,
|
|
421
|
+
has_last_date_invoiced,
|
|
422
|
+
is_auto_renew,
|
|
423
|
+
successor_contract_line_id,
|
|
424
|
+
predecessor_contract_line_id,
|
|
425
|
+
is_canceled,
|
|
426
|
+
)
|
|
427
|
+
if criteria in criteria_allowed_dict:
|
|
428
|
+
return criteria_allowed_dict[criteria]
|
|
429
|
+
return False
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# Copyright 2025 ACSONE SA/NV
|
|
2
|
+
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
|
|
3
|
+
|
|
4
|
+
from odoo import fields, models
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class ContractTemplateLine(models.Model):
|
|
8
|
+
_inherit = "contract.template.line"
|
|
9
|
+
|
|
10
|
+
is_auto_renew = fields.Boolean(string="Auto Renew", default=False)
|
|
11
|
+
auto_renew_interval = fields.Integer(
|
|
12
|
+
string="Renew Every",
|
|
13
|
+
default=1,
|
|
14
|
+
help="Renew every (Days/Weeks/Months/Years)",
|
|
15
|
+
)
|
|
16
|
+
auto_renew_rule_type = fields.Selection(
|
|
17
|
+
[
|
|
18
|
+
("daily", "Day(s)"),
|
|
19
|
+
("weekly", "Week(s)"),
|
|
20
|
+
("monthly", "Month(s)"),
|
|
21
|
+
("yearly", "Year(s)"),
|
|
22
|
+
],
|
|
23
|
+
default="yearly",
|
|
24
|
+
string="Renewal type",
|
|
25
|
+
help="Specify interval for automatic renewal.",
|
|
26
|
+
)
|
|
27
|
+
termination_notice_interval = fields.Integer(
|
|
28
|
+
default=1,
|
|
29
|
+
string="Termination Notice Before",
|
|
30
|
+
)
|
|
31
|
+
termination_notice_rule_type = fields.Selection(
|
|
32
|
+
[("daily", "Day(s)"), ("weekly", "Week(s)"), ("monthly", "Month(s)")],
|
|
33
|
+
default="monthly",
|
|
34
|
+
string="Termination Notice type",
|
|
35
|
+
)
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# Copyright 2019 ACSONE SA/NV
|
|
2
|
+
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
|
3
|
+
|
|
4
|
+
from odoo import fields, models
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class ResCompany(models.Model):
|
|
8
|
+
_inherit = "res.company"
|
|
9
|
+
|
|
10
|
+
create_new_line_at_contract_line_renew = fields.Boolean(
|
|
11
|
+
help="If checked, a new line will be generated at contract line renew "
|
|
12
|
+
"and linked to the original one as successor. The default "
|
|
13
|
+
"behavior is to extend the end date of the contract by a new "
|
|
14
|
+
"subscription period",
|
|
15
|
+
)
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# Copyright 2019 ACSONE SA/NV
|
|
2
|
+
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
|
3
|
+
|
|
4
|
+
from odoo import fields, models
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class ResConfigSettings(models.TransientModel):
|
|
8
|
+
_inherit = "res.config.settings"
|
|
9
|
+
|
|
10
|
+
create_new_line_at_contract_line_renew = fields.Boolean(
|
|
11
|
+
related="company_id.create_new_line_at_contract_line_renew",
|
|
12
|
+
readonly=False,
|
|
13
|
+
string="Create New Line At Contract Line Renew",
|
|
14
|
+
help="If checked, a new line will be generated at contract line renew "
|
|
15
|
+
"and linked to the original one as successor. The default "
|
|
16
|
+
"behavior is to extend the end date of the contract by a new "
|
|
17
|
+
"subscription period",
|
|
18
|
+
)
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
- **Auto-Renewal Strategy**
|
|
2
|
+
In the company settings, define whether renewing a contract line:
|
|
3
|
+
- Extends the current line (updates `date_end`),
|
|
4
|
+
- or creates a new successor contract line.
|
|
5
|
+
|
|
6
|
+
Field:
|
|
7
|
+
`Company > Configuration > Contracts > Create new contract line at renewal`
|
|
8
|
+
- **Scheduled Actions**
|
|
9
|
+
Ensure the scheduled action `Contract Line: Auto Renew` is activated if you
|
|
10
|
+
want automatic renewal without manual intervention.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
- Souheil Bejaoui <souheil.bejaoui@acsone.eu> (ACSONE SA/NV)
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
**Contract Line Successor** extends `contract.line` model to support advanced
|
|
2
|
+
contract lifecycle management, including suspension, successor planning,
|
|
3
|
+
cancellation, and renewal.
|
|
4
|
+
It provides a flexible and robust framework for managing complex contract
|
|
5
|
+
line scenarios in a clean and structured way.
|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
- **Successor and Predecessor Management**
|
|
10
|
+
- Link contract lines with successor and predecessor lines.
|
|
11
|
+
- Plan successors automatically or manually after a stop or suspension.
|
|
12
|
+
|
|
13
|
+
- **Contract Line Lifecycle States**
|
|
14
|
+
- Manage contract lines with the following computed states:
|
|
15
|
+
- `Upcoming`
|
|
16
|
+
- `In-Progress`
|
|
17
|
+
- `To Renew`
|
|
18
|
+
- `Upcoming Close`
|
|
19
|
+
- `Closed`
|
|
20
|
+
- `Canceled`
|
|
21
|
+
|
|
22
|
+
- **Lifecycle Operations**
|
|
23
|
+
- Stop a contract line.
|
|
24
|
+
- Plan a successor for a contract line.
|
|
25
|
+
- Stop and plan a successor in one operation (useful for suspensions).
|
|
26
|
+
- Cancel and un-cancel contract lines.
|
|
27
|
+
- Renew contract lines automatically (new line or extension).
|
|
28
|
+
|
|
29
|
+
- **Auto-Renewal Handling**
|
|
30
|
+
- Auto-renewal based on company settings (extend existing line or create a new one).
|
|
31
|
+
- Cron job to automate renewal of eligible contract lines.
|
|
32
|
+
|
|
33
|
+
- **Data Integrity and Validation**
|
|
34
|
+
- Prevent invalid successor or predecessor configurations.
|
|
35
|
+
- Validate state transitions and date overlaps.
|
|
36
|
+
- Ensure clean renewal and cancellation workflows.
|
|
37
|
+
|
|
38
|
+
- **Audit Trail**
|
|
39
|
+
- Automatic posting of chatter messages for lifecycle events like stops,
|
|
40
|
+
renewals, suspensions, cancellations, etc.
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
1. **Select a contract** and enable the option **"Recurrence at line level?"**.
|
|
2
|
+
2. Once enabled, you will have access to several actions at the contract line level:
|
|
3
|
+
- **Stop** a contract line and optionally **plan a successor**.
|
|
4
|
+
- **Handle temporary suspensions** and **resume** the contract line after the suspension period.
|
|
5
|
+
- **Cancel** and **un-cancel** contract lines if necessary.
|
|
6
|
+
- **Renew** contract lines either by **extending** the current line or by **creating a new successor line** automatically.
|
|
Binary file
|