diff --git a/contract/__openerp__.py b/contract/__openerp__.py index 118ac69b..ec8eb58a 100644 --- a/contract/__openerp__.py +++ b/contract/__openerp__.py @@ -1,11 +1,12 @@ # -*- coding: utf-8 -*- -# © 2004-2010 OpenERP SA -# © 2016 Carlos Dauden +# Copyright 2004-2010 OpenERP SA +# Copyright 2016 Carlos Dauden +# Copyright 2015-2017 Pedro M. Baeza # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). { 'name': 'Contracts Management recurring', - 'version': '9.0.1.2.0', + 'version': '9.0.1.2.1', 'category': 'Contract Management', 'license': 'AGPL-3', 'author': "OpenERP SA," diff --git a/contract/models/contract.py b/contract/models/contract.py index f7bc7155..ca17df74 100644 --- a/contract/models/contract.py +++ b/contract/models/contract.py @@ -1,17 +1,16 @@ # -*- coding: utf-8 -*- -# © 2004-2010 OpenERP SA -# © 2014 Angel Moya -# © 2015 Pedro M. Baeza -# © 2016 Carlos Dauden +# Copyright 2004-2010 OpenERP SA +# Copyright 2014 Angel Moya +# Copyright 2015-2017 Pedro M. Baeza +# Copyright 2016 Carlos Dauden # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). from dateutil.relativedelta import relativedelta import logging -from openerp import api, fields, models +from openerp import _, api, fields, models from openerp.addons.decimal_precision import decimal_precision as dp from openerp.exceptions import ValidationError -from openerp.tools.translate import _ _logger = logging.getLogger(__name__) @@ -222,36 +221,46 @@ class AccountAnalyticAccount(models.Model): @api.multi def _prepare_invoice(self): - self.ensure_one() - if not self.partner_id: + """Prepare the values for the invoice creation from the contract(s) + given. It's possible to provide several contracts. Only one invoice + will be created and most of the values will be taken from first + contract, but there are certain values that can be obtained from all + of them (for example, the origin field). + + :param self: Recordset of contract(s). + :returns: Values for invoice creation. + :rtype: dict + """ + contract = self[:1] + if not contract.partner_id: raise ValidationError( _("You must first select a Customer for Contract %s!") % - self.name) - journal = self.journal_id or self.env['account.journal'].search( + contract.name) + journal = contract.journal_id or self.env['account.journal'].search( [('type', '=', 'sale'), - ('company_id', '=', self.company_id.id)], + ('company_id', '=', contract.company_id.id)], limit=1) if not journal: raise ValidationError( _("Please define a sale journal for the company '%s'.") % - (self.company_id.name or '',)) + (contract.company_id.name or '',)) currency = ( - self.pricelist_id.currency_id or - self.partner_id.property_product_pricelist.currency_id or - self.company_id.currency_id + contract.pricelist_id.currency_id or + contract.partner_id.property_product_pricelist.currency_id or + contract.company_id.currency_id ) invoice = self.env['account.invoice'].new({ - 'reference': self.code, + 'reference': ', '.join(self.filtered('code').mapped('code')), 'type': 'out_invoice', - 'partner_id': self.partner_id.address_get( + 'partner_id': contract.partner_id.address_get( ['invoice'])['invoice'], 'currency_id': currency.id, 'journal_id': journal.id, - 'date_invoice': self.recurring_next_date, - 'origin': self.name, - 'company_id': self.company_id.id, - 'contract_id': self.id, - 'user_id': self.partner_id.user_id.id, + 'date_invoice': contract.recurring_next_date, + 'origin': ', '.join(self.mapped('name')), + 'company_id': contract.company_id.id, + 'contract_id': contract.id, + 'user_id': contract.partner_id.user_id.id, }) # Get other invoice values from partner onchange invoice._onchange_partner_id() @@ -259,35 +268,55 @@ class AccountAnalyticAccount(models.Model): @api.multi def _create_invoice(self): - self.ensure_one() + """Create the invoice from the source contracts. + + :param self: Contract records. Invoice header data will be obtained + from the first record of this recordset. + + :return Created invoice record. + """ invoice_vals = self._prepare_invoice() invoice = self.env['account.invoice'].create(invoice_vals) - for line in self.recurring_invoice_line_ids: - invoice_line_vals = self._prepare_invoice_line(line, invoice.id) - self.env['account.invoice.line'].create(invoice_line_vals) - invoice.compute_taxes() - return invoice - - @api.multi - def recurring_create_invoice(self): - invoices = self.env['account.invoice'] for contract in self: old_date = fields.Date.from_string( - contract.recurring_next_date or fields.Date.today()) + contract.recurring_next_date or fields.Date.today(), + ) new_date = old_date + self.get_relalive_delta( - contract.recurring_rule_type, contract.recurring_interval) - ctx = self.env.context.copy() - ctx.update({ - 'old_date': old_date, - 'next_date': new_date, - # Force company for correct evaluate domain access rules - 'force_company': contract.company_id.id, - }) - # Re-read contract with correct company - invoices |= contract.with_context(ctx)._create_invoice() + contract.recurring_rule_type, contract.recurring_interval, + ) + obj = self.with_context( + old_date=old_date, + next_date=new_date, + # For correct evaluating of domain access rules + properties + force_company=contract.company_id.id, + ) + for line in contract.recurring_invoice_line_ids: + invoice_line_vals = obj._prepare_invoice_line(line, invoice.id) + self.env['account.invoice.line'].create(invoice_line_vals) contract.write({ 'recurring_next_date': new_date.strftime('%Y-%m-%d') }) + invoice.compute_taxes() + return invoice + + @api.multi + def _get_contracts2invoice(self, rest_contracts): + """Method for being inherited by other modules to specify contract + grouping rules. By default, each contract is invoiced separately. + + :param rest_contracts: Rest of the outstanding contracts to be invoiced + """ + self.ensure_one() + return self + + @api.multi + def recurring_create_invoice(self): + invoices = self.env['account.invoice'] + contracts = self + while contracts: + contracts2invoice = contracts[0]._get_contracts2invoice(contracts) + contracts -= contracts2invoice + invoices |= contracts2invoice._create_invoice() return invoices @api.model diff --git a/contract_invoice_merge_by_partner/README.rst b/contract_invoice_merge_by_partner/README.rst index 8df01a9b..7c0eb36c 100644 --- a/contract_invoice_merge_by_partner/README.rst +++ b/contract_invoice_merge_by_partner/README.rst @@ -8,13 +8,6 @@ Contract Invoice Merge By Partner This module merges same partner invoices generated by contracts. -Installation -============ - -To install this module you need *account_invoice_merge*, available in: - -* Install repository `OCA/account-invoicing `_. - Usage ===== diff --git a/contract_invoice_merge_by_partner/__openerp__.py b/contract_invoice_merge_by_partner/__openerp__.py index 1459bdcb..d269241b 100644 --- a/contract_invoice_merge_by_partner/__openerp__.py +++ b/contract_invoice_merge_by_partner/__openerp__.py @@ -1,13 +1,14 @@ # -*- coding: utf-8 -*- # Copyright 2016 Carlos Dauden # Copyright 2017 Vicent Cubells +# Copyright 2016-2017 Pedro M. Baeza # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). { 'name': 'Contract Invoice Merge By Partner', 'summary': 'This module merges same partner invoices generated by ' 'contracts', - 'version': '9.0.1.0.0', + 'version': '9.0.2.0.0', 'category': 'Account', 'license': 'AGPL-3', 'author': "Tecnativa, " @@ -15,7 +16,6 @@ 'website': 'http://www.tecnativa.com', 'depends': [ 'contract', - 'account_invoice_merge', ], 'data': [ 'views/res_partner_view.xml', diff --git a/contract_invoice_merge_by_partner/models/account_analytic_analysis.py b/contract_invoice_merge_by_partner/models/account_analytic_analysis.py index 47f72730..0070b380 100644 --- a/contract_invoice_merge_by_partner/models/account_analytic_analysis.py +++ b/contract_invoice_merge_by_partner/models/account_analytic_analysis.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- # Copyright 2016 Carlos Dauden -# Copyright 2016 Pedro M. Baeza +# Copyright 2016-2017 Pedro M. Baeza # Copyright 2017 Vicent Cubells # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). @@ -11,17 +11,13 @@ class AccountAnalyticAccount(models.Model): _inherit = 'account.analytic.account' @api.multi - def recurring_create_invoice(self): - invoices = super( - AccountAnalyticAccount, self).recurring_create_invoice() - invoices_info = {} - invoices2unlink = AccountInvoice = self.env['account.invoice'] - for partner in invoices.mapped('partner_id'): - invoices2merge = invoices.filtered( - lambda x: x.partner_id == partner) - if partner.contract_invoice_merge and len(invoices2merge) > 1: - invoices_info.update(invoices2merge.do_merge()) - invoices2unlink += invoices2merge - invoices -= invoices2unlink - invoices2unlink.unlink() - return invoices | AccountInvoice.browse(invoices_info.keys()) + def _get_contracts2invoice(self, rest_contracts): + """Invoice together contracts from partners that have the option + checked and that have several contracts to invoice""" + if self.partner_id.contract_invoice_merge: + return rest_contracts.filtered( + lambda x: x.partner_id == self.partner_id + ) | self + return super(AccountAnalyticAccount, self)._get_contracts2invoice( + rest_contracts + ) diff --git a/contract_invoice_merge_by_partner/tests/test_contract_invoice_merge_by_partner.py b/contract_invoice_merge_by_partner/tests/test_contract_invoice_merge_by_partner.py index efae1fa8..f6bb4c69 100644 --- a/contract_invoice_merge_by_partner/tests/test_contract_invoice_merge_by_partner.py +++ b/contract_invoice_merge_by_partner/tests/test_contract_invoice_merge_by_partner.py @@ -43,14 +43,10 @@ class TestContractInvoiceMergeByPartner(common.SavepointCase): 'product_id': self.product.id, 'uom_id': self.uom.id})], }) - self.contract2 = self.contract1.copy() - self.contract3 = self.contract1.copy() - self.contract4 = self.contract1.copy() - contracts = self.env['account.analytic.account'].search([ - ('partner_id', '=', self.partner.id) - ]) - invoices = contracts.recurring_create_invoice() - inv_draft = invoices.filtered(lambda x: x.state == 'draft') - self.assertEqual(len(inv_draft), 1) - inv_cancel = invoices.filtered(lambda x: x.state == 'cancel') - self.assertFalse(inv_cancel) + contract2 = self.contract1.copy() + contract3 = self.contract1.copy() + contract4 = self.contract1.copy() + contracts = self.contract1 + contract2 + contract3 + contract4 + invoice = contracts.recurring_create_invoice() + self.assertEqual(len(invoice), 1) + self.assertEqual(len(invoice.invoice_line_ids), 4) diff --git a/contract_payment_mode/models/contract.py b/contract_payment_mode/models/contract.py index fb334a21..e3eac936 100644 --- a/contract_payment_mode/models/contract.py +++ b/contract_payment_mode/models/contract.py @@ -19,8 +19,9 @@ class AccountAnalyticAccount(models.Model): @api.multi def _prepare_invoice(self): invoice_vals = super(AccountAnalyticAccount, self)._prepare_invoice() - if self.payment_mode_id: - invoice_vals['payment_mode_id'] = self.payment_mode_id.id + contract = self[:1] + if contract.payment_mode_id: + invoice_vals['payment_mode_id'] = contract.payment_mode_id.id invoice = self.env['account.invoice'].new(invoice_vals) invoice.payment_mode_id_change() invoice_vals = invoice._convert_to_write(invoice._cache)