From a874e94802e8d96ae78ccd5e68edb972ed936050 Mon Sep 17 00:00:00 2001 From: Thomas Binsfeld Date: Tue, 18 Dec 2018 17:27:41 +0100 Subject: [PATCH] [REF] Contract: invoice creation [FIX] - Fix typo [IMP] - date start required for contract line [REF] Gitignore: .eggs --- .gitignore | 1 + contract/models/account_invoice.py | 8 ++- contract/models/contract.py | 90 ++++++++++++++++++++++-------- contract/models/contract_line.py | 66 ++-------------------- 4 files changed, 81 insertions(+), 84 deletions(-) diff --git a/.gitignore b/.gitignore index f7f8a408..5e48466d 100644 --- a/.gitignore +++ b/.gitignore @@ -21,6 +21,7 @@ var/ *.egg-info/ .installed.cfg *.egg +*.eggs # Installer logs pip-log.txt diff --git a/contract/models/account_invoice.py b/contract/models/account_invoice.py index b651ea2a..fbe10a11 100644 --- a/contract/models/account_invoice.py +++ b/contract/models/account_invoice.py @@ -1,7 +1,7 @@ # © 2016 Carlos Dauden # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from odoo import fields, models +from odoo import api, fields, models class AccountInvoice(models.Model): @@ -10,3 +10,9 @@ class AccountInvoice(models.Model): contract_id = fields.Many2one( 'account.analytic.account', string='Contract' ) + + @api.multi + def finalize_creation_from_contract(self): + for invoice in self: + invoice._onchange_partner_id() + self.compute_taxes() diff --git a/contract/models/contract.py b/contract/models/contract.py index 8a7dfd00..a40bba80 100644 --- a/contract/models/contract.py +++ b/contract/models/contract.py @@ -173,25 +173,20 @@ class AccountAnalyticAccount(models.Model): invoice_type = 'out_invoice' if self.contract_type == 'purchase': invoice_type = 'in_invoice' - invoice = self.env['account.invoice'].new( - { - 'reference': self.code, - 'type': invoice_type, - 'partner_id': self.partner_id.address_get(['invoice'])[ - 'invoice' - ], - 'currency_id': currency.id, - 'date_invoice': date_invoice, - '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, - } - ) - # Get other invoice values from partner onchange - invoice._onchange_partner_id() - return invoice._convert_to_write(invoice._cache) + return { + 'reference': self.code, + 'type': invoice_type, + 'partner_id': self.partner_id.address_get(['invoice'])[ + 'invoice' + ], + 'currency_id': currency.id, + 'date_invoice': date_invoice, + '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, + } @api.multi def action_contract_send(self): @@ -217,12 +212,61 @@ class AccountAnalyticAccount(models.Model): 'context': ctx, } + @api.model + def _get_recurring_create_invoice_domain(self, contract=False): + domain = [] + date_ref = fields.Date.context_today(self) + if contract: + contract.ensure_one() + date_ref = contract.recurring_next_date + domain.append(('id', '=', contract.id)) + domain.extend( + [ + ('recurring_invoices', '=', True), + ('recurring_next_date', '<=', date_ref), + ] + ) + return domain + + @api.multi + def _get_lines_to_invoice(self, date_ref=False): + self.ensure_one() + if not date_ref: + date_ref = fields.Date.context_today(self) + return self.recurring_invoice_line_ids.filtered( + lambda l: not l.is_canceled and l.recurring_next_date <= date_ref) + @api.multi def recurring_create_invoice(self): - return self.env[ - 'account.analytic.invoice.line' - ].recurring_create_invoice(self) + invoice_model = self.env['account.invoice'] + invoices_values = [] + for contract in self: + contract_lines = contract._get_lines_to_invoice() + if not contract_lines: + continue + invoice_values = contract._prepare_invoice( + contract.recurring_next_date) + for line in contract_lines: + invoice_values.setdefault('invoice_line_ids', []) + invoice_values['invoice_line_ids'].append( + (0, 0, line._prepare_invoice_line(False)) + ) + # If no account on the product, the invoice lines account is + # taken from the invoice's journal in _onchange_product_id + # This code is not in finalize_creation_from_contract because it's + # not possible to create an invoice line with no account + new_invoice = invoice_model.new(invoice_values) + for invoice_line in new_invoice.invoice_line_ids: + invoice_line.invoice_id = new_invoice + invoice_line._onchange_product_id() + invoice_values = new_invoice._convert_to_write(new_invoice._cache) + invoices_values.append(invoice_values) + contract_lines._update_recurring_next_date() + invoices = invoice_model.create(invoices_values) + invoices.finalize_creation_from_contract() @api.model def cron_recurring_create_invoice(self): - self.env['account.analytic.invoice.line'].recurring_create_invoice() + domain = self._get_recurring_create_invoice_domain() + contracts_to_invoice = self.search(domain) + contracts_to_invoice.recurring_create_invoice() diff --git a/contract/models/contract_line.py b/contract/models/contract_line.py index f0fa15c8..8e113cda 100644 --- a/contract/models/contract_line.py +++ b/contract/models/contract_line.py @@ -24,6 +24,7 @@ class AccountAnalyticInvoiceLine(models.Model): ) date_start = fields.Date( string='Date Start', + required=True, default=lambda self: fields.Date.context_today(self), ) date_end = fields.Date(string='Date End', index=True) @@ -78,7 +79,10 @@ class AccountAnalyticInvoiceLine(models.Model): compute="_compute_state", ) active = fields.Boolean( - string="Active", related="contract_id.active", strore=True + string="Active", + related="contract_id.active", + store=True, + readonly=True, ) @api.multi @@ -312,69 +316,11 @@ class AccountAnalyticInvoiceLine(models.Model): rec.recurring_next_date ) - @api.model - def _get_recurring_create_invoice_domain(self, contract=False): - domain = [] - date_ref = fields.Date.context_today(self) - if contract: - contract.ensure_one() - date_ref = contract.recurring_next_date - domain.append(('contract_id', '=', contract.id)) - - domain.extend( - [ - ('contract_id.recurring_invoices', '=', True), - ('recurring_next_date', '<=', date_ref), - ('is_canceled', '=', False), - ] - ) - return domain - - @api.model - def recurring_create_invoice(self, contract=False): - domain = self._get_recurring_create_invoice_domain(contract) - contract_to_invoice = self.read_group( - domain, ['id', 'contract_id'], ['contract_id'] - ) - return self._recurring_create_invoice(contract_to_invoice) - - @api.model - def _recurring_create_invoice(self, contract_to_invoice): - """Create invoices from contracts - - :return: invoices created - """ - invoices = self.env['account.invoice'] - for contract in contract_to_invoice: - lines = self.search(contract['__domain']) - if lines: - invoices |= lines._create_invoice() - lines._update_recurring_next_date() - return invoices - - @api.multi - def _create_invoice(self): - """ - :return: invoice created - """ - contract = self.mapped('contract_id') - date_invoice = min(self.mapped('recurring_next_date')) - invoice = self.env['account.invoice'].create( - contract._prepare_invoice(date_invoice) - ) - for line in self: - invoice_line_vals = line._prepare_invoice_line(invoice.id) - if invoice_line_vals: - self.env['account.invoice.line'].create(invoice_line_vals) - invoice.compute_taxes() - return invoice - @api.multi - def _prepare_invoice_line(self, invoice_id): + def _prepare_invoice_line(self, invoice_id=False): self.ensure_one() invoice_line = self.env['account.invoice.line'].new( { - 'invoice_id': invoice_id, 'product_id': self.product_id.id, 'quantity': self.quantity, 'uom_id': self.uom_id.id,