Browse Source

[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.
pull/97/head
Pedro M. Baeza 8 years ago
parent
commit
7d7295c8bb
  1. 7
      contract/__openerp__.py
  2. 115
      contract/models/contract.py
  3. 7
      contract_invoice_merge_by_partner/README.rst
  4. 4
      contract_invoice_merge_by_partner/__openerp__.py
  5. 26
      contract_invoice_merge_by_partner/models/account_analytic_analysis.py
  6. 18
      contract_invoice_merge_by_partner/tests/test_contract_invoice_merge_by_partner.py
  7. 5
      contract_payment_mode/models/contract.py

7
contract/__openerp__.py

@ -1,11 +1,12 @@
# -*- coding: utf-8 -*- # -*- 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). # 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,"

115
contract/models/contract.py

@ -1,17 +1,16 @@
# -*- coding: utf-8 -*- # -*- 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). # 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()
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( raise ValidationError(
_("You must first select a Customer for Contract %s!") % _("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'), [('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
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({ 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,
'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 # 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:
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: for contract in self:
old_date = fields.Date.from_string( 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( 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({ contract.write({
'recurring_next_date': new_date.strftime('%Y-%m-%d') '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 return invoices
@api.model @api.model

7
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. 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
===== =====

4
contract_invoice_merge_by_partner/__openerp__.py

@ -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',

26
contract_invoice_merge_by_partner/models/account_analytic_analysis.py

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

18
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, 'product_id': self.product.id,
'uom_id': self.uom.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)

5
contract_payment_mode/models/contract.py

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

Loading…
Cancel
Save