odoo-addon-contract 17.0.1.4.4__py3-none-any.whl → 18.0.2.0.8__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/README.rst +14 -10
- odoo/addons/contract/__manifest__.py +3 -10
- odoo/addons/contract/controllers/main.py +1 -8
- odoo/addons/contract/data/contract_cron.xml +0 -2
- odoo/addons/contract/data/mail_template.xml +18 -17
- odoo/addons/contract/data/template_mail_notification.xml +1 -1
- odoo/addons/contract/i18n/am.po +141 -821
- odoo/addons/contract/i18n/ar.po +141 -821
- odoo/addons/contract/i18n/bg.po +141 -821
- odoo/addons/contract/i18n/bs.po +141 -821
- odoo/addons/contract/i18n/ca.po +831 -901
- odoo/addons/contract/i18n/ca_ES.po +141 -821
- odoo/addons/contract/i18n/contract.pot +140 -818
- odoo/addons/contract/i18n/cs.po +141 -821
- odoo/addons/contract/i18n/da.po +141 -821
- odoo/addons/contract/i18n/de.po +708 -954
- odoo/addons/contract/i18n/el_GR.po +141 -821
- odoo/addons/contract/i18n/en_GB.po +141 -821
- odoo/addons/contract/i18n/es.po +710 -948
- odoo/addons/contract/i18n/es_AR.po +548 -880
- odoo/addons/contract/i18n/es_CL.po +141 -821
- odoo/addons/contract/i18n/es_CO.po +141 -821
- odoo/addons/contract/i18n/es_CR.po +141 -821
- odoo/addons/contract/i18n/es_DO.po +141 -821
- odoo/addons/contract/i18n/es_EC.po +141 -821
- odoo/addons/contract/i18n/es_MX.po +141 -821
- odoo/addons/contract/i18n/es_PY.po +141 -821
- odoo/addons/contract/i18n/es_VE.po +141 -821
- odoo/addons/contract/i18n/et.po +141 -821
- odoo/addons/contract/i18n/eu.po +141 -821
- odoo/addons/contract/i18n/fa.po +141 -821
- odoo/addons/contract/i18n/fi.po +419 -850
- odoo/addons/contract/i18n/fr.po +706 -951
- odoo/addons/contract/i18n/fr_CA.po +141 -821
- odoo/addons/contract/i18n/fr_CH.po +141 -821
- odoo/addons/contract/i18n/fr_FR.po +449 -850
- odoo/addons/contract/i18n/gl.po +252 -846
- odoo/addons/contract/i18n/gl_ES.po +141 -821
- odoo/addons/contract/i18n/he.po +141 -821
- odoo/addons/contract/i18n/hi_IN.po +186 -831
- odoo/addons/contract/i18n/hr.po +206 -837
- odoo/addons/contract/i18n/hr_HR.po +218 -839
- odoo/addons/contract/i18n/hu.po +141 -821
- odoo/addons/contract/i18n/id.po +141 -821
- odoo/addons/contract/i18n/it.po +746 -900
- odoo/addons/contract/i18n/ja.po +141 -821
- odoo/addons/contract/i18n/ko.po +141 -821
- odoo/addons/contract/i18n/lt.po +141 -821
- odoo/addons/contract/i18n/lt_LT.po +141 -821
- odoo/addons/contract/i18n/lv.po +141 -821
- odoo/addons/contract/i18n/mk.po +141 -821
- odoo/addons/contract/i18n/mn.po +141 -821
- odoo/addons/contract/i18n/nb.po +141 -821
- odoo/addons/contract/i18n/nb_NO.po +141 -821
- odoo/addons/contract/i18n/nl.po +694 -953
- odoo/addons/contract/i18n/nl_BE.po +141 -821
- odoo/addons/contract/i18n/nl_NL.po +186 -831
- odoo/addons/contract/i18n/pl.po +141 -821
- odoo/addons/contract/i18n/pt.po +410 -839
- odoo/addons/contract/i18n/pt_BR.po +701 -949
- odoo/addons/contract/i18n/pt_PT.po +141 -821
- odoo/addons/contract/i18n/ro.po +141 -821
- odoo/addons/contract/i18n/ru.po +186 -831
- odoo/addons/contract/i18n/sk.po +141 -821
- odoo/addons/contract/i18n/sk_SK.po +141 -821
- odoo/addons/contract/i18n/sl.po +141 -821
- odoo/addons/contract/i18n/sr.po +141 -821
- odoo/addons/contract/i18n/sr@latin.po +141 -821
- odoo/addons/contract/i18n/sv.po +780 -934
- odoo/addons/contract/i18n/th.po +141 -821
- odoo/addons/contract/i18n/tr.po +556 -877
- odoo/addons/contract/i18n/tr_TR.po +216 -838
- odoo/addons/contract/i18n/uk.po +141 -821
- odoo/addons/contract/i18n/vi.po +141 -821
- odoo/addons/contract/i18n/vi_VN.po +141 -821
- odoo/addons/contract/i18n/zh_CN.po +407 -845
- odoo/addons/contract/i18n/zh_TW.po +145 -822
- odoo/addons/contract/migrations/18.0.2.0.0/end-migrate.py +27 -0
- odoo/addons/contract/migrations/18.0.2.0.0/pre-migrate.py +94 -0
- odoo/addons/contract/models/__init__.py +2 -6
- odoo/addons/contract/models/account_move.py +0 -8
- odoo/addons/contract/models/account_move_line.py +14 -0
- odoo/addons/contract/models/contract.py +272 -308
- odoo/addons/contract/models/contract_line.py +37 -859
- odoo/addons/contract/models/{contract_recurrency_mixin.py → contract_recurring_mixin.py} +101 -82
- odoo/addons/contract/models/contract_tag.py +1 -3
- odoo/addons/contract/models/contract_template.py +81 -2
- odoo/addons/contract/models/contract_template_line.py +250 -3
- odoo/addons/contract/report/contract_views.xml +0 -2
- odoo/addons/contract/report/report_contract.xml +13 -13
- odoo/addons/contract/security/contract_security.xml +6 -15
- odoo/addons/contract/security/contract_tag.xml +1 -3
- odoo/addons/contract/security/ir.model.access.csv +0 -2
- odoo/addons/contract/static/description/index.html +24 -18
- odoo/addons/contract/static/src/js/contract_portal_tour.esm.js +6 -4
- odoo/addons/contract/tests/test_contract.py +82 -928
- odoo/addons/contract/tests/test_multicompany.py +5 -4
- odoo/addons/contract/tests/test_portal.py +6 -3
- odoo/addons/contract/views/contract.xml +92 -235
- odoo/addons/contract/views/contract_line.xml +48 -117
- odoo/addons/contract/views/contract_portal_templates.xml +181 -222
- odoo/addons/contract/views/contract_tag.xml +3 -3
- odoo/addons/contract/views/contract_template.xml +100 -72
- odoo/addons/contract/views/contract_template_line.xml +76 -5
- odoo/addons/contract/views/res_config_settings.xml +5 -6
- odoo/addons/contract/views/res_partner_view.xml +0 -5
- odoo/addons/contract/wizards/__init__.py +0 -2
- odoo/addons/contract/wizards/contract_manually_create_invoice.py +6 -6
- odoo/addons/contract/wizards/contract_manually_create_invoice.xml +2 -3
- {odoo_addon_contract-17.0.1.4.4.dist-info → odoo_addon_contract-18.0.2.0.8.dist-info}/METADATA +18 -13
- odoo_addon_contract-18.0.2.0.8.dist-info/RECORD +132 -0
- {odoo_addon_contract-17.0.1.4.4.dist-info → odoo_addon_contract-18.0.2.0.8.dist-info}/WHEEL +1 -1
- odoo/addons/contract/data/contract_renew_cron.xml +0 -14
- odoo/addons/contract/models/abstract_contract.py +0 -82
- odoo/addons/contract/models/abstract_contract_line.py +0 -271
- odoo/addons/contract/models/contract_line_constraints.py +0 -429
- odoo/addons/contract/models/contract_terminate_reason.py +0 -14
- odoo/addons/contract/models/res_company.py +0 -15
- odoo/addons/contract/models/res_config_settings.py +0 -18
- odoo/addons/contract/security/contract_terminate_reason.xml +0 -23
- odoo/addons/contract/security/groups.xml +0 -9
- odoo/addons/contract/views/abstract_contract_line.xml +0 -117
- odoo/addons/contract/views/contract_terminate_reason.xml +0 -38
- odoo/addons/contract/wizards/contract_contract_terminate.py +0 -42
- odoo/addons/contract/wizards/contract_contract_terminate.xml +0 -33
- odoo/addons/contract/wizards/contract_line_wizard.py +0 -53
- odoo/addons/contract/wizards/contract_line_wizard.xml +0 -111
- odoo_addon_contract-17.0.1.4.4.dist-info/RECORD +0 -144
- {odoo_addon_contract-17.0.1.4.4.dist-info → odoo_addon_contract-18.0.2.0.8.dist-info}/top_level.txt +0 -0
|
@@ -3,22 +3,20 @@
|
|
|
3
3
|
# Copyright 2020 Tecnativa - Pedro M. Baeza
|
|
4
4
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
|
|
7
|
+
import warnings
|
|
7
8
|
|
|
8
9
|
from dateutil.relativedelta import relativedelta
|
|
9
10
|
|
|
10
|
-
from odoo import
|
|
11
|
+
from odoo import api, fields, models
|
|
11
12
|
from odoo.exceptions import ValidationError
|
|
12
13
|
|
|
13
|
-
from .contract_line_constraints import get_allowed
|
|
14
|
-
|
|
15
14
|
|
|
16
15
|
class ContractLine(models.Model):
|
|
17
16
|
_name = "contract.line"
|
|
18
17
|
_description = "Contract Line"
|
|
19
18
|
_inherit = [
|
|
20
|
-
"contract.
|
|
21
|
-
"contract.recurrency.mixin",
|
|
19
|
+
"contract.template.line",
|
|
22
20
|
"analytic.mixin",
|
|
23
21
|
]
|
|
24
22
|
_order = "sequence,id"
|
|
@@ -33,366 +31,21 @@ class ContractLine(models.Model):
|
|
|
33
31
|
ondelete="cascade",
|
|
34
32
|
)
|
|
35
33
|
currency_id = fields.Many2one(related="contract_id.currency_id")
|
|
36
|
-
date_start = fields.Date(required=True)
|
|
37
|
-
date_end = fields.Date(compute="_compute_date_end", store=True, readonly=False)
|
|
38
|
-
termination_notice_date = fields.Date(
|
|
39
|
-
compute="_compute_termination_notice_date",
|
|
40
|
-
store=True,
|
|
41
|
-
copy=False,
|
|
42
|
-
)
|
|
43
34
|
create_invoice_visibility = fields.Boolean(
|
|
44
35
|
compute="_compute_create_invoice_visibility"
|
|
45
36
|
)
|
|
46
|
-
successor_contract_line_id = fields.Many2one(
|
|
47
|
-
comodel_name="contract.line",
|
|
48
|
-
string="Successor Contract Line",
|
|
49
|
-
required=False,
|
|
50
|
-
readonly=True,
|
|
51
|
-
index=True,
|
|
52
|
-
copy=False,
|
|
53
|
-
help="In case of restart after suspension, this field contain the new "
|
|
54
|
-
"contract line created.",
|
|
55
|
-
)
|
|
56
|
-
predecessor_contract_line_id = fields.Many2one(
|
|
57
|
-
comodel_name="contract.line",
|
|
58
|
-
string="Predecessor Contract Line",
|
|
59
|
-
required=False,
|
|
60
|
-
readonly=True,
|
|
61
|
-
index=True,
|
|
62
|
-
copy=False,
|
|
63
|
-
help="Contract Line origin of this one.",
|
|
64
|
-
)
|
|
65
|
-
manual_renew_needed = fields.Boolean(
|
|
66
|
-
default=False,
|
|
67
|
-
help="This flag is used to make a difference between a definitive stop"
|
|
68
|
-
"and temporary one for which a user is not able to plan a"
|
|
69
|
-
"successor in advance",
|
|
70
|
-
)
|
|
71
|
-
is_plan_successor_allowed = fields.Boolean(
|
|
72
|
-
string="Plan successor allowed?", compute="_compute_allowed"
|
|
73
|
-
)
|
|
74
|
-
is_stop_plan_successor_allowed = fields.Boolean(
|
|
75
|
-
string="Stop/Plan successor allowed?", compute="_compute_allowed"
|
|
76
|
-
)
|
|
77
|
-
is_stop_allowed = fields.Boolean(string="Stop allowed?", compute="_compute_allowed")
|
|
78
|
-
is_cancel_allowed = fields.Boolean(
|
|
79
|
-
string="Cancel allowed?", compute="_compute_allowed"
|
|
80
|
-
)
|
|
81
|
-
is_un_cancel_allowed = fields.Boolean(
|
|
82
|
-
string="Un-Cancel allowed?", compute="_compute_allowed"
|
|
83
|
-
)
|
|
84
|
-
state = fields.Selection(
|
|
85
|
-
selection=[
|
|
86
|
-
("upcoming", "Upcoming"),
|
|
87
|
-
("in-progress", "In-progress"),
|
|
88
|
-
("to-renew", "To renew"),
|
|
89
|
-
("upcoming-close", "Upcoming Close"),
|
|
90
|
-
("closed", "Closed"),
|
|
91
|
-
("canceled", "Canceled"),
|
|
92
|
-
],
|
|
93
|
-
compute="_compute_state",
|
|
94
|
-
search="_search_state",
|
|
95
|
-
)
|
|
96
37
|
active = fields.Boolean(
|
|
97
38
|
string="Active",
|
|
98
39
|
related="contract_id.active",
|
|
99
40
|
store=True,
|
|
100
41
|
readonly=True,
|
|
101
42
|
)
|
|
43
|
+
product_id = fields.Many2one(index=True)
|
|
102
44
|
|
|
103
|
-
@api.depends(
|
|
104
|
-
|
|
105
|
-
"date_start",
|
|
106
|
-
"date_end",
|
|
107
|
-
"contract_id.last_date_invoiced",
|
|
108
|
-
"contract_id.contract_line_ids.last_date_invoiced",
|
|
109
|
-
)
|
|
110
|
-
# pylint: disable=missing-return
|
|
111
|
-
def _compute_next_period_date_start(self):
|
|
112
|
-
"""Rectify next period date start if another line in the contract has been
|
|
113
|
-
already invoiced previously when the recurrence is by contract.
|
|
114
|
-
"""
|
|
115
|
-
rest = self.filtered(lambda x: x.contract_id.line_recurrence)
|
|
116
|
-
for rec in self - rest:
|
|
117
|
-
lines = rec.contract_id.contract_line_ids
|
|
118
|
-
if not rec.last_date_invoiced and any(lines.mapped("last_date_invoiced")):
|
|
119
|
-
next_period_date_start = max(
|
|
120
|
-
lines.filtered("last_date_invoiced").mapped("last_date_invoiced")
|
|
121
|
-
) + relativedelta(days=1)
|
|
122
|
-
if rec.date_end and next_period_date_start > rec.date_end:
|
|
123
|
-
next_period_date_start = False
|
|
124
|
-
rec.next_period_date_start = next_period_date_start
|
|
125
|
-
else:
|
|
126
|
-
rest |= rec
|
|
127
|
-
super(ContractLine, rest)._compute_next_period_date_start()
|
|
128
|
-
|
|
129
|
-
@api.depends("contract_id.date_end", "contract_id.line_recurrence")
|
|
130
|
-
def _compute_date_end(self):
|
|
131
|
-
self._set_recurrence_field("date_end")
|
|
132
|
-
|
|
133
|
-
@api.depends(
|
|
134
|
-
"date_end",
|
|
135
|
-
"termination_notice_rule_type",
|
|
136
|
-
"termination_notice_interval",
|
|
137
|
-
)
|
|
138
|
-
def _compute_termination_notice_date(self):
|
|
139
|
-
for rec in self:
|
|
140
|
-
if rec.date_end:
|
|
141
|
-
rec.termination_notice_date = rec.date_end - self.get_relative_delta(
|
|
142
|
-
rec.termination_notice_rule_type,
|
|
143
|
-
rec.termination_notice_interval,
|
|
144
|
-
)
|
|
145
|
-
else:
|
|
146
|
-
rec.termination_notice_date = False
|
|
147
|
-
|
|
148
|
-
@api.depends(
|
|
149
|
-
"is_canceled",
|
|
150
|
-
"date_start",
|
|
151
|
-
"date_end",
|
|
152
|
-
"is_auto_renew",
|
|
153
|
-
"manual_renew_needed",
|
|
154
|
-
"termination_notice_date",
|
|
155
|
-
"successor_contract_line_id",
|
|
156
|
-
)
|
|
157
|
-
def _compute_state(self):
|
|
158
|
-
today = fields.Date.context_today(self)
|
|
45
|
+
@api.depends("name", "date_start")
|
|
46
|
+
def _compute_display_name(self):
|
|
159
47
|
for rec in self:
|
|
160
|
-
rec.
|
|
161
|
-
if rec.display_type:
|
|
162
|
-
continue
|
|
163
|
-
if rec.is_canceled:
|
|
164
|
-
rec.state = "canceled"
|
|
165
|
-
continue
|
|
166
|
-
|
|
167
|
-
if rec.date_start and rec.date_start > today:
|
|
168
|
-
# Before period
|
|
169
|
-
rec.state = "upcoming"
|
|
170
|
-
continue
|
|
171
|
-
if (
|
|
172
|
-
rec.date_start
|
|
173
|
-
and rec.date_start <= today
|
|
174
|
-
and (not rec.date_end or rec.date_end >= today)
|
|
175
|
-
):
|
|
176
|
-
# In period
|
|
177
|
-
if (
|
|
178
|
-
rec.termination_notice_date
|
|
179
|
-
and rec.termination_notice_date < today
|
|
180
|
-
and not rec.is_auto_renew
|
|
181
|
-
and not rec.manual_renew_needed
|
|
182
|
-
):
|
|
183
|
-
rec.state = "upcoming-close"
|
|
184
|
-
else:
|
|
185
|
-
rec.state = "in-progress"
|
|
186
|
-
continue
|
|
187
|
-
if rec.date_end and rec.date_end < today:
|
|
188
|
-
# After
|
|
189
|
-
if (
|
|
190
|
-
rec.manual_renew_needed
|
|
191
|
-
and not rec.successor_contract_line_id
|
|
192
|
-
or rec.is_auto_renew
|
|
193
|
-
):
|
|
194
|
-
rec.state = "to-renew"
|
|
195
|
-
else:
|
|
196
|
-
rec.state = "closed"
|
|
197
|
-
|
|
198
|
-
@api.model
|
|
199
|
-
def _get_state_domain(self, state):
|
|
200
|
-
today = fields.Date.context_today(self)
|
|
201
|
-
if state == "upcoming":
|
|
202
|
-
return [
|
|
203
|
-
"&",
|
|
204
|
-
("date_start", ">", today),
|
|
205
|
-
("is_canceled", "=", False),
|
|
206
|
-
]
|
|
207
|
-
if state == "in-progress":
|
|
208
|
-
return [
|
|
209
|
-
"&",
|
|
210
|
-
"&",
|
|
211
|
-
"&",
|
|
212
|
-
("date_start", "<=", today),
|
|
213
|
-
("is_canceled", "=", False),
|
|
214
|
-
"|",
|
|
215
|
-
("date_end", ">=", today),
|
|
216
|
-
("date_end", "=", False),
|
|
217
|
-
"|",
|
|
218
|
-
("is_auto_renew", "=", True),
|
|
219
|
-
"&",
|
|
220
|
-
("is_auto_renew", "=", False),
|
|
221
|
-
("termination_notice_date", ">", today),
|
|
222
|
-
]
|
|
223
|
-
if state == "to-renew":
|
|
224
|
-
return [
|
|
225
|
-
"&",
|
|
226
|
-
"&",
|
|
227
|
-
("is_canceled", "=", False),
|
|
228
|
-
("date_end", "<", today),
|
|
229
|
-
"|",
|
|
230
|
-
"&",
|
|
231
|
-
("manual_renew_needed", "=", True),
|
|
232
|
-
("successor_contract_line_id", "=", False),
|
|
233
|
-
("is_auto_renew", "=", True),
|
|
234
|
-
]
|
|
235
|
-
if state == "upcoming-close":
|
|
236
|
-
return [
|
|
237
|
-
"&",
|
|
238
|
-
"&",
|
|
239
|
-
"&",
|
|
240
|
-
"&",
|
|
241
|
-
"&",
|
|
242
|
-
("date_start", "<=", today),
|
|
243
|
-
("is_auto_renew", "=", False),
|
|
244
|
-
("manual_renew_needed", "=", False),
|
|
245
|
-
("is_canceled", "=", False),
|
|
246
|
-
("termination_notice_date", "<", today),
|
|
247
|
-
("date_end", ">=", today),
|
|
248
|
-
]
|
|
249
|
-
if state == "closed":
|
|
250
|
-
return [
|
|
251
|
-
"&",
|
|
252
|
-
"&",
|
|
253
|
-
"&",
|
|
254
|
-
("is_canceled", "=", False),
|
|
255
|
-
("date_end", "<", today),
|
|
256
|
-
("is_auto_renew", "=", False),
|
|
257
|
-
"|",
|
|
258
|
-
"&",
|
|
259
|
-
("manual_renew_needed", "=", True),
|
|
260
|
-
("successor_contract_line_id", "!=", False),
|
|
261
|
-
("manual_renew_needed", "=", False),
|
|
262
|
-
]
|
|
263
|
-
if state == "canceled":
|
|
264
|
-
return [("is_canceled", "=", True)]
|
|
265
|
-
if not state:
|
|
266
|
-
return [("display_type", "!=", False)]
|
|
267
|
-
|
|
268
|
-
@api.model
|
|
269
|
-
def _search_state(self, operator, value):
|
|
270
|
-
states = [
|
|
271
|
-
"upcoming",
|
|
272
|
-
"in-progress",
|
|
273
|
-
"to-renew",
|
|
274
|
-
"upcoming-close",
|
|
275
|
-
"closed",
|
|
276
|
-
"canceled",
|
|
277
|
-
False,
|
|
278
|
-
]
|
|
279
|
-
if operator == "=":
|
|
280
|
-
return self._get_state_domain(value)
|
|
281
|
-
if operator == "!=":
|
|
282
|
-
domain = []
|
|
283
|
-
for state in states:
|
|
284
|
-
if state != value:
|
|
285
|
-
if domain:
|
|
286
|
-
domain.insert(0, "|")
|
|
287
|
-
domain.extend(self._get_state_domain(state))
|
|
288
|
-
return domain
|
|
289
|
-
if operator == "in":
|
|
290
|
-
domain = []
|
|
291
|
-
for state in value:
|
|
292
|
-
if domain:
|
|
293
|
-
domain.insert(0, "|")
|
|
294
|
-
domain.extend(self._get_state_domain(state))
|
|
295
|
-
return domain
|
|
296
|
-
|
|
297
|
-
if operator == "not in":
|
|
298
|
-
if set(value) == set(states):
|
|
299
|
-
return [("id", "=", False)]
|
|
300
|
-
return self._search_state(
|
|
301
|
-
"in", [state for state in states if state not in value]
|
|
302
|
-
)
|
|
303
|
-
|
|
304
|
-
@api.depends(
|
|
305
|
-
"date_start",
|
|
306
|
-
"date_end",
|
|
307
|
-
"last_date_invoiced",
|
|
308
|
-
"is_auto_renew",
|
|
309
|
-
"successor_contract_line_id",
|
|
310
|
-
"predecessor_contract_line_id",
|
|
311
|
-
"is_canceled",
|
|
312
|
-
"contract_id.is_terminated",
|
|
313
|
-
)
|
|
314
|
-
def _compute_allowed(self):
|
|
315
|
-
for rec in self:
|
|
316
|
-
rec.update(
|
|
317
|
-
{
|
|
318
|
-
"is_plan_successor_allowed": False,
|
|
319
|
-
"is_stop_plan_successor_allowed": False,
|
|
320
|
-
"is_stop_allowed": False,
|
|
321
|
-
"is_cancel_allowed": False,
|
|
322
|
-
"is_un_cancel_allowed": False,
|
|
323
|
-
}
|
|
324
|
-
)
|
|
325
|
-
if rec.contract_id.is_terminated:
|
|
326
|
-
continue
|
|
327
|
-
if rec.date_start:
|
|
328
|
-
allowed = get_allowed(
|
|
329
|
-
rec.date_start,
|
|
330
|
-
rec.date_end,
|
|
331
|
-
rec.last_date_invoiced,
|
|
332
|
-
rec.is_auto_renew,
|
|
333
|
-
rec.successor_contract_line_id,
|
|
334
|
-
rec.predecessor_contract_line_id,
|
|
335
|
-
rec.is_canceled,
|
|
336
|
-
)
|
|
337
|
-
if allowed:
|
|
338
|
-
rec.update(
|
|
339
|
-
{
|
|
340
|
-
"is_plan_successor_allowed": allowed.plan_successor,
|
|
341
|
-
"is_stop_plan_successor_allowed": (
|
|
342
|
-
allowed.stop_plan_successor
|
|
343
|
-
),
|
|
344
|
-
"is_stop_allowed": allowed.stop,
|
|
345
|
-
"is_cancel_allowed": allowed.cancel,
|
|
346
|
-
"is_un_cancel_allowed": allowed.uncancel,
|
|
347
|
-
}
|
|
348
|
-
)
|
|
349
|
-
|
|
350
|
-
@api.constrains("is_auto_renew", "successor_contract_line_id", "date_end")
|
|
351
|
-
def _check_allowed(self):
|
|
352
|
-
"""
|
|
353
|
-
logical impossible combination:
|
|
354
|
-
* a line with is_auto_renew True should have date_end and
|
|
355
|
-
couldn't have successor_contract_line_id
|
|
356
|
-
* a line without date_end can't have successor_contract_line_id
|
|
357
|
-
|
|
358
|
-
"""
|
|
359
|
-
for rec in self:
|
|
360
|
-
if rec.is_auto_renew:
|
|
361
|
-
if rec.successor_contract_line_id:
|
|
362
|
-
raise ValidationError(
|
|
363
|
-
_(
|
|
364
|
-
"A contract line with a successor "
|
|
365
|
-
"can't be set to auto-renew"
|
|
366
|
-
)
|
|
367
|
-
)
|
|
368
|
-
if not rec.date_end:
|
|
369
|
-
raise ValidationError(_("An auto-renew line must have a end date"))
|
|
370
|
-
else:
|
|
371
|
-
if not rec.date_end and rec.successor_contract_line_id:
|
|
372
|
-
raise ValidationError(
|
|
373
|
-
_("A contract line with a successor " "must have a end date")
|
|
374
|
-
)
|
|
375
|
-
|
|
376
|
-
@api.constrains("successor_contract_line_id", "date_end")
|
|
377
|
-
def _check_overlap_successor(self):
|
|
378
|
-
for rec in self:
|
|
379
|
-
if rec.date_end and rec.successor_contract_line_id:
|
|
380
|
-
if rec.date_end >= rec.successor_contract_line_id.date_start:
|
|
381
|
-
raise ValidationError(
|
|
382
|
-
_("Contract line and its successor overlapped")
|
|
383
|
-
)
|
|
384
|
-
|
|
385
|
-
@api.constrains("predecessor_contract_line_id", "date_start")
|
|
386
|
-
def _check_overlap_predecessor(self):
|
|
387
|
-
for rec in self:
|
|
388
|
-
if (
|
|
389
|
-
rec.predecessor_contract_line_id
|
|
390
|
-
and rec.predecessor_contract_line_id.date_end
|
|
391
|
-
):
|
|
392
|
-
if rec.date_start <= rec.predecessor_contract_line_id.date_end:
|
|
393
|
-
raise ValidationError(
|
|
394
|
-
_("Contract line and its predecessor overlapped")
|
|
395
|
-
)
|
|
48
|
+
rec.display_name = f"{rec.date_start} - {rec.name}"
|
|
396
49
|
|
|
397
50
|
@api.model
|
|
398
51
|
def _compute_first_recurring_next_date(
|
|
@@ -414,53 +67,15 @@ class ContractLine(models.Model):
|
|
|
414
67
|
max_date_end=False,
|
|
415
68
|
)
|
|
416
69
|
|
|
417
|
-
@api.model
|
|
418
|
-
def _get_first_date_end(
|
|
419
|
-
self, date_start, auto_renew_rule_type, auto_renew_interval
|
|
420
|
-
):
|
|
421
|
-
return (
|
|
422
|
-
date_start
|
|
423
|
-
+ self.get_relative_delta(auto_renew_rule_type, auto_renew_interval)
|
|
424
|
-
- relativedelta(days=1)
|
|
425
|
-
)
|
|
426
|
-
|
|
427
|
-
@api.onchange(
|
|
428
|
-
"date_start",
|
|
429
|
-
"is_auto_renew",
|
|
430
|
-
"auto_renew_rule_type",
|
|
431
|
-
"auto_renew_interval",
|
|
432
|
-
)
|
|
433
|
-
def _onchange_is_auto_renew(self):
|
|
434
|
-
"""Date end should be auto-computed if a contract line is set to
|
|
435
|
-
auto_renew"""
|
|
436
|
-
for rec in self.filtered("is_auto_renew"):
|
|
437
|
-
if rec.date_start:
|
|
438
|
-
rec.date_end = self._get_first_date_end(
|
|
439
|
-
rec.date_start,
|
|
440
|
-
rec.auto_renew_rule_type,
|
|
441
|
-
rec.auto_renew_interval,
|
|
442
|
-
)
|
|
443
|
-
|
|
444
|
-
@api.constrains("is_canceled", "is_auto_renew")
|
|
445
|
-
def _check_auto_renew_canceled_lines(self):
|
|
446
|
-
for rec in self:
|
|
447
|
-
if rec.is_canceled and rec.is_auto_renew:
|
|
448
|
-
raise ValidationError(
|
|
449
|
-
_("A canceled contract line can't be set to auto-renew")
|
|
450
|
-
)
|
|
451
|
-
|
|
452
70
|
@api.constrains("recurring_next_date", "date_start")
|
|
453
71
|
def _check_recurring_next_date_start_date(self):
|
|
454
72
|
for line in self:
|
|
455
|
-
if
|
|
456
|
-
line.display_type in ("line_section", "line_note")
|
|
457
|
-
or not line.recurring_next_date
|
|
458
|
-
):
|
|
73
|
+
if line.display_type == "line_section" or not line.recurring_next_date:
|
|
459
74
|
continue
|
|
460
75
|
if line.date_start and line.recurring_next_date:
|
|
461
76
|
if line.date_start > line.recurring_next_date:
|
|
462
77
|
raise ValidationError(
|
|
463
|
-
_(
|
|
78
|
+
self.env._(
|
|
464
79
|
"You can't have a date of next invoice anterior "
|
|
465
80
|
"to the start of the contract line '%s'"
|
|
466
81
|
)
|
|
@@ -474,7 +89,7 @@ class ContractLine(models.Model):
|
|
|
474
89
|
for rec in self.filtered("last_date_invoiced"):
|
|
475
90
|
if rec.date_end and rec.date_end < rec.last_date_invoiced:
|
|
476
91
|
raise ValidationError(
|
|
477
|
-
_(
|
|
92
|
+
self.env._(
|
|
478
93
|
"You can't have the end date before the date of last "
|
|
479
94
|
"invoice for the contract line '%s'"
|
|
480
95
|
)
|
|
@@ -484,7 +99,7 @@ class ContractLine(models.Model):
|
|
|
484
99
|
continue
|
|
485
100
|
if rec.date_start and rec.date_start > rec.last_date_invoiced:
|
|
486
101
|
raise ValidationError(
|
|
487
|
-
_(
|
|
102
|
+
self.env._(
|
|
488
103
|
"You can't have the start date after the date of last "
|
|
489
104
|
"invoice for the contract line '%s'"
|
|
490
105
|
)
|
|
@@ -495,7 +110,7 @@ class ContractLine(models.Model):
|
|
|
495
110
|
and rec.recurring_next_date <= rec.last_date_invoiced
|
|
496
111
|
):
|
|
497
112
|
raise ValidationError(
|
|
498
|
-
_(
|
|
113
|
+
self.env._(
|
|
499
114
|
"You can't have the next invoice date before the date "
|
|
500
115
|
"of last invoice for the contract line '%s'"
|
|
501
116
|
)
|
|
@@ -511,7 +126,7 @@ class ContractLine(models.Model):
|
|
|
511
126
|
or rec.last_date_invoiced < rec.date_end
|
|
512
127
|
):
|
|
513
128
|
raise ValidationError(
|
|
514
|
-
_(
|
|
129
|
+
self.env._(
|
|
515
130
|
"You must supply a date of next invoice for contract "
|
|
516
131
|
"line '%s'"
|
|
517
132
|
)
|
|
@@ -524,7 +139,7 @@ class ContractLine(models.Model):
|
|
|
524
139
|
if line.date_start and line.date_end:
|
|
525
140
|
if line.date_start > line.date_end:
|
|
526
141
|
raise ValidationError(
|
|
527
|
-
_(
|
|
142
|
+
self.env._(
|
|
528
143
|
"Contract line '%s' start date can't be later than"
|
|
529
144
|
" end date"
|
|
530
145
|
)
|
|
@@ -597,18 +212,18 @@ class ContractLine(models.Model):
|
|
|
597
212
|
|
|
598
213
|
def _translate_marker_month_name(self, month_name):
|
|
599
214
|
months = {
|
|
600
|
-
"01": _("January"),
|
|
601
|
-
"02": _("February"),
|
|
602
|
-
"03": _("March"),
|
|
603
|
-
"04": _("April"),
|
|
604
|
-
"05": _("May"),
|
|
605
|
-
"06": _("June"),
|
|
606
|
-
"07": _("July"),
|
|
607
|
-
"08": _("August"),
|
|
608
|
-
"09": _("September"),
|
|
609
|
-
"10": _("October"),
|
|
610
|
-
"11": _("November"),
|
|
611
|
-
"12": _("December"),
|
|
215
|
+
"01": self.env._("January"),
|
|
216
|
+
"02": self.env._("February"),
|
|
217
|
+
"03": self.env._("March"),
|
|
218
|
+
"04": self.env._("April"),
|
|
219
|
+
"05": self.env._("May"),
|
|
220
|
+
"06": self.env._("June"),
|
|
221
|
+
"07": self.env._("July"),
|
|
222
|
+
"08": self.env._("August"),
|
|
223
|
+
"09": self.env._("September"),
|
|
224
|
+
"10": self.env._("October"),
|
|
225
|
+
"11": self.env._("November"),
|
|
226
|
+
"12": self.env._("December"),
|
|
612
227
|
}
|
|
613
228
|
return months[month_name]
|
|
614
229
|
|
|
@@ -629,8 +244,15 @@ class ContractLine(models.Model):
|
|
|
629
244
|
return name
|
|
630
245
|
|
|
631
246
|
def _update_recurring_next_date(self):
|
|
632
|
-
|
|
633
|
-
|
|
247
|
+
warnings.warn(
|
|
248
|
+
"Deprecated _update_recurring_next_date, "
|
|
249
|
+
"use _update_last_date_invoiced instead",
|
|
250
|
+
DeprecationWarning,
|
|
251
|
+
stacklevel=2,
|
|
252
|
+
)
|
|
253
|
+
return self._update_last_date_invoiced()
|
|
254
|
+
|
|
255
|
+
def _update_last_date_invoiced(self):
|
|
634
256
|
for rec in self:
|
|
635
257
|
last_date_invoiced = rec.next_period_date_end
|
|
636
258
|
rec.write(
|
|
@@ -639,447 +261,10 @@ class ContractLine(models.Model):
|
|
|
639
261
|
}
|
|
640
262
|
)
|
|
641
263
|
|
|
642
|
-
def _delay(self, delay_delta):
|
|
643
|
-
"""
|
|
644
|
-
Delay a contract line
|
|
645
|
-
:param delay_delta: delay relative delta
|
|
646
|
-
:return: delayed contract line
|
|
647
|
-
"""
|
|
648
|
-
for rec in self:
|
|
649
|
-
if rec.last_date_invoiced:
|
|
650
|
-
raise ValidationError(
|
|
651
|
-
_("You can't delay a contract line " "invoiced at least one time.")
|
|
652
|
-
)
|
|
653
|
-
new_date_start = rec.date_start + delay_delta
|
|
654
|
-
if rec.date_end:
|
|
655
|
-
new_date_end = rec.date_end + delay_delta
|
|
656
|
-
else:
|
|
657
|
-
new_date_end = False
|
|
658
|
-
new_recurring_next_date = self.get_next_invoice_date(
|
|
659
|
-
new_date_start,
|
|
660
|
-
rec.recurring_invoicing_type,
|
|
661
|
-
rec.recurring_invoicing_offset,
|
|
662
|
-
rec.recurring_rule_type,
|
|
663
|
-
rec.recurring_interval,
|
|
664
|
-
max_date_end=new_date_end,
|
|
665
|
-
)
|
|
666
|
-
rec.write(
|
|
667
|
-
{
|
|
668
|
-
"date_start": new_date_start,
|
|
669
|
-
"date_end": new_date_end,
|
|
670
|
-
"recurring_next_date": new_recurring_next_date,
|
|
671
|
-
}
|
|
672
|
-
)
|
|
673
|
-
|
|
674
|
-
def _prepare_value_for_stop(self, date_end, manual_renew_needed):
|
|
675
|
-
self.ensure_one()
|
|
676
|
-
return {
|
|
677
|
-
"date_end": date_end,
|
|
678
|
-
"is_auto_renew": False,
|
|
679
|
-
"manual_renew_needed": manual_renew_needed,
|
|
680
|
-
"recurring_next_date": self.get_next_invoice_date(
|
|
681
|
-
self.next_period_date_start,
|
|
682
|
-
self.recurring_invoicing_type,
|
|
683
|
-
self.recurring_invoicing_offset,
|
|
684
|
-
self.recurring_rule_type,
|
|
685
|
-
self.recurring_interval,
|
|
686
|
-
max_date_end=date_end,
|
|
687
|
-
),
|
|
688
|
-
}
|
|
689
|
-
|
|
690
|
-
def stop(self, date_end, manual_renew_needed=False, post_message=True):
|
|
691
|
-
"""
|
|
692
|
-
Put date_end on contract line
|
|
693
|
-
We don't consider contract lines that end's before the new end date
|
|
694
|
-
:param date_end: new date end for contract line
|
|
695
|
-
:return: True
|
|
696
|
-
"""
|
|
697
|
-
if not all(self.mapped("is_stop_allowed")):
|
|
698
|
-
raise ValidationError(_("Stop not allowed for this line"))
|
|
699
|
-
for rec in self:
|
|
700
|
-
if date_end < rec.date_start:
|
|
701
|
-
rec.cancel()
|
|
702
|
-
else:
|
|
703
|
-
if not rec.date_end or rec.date_end > date_end:
|
|
704
|
-
old_date_end = rec.date_end
|
|
705
|
-
rec.write(
|
|
706
|
-
rec._prepare_value_for_stop(date_end, manual_renew_needed)
|
|
707
|
-
)
|
|
708
|
-
if post_message:
|
|
709
|
-
msg = _(
|
|
710
|
-
"""Contract line for <strong>%(product)s</strong>
|
|
711
|
-
stopped: <br/>
|
|
712
|
-
- <strong>End</strong>: %(old_end)s -- %(new_end)s
|
|
713
|
-
"""
|
|
714
|
-
) % {
|
|
715
|
-
"product": rec.name,
|
|
716
|
-
"old_end": old_date_end,
|
|
717
|
-
"new_end": rec.date_end,
|
|
718
|
-
}
|
|
719
|
-
rec.contract_id.message_post(body=msg)
|
|
720
|
-
else:
|
|
721
|
-
rec.write(
|
|
722
|
-
{
|
|
723
|
-
"is_auto_renew": False,
|
|
724
|
-
"manual_renew_needed": manual_renew_needed,
|
|
725
|
-
}
|
|
726
|
-
)
|
|
727
|
-
return True
|
|
728
|
-
|
|
729
|
-
def _prepare_value_for_plan_successor(
|
|
730
|
-
self, date_start, date_end, is_auto_renew, recurring_next_date=False
|
|
731
|
-
):
|
|
732
|
-
self.ensure_one()
|
|
733
|
-
if not recurring_next_date:
|
|
734
|
-
recurring_next_date = self.get_next_invoice_date(
|
|
735
|
-
date_start,
|
|
736
|
-
self.recurring_invoicing_type,
|
|
737
|
-
self.recurring_invoicing_offset,
|
|
738
|
-
self.recurring_rule_type,
|
|
739
|
-
self.recurring_interval,
|
|
740
|
-
max_date_end=date_end,
|
|
741
|
-
)
|
|
742
|
-
new_vals = self.read()[0]
|
|
743
|
-
new_vals.pop("id", None)
|
|
744
|
-
new_vals.pop("last_date_invoiced", None)
|
|
745
|
-
values = self._convert_to_write(new_vals)
|
|
746
|
-
values["date_start"] = date_start
|
|
747
|
-
values["date_end"] = date_end
|
|
748
|
-
values["recurring_next_date"] = recurring_next_date
|
|
749
|
-
values["is_auto_renew"] = is_auto_renew
|
|
750
|
-
values["predecessor_contract_line_id"] = self.id
|
|
751
|
-
return values
|
|
752
|
-
|
|
753
|
-
def plan_successor(
|
|
754
|
-
self,
|
|
755
|
-
date_start,
|
|
756
|
-
date_end,
|
|
757
|
-
is_auto_renew,
|
|
758
|
-
recurring_next_date=False,
|
|
759
|
-
post_message=True,
|
|
760
|
-
):
|
|
761
|
-
"""
|
|
762
|
-
Create a copy of a contract line in a new interval
|
|
763
|
-
:param date_start: date_start for the successor_contract_line
|
|
764
|
-
:param date_end: date_end for the successor_contract_line
|
|
765
|
-
:param is_auto_renew: is_auto_renew option for successor_contract_line
|
|
766
|
-
:param recurring_next_date: recurring_next_date for the
|
|
767
|
-
successor_contract_line
|
|
768
|
-
:return: successor_contract_line
|
|
769
|
-
"""
|
|
770
|
-
contract_line = self.env["contract.line"]
|
|
771
|
-
for rec in self:
|
|
772
|
-
if not rec.is_plan_successor_allowed:
|
|
773
|
-
raise ValidationError(_("Plan successor not allowed for this line"))
|
|
774
|
-
rec.is_auto_renew = False
|
|
775
|
-
new_line = self.create(
|
|
776
|
-
rec._prepare_value_for_plan_successor(
|
|
777
|
-
date_start, date_end, is_auto_renew, recurring_next_date
|
|
778
|
-
)
|
|
779
|
-
)
|
|
780
|
-
rec.successor_contract_line_id = new_line
|
|
781
|
-
contract_line |= new_line
|
|
782
|
-
if post_message:
|
|
783
|
-
msg = _(
|
|
784
|
-
"""Contract line for <strong>%(product)s</strong>
|
|
785
|
-
planned a successor: <br/>
|
|
786
|
-
- <strong>Start</strong>: %(new_date_start)s
|
|
787
|
-
<br/>
|
|
788
|
-
- <strong>End</strong>: %(new_date_end)s
|
|
789
|
-
"""
|
|
790
|
-
) % {
|
|
791
|
-
"product": rec.name,
|
|
792
|
-
"new_date_start": new_line.date_start,
|
|
793
|
-
"new_date_end": new_line.date_end,
|
|
794
|
-
}
|
|
795
|
-
rec.contract_id.message_post(body=msg)
|
|
796
|
-
return contract_line
|
|
797
|
-
|
|
798
|
-
def stop_plan_successor(self, date_start, date_end, is_auto_renew):
|
|
799
|
-
"""
|
|
800
|
-
Stop a contract line for a defined period and start it later
|
|
801
|
-
Cases to consider:
|
|
802
|
-
* contract line end's before the suspension period:
|
|
803
|
-
-> apply stop
|
|
804
|
-
* contract line start before the suspension period and end in it
|
|
805
|
-
-> apply stop at suspension start date
|
|
806
|
-
-> apply plan successor:
|
|
807
|
-
- date_start: suspension.date_end
|
|
808
|
-
- date_end: date_end + (contract_line.date_end
|
|
809
|
-
- suspension.date_start)
|
|
810
|
-
* contract line start before the suspension period and end after it
|
|
811
|
-
-> apply stop at suspension start date
|
|
812
|
-
-> apply plan successor:
|
|
813
|
-
- date_start: suspension.date_end
|
|
814
|
-
- date_end: date_end + (suspension.date_end
|
|
815
|
-
- suspension.date_start)
|
|
816
|
-
* contract line start and end's in the suspension period
|
|
817
|
-
-> apply delay
|
|
818
|
-
- delay: suspension.date_end - contract_line.date_start
|
|
819
|
-
* contract line start in the suspension period and end after it
|
|
820
|
-
-> apply delay
|
|
821
|
-
- delay: suspension.date_end - contract_line.date_start
|
|
822
|
-
* contract line start and end after the suspension period
|
|
823
|
-
-> apply delay
|
|
824
|
-
- delay: suspension.date_end - suspension.start_date
|
|
825
|
-
:param date_start: suspension start date
|
|
826
|
-
:param date_end: suspension end date
|
|
827
|
-
:param is_auto_renew: is the new line is set to auto_renew
|
|
828
|
-
:return: created contract line
|
|
829
|
-
"""
|
|
830
|
-
if not all(self.mapped("is_stop_plan_successor_allowed")):
|
|
831
|
-
raise ValidationError(_("Stop/Plan successor not allowed for this line"))
|
|
832
|
-
contract_line = self.env["contract.line"]
|
|
833
|
-
for rec in self:
|
|
834
|
-
if rec.date_start >= date_start:
|
|
835
|
-
if rec.date_start < date_end:
|
|
836
|
-
delay = (date_end - rec.date_start) + timedelta(days=1)
|
|
837
|
-
else:
|
|
838
|
-
delay = (date_end - date_start) + timedelta(days=1)
|
|
839
|
-
rec._delay(delay)
|
|
840
|
-
contract_line |= rec
|
|
841
|
-
else:
|
|
842
|
-
if rec.date_end and rec.date_end < date_start:
|
|
843
|
-
rec.stop(date_start, post_message=False)
|
|
844
|
-
elif (
|
|
845
|
-
rec.date_end
|
|
846
|
-
and rec.date_end > date_start
|
|
847
|
-
and rec.date_end < date_end
|
|
848
|
-
):
|
|
849
|
-
new_date_start = date_end + relativedelta(days=1)
|
|
850
|
-
new_date_end = (
|
|
851
|
-
date_end + (rec.date_end - date_start) + relativedelta(days=1)
|
|
852
|
-
)
|
|
853
|
-
rec.stop(
|
|
854
|
-
date_start - relativedelta(days=1),
|
|
855
|
-
manual_renew_needed=True,
|
|
856
|
-
post_message=False,
|
|
857
|
-
)
|
|
858
|
-
contract_line |= rec.plan_successor(
|
|
859
|
-
new_date_start,
|
|
860
|
-
new_date_end,
|
|
861
|
-
is_auto_renew,
|
|
862
|
-
post_message=False,
|
|
863
|
-
)
|
|
864
|
-
else:
|
|
865
|
-
new_date_start = date_end + relativedelta(days=1)
|
|
866
|
-
if rec.date_end:
|
|
867
|
-
new_date_end = (
|
|
868
|
-
rec.date_end
|
|
869
|
-
+ (date_end - date_start)
|
|
870
|
-
+ relativedelta(days=1)
|
|
871
|
-
)
|
|
872
|
-
else:
|
|
873
|
-
new_date_end = rec.date_end
|
|
874
|
-
|
|
875
|
-
rec.stop(
|
|
876
|
-
date_start - relativedelta(days=1),
|
|
877
|
-
manual_renew_needed=True,
|
|
878
|
-
post_message=False,
|
|
879
|
-
)
|
|
880
|
-
contract_line |= rec.plan_successor(
|
|
881
|
-
new_date_start,
|
|
882
|
-
new_date_end,
|
|
883
|
-
is_auto_renew,
|
|
884
|
-
post_message=False,
|
|
885
|
-
)
|
|
886
|
-
msg = _(
|
|
887
|
-
"""Contract line for <strong>%(product)s</strong>
|
|
888
|
-
suspended: <br/>
|
|
889
|
-
- <strong>Suspension Start</strong>: %(new_date_start)s
|
|
890
|
-
<br/>
|
|
891
|
-
- <strong>Suspension End</strong>: %(new_date_end)s
|
|
892
|
-
"""
|
|
893
|
-
) % {
|
|
894
|
-
"product": rec.name,
|
|
895
|
-
"new_date_start": date_start,
|
|
896
|
-
"new_date_end": date_end,
|
|
897
|
-
}
|
|
898
|
-
rec.contract_id.message_post(body=msg)
|
|
899
|
-
return contract_line
|
|
900
|
-
|
|
901
|
-
def cancel(self):
|
|
902
|
-
if not all(self.mapped("is_cancel_allowed")):
|
|
903
|
-
raise ValidationError(_("Cancel not allowed for this line"))
|
|
904
|
-
for contract in self.mapped("contract_id"):
|
|
905
|
-
lines = self.filtered(lambda line, c=contract: line.contract_id == c)
|
|
906
|
-
msg = _(
|
|
907
|
-
"Contract line canceled: %s",
|
|
908
|
-
"<br/>- ".join(
|
|
909
|
-
[f"<strong>{name}</strong>" for name in lines.mapped("name")]
|
|
910
|
-
),
|
|
911
|
-
)
|
|
912
|
-
contract.message_post(body=msg)
|
|
913
|
-
self.mapped("predecessor_contract_line_id").write(
|
|
914
|
-
{"successor_contract_line_id": False}
|
|
915
|
-
)
|
|
916
|
-
return self.write({"is_canceled": True, "is_auto_renew": False})
|
|
917
|
-
|
|
918
|
-
def uncancel(self, recurring_next_date):
|
|
919
|
-
if not all(self.mapped("is_un_cancel_allowed")):
|
|
920
|
-
raise ValidationError(_("Un-cancel not allowed for this line"))
|
|
921
|
-
for contract in self.mapped("contract_id"):
|
|
922
|
-
lines = self.filtered(lambda line, c=contract: line.contract_id == c)
|
|
923
|
-
msg = _(
|
|
924
|
-
"Contract line Un-canceled: %s",
|
|
925
|
-
"<br/>- ".join(
|
|
926
|
-
[f"<strong>{name}</strong>" for name in lines.mapped("name")]
|
|
927
|
-
),
|
|
928
|
-
)
|
|
929
|
-
contract.message_post(body=msg)
|
|
930
|
-
for rec in self:
|
|
931
|
-
if rec.predecessor_contract_line_id:
|
|
932
|
-
predecessor_contract_line = rec.predecessor_contract_line_id
|
|
933
|
-
assert not predecessor_contract_line.successor_contract_line_id
|
|
934
|
-
predecessor_contract_line.successor_contract_line_id = rec
|
|
935
|
-
rec.is_canceled = False
|
|
936
|
-
rec.recurring_next_date = recurring_next_date
|
|
937
|
-
return True
|
|
938
|
-
|
|
939
|
-
def action_uncancel(self):
|
|
940
|
-
self.ensure_one()
|
|
941
|
-
context = {
|
|
942
|
-
"default_contract_line_id": self.id,
|
|
943
|
-
"default_recurring_next_date": fields.Date.context_today(self),
|
|
944
|
-
}
|
|
945
|
-
context.update(self.env.context)
|
|
946
|
-
view_id = self.env.ref("contract.contract_line_wizard_uncancel_form_view").id
|
|
947
|
-
return {
|
|
948
|
-
"type": "ir.actions.act_window",
|
|
949
|
-
"name": "Un-Cancel Contract Line",
|
|
950
|
-
"res_model": "contract.line.wizard",
|
|
951
|
-
"view_mode": "form",
|
|
952
|
-
"views": [(view_id, "form")],
|
|
953
|
-
"target": "new",
|
|
954
|
-
"context": context,
|
|
955
|
-
}
|
|
956
|
-
|
|
957
|
-
def action_plan_successor(self):
|
|
958
|
-
self.ensure_one()
|
|
959
|
-
context = {
|
|
960
|
-
"default_contract_line_id": self.id,
|
|
961
|
-
"default_is_auto_renew": self.is_auto_renew,
|
|
962
|
-
}
|
|
963
|
-
context.update(self.env.context)
|
|
964
|
-
view_id = self.env.ref(
|
|
965
|
-
"contract.contract_line_wizard_plan_successor_form_view"
|
|
966
|
-
).id
|
|
967
|
-
return {
|
|
968
|
-
"type": "ir.actions.act_window",
|
|
969
|
-
"name": "Plan contract line successor",
|
|
970
|
-
"res_model": "contract.line.wizard",
|
|
971
|
-
"view_mode": "form",
|
|
972
|
-
"views": [(view_id, "form")],
|
|
973
|
-
"target": "new",
|
|
974
|
-
"context": context,
|
|
975
|
-
}
|
|
976
|
-
|
|
977
|
-
def action_stop(self):
|
|
978
|
-
self.ensure_one()
|
|
979
|
-
context = {
|
|
980
|
-
"default_contract_line_id": self.id,
|
|
981
|
-
"default_date_end": self.date_end,
|
|
982
|
-
}
|
|
983
|
-
context.update(self.env.context)
|
|
984
|
-
view_id = self.env.ref("contract.contract_line_wizard_stop_form_view").id
|
|
985
|
-
return {
|
|
986
|
-
"type": "ir.actions.act_window",
|
|
987
|
-
"name": "Terminate contract line",
|
|
988
|
-
"res_model": "contract.line.wizard",
|
|
989
|
-
"view_mode": "form",
|
|
990
|
-
"views": [(view_id, "form")],
|
|
991
|
-
"target": "new",
|
|
992
|
-
"context": context,
|
|
993
|
-
}
|
|
994
|
-
|
|
995
|
-
def action_stop_plan_successor(self):
|
|
996
|
-
self.ensure_one()
|
|
997
|
-
context = {
|
|
998
|
-
"default_contract_line_id": self.id,
|
|
999
|
-
"default_is_auto_renew": self.is_auto_renew,
|
|
1000
|
-
}
|
|
1001
|
-
context.update(self.env.context)
|
|
1002
|
-
view_id = self.env.ref(
|
|
1003
|
-
"contract.contract_line_wizard_stop_plan_successor_form_view"
|
|
1004
|
-
).id
|
|
1005
|
-
return {
|
|
1006
|
-
"type": "ir.actions.act_window",
|
|
1007
|
-
"name": "Suspend contract line",
|
|
1008
|
-
"res_model": "contract.line.wizard",
|
|
1009
|
-
"view_mode": "form",
|
|
1010
|
-
"views": [(view_id, "form")],
|
|
1011
|
-
"target": "new",
|
|
1012
|
-
"context": context,
|
|
1013
|
-
}
|
|
1014
|
-
|
|
1015
|
-
def _get_renewal_new_date_end(self):
|
|
1016
|
-
self.ensure_one()
|
|
1017
|
-
date_start = self.date_end + relativedelta(days=1)
|
|
1018
|
-
date_end = self._get_first_date_end(
|
|
1019
|
-
date_start, self.auto_renew_rule_type, self.auto_renew_interval
|
|
1020
|
-
)
|
|
1021
|
-
return date_end
|
|
1022
|
-
|
|
1023
|
-
def _renew_create_line(self, date_end):
|
|
1024
|
-
self.ensure_one()
|
|
1025
|
-
date_start = self.date_end + relativedelta(days=1)
|
|
1026
|
-
is_auto_renew = self.is_auto_renew
|
|
1027
|
-
self.stop(self.date_end, post_message=False)
|
|
1028
|
-
new_line = self.plan_successor(
|
|
1029
|
-
date_start, date_end, is_auto_renew, post_message=False
|
|
1030
|
-
)
|
|
1031
|
-
return new_line
|
|
1032
|
-
|
|
1033
|
-
def _renew_extend_line(self, date_end):
|
|
1034
|
-
self.ensure_one()
|
|
1035
|
-
self.date_end = date_end
|
|
1036
|
-
return self
|
|
1037
|
-
|
|
1038
|
-
def renew(self):
|
|
1039
|
-
res = self.env["contract.line"]
|
|
1040
|
-
for rec in self:
|
|
1041
|
-
company = rec.contract_id.company_id
|
|
1042
|
-
date_end = rec._get_renewal_new_date_end()
|
|
1043
|
-
date_start = rec.date_end + relativedelta(days=1)
|
|
1044
|
-
if company.create_new_line_at_contract_line_renew:
|
|
1045
|
-
new_line = rec._renew_create_line(date_end)
|
|
1046
|
-
else:
|
|
1047
|
-
new_line = rec._renew_extend_line(date_end)
|
|
1048
|
-
res |= new_line
|
|
1049
|
-
msg = _(
|
|
1050
|
-
"""Contract line for <strong>%(product)s</strong>
|
|
1051
|
-
renewed: <br/>
|
|
1052
|
-
- <strong>Start</strong>: %(new_date_start)s
|
|
1053
|
-
<br/>
|
|
1054
|
-
- <strong>End</strong>: %(new_date_end)s
|
|
1055
|
-
"""
|
|
1056
|
-
) % {
|
|
1057
|
-
"product": rec.name,
|
|
1058
|
-
"new_date_start": date_start,
|
|
1059
|
-
"new_date_end": date_end,
|
|
1060
|
-
}
|
|
1061
|
-
rec.contract_id.message_post(body=msg)
|
|
1062
|
-
return res
|
|
1063
|
-
|
|
1064
|
-
@api.model
|
|
1065
|
-
def _contract_line_to_renew_domain(self):
|
|
1066
|
-
return [
|
|
1067
|
-
("contract_id.is_terminated", "=", False),
|
|
1068
|
-
("is_auto_renew", "=", True),
|
|
1069
|
-
("is_canceled", "=", False),
|
|
1070
|
-
("termination_notice_date", "<=", fields.Date.context_today(self)),
|
|
1071
|
-
]
|
|
1072
|
-
|
|
1073
|
-
@api.model
|
|
1074
|
-
def cron_renew_contract_line(self):
|
|
1075
|
-
domain = self._contract_line_to_renew_domain()
|
|
1076
|
-
to_renew = self.search(domain)
|
|
1077
|
-
to_renew.renew()
|
|
1078
|
-
|
|
1079
264
|
@api.model
|
|
1080
265
|
def get_view(self, view_id=None, view_type="form", **options):
|
|
1081
266
|
default_contract_type = self.env.context.get("default_contract_type")
|
|
1082
|
-
if view_type == "
|
|
267
|
+
if view_type == "list" and default_contract_type == "purchase":
|
|
1083
268
|
view_id = self.env.ref("contract.contract_line_supplier_tree_view").id
|
|
1084
269
|
if view_type == "form":
|
|
1085
270
|
if default_contract_type == "purchase":
|
|
@@ -1088,13 +273,6 @@ class ContractLine(models.Model):
|
|
|
1088
273
|
view_id = self.env.ref("contract.contract_line_customer_form_view").id
|
|
1089
274
|
return super().get_view(view_id, view_type, **options)
|
|
1090
275
|
|
|
1091
|
-
def unlink(self):
|
|
1092
|
-
"""stop unlink uncnacled lines"""
|
|
1093
|
-
for record in self:
|
|
1094
|
-
if not (record.is_canceled or record.display_type):
|
|
1095
|
-
raise ValidationError(_("Contract line must be canceled before delete"))
|
|
1096
|
-
return super().unlink()
|
|
1097
|
-
|
|
1098
276
|
def _get_quantity_to_invoice(
|
|
1099
277
|
self, period_first_date, period_last_date, invoice_date
|
|
1100
278
|
):
|