![Pedro M. Baeza](/assets/img/avatar_default.png)
AR/AP netting ============= This module allows to compensate the balance of a receivable account with the balance of a payable account for the same partner, creating a journal item that reflects this operation. **WARNING**: This operation can be forbidden in your country by the accounting regulations, so you should check current laws before using it. For example, in Spain, this is not allowed at first instance, unless you document well the operation from both parties. Usage ===== From any account journal entries view: * Accounting/Journal Entries/Journal Items * Accounting/Periodic Processing/Reconciliation/Manual Reconciliation select all the lines that corresponds to both AR/AP operations from the same partner. Click on "More > Compensate". If the items don't correspond to the same partner or they aren't AR/AP accounts, you will get an error. On contrary, a dialog box will be presented with the result of the operation and a selection of the journal to register the operation. When you click on the "Compensate" button, a journal entry is created with the corresponding counterparts of the AR/AP operations. OCA Transbot updated translations from Transifex
116 lines
4.7 KiB
Python
116 lines
4.7 KiB
Python
# -*- coding: utf-8 -*-
|
|
# (c) 2015 Pedro M. Baeza
|
|
# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html
|
|
|
|
from openerp import models, fields, api, exceptions, _
|
|
|
|
|
|
class AccountMoveMakeNetting(models.TransientModel):
|
|
_name = "account.move.make.netting"
|
|
|
|
journal = fields.Many2one(
|
|
comodel_name="account.journal", required=True,
|
|
domain="[('type', '=', 'general')]")
|
|
move_lines = fields.Many2many(comodel_name="account.move.line")
|
|
balance = fields.Float(readonly=True)
|
|
balance_type = fields.Selection(
|
|
selection=[('pay', 'To pay'), ('receive', 'To receive')],
|
|
readonly=True)
|
|
|
|
@api.model
|
|
def default_get(self, fields):
|
|
if len(self.env.context.get('active_ids', [])) < 2:
|
|
raise exceptions.ValidationError(
|
|
_("You should compensate at least 2 journal entries."))
|
|
move_lines = self.env['account.move.line'].browse(
|
|
self.env.context['active_ids'])
|
|
if (any(x not in ('payable', 'receivable') for
|
|
x in move_lines.mapped('account_id.type'))):
|
|
raise exceptions.ValidationError(
|
|
_("All entries must have a receivable or payable account"))
|
|
if any(move_lines.mapped('reconcile_id')):
|
|
raise exceptions.ValidationError(
|
|
_("All entries mustn't been reconciled"))
|
|
partner_id = None
|
|
for move in move_lines:
|
|
if (not move.partner_id or (
|
|
move.partner_id != partner_id and partner_id is not None)):
|
|
raise exceptions.ValidationError(
|
|
_("All entries should have a partner and the partner must "
|
|
"be the same for all."))
|
|
partner_id = move.partner_id
|
|
res = super(AccountMoveMakeNetting, self).default_get(fields)
|
|
res['move_lines'] = [(6, 0, move_lines.ids)]
|
|
balance = (sum(move_lines.mapped('debit')) -
|
|
sum(move_lines.mapped('credit')))
|
|
res['balance'] = abs(balance)
|
|
res['balance_type'] = 'pay' if balance < 0 else 'receive'
|
|
return res
|
|
|
|
@api.multi
|
|
def button_compensate(self):
|
|
self.ensure_one()
|
|
# Create account move
|
|
move = self.env['account.move'].create(
|
|
{
|
|
'ref': _('AR/AP netting'),
|
|
'journal_id': self.journal.id,
|
|
})
|
|
# Group amounts by account
|
|
account_groups = self.move_lines.read_group(
|
|
[('id', 'in', self.move_lines.ids)],
|
|
['account_id', 'debit', 'credit'], ['account_id'])
|
|
debtors = []
|
|
creditors = []
|
|
total_debtors = 0
|
|
total_creditors = 0
|
|
for account_group in account_groups:
|
|
balance = account_group['debit'] - account_group['credit']
|
|
group_vals = {
|
|
'account_id': account_group['account_id'][0],
|
|
'balance': abs(balance),
|
|
}
|
|
if balance > 0:
|
|
debtors.append(group_vals)
|
|
total_debtors += balance
|
|
else:
|
|
creditors.append(group_vals)
|
|
total_creditors += abs(balance)
|
|
# Create move lines
|
|
move_line_model = self.env['account.move.line']
|
|
netting_amount = min(total_creditors, total_debtors)
|
|
field_map = {1: 'debit', 0: 'credit'}
|
|
for i, group in enumerate([debtors, creditors]):
|
|
available_amount = netting_amount
|
|
for account_group in group:
|
|
if account_group['balance'] > available_amount:
|
|
amount = available_amount
|
|
else:
|
|
amount = account_group['balance']
|
|
move_line_vals = {
|
|
field_map[i]: amount,
|
|
'move_id': move.id,
|
|
'partner_id': self.move_lines[0].partner_id.id,
|
|
'date': move.date,
|
|
'period_id': move.period_id.id,
|
|
'journal_id': move.journal_id.id,
|
|
'name': move.ref,
|
|
'account_id': account_group['account_id'],
|
|
}
|
|
move_line_model.create(move_line_vals)
|
|
available_amount -= account_group['balance']
|
|
if available_amount <= 0:
|
|
break
|
|
# Make reconciliation
|
|
for move_line in move.line_id:
|
|
to_reconcile = move_line + self.move_lines.filtered(
|
|
lambda x: x.account_id == move_line.account_id)
|
|
to_reconcile.reconcile_partial()
|
|
# Open created move
|
|
action = self.env.ref('account.action_move_journal_line').read()[0]
|
|
action['view_mode'] = 'form'
|
|
del action['views']
|
|
del action['view_id']
|
|
action['res_id'] = move.id
|
|
return action
|