[REF] contract: Several things

[REF] use context_today instead of time in filters
[REF] Contract: split from analytic account
[REF] Contract Sale Invoicing: split from analytic account
[REF] Contract Sale Invoicing: update translations
[IMP] - Assert that the predecessor is available for new link at uncancel
[RMV] - remove usless changes
[RMV] - Remove usless field recurring_invoices
  after the total isolation between contract model and account analytic one.
  recurring_invoices which was used to mark analytic account as contract became usless
[IMP] - P3 syntax
[IMP] - use @openupgrade.migrate() and openupgrade.logged_query
[IMP] - drop transient table in migration script
This commit is contained in:
Thomas Binsfeld
2019-05-28 19:06:11 +02:00
committed by Pedro M. Baeza
parent 6e24d1d83a
commit a3f1cba1d1
30 changed files with 606 additions and 444 deletions

View File

@@ -9,8 +9,8 @@
from odoo import api, models, fields
class AbstractAccountAnalyticContract(models.AbstractModel):
_name = 'account.abstract.analytic.contract'
class ContractAbstractContract(models.AbstractModel):
_name = 'contract.abstract.contract'
_description = 'Abstract Recurring Contract'
# These fields will not be synced to the contract
@@ -50,7 +50,7 @@ class AbstractAccountAnalyticContract(models.AbstractModel):
@api.onchange('contract_type')
def _onchange_contract_type(self):
if self.contract_type == 'purchase':
self.recurring_invoice_line_ids.filtered('automatic_price').update(
self.contract_line_ids.filtered('automatic_price').update(
{'automatic_price': False}
)
self.journal_id = self.env['account.journal'].search(

View File

@@ -12,8 +12,8 @@ from odoo.exceptions import ValidationError
from odoo.tools.translate import _
class AccountAbstractAnalyticContractLine(models.AbstractModel):
_name = 'account.abstract.analytic.contract.line'
class ContractAbstractContractLine(models.AbstractModel):
_name = 'contract.abstract.contract.line'
_description = 'Abstract Recurring Contract Line'
product_id = fields.Many2one(
@@ -110,7 +110,7 @@ class AccountAbstractAnalyticContractLine(models.AbstractModel):
)
contract_id = fields.Many2one(
string='Contract',
comodel_name='account.abstract.analytic.contract',
comodel_name='contract.abstract.contract',
required=True,
ondelete='cascade',
oldname='analytic_account_id',

View File

@@ -9,5 +9,5 @@ class AccountInvoice(models.Model):
# We keep this field for migration purpose
old_contract_id = fields.Many2one(
'account.analytic.account', oldname="contract_id"
'contract.contract', oldname="contract_id"
)

View File

@@ -8,5 +8,5 @@ class AccountInvoiceLine(models.Model):
_inherit = 'account.invoice.line'
contract_line_id = fields.Many2one(
'account.analytic.invoice.line', string='Contract Line', index=True
'contract.line', string='Contract Line', index=True
)

View File

@@ -11,25 +11,41 @@ from odoo.exceptions import ValidationError
from odoo.tools.translate import _
class AccountAnalyticAccount(models.Model):
_name = 'account.analytic.account'
_inherit = [
'account.analytic.account',
'account.abstract.analytic.contract',
]
class ContractContract(models.Model):
_name = 'contract.contract'
_inherit = ['mail.thread', 'contract.abstract.contract']
contract_template_id = fields.Many2one(
string='Contract Template', comodel_name='account.analytic.contract'
active = fields.Boolean(
default=True,
)
recurring_invoice_line_ids = fields.One2many(
string='Invoice Lines',
comodel_name='account.analytic.invoice.line',
code = fields.Char(
string="Reference",
)
group_id = fields.Many2one(
string="Group",
comodel_name='account.analytic.account',
ondelete='restrict',
)
analytic_account_id = fields.Many2one(
string="Analytic account",
comodel_name='account.analytic.account',
)
currency_id = fields.Many2one(
related="company_id.currency_id",
string="Currency",
readonly=True,
)
contract_template_id = fields.Many2one(
string='Contract Template', comodel_name='contract.template'
)
contract_line_ids = fields.One2many(
string='Contract lines',
comodel_name='contract.line',
inverse_name='contract_id',
copy=True,
oldnae='contract_line_ids',
)
recurring_invoices = fields.Boolean(
string='Generate recurring invoices automatically'
)
user_id = fields.Many2one(
comodel_name='res.users',
string='Responsible',
@@ -62,7 +78,9 @@ class AccountAnalyticAccount(models.Model):
ondelete='restrict',
)
partner_id = fields.Many2one(
comodel_name='res.partner', inverse='_inverse_partner_id'
comodel_name='res.partner',
inverse='_inverse_partner_id',
required=True
)
@api.multi
@@ -84,7 +102,7 @@ class AccountAnalyticAccount(models.Model):
(
'contract_line_id',
'in',
self.recurring_invoice_line_ids.ids,
self.contract_line_ids.ids,
)
]
)
@@ -127,31 +145,31 @@ class AccountAnalyticAccount(models.Model):
action['views'] = [(tree_view.id, 'tree'), (form_view.id, 'form')]
return action
@api.depends('recurring_invoice_line_ids.date_end')
@api.depends('contract_line_ids.date_end')
def _compute_date_end(self):
for contract in self:
contract.date_end = False
date_end = contract.recurring_invoice_line_ids.mapped('date_end')
date_end = contract.contract_line_ids.mapped('date_end')
if date_end and all(date_end):
contract.date_end = max(date_end)
@api.depends(
'recurring_invoice_line_ids.recurring_next_date',
'recurring_invoice_line_ids.is_canceled',
'contract_line_ids.recurring_next_date',
'contract_line_ids.is_canceled',
)
def _compute_recurring_next_date(self):
for contract in self:
recurring_next_date = contract.recurring_invoice_line_ids.filtered(
recurring_next_date = contract.contract_line_ids.filtered(
lambda l: l.recurring_next_date and not l.is_canceled
).mapped('recurring_next_date')
if recurring_next_date:
contract.recurring_next_date = min(recurring_next_date)
@api.depends('recurring_invoice_line_ids.create_invoice_visibility')
@api.depends('contract_line_ids.create_invoice_visibility')
def _compute_create_invoice_visibility(self):
for contract in self:
contract.create_invoice_visibility = any(
contract.recurring_invoice_line_ids.mapped(
contract.contract_line_ids.mapped(
'create_invoice_visibility'
)
)
@@ -160,7 +178,7 @@ class AccountAnalyticAccount(models.Model):
def _onchange_contract_template_id(self):
"""Update the contract fields with that of the template.
Take special consideration with the `recurring_invoice_line_ids`,
Take special consideration with the `contract_line_ids`,
which must be created using the data from the contract lines. Cascade
deletion ensures that any errant lines that are created are also
deleted.
@@ -169,9 +187,9 @@ class AccountAnalyticAccount(models.Model):
if not contract_template_id:
return
for field_name, field in contract_template_id._fields.items():
if field.name == 'recurring_invoice_line_ids':
if field.name == 'contract_line_ids':
lines = self._convert_contract_lines(contract_template_id)
self.recurring_invoice_line_ids += lines
self.contract_line_ids += lines
elif not any(
(
field.compute,
@@ -207,21 +225,12 @@ class AccountAnalyticAccount(models.Model):
}
}
@api.constrains('partner_id', 'recurring_invoices')
def _check_partner_id_recurring_invoices(self):
for contract in self.filtered('recurring_invoices'):
if not contract.partner_id:
raise ValidationError(
_("You must supply a partner for the contract '%s'")
% contract.name
)
@api.multi
def _convert_contract_lines(self, contract):
self.ensure_one()
new_lines = self.env['account.analytic.invoice.line']
contract_line_model = self.env['account.analytic.invoice.line']
for contract_line in contract.recurring_invoice_line_ids:
new_lines = self.env['contract.line']
contract_line_model = self.env['contract.line']
for contract_line in contract.contract_line_ids:
vals = contract_line._convert_to_write(contract_line.read()[0])
# Remove template link field
vals.pop('contract_template_id', False)
@@ -282,7 +291,7 @@ class AccountAnalyticAccount(models.Model):
template = self.env.ref('contract.email_contract_template', False)
compose_form = self.env.ref('mail.email_compose_message_wizard_form')
ctx = dict(
default_model='account.analytic.account',
default_model='contract.contract',
default_res_id=self.id,
default_use_template=bool(template),
default_template_id=template and template.id or False,
@@ -368,19 +377,14 @@ class AccountAnalyticAccount(models.Model):
def _get_contracts_to_invoice_domain(self, date_ref=None):
"""
This method builds the domain to use to find all
contracts (account.analytic.account) to invoice.
contracts (contract.contract) to invoice.
:param date_ref: optional reference date to use instead of today
:return: list (domain) usable on account.analytic.account
:return: list (domain) usable on contract.contract
"""
domain = []
if not date_ref:
date_ref = fields.Date.context_today(self)
domain.extend(
[
('recurring_invoices', '=', True),
('recurring_next_date', '<=', date_ref),
]
)
domain.extend([('recurring_next_date', '<=', date_ref)])
return domain
@api.multi
@@ -389,10 +393,10 @@ class AccountAnalyticAccount(models.Model):
This method fetches and returns the lines to invoice on the contract
(self), based on the given date.
:param date_ref: date used as reference date to find lines to invoice
:return: contract lines (account.analytic.invoice.line recordset)
:return: contract lines (contract.line recordset)
"""
self.ensure_one()
return self.recurring_invoice_line_ids.filtered(
return self.contract_line_ids.filtered(
lambda l: not l.is_canceled
and l.recurring_next_date
and l.recurring_next_date <= date_ref

View File

@@ -11,12 +11,15 @@ from odoo.exceptions import ValidationError
from .contract_line_constraints import get_allowed
class AccountAnalyticInvoiceLine(models.Model):
_name = 'account.analytic.invoice.line'
_inherit = 'account.abstract.analytic.contract.line'
class ContractLine(models.Model):
_name = 'contract.line'
_inherit = 'contract.abstract.contract.line'
sequence = fields.Integer(
string="Sequence",
)
contract_id = fields.Many2one(
comodel_name='account.analytic.account',
comodel_name='contract.contract',
string='Contract',
required=True,
index=True,
@@ -43,7 +46,7 @@ class AccountAnalyticInvoiceLine(models.Model):
compute='_compute_create_invoice_visibility'
)
successor_contract_line_id = fields.Many2one(
comodel_name='account.analytic.invoice.line',
comodel_name='contract.line',
string="Successor Contract Line",
required=False,
readonly=True,
@@ -53,7 +56,7 @@ class AccountAnalyticInvoiceLine(models.Model):
"contract line created.",
)
predecessor_contract_line_id = fields.Many2one(
comodel_name='account.analytic.invoice.line',
comodel_name='contract.line',
string="Predecessor Contract Line",
required=False,
readonly=True,
@@ -452,7 +455,7 @@ class AccountAnalyticInvoiceLine(models.Model):
@api.constrains('recurring_next_date')
def _check_recurring_next_date_recurring_invoices(self):
for rec in self.filtered('contract_id.recurring_invoices'):
for rec in self:
if not rec.recurring_next_date and (
not rec.date_end
or not rec.last_date_invoiced
@@ -511,12 +514,11 @@ class AccountAnalyticInvoiceLine(models.Model):
invoice_line._onchange_product_id()
invoice_line_vals = invoice_line._convert_to_write(invoice_line._cache)
# Insert markers
contract = self.contract_id
name = self._insert_markers(dates[0], dates[1])
invoice_line_vals.update(
{
'name': name,
'account_analytic_id': contract.id,
'account_analytic_id': self.contract_id.analytic_account_id.id,
'price_unit': self.price_unit,
}
)
@@ -737,7 +739,7 @@ class AccountAnalyticInvoiceLine(models.Model):
successor_contract_line
:return: successor_contract_line
"""
contract_line = self.env['account.analytic.invoice.line']
contract_line = self.env['contract.line']
for rec in self:
if not rec.is_plan_successor_allowed:
raise ValidationError(
@@ -804,7 +806,7 @@ class AccountAnalyticInvoiceLine(models.Model):
raise ValidationError(
_('Stop/Plan successor not allowed for this line')
)
contract_line = self.env['account.analytic.invoice.line']
contract_line = self.env['contract.line']
for rec in self:
if rec.date_start >= date_start:
if rec.date_start < date_end:
@@ -914,9 +916,9 @@ class AccountAnalyticInvoiceLine(models.Model):
contract.message_post(body=msg)
for rec in self:
if rec.predecessor_contract_line_id:
rec.predecessor_contract_line_id.successor_contract_line_id = (
rec
)
predecessor_contract_line = rec.predecessor_contract_line_id
assert not predecessor_contract_line.successor_contract_line_id
predecessor_contract_line.successor_contract_line_id = rec
rec.is_canceled = False
rec.recurring_next_date = recurring_next_date
return True
@@ -935,7 +937,7 @@ class AccountAnalyticInvoiceLine(models.Model):
return {
'type': 'ir.actions.act_window',
'name': 'Un-Cancel Contract Line',
'res_model': 'account.analytic.invoice.line.wizard',
'res_model': 'contract.line.wizard',
'view_type': 'form',
'view_mode': 'form',
'views': [(view_id, 'form')],
@@ -957,7 +959,7 @@ class AccountAnalyticInvoiceLine(models.Model):
return {
'type': 'ir.actions.act_window',
'name': 'Plan contract line successor',
'res_model': 'account.analytic.invoice.line.wizard',
'res_model': 'contract.line.wizard',
'view_type': 'form',
'view_mode': 'form',
'views': [(view_id, 'form')],
@@ -979,7 +981,7 @@ class AccountAnalyticInvoiceLine(models.Model):
return {
'type': 'ir.actions.act_window',
'name': 'Resiliate contract line',
'res_model': 'account.analytic.invoice.line.wizard',
'res_model': 'contract.line.wizard',
'view_type': 'form',
'view_mode': 'form',
'views': [(view_id, 'form')],
@@ -1001,7 +1003,7 @@ class AccountAnalyticInvoiceLine(models.Model):
return {
'type': 'ir.actions.act_window',
'name': 'Suspend contract line',
'res_model': 'account.analytic.invoice.line.wizard',
'res_model': 'contract.line.wizard',
'view_type': 'form',
'view_mode': 'form',
'views': [(view_id, 'form')],
@@ -1020,7 +1022,7 @@ class AccountAnalyticInvoiceLine(models.Model):
@api.multi
def renew(self):
res = self.env['account.analytic.invoice.line']
res = self.env['contract.line']
for rec in self:
is_auto_renew = rec.is_auto_renew
rec.stop(rec.date_end, post_message=False)
@@ -1050,7 +1052,6 @@ class AccountAnalyticInvoiceLine(models.Model):
return [
('is_auto_renew', '=', True),
('is_canceled', '=', False),
('contract_id.recurring_invoices', '=', True),
('termination_notice_date', '<=', fields.Date.context_today(self)),
]
@@ -1067,18 +1068,18 @@ class AccountAnalyticInvoiceLine(models.Model):
default_contract_type = self.env.context.get('default_contract_type')
if view_type == 'tree' and default_contract_type == 'purchase':
view_id = self.env.ref(
'contract.account_analytic_invoice_line_purchase_view_tree'
'contract.contract_line_supplier_tree_view'
).id
if view_type == 'form':
if default_contract_type == 'purchase':
view_id = self.env.ref(
'contract.account_analytic_invoice_line_purchase_view_form'
'contract.contract_line_supplier_form_view'
).id
elif default_contract_type == 'sale':
view_id = self.env.ref(
'contract.account_analytic_invoice_line_sale_view_form'
'contract.contract_line_customer_form_view'
).id
return super(AccountAnalyticInvoiceLine, self).fields_view_get(
return super().fields_view_get(
view_id, view_type, toolbar, submenu
)
@@ -1089,7 +1090,7 @@ class AccountAnalyticInvoiceLine(models.Model):
raise ValidationError(
_("Contract line must be canceled before delete")
)
return super(AccountAnalyticInvoiceLine, self).unlink()
return super().unlink()
@api.multi
def _get_quantity_to_invoice(

View File

@@ -9,14 +9,15 @@
from odoo import fields, models
class AccountAnalyticContract(models.Model):
_name = 'account.analytic.contract'
_inherit = 'account.abstract.analytic.contract'
_description = "Account Analytic Contract"
class ContractTemplate(models.Model):
_name = 'contract.template'
_inherit = 'contract.abstract.contract'
_description = "Contract Template"
recurring_invoice_line_ids = fields.One2many(
comodel_name='account.analytic.contract.line',
contract_line_ids = fields.One2many(
comodel_name='contract.template.line',
inverse_name='contract_id',
copy=True,
string='Invoice Lines',
string='Contract template lines',
oldname='contract_line_ids',
)

View File

@@ -9,15 +9,15 @@
from odoo import fields, models
class AccountAnalyticContractLine(models.Model):
_name = 'account.analytic.contract.line'
_inherit = 'account.abstract.analytic.contract.line'
_description = 'Contract Lines'
class ContractTemplateLine(models.Model):
_name = 'contract.template.line'
_inherit = 'contract.abstract.contract.line'
_description = "Contract Template Line"
_order = "sequence,id"
contract_id = fields.Many2one(
string='Contract',
comodel_name='account.analytic.contract',
comodel_name='contract.template',
required=True,
ondelete='cascade',
oldname='analytic_account_id',

View File

@@ -8,43 +8,30 @@ class ResPartner(models.Model):
_inherit = 'res.partner'
sale_contract_count = fields.Integer(
string='Sale Contracts', compute='_compute_contract_count'
string='Sale Contracts',
compute='_compute_contract_count',
)
purchase_contract_count = fields.Integer(
string='Purchase Contracts', compute='_compute_contract_count'
string='Purchase Contracts',
compute='_compute_contract_count',
)
def _compute_contract_count(self):
contract_model = self.env['account.analytic.account']
fetch_data = contract_model.read_group(
[
('recurring_invoices', '=', True),
('partner_id', 'child_of', self.ids),
],
['partner_id', 'contract_type'],
['partner_id', 'contract_type'],
lazy=False,
)
result = [
[data['partner_id'][0], data['contract_type'], data['__count']]
for data in fetch_data
]
contract_model = self.env['contract.contract']
fetch_data = contract_model.read_group([
('partner_id', 'child_of', self.ids)],
['partner_id', 'contract_type'], ['partner_id', 'contract_type'],
lazy=False)
result = [[data['partner_id'][0], data['contract_type'],
data['__count']] for data in fetch_data]
for partner in self:
partner_child_ids = partner.child_ids.ids + partner.ids
partner.sale_contract_count = sum(
[
r[2]
for r in result
if r[0] in partner_child_ids and r[1] == 'sale'
]
)
partner.purchase_contract_count = sum(
[
r[2]
for r in result
if r[0] in partner_child_ids and r[1] == 'purchase'
]
)
partner.sale_contract_count = sum([
r[2] for r in result
if r[0] in partner_child_ids and r[1] == 'sale'])
partner.purchase_contract_count = sum([
r[2] for r in result
if r[0] in partner_child_ids and r[1] == 'purchase'])
def act_show_contract(self):
""" This opens contract view
@@ -57,21 +44,19 @@ class ResPartner(models.Model):
res.update(
context=dict(
self.env.context,
search_default_recurring_invoices=True,
search_default_partner_id=self.id,
default_partner_id=self.id,
default_recurring_invoices=True,
default_pricelist_id=self.property_product_pricelist.id,
)
),
)
return res
def _get_act_window_contract_xml(self, contract_type):
if contract_type == 'purchase':
return self.env['ir.actions.act_window'].for_xml_id(
'contract', 'action_account_analytic_purchase_overdue_all'
'contract', 'action_supplier_contract'
)
else:
return self.env['ir.actions.act_window'].for_xml_id(
'contract', 'action_account_analytic_sale_overdue_all'
'contract', 'action_customer_contract'
)