[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 -*-
# © 2004-2010 OpenERP SA
# © 2016 Carlos Dauden <carlos.dauden@tecnativa.com>
# Copyright 2004-2010 OpenERP SA
# 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).
{
'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,"

View File

@@ -1,17 +1,16 @@
# -*- coding: utf-8 -*-
# © 2004-2010 OpenERP SA
# © 2014 Angel Moya <angel.moya@domatix.com>
# © 2015 Pedro M. Baeza <pedro.baeza@tecnativa.com>
# © 2016 Carlos Dauden <carlos.dauden@tecnativa.com>
# Copyright 2004-2010 OpenERP SA
# Copyright 2014 Angel Moya <angel.moya@domatix.com>
# Copyright 2015-2017 Pedro M. Baeza <pedro.baeza@tecnativa.com>
# Copyright 2016 Carlos Dauden <carlos.dauden@tecnativa.com>
# 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)
for contract in self:
old_date = fields.Date.from_string(
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()
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']
for contract in self:
old_date = fields.Date.from_string(
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.write({
'recurring_next_date': new_date.strftime('%Y-%m-%d')
})
contracts = self
while contracts:
contracts2invoice = contracts[0]._get_contracts2invoice(contracts)
contracts -= contracts2invoice
invoices |= contracts2invoice._create_invoice()
return invoices
@api.model

View File

@@ -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 <https://github.com/OCA/account-invoicing>`_.
Usage
=====

View File

@@ -1,13 +1,14 @@
# -*- coding: utf-8 -*-
# Copyright 2016 Carlos Dauden <carlos.dauden@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).
{
'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',

View File

@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
# 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>
# 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
)

View File

@@ -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)

View File

@@ -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)