Skip to content

Commit

Permalink
[IMP+REF] contract: Allow to set recurrency at header level
Browse files Browse the repository at this point in the history
Big refactoring for allowing to define recurrency at header level for simplifying
the use of the module for most of the cases where you don't need different
recurrency at line level.
  • Loading branch information
pedrobaeza committed Oct 24, 2020
1 parent 321c688 commit cd086dd
Show file tree
Hide file tree
Showing 12 changed files with 536 additions and 340 deletions.
1 change: 1 addition & 0 deletions contract/README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ Known issues / Roadmap
======================

* Recover states and others functional fields in Contracts.
* Add recurrence flag at template level.

Bug Tracker
===========
Expand Down
1 change: 1 addition & 0 deletions contract/models/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).

from . import contract_recurrency_mixin # should be first
from . import abstract_contract
from . import abstract_contract_line
from . import contract_template
Expand Down
39 changes: 21 additions & 18 deletions contract/models/abstract_contract.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@


class ContractAbstractContract(models.AbstractModel):
_inherit = "contract.recurrency.basic.mixin"
_name = "contract.abstract.contract"
_description = "Abstract Recurring Contract"

Expand All @@ -27,12 +28,13 @@ class ContractAbstractContract(models.AbstractModel):
default="sale",
index=True,
)

journal_id = fields.Many2one(
"account.journal",
comodel_name="account.journal",
string="Journal",
default=lambda s: s._default_journal(),
domain="[('type', '=', contract_type)," "('company_id', '=', company_id)]",
compute="_compute_journal_id",
store=True,
readonly=False,
index=True,
)
company_id = fields.Many2one(
Expand All @@ -41,26 +43,27 @@ class ContractAbstractContract(models.AbstractModel):
required=True,
default=lambda self: self.env.company.id,
)
line_recurrence = fields.Boolean(
string="Recurrence at line level?",
help="Mark this check if you want to control recurrrence at line level instead"
" of all together for the whole contract.",
)

@api.onchange("contract_type")
def _onchange_contract_type(self):
if self.contract_type == "purchase":
self.contract_line_ids.filtered("automatic_price").update(
{"automatic_price": False}
)
self.journal_id = self.env["account.journal"].search(
[
("type", "=", self.contract_type),
("company_id", "=", self.company_id.id),
],
limit=1,
)

@api.model
def _default_journal(self):
company_id = self.env.context.get("company_id", self.env.user.company_id.id)
domain = [
("type", "=", self.contract_type),
("company_id", "=", company_id),
]
return self.env["account.journal"].search(domain, limit=1)
@api.depends("contract_type", "company_id")
def _compute_journal_id(self):
AccountJournal = self.env["account.journal"]
for contract in self:
domain = [
("type", "=", contract.contract_type),
("company_id", "=", contract.company_id.id),
]
journal = AccountJournal.search(domain, limit=1)
if journal:
contract.journal_id = journal.id
99 changes: 48 additions & 51 deletions contract/models/abstract_contract_line.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@


class ContractAbstractContractLine(models.AbstractModel):
_inherit = "contract.recurrency.basic.mixin"
_name = "contract.abstract.contract.line"
_description = "Abstract Recurring Contract Line"

Expand Down Expand Up @@ -47,46 +48,29 @@ class ContractAbstractContractLine(models.AbstractModel):
help="Sequence of the contract line when displaying contracts",
)
recurring_rule_type = fields.Selection(
[
("daily", "Day(s)"),
("weekly", "Week(s)"),
("monthly", "Month(s)"),
("monthlylastday", "Month(s) last day"),
("quarterly", "Quarter(s)"),
("semesterly", "Semester(s)"),
("yearly", "Year(s)"),
],
default="monthly",
string="Recurrence",
help="Specify Interval for automatic invoice generation.",
compute="_compute_recurring_rule_type",
store=True,
readonly=False,
required=True,
copy=True,
)
recurring_invoicing_type = fields.Selection(
[("pre-paid", "Pre-paid"), ("post-paid", "Post-paid")],
default="pre-paid",
string="Invoicing type",
help=(
"Specify if the invoice must be generated at the beginning "
"(pre-paid) or end (post-paid) of the period."
),
compute="_compute_recurring_invoicing_type",
store=True,
readonly=False,
required=True,
)
recurring_invoicing_offset = fields.Integer(
compute="_compute_recurring_invoicing_offset",
string="Invoicing offset",
help=(
"Number of days to offset the invoice from the period end "
"date (in post-paid mode) or start date (in pre-paid mode)."
),
copy=True,
)
recurring_interval = fields.Integer(
default=1,
string="Invoice Every",
help="Invoice every (Days/Week/Month/Year)",
compute="_compute_recurring_interval",
store=True,
readonly=False,
required=True,
copy=True,
)
date_start = fields.Date(
compute="_compute_date_start", store=True, readonly=False, copy=True,
)
date_start = fields.Date(string="Date Start")
recurring_next_date = fields.Date(string="Date of Next Invoice")
last_date_invoiced = fields.Date(string="Last Date Invoiced")
is_canceled = fields.Boolean(string="Canceled", default=False)
is_auto_renew = fields.Boolean(string="Auto Renew", default=False)
Expand Down Expand Up @@ -138,17 +122,38 @@ class ContractAbstractContractLine(models.AbstractModel):
is_recurring_note = fields.Boolean(compute="_compute_is_recurring_note")
company_id = fields.Many2one(related="contract_id.company_id", store=True)

