mirror of
https://github.com/OCA/contract.git
synced 2025-02-13 17:57:24 +02:00
[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:
@@ -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,"
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
=====
|
=====
|
||||||
|
|
||||||
|
|||||||
@@ -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',
|
||||||
|
|||||||
@@ -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())
|
|
||||||
|
|||||||
@@ -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)
|
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
Reference in New Issue
Block a user