diff --git a/contract/__manifest__.py b/contract/__manifest__.py
index 10d14ddc..dcf9aac9 100644
--- a/contract/__manifest__.py
+++ b/contract/__manifest__.py
@@ -33,7 +33,6 @@
'views/contract.xml',
'views/contract_template_line.xml',
'views/contract_template.xml',
- 'views/account_invoice_view.xml',
'views/contract_line.xml',
'views/res_partner_view.xml',
],
diff --git a/contract/migrations/12.0.2.0.0/pre-migration.py b/contract/migrations/12.0.2.0.0/pre-migration.py
index 5262b394..0d53bb05 100644
--- a/contract/migrations/12.0.2.0.0/pre-migration.py
+++ b/contract/migrations/12.0.2.0.0/pre-migration.py
@@ -20,6 +20,5 @@ def migrate(cr, version):
)
cr.execute(
"UPDATE account_analytic_account set recurring_next_date=null "
- "where id in (%)"
- % ','.join(finished_contract.ids)
+ "where id in (%)" % ','.join(finished_contract.ids)
)
diff --git a/contract/models/__init__.py b/contract/models/__init__.py
index 0bbc06e9..28dc9922 100644
--- a/contract/models/__init__.py
+++ b/contract/models/__init__.py
@@ -7,4 +7,5 @@ from . import contract
from . import contract_template_line
from . import contract_line
from . import account_invoice
+from . import account_invoice_line
from . import res_partner
diff --git a/contract/models/account_invoice.py b/contract/models/account_invoice.py
index b651ea2a..7d9f4ceb 100644
--- a/contract/models/account_invoice.py
+++ b/contract/models/account_invoice.py
@@ -7,6 +7,7 @@ from odoo import fields, models
class AccountInvoice(models.Model):
_inherit = 'account.invoice'
- contract_id = fields.Many2one(
- 'account.analytic.account', string='Contract'
+ # We keep this field for migration purpose
+ old_contract_id = fields.Many2one(
+ 'account.analytic.account', oldname="contract_id"
)
diff --git a/contract/models/account_invoice_line.py b/contract/models/account_invoice_line.py
new file mode 100644
index 00000000..30e1829c
--- /dev/null
+++ b/contract/models/account_invoice_line.py
@@ -0,0 +1,12 @@
+# Copyright 2018 ACSONE SA/NV.
+# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
+
+from odoo import fields, models
+
+
+class AccountInvoiceLine(models.Model):
+ _inherit = 'account.invoice.line'
+
+ contract_line_id = fields.Many2one(
+ 'account.analytic.invoice.line', string='Contract Line', index=True
+ )
diff --git a/contract/models/contract.py b/contract/models/contract.py
index 55e5acf8..63d02038 100644
--- a/contract/models/contract.py
+++ b/contract/models/contract.py
@@ -50,6 +50,46 @@ class AccountAnalyticAccount(models.Model):
payment_term_id = fields.Many2one(
comodel_name='account.payment.term', string='Payment Terms'
)
+ invoice_count = fields.Integer(compute="_compute_invoice_count")
+
+ @api.multi
+ def _get_related_invoices(self):
+ self.ensure_one()
+
+ invoices = (
+ self.env['account.invoice.line']
+ .search(
+ [
+ (
+ 'contract_line_id',
+ 'in',
+ self.recurring_invoice_line_ids.ids,
+ )
+ ]
+ )
+ .mapped('invoice_id')
+ )
+ invoices |= self.env['account.invoice'].search(
+ [('old_contract_id', '=', self.id)]
+ )
+ return invoices
+
+ @api.multi
+ def _compute_invoice_count(self):
+ for rec in self:
+ rec.invoice_count = len(rec._get_related_invoices())
+
+ @api.multi
+ def action_show_invoices(self):
+ self.ensure_one()
+ return {
+ 'type': 'ir.actions.act_window',
+ 'name': 'Invoices',
+ 'res_model': 'account.invoice',
+ 'view_type': 'form',
+ 'view_mode': 'tree,kanban,form,calendar,pivot,graph,activity',
+ 'domain': [('id', 'in', self._get_related_invoices().ids)],
+ }
@api.depends('recurring_invoice_line_ids.date_end')
def _compute_date_end(self):
@@ -185,7 +225,6 @@ class AccountAnalyticAccount(models.Model):
'journal_id': journal.id,
'origin': self.name,
'company_id': self.company_id.id,
- 'contract_id': self.id,
'user_id': self.partner_id.user_id.id,
}
diff --git a/contract/models/contract_line.py b/contract/models/contract_line.py
index b2d51428..2b000d26 100644
--- a/contract/models/contract_line.py
+++ b/contract/models/contract_line.py
@@ -1,4 +1,5 @@
# Copyright 2017 LasLabs Inc.
+# Copyright 2018 ACSONE SA/NV.
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from datetime import timedelta
@@ -40,6 +41,7 @@ class AccountAnalyticInvoiceLine(models.Model):
string="Successor Contract Line",
required=False,
readonly=True,
+ index=True,
copy=False,
help="In case of restart after suspension, this field contain the new "
"contract line created.",
@@ -49,9 +51,11 @@ class AccountAnalyticInvoiceLine(models.Model):
string="Predecessor Contract Line",
required=False,
readonly=True,
+ index=True,
copy=False,
help="Contract Line origin of this one.",
)
+ is_suspended = fields.Boolean(string="Suspended", default=False)
is_plan_successor_allowed = fields.Boolean(
string="Plan successor allowed?", compute='_compute_allowed'
)
@@ -72,11 +76,14 @@ class AccountAnalyticInvoiceLine(models.Model):
selection=[
('upcoming', 'Upcoming'),
('in-progress', 'In-progress'),
+ ('suspension-planed', 'Suspension Planed'),
+ ('suspended', 'Suspended'),
('upcoming-close', 'Upcoming Close'),
('closed', 'Closed'),
('canceled', 'Canceled'),
],
compute="_compute_state",
+ search='_search_state',
)
active = fields.Boolean(
string="Active",
@@ -90,20 +97,135 @@ class AccountAnalyticInvoiceLine(models.Model):
def _compute_state(self):
today = fields.Date.context_today(self)
for rec in self:
- if rec.date_start:
- if rec.is_canceled:
- rec.state = 'canceled'
- elif today < rec.date_start:
- rec.state = 'upcoming'
- elif not rec.date_end or (
- today <= rec.date_end and rec.is_auto_renew
- ):
- rec.state = 'in-progress'
- elif today <= rec.date_end and not rec.is_auto_renew:
+ if rec.is_canceled:
+ rec.state = 'canceled'
+ continue
+
+ if rec.date_start and rec.date_start > today:
+ # Before period
+ rec.state = 'upcoming'
+ continue
+ if (
+ rec.date_start
+ and rec.date_start <= today
+ and (not rec.date_end or rec.date_end >= today)
+ ):
+ # In period
+ if rec.is_suspended:
+ rec.state = 'suspension-planed'
+ continue
+ if rec.date_end and not rec.is_auto_renew:
rec.state = 'upcoming-close'
+ else:
+ rec.state = 'in-progress'
+ continue
+ if rec.date_end and rec.date_end < today:
+ if rec.is_suspended and not rec.successor_contract_line_id:
+ rec.state = 'suspended'
else:
rec.state = 'closed'
+ @api.model
+ def _get_state_domain(self, state):
+ today = fields.Date.context_today(self)
+ if state == 'upcoming':
+ return [
+ "&",
+ ('date_start', '>', today),
+ ('is_canceled', '=', False),
+ ]
+ if state == 'in-progress':
+ return [
+ "&",
+ "&",
+ "&",
+ "&",
+ ('date_start', '<=', today),
+ ('is_auto_renew', '=', True),
+ ('is_suspended', '=', False),
+ ('is_canceled', '=', False),
+ "|",
+ ('date_end', '>=', today),
+ ('date_end', '=', False),
+ ]
+ if state == 'suspension-planed':
+ return [
+ "&",
+ "&",
+ "&",
+ ('date_start', '<=', today),
+ ('is_suspended', '=', True),
+ ('is_canceled', '=', False),
+ '|',
+ ('date_end', '>=', today),
+ ('date_end', '=', False),
+ ]
+ if state == 'suspended':
+ return [
+ "&",
+ "&",
+ "&",
+ ('date_end', '<', today),
+ ('successor_contract_line_id', '=', False),
+ ('is_suspended', '=', True),
+ ('is_canceled', '=', False),
+ ]
+ if state == 'upcoming-close':
+ return [
+ "&",
+ "&",
+ "&",
+ "&",
+ ('date_start', '<=', today),
+ ('is_auto_renew', '=', False),
+ ('is_suspended', '=', False),
+ ('is_canceled', '=', False),
+ '|',
+ ('date_end', '>=', today),
+ ('date_end', '=', False),
+ ]
+ if state == 'closed':
+ return [
+ "&",
+ "&",
+ ('is_canceled', '=', False),
+ ('date_end', '<', today),
+ "|",
+ "&",
+ ('is_suspended', '=', True),
+ ('successor_contract_line_id', '!=', False),
+ ('is_suspended', '=', False),
+ ]
+
+ if state == 'canceled':
+ return [('is_canceled', '=', True)]
+
+ @api.model
+ def _search_state(self, operator, value):
+ if operator == '!=' and not value:
+ return []
+ if operator == '=' and not value:
+ return [('id', '=', False)]
+ if operator == '=':
+ return self._get_state_domain(value)
+ if operator == '!=':
+ states = [
+ 'upcoming',
+ 'in-progress',
+ 'suspension-planed',
+ 'suspended',
+ 'upcoming-close',
+ 'closed',
+ 'canceled',
+ ]
+ domain = []
+ for state in states:
+ if state != value:
+ if domain:
+ domain.insert(0, '|')
+ domain.extend(self._get_state_domain(state))
+ return domain
+
@api.depends(
'date_start',
'date_end',
@@ -316,6 +438,7 @@ class AccountAnalyticInvoiceLine(models.Model):
'quantity': self.quantity,
'uom_id': self.uom_id.id,
'discount': self.discount,
+ 'contract_line_id': self.id,
}
if invoice_id:
invoice_line_vals['invoice_id'] = invoice_id.id
@@ -460,7 +583,7 @@ class AccountAnalyticInvoiceLine(models.Model):
rec.date_start = new_date_start
@api.multi
- def stop(self, date_end, post_message=True):
+ def stop(self, date_end, is_suspended=False, post_message=True):
"""
Put date_end on contract line
We don't consider contract lines that end's before the new end date
@@ -487,9 +610,17 @@ class AccountAnalyticInvoiceLine(models.Model):
)
)
rec.contract_id.message_post(body=msg)
- rec.write({'date_end': date_end, 'is_auto_renew': False})
+ rec.write(
+ {
+ 'date_end': date_end,
+ 'is_auto_renew': False,
+ "is_suspended": is_suspended,
+ }
+ )
else:
- rec.write({'is_auto_renew': False})
+ rec.write(
+ {'is_auto_renew': False, "is_suspended": is_suspended}
+ )
return True
@api.multi
@@ -624,7 +755,9 @@ class AccountAnalyticInvoiceLine(models.Model):
+ relativedelta(days=1)
)
rec.stop(
- date_start - relativedelta(days=1), post_message=False
+ date_start - relativedelta(days=1),
+ is_suspended=True,
+ post_message=False,
)
contract_line |= rec.plan_successor(
new_date_start,
@@ -644,7 +777,9 @@ class AccountAnalyticInvoiceLine(models.Model):
new_date_end = rec.date_end
rec.stop(
- date_start - relativedelta(days=1), post_message=False
+ date_start - relativedelta(days=1),
+ is_suspended=True,
+ post_message=False,
)
contract_line |= rec.plan_successor(
new_date_start,
diff --git a/contract/tests/test_contract.py b/contract/tests/test_contract.py
index 5194f99b..9e90417d 100644
--- a/contract/tests/test_contract.py
+++ b/contract/tests/test_contract.py
@@ -143,9 +143,7 @@ class TestContract(TestContractBase):
self.contract.partner_id = False
self.contract.partner_id = self.partner.id
self.contract.recurring_create_invoice()
- self.invoice_monthly = self.env['account.invoice'].search(
- [('contract_id', '=', self.contract.id)]
- )
+ self.invoice_monthly = self.contract._get_related_invoices()
self.assertTrue(self.invoice_monthly)
self.assertEqual(
self.acct_line.recurring_next_date, recurring_next_date
@@ -182,9 +180,7 @@ class TestContract(TestContractBase):
self.acct_line.recurring_rule_type = 'daily'
self.contract.pricelist_id = False
self.contract.recurring_create_invoice()
- invoice_daily = self.env['account.invoice'].search(
- [('contract_id', '=', self.contract.id)]
- )
+ invoice_daily = self.contract._get_related_invoices()
self.assertTrue(invoice_daily)
self.assertEqual(
self.acct_line.recurring_next_date, recurring_next_date
@@ -198,9 +194,7 @@ class TestContract(TestContractBase):
self.acct_line.recurring_rule_type = 'weekly'
self.acct_line.recurring_invoicing_type = 'post-paid'
self.contract.recurring_create_invoice()
- invoices_weekly = self.env['account.invoice'].search(
- [('contract_id', '=', self.contract.id)]
- )
+ invoices_weekly = self.contract._get_related_invoices()
self.assertTrue(invoices_weekly)
self.assertEqual(
self.acct_line.recurring_next_date, recurring_next_date
@@ -214,9 +208,7 @@ class TestContract(TestContractBase):
self.acct_line.recurring_rule_type = 'weekly'
self.acct_line.recurring_invoicing_type = 'pre-paid'
self.contract.recurring_create_invoice()
- invoices_weekly = self.env['account.invoice'].search(
- [('contract_id', '=', self.contract.id)]
- )
+ invoices_weekly = self.contract._get_related_invoices()
self.assertTrue(invoices_weekly)
self.assertEqual(
self.acct_line.recurring_next_date, recurring_next_date
@@ -230,9 +222,7 @@ class TestContract(TestContractBase):
self.acct_line.recurring_rule_type = 'yearly'
self.acct_line.recurring_invoicing_type = 'post-paid'
self.contract.recurring_create_invoice()
- invoices_weekly = self.env['account.invoice'].search(
- [('contract_id', '=', self.contract.id)]
- )
+ invoices_weekly = self.contract._get_related_invoices()
self.assertTrue(invoices_weekly)
self.assertEqual(
self.acct_line.recurring_next_date, recurring_next_date
@@ -247,9 +237,7 @@ class TestContract(TestContractBase):
self.acct_line.recurring_rule_type = 'yearly'
self.acct_line.recurring_invoicing_type = 'pre-paid'
self.contract.recurring_create_invoice()
- invoices_weekly = self.env['account.invoice'].search(
- [('contract_id', '=', self.contract.id)]
- )
+ invoices_weekly = self.contract._get_related_invoices()
self.assertTrue(invoices_weekly)
self.assertEqual(
self.acct_line.recurring_next_date, recurring_next_date
@@ -263,9 +251,7 @@ class TestContract(TestContractBase):
self.acct_line.recurring_invoicing_type = 'post-paid'
self.acct_line.recurring_rule_type = 'monthlylastday'
self.contract.recurring_create_invoice()
- invoices_monthly_lastday = self.env['account.invoice'].search(
- [('contract_id', '=', self.contract.id)]
- )
+ invoices_monthly_lastday = self.contract._get_related_invoices()
self.assertTrue(invoices_monthly_lastday)
self.assertEqual(
self.acct_line.recurring_next_date, recurring_next_date
@@ -302,13 +288,9 @@ class TestContract(TestContractBase):
)
self.assertFalse(self.acct_line.recurring_next_date)
self.assertFalse(self.acct_line.create_invoice_visibility)
- invoices = self.env['account.invoice'].search(
- [('contract_id', '=', self.contract.id)]
- )
+ invoices = self.contract._get_related_invoices()
self.contract.recurring_create_invoice()
- new_invoices = self.env['account.invoice'].search(
- [('contract_id', '=', self.contract.id)]
- )
+ new_invoices = self.contract._get_related_invoices()
self.assertEqual(
invoices,
new_invoices,
@@ -345,13 +327,9 @@ class TestContract(TestContractBase):
)
self.assertFalse(self.acct_line.recurring_next_date)
self.assertFalse(self.acct_line.create_invoice_visibility)
- invoices = self.env['account.invoice'].search(
- [('contract_id', '=', self.contract.id)]
- )
+ invoices = self.contract._get_related_invoices()
self.contract.recurring_create_invoice()
- new_invoices = self.env['account.invoice'].search(
- [('contract_id', '=', self.contract.id)]
- )
+ new_invoices = self.contract._get_related_invoices()
self.assertEqual(
invoices,
new_invoices,
@@ -522,7 +500,6 @@ class TestContract(TestContractBase):
def test_same_date_start_and_date_end(self):
"""It should create one invoice with same start and end date."""
- account_invoice_model = self.env['account.invoice']
self.acct_line.write(
{
'date_start': fields.Date.today(),
@@ -531,18 +508,12 @@ class TestContract(TestContractBase):
}
)
self.contract._compute_recurring_next_date()
- init_count = account_invoice_model.search_count(
- [('contract_id', '=', self.contract.id)]
- )
+ init_count = len(self.contract._get_related_invoices())
self.contract.recurring_create_invoice()
- last_count = account_invoice_model.search_count(
- [('contract_id', '=', self.contract.id)]
- )
+ last_count = len(self.contract._get_related_invoices())
self.assertEqual(last_count, init_count + 1)
self.contract.recurring_create_invoice()
- last_count = account_invoice_model.search_count(
- [('contract_id', '=', self.contract.id)]
- )
+ last_count = len(self.contract._get_related_invoices())
self.assertEqual(last_count, init_count + 1)
def test_act_show_contract(self):
@@ -798,6 +769,7 @@ class TestContract(TestContractBase):
new_line.date_start, suspension_end + relativedelta(days=1)
)
self.assertEqual(new_line.date_end, new_date_end)
+ self.assertTrue(self.acct_line.is_suspended)
def test_stop_plan_successor_contract_line_3(self):
"""
@@ -838,6 +810,7 @@ class TestContract(TestContractBase):
new_line.date_start, suspension_end + relativedelta(days=1)
)
self.assertEqual(new_line.date_end, new_date_end)
+ self.assertTrue(self.acct_line.is_suspended)
def test_stop_plan_successor_contract_line_3_without_end_date(self):
"""
@@ -874,6 +847,7 @@ class TestContract(TestContractBase):
new_line.date_start, suspension_end + relativedelta(days=1)
)
self.assertFalse(new_line.date_end)
+ self.assertTrue(self.acct_line.is_suspended)
def test_stop_plan_successor_contract_line_4(self):
"""
diff --git a/contract/views/account_invoice_view.xml b/contract/views/account_invoice_view.xml
index e618fbe2..e69de29b 100644
--- a/contract/views/account_invoice_view.xml
+++ b/contract/views/account_invoice_view.xml
@@ -1,44 +0,0 @@
-
-