@api.model
def _get_default_recurring_invoicing_offset(
self, recurring_invoicing_type, recurring_rule_type
):
if (
recurring_invoicing_type == "pre-paid"
or recurring_rule_type == "monthlylastday"
):
return 0
else:
return 1
def _set_recurrence_field(self, field):
"""Helper method for computed methods that gets the equivalent field
in the header.
We need to re-assign the original value for avoiding a missing error.
"""
for record in self:
if record.contract_id.line_recurrence:
record[field] = record[field]
else:
record[field] = record.contract_id[field]

@api.depends("contract_id.recurring_rule_type", "contract_id.line_recurrence")
def _compute_recurring_rule_type(self):
self._set_recurrence_field("recurring_rule_type")

@api.depends("contract_id.recurring_invoicing_type", "contract_id.line_recurrence")
def _compute_recurring_invoicing_type(self):
self._set_recurrence_field("recurring_invoicing_type")

@api.depends("contract_id.recurring_interval", "contract_id.line_recurrence")
def _compute_recurring_interval(self):
self._set_recurrence_field("recurring_interval")

@api.depends("contract_id.date_start", "contract_id.line_recurrence")
def _compute_date_start(self):
self._set_recurrence_field("date_start")

@api.depends("contract_id.recurring_next_date", "contract_id.line_recurrence")
def _compute_recurring_next_date(self):
super()._compute_recurring_next_date()
self._set_recurrence_field("recurring_next_date")

@api.depends("display_type", "note_invoicing_mode")
def _compute_is_recurring_note(self):
Expand All @@ -158,14 +163,6 @@ def _compute_is_recurring_note(self):
and record.note_invoicing_mode == "custom"
)

@api.depends("recurring_invoicing_type", "recurring_rule_type")
def _compute_recurring_invoicing_offset(self):
for rec in self:
method = self._get_default_recurring_invoicing_offset
rec.recurring_invoicing_offset = method(
rec.recurring_invoicing_type, rec.recurring_rule_type
)

@api.depends(
"automatic_price",
"specific_price",
Expand Down
2 changes: 1 addition & 1 deletion contract/models/account_move.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from odoo import fields, models


class AccountInvoice(models.Model):
class AccountMove(models.Model):
_inherit = "account.move"

# We keep this field for migration purpose
Expand Down
32 changes: 22 additions & 10 deletions contract/models/contract.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ class ContractContract(models.Model):
"mail.thread",
"mail.activity.mixin",
"contract.abstract.contract",
"contract.recurrency.mixin",
]

active = fields.Boolean(default=True,)
Expand All @@ -43,6 +44,16 @@ class ContractContract(models.Model):
inverse_name="contract_id",
copy=True,
)
# Trick for being able to have 2 different views for the same o2m
# We need this as one2many widget doesn't allow to define in the view
# the same field 2 times with different views. 2 views are needed because
# one of them must be editable inline and the other not, which can't be
# parametrized through attrs.
contract_line_fixed_ids = fields.One2many(
string="Contract lines (fixed)",
comodel_name="contract.line",
inverse_name="contract_id",
)

user_id = fields.Many2one(
comodel_name="res.users",
Expand All @@ -53,12 +64,7 @@ class ContractContract(models.Model):
create_invoice_visibility = fields.Boolean(
compute="_compute_create_invoice_visibility"
)
recurring_next_date = fields.Date(
compute="_compute_recurring_next_date",
string="Date of Next Invoice",
store=True,
)
date_end = fields.Date(compute="_compute_date_end", string="Date End", store=True)
date_end = fields.Date(compute="_compute_date_end", store=True, readonly=False)
payment_term_id = fields.Many2one(
comodel_name="account.payment.term", string="Payment Terms", index=True
)
Expand Down Expand Up @@ -122,6 +128,8 @@ def _get_related_invoices(self):
.search([("contract_line_id", "in", self.contract_line_ids.ids,)])
.mapped("move_id")
)
# we are forced to always search for this for not losing possible <=v11
# generated invoices
invoices |= self.env["account.move"].search([("old_contract_id", "=", self.id)])
return invoices

Expand Down Expand Up @@ -198,10 +206,15 @@ def _compute_recurring_next_date(self):
and (not l.display_type or l.is_recurring_note)
)
).mapped("recurring_next_date")
if recurring_next_date:
contract.recurring_next_date = min(recurring_next_date)
# we give priority to computation from date_start if modified
if (
contract._origin
and contract._origin.date_start != contract.date_start
or not recurring_next_date
):
super(ContractContract, contract)._compute_recurring_next_date()
else:
contract.recurring_next_date = False
contract.recurring_next_date = min(recurring_next_date)

@api.depends("contract_line_ids.create_invoice_visibility")
def _compute_create_invoice_visibility(self):
Expand Down Expand Up @@ -276,7 +289,6 @@ def _convert_contract_lines(self, contract):
vals["date_start"] = fields.Date.context_today(contract_line)
vals["recurring_next_date"] = fields.Date.context_today(contract_line)
new_lines += contract_line_model.new(vals)
new_lines._onchange_date_start()
new_lines._onchange_is_auto_renew()
return new_lines

Expand Down
Loading

0 comments on commit cd086dd

Please sign in to comment.