[IMP] contract_invoice_merge_by_partner: Don't require to merge invoices later

Adapting source contract module, we don't need to rely on account_invoice_merge
functionality for having all the invoices merged.
This commit is contained in:
Pedro M. Baeza
2017-07-01 18:51:47 +02:00
parent 7695115130
commit 7d7295c8bb
7 changed files with 99 additions and 83 deletions

View File

@@ -1,11 +1,12 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# © 2004-2010 OpenERP SA # Copyright 2004-2010 OpenERP SA
# © 2016 Carlos Dauden <carlos.dauden@tecnativa.com> # Copyright 2016 Carlos Dauden <carlos.dauden@tecnativa.com>
# Copyright 2015-2017 Pedro M. Baeza <pedro.baeza@tecnativa.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
{ {
'name': 'Contracts Management recurring', 'name': 'Contracts Management recurring',
'version': '9.0.1.2.0', 'version': '9.0.1.2.1',
'category': 'Contract Management', 'category': 'Contract Management',
'license': 'AGPL-3', 'license': 'AGPL-3',
'author': "OpenERP SA," 'author': "OpenERP SA,"

View File

@@ -1,17 +1,16 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# © 2004-2010 OpenERP SA # Copyright 2004-2010 OpenERP SA
# © 2014 Angel Moya <angel.moya@domatix.com> # Copyright 2014 Angel Moya <angel.moya@domatix.com>
# © 2015 Pedro M. Baeza <pedro.baeza@tecnativa.com> # Copyright 2015-2017 Pedro M. Baeza <pedro.baeza@tecnativa.com>
# © 2016 Carlos Dauden <carlos.dauden@tecnativa.com> # Copyright 2016 Carlos Dauden <carlos.dauden@tecnativa.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from dateutil.relativedelta import relativedelta from dateutil.relativedelta import relativedelta
import logging 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.addons.decimal_precision import decimal_precision as dp
from openerp.exceptions import ValidationError from openerp.exceptions import ValidationError
from openerp.tools.translate import _
_logger = logging.getLogger(__name__) _logger = logging.getLogger(__name__)
@@ -222,36 +221,46 @@ class AccountAnalyticAccount(models.Model):
@api.multi @api.multi
def _prepare_invoice(self): def _prepare_invoice(self):
self.ensure_one() """Prepare the values for the invoice creation from the contract(s)
if not self.partner_id: 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( raise ValidationError(
_("You must first select a Customer for Contract %s!") % _("You must first select a Customer for Contract %s!") %
self.name) contract.name)
journal = self.journal_id or self.env['account.journal'].search( journal = contract.journal_id or self.env['account.journal'].search(
[('type', '=', 'sale'), [('type', '=', 'sale'),
('company_id', '=', self.company_id.id)], ('company_id', '=', contract.company_id.id)],
limit=1) limit=1)
if not journal: if not journal:
raise ValidationError( raise ValidationError(
_("Please define a sale journal for the company '%s'.") % _("Please define a sale journal for the company '%s'.") %
(self.company_id.name or '',)) (contract.company_id.name or '',))
currency = ( currency = (
self.pricelist_id.currency_id or contract.pricelist_id.currency_id or
self.partner_id.property_product_pricelist.currency_id or contract.partner_id.property_product_pricelist.currency_id or
self.company_id.currency_id contract.company_id.currency_id
) )
invoice = self.env['account.invoice'].new({ invoice = self.env['account.invoice'].new({
'reference': self.code, 'reference': ', '.join(self.filtered('code').mapped('code')),
'type': 'out_invoice', 'type': 'out_invoice',
'partner_id': self.partner_id.address_get( 'partner_id': contract.partner_id.address_get(
['invoice'])['invoice'], ['invoice'])['invoice'],
'currency_id': currency.id, 'currency_id': currency.id,
'journal_id': journal.id, 'journal_id': journal.id,
'date_invoice': self.recurring_next_date, 'date_invoice': contract.recurring_next_date,
'origin': self.name, 'origin': ', '.join(self.mapped('name')),
'company_id': self.company_id.id, 'company_id': contract.company_id.id,
'contract_id': self.id, 'contract_id': contract.id,
'user_id': self.partner_id.user_id.id, 'user_id': contract.partner_id.user_id.id,
}) })
# Get other invoice values from partner onchange # Get other invoice values from partner onchange
invoice._onchange_partner_id() invoice._onchange_partner_id()
@@ -259,35 +268,55 @@ class AccountAnalyticAccount(models.Model):
@api.multi @api.multi
def _create_invoice(self): 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_vals = self._prepare_invoice()
invoice = self.env['account.invoice'].create(invoice_vals) invoice = self.env['account.invoice'].create(invoice_vals)
for line in self.recurring_invoice_line_ids: for contract in self:
invoice_line_vals = self._prepare_invoice_line(line, invoice.id) old_date = fields.Date.from_string(
self.env['account.invoice.line'].create(invoice_line_vals) contract.recurring_next_date or fields.Date.today(),
)
new_date = old_date + self.get_relalive_delta(
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() invoice.compute_taxes()
return invoice 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 @api.multi
def recurring_create_invoice(self): def recurring_create_invoice(self):
invoices = self.env['account.invoice'] invoices = self.env['account.invoice']
for contract in self: contracts = self
old_date = fields.Date.from_string( while contracts:
contract.recurring_next_date or fields.Date.today()) contracts2invoice = contracts[0]._get_contracts2invoice(contracts)
new_date = old_date + self.get_relalive_delta( contracts -= contracts2invoice
contract.recurring_rule_type, contract.recurring_interval) invoices |= contracts2invoice._create_invoice()
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.write({
'recurring_next_date': new_date.strftime('%Y-%m-%d')
})
return invoices return invoices
@api.model @api.model

View File

@@ -8,13 +8,6 @@ Contract Invoice Merge By Partner
This module merges same partner invoices generated by contracts. 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 <https://github.com/OCA/account-invoicing>`_.
Usage Usage
===== =====

View File

@@ -1,13 +1,14 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Copyright 2016 Carlos Dauden <carlos.dauden@tecnativa.com> # Copyright 2016 Carlos Dauden <carlos.dauden@tecnativa.com>
# Copyright 2017 Vicent Cubells <vicent.cubells@tecnativa.com> # Copyright 2017 Vicent Cubells <vicent.cubells@tecnativa.com>
# Copyright 2016-2017 Pedro M. Baeza <pedro.baeza@tecnativa.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
{ {
'name': 'Contract Invoice Merge By Partner', 'name': 'Contract Invoice Merge By Partner',
'summary': 'This module merges same partner invoices generated by ' 'summary': 'This module merges same partner invoices generated by '
'contracts', 'contracts',
'version': '9.0.1.0.0', 'version': '9.0.2.0.0',
'category': 'Account', 'category': 'Account',
'license': 'AGPL-3', 'license': 'AGPL-3',
'author': "Tecnativa, " 'author': "Tecnativa, "
@@ -15,7 +16,6 @@
'website': 'http://www.tecnativa.com', 'website': 'http://www.tecnativa.com',
'depends': [ 'depends': [
'contract', 'contract',
'account_invoice_merge',
], ],
'data': [ 'data': [
'views/res_partner_view.xml', 'views/res_partner_view.xml',

View File

@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Copyright 2016 Carlos Dauden <carlos.dauden@tecnativa.com> # Copyright 2016 Carlos Dauden <carlos.dauden@tecnativa.com>
# Copyright 2016 Pedro M. Baeza <pedro.baeza@tecnativa.com> # Copyright 2016-2017 Pedro M. Baeza <pedro.baeza@tecnativa.com>
# Copyright 2017 Vicent Cubells <vicent.cubells@tecnativa.com> # Copyright 2017 Vicent Cubells <vicent.cubells@tecnativa.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
@@ -11,17 +11,13 @@ class AccountAnalyticAccount(models.Model):
_inherit = 'account.analytic.account' _inherit = 'account.analytic.account'
@api.multi @api.multi
def recurring_create_invoice(self): def _get_contracts2invoice(self, rest_contracts):
invoices = super( """Invoice together contracts from partners that have the option
AccountAnalyticAccount, self).recurring_create_invoice() checked and that have several contracts to invoice"""
invoices_info = {} if self.partner_id.contract_invoice_merge:
invoices2unlink = AccountInvoice = self.env['account.invoice'] return rest_contracts.filtered(
for partner in invoices.mapped('partner_id'): lambda x: x.partner_id == self.partner_id
invoices2merge = invoices.filtered( ) | self
lambda x: x.partner_id == partner) return super(AccountAnalyticAccount, self)._get_contracts2invoice(
if partner.contract_invoice_merge and len(invoices2merge) > 1: rest_contracts
invoices_info.update(invoices2merge.do_merge()) )
invoices2unlink += invoices2merge
invoices -= invoices2unlink
invoices2unlink.unlink()
return invoices | AccountInvoice.browse(invoices_info.keys())

View File

@@ -43,14 +43,10 @@ class TestContractInvoiceMergeByPartner(common.SavepointCase):
'product_id': self.product.id, 'product_id': self.product.id,
'uom_id': self.uom.id})], 'uom_id': self.uom.id})],
}) })
self.contract2 = self.contract1.copy() contract2 = self.contract1.copy()
self.contract3 = self.contract1.copy() contract3 = self.contract1.copy()
self.contract4 = self.contract1.copy() contract4 = self.contract1.copy()
contracts = self.env['account.analytic.account'].search([ contracts = self.contract1 + contract2 + contract3 + contract4
('partner_id', '=', self.partner.id) invoice = contracts.recurring_create_invoice()
]) self.assertEqual(len(invoice), 1)
invoices = contracts.recurring_create_invoice() self.assertEqual(len(invoice.invoice_line_ids), 4)
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)

View File

@@ -19,8 +19,9 @@ class AccountAnalyticAccount(models.Model):
@api.multi @api.multi
def _prepare_invoice(self): def _prepare_invoice(self):
invoice_vals = super(AccountAnalyticAccount, self)._prepare_invoice() invoice_vals = super(AccountAnalyticAccount, self)._prepare_invoice()
if self.payment_mode_id: contract = self[:1]
invoice_vals['payment_mode_id'] = self.payment_mode_id.id if contract.payment_mode_id:
invoice_vals['payment_mode_id'] = contract.payment_mode_id.id
invoice = self.env['account.invoice'].new(invoice_vals) invoice = self.env['account.invoice'].new(invoice_vals)
invoice.payment_mode_id_change() invoice.payment_mode_id_change()
invoice_vals = invoice._convert_to_write(invoice._cache) invoice_vals = invoice._convert_to_write(invoice._cache)