mirror of
https://github.com/OCA/account-financial-tools.git
synced 2025-02-02 12:47:26 +02:00
[MIG] account_reversal
This commit is contained in:
committed by
Pedro M. Baeza
parent
5a60beafe6
commit
4ff1b53966
@@ -1,3 +1,7 @@
|
||||
.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg
|
||||
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
|
||||
:alt: License: AGPL-3
|
||||
|
||||
Account Reversal
|
||||
================
|
||||
|
||||
@@ -5,19 +9,68 @@ This module adds an action "Reversal" on account moves,
|
||||
to allow the accountant to create reversal account moves in 2 clicks.
|
||||
Also add on account entries:
|
||||
|
||||
* a checkbox and filter "to be reversed"
|
||||
* a link between an entry and its reversal entry
|
||||
* a checkbox and filter "to be reversed"
|
||||
* a link between an entry and its reversal entry
|
||||
|
||||
Module originally developped by Alexis de Lattre <alexis.delattre@akretion.com>
|
||||
during the Akretion-Camptocamp code sprint of June 2011.
|
||||
Odoo v9c include a similar action (overwritten by this addon), but with less
|
||||
features, for instance:
|
||||
|
||||
* Allowing inheritance
|
||||
* Options like prefix (for journal entry and journal item), post and reconcile.
|
||||
* Create a link between the entry and its reversal
|
||||
* Mark entries to be reversed in the future.
|
||||
|
||||
Usage
|
||||
=====
|
||||
|
||||
As in Odoo v9c, if you select an entry from Accounting > Adviser > Journal Entries,
|
||||
then an action menu 'Reverse Entries' is available. If clicked, then a wizard
|
||||
allows user to select Reversal Date, Reversal Journal, Prefix, Post and Reconcile.
|
||||
|
||||
* If no Reversal Journal is selected, then the same journal is used
|
||||
* If Post is True, then reversal entry will be posted else it will be leaved
|
||||
as a draft entry
|
||||
* If Post and Reconcile are True, then all entry lines with reconciled accounts
|
||||
of the entry will be reconciled with the reserval entry ones.
|
||||
|
||||
There is also a new menu Accounting > Adviser > Journal Entries to be Reversed
|
||||
in order to allow tracking entries that must be reserved for any reason.
|
||||
|
||||
.. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas
|
||||
:alt: Try me on Runbot
|
||||
:target: https://runbot.odoo-community.org/runbot/92/9.0
|
||||
|
||||
|
||||
Credits
|
||||
=======
|
||||
|
||||
Images
|
||||
------
|
||||
|
||||
* Odoo Community Association: `Icon <https://github.com/OCA/maintainer-tools/blob/master/template/module/static/description/icon.svg>`_.
|
||||
|
||||
Contributors
|
||||
============
|
||||
------------
|
||||
|
||||
* Alexis de Lattre (Akretion)
|
||||
* Guewen Baconnier (Camptocamp)
|
||||
* Nicolas Bessi (Camptocamp)
|
||||
* Torvald Bringsvor (Bringsvor Consulting)
|
||||
* Sandy Carter (Savoir-faire Linux)
|
||||
* Stéphane Bidoul (ACSONE)
|
||||
* Alexis de Lattre (Akretion)
|
||||
* Guewen Baconnier (Camptocamp)
|
||||
* Nicolas Bessi (Camptocamp)
|
||||
* Torvald Bringsvor (Bringsvor Consulting)
|
||||
* Sandy Carter (Savoir-faire Linux)
|
||||
* Stéphane Bidoul (ACSONE)
|
||||
* Antonio Espinosa (Tecnativa)
|
||||
|
||||
Maintainer
|
||||
----------
|
||||
|
||||
.. image:: https://odoo-community.org/logo.png
|
||||
:alt: Odoo Community Association
|
||||
:target: https://odoo-community.org
|
||||
|
||||
This module is maintained by the OCA.
|
||||
|
||||
OCA, or the Odoo Community Association, is a nonprofit organization whose
|
||||
mission is to support the collaborative development of Odoo features and
|
||||
promote its widespread use.
|
||||
|
||||
To contribute to this module, please visit https://odoo-community.org.
|
||||
|
||||
@@ -1,24 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
# Account reversal module for OpenERP
|
||||
# Copyright (C) 2011 Akretion (http://www.akretion.com). All Rights Reserved
|
||||
# @author Alexis de Lattre <alexis.delattre@akretion.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
# published by the Free Software Foundation, either version 3 of the
|
||||
# License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||
|
||||
from . import account_reversal
|
||||
from . import models
|
||||
from . import wizard
|
||||
|
||||
@@ -1,40 +1,28 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
# Account reversal module for OpenERP
|
||||
# Copyright (C) 2011 Akretion (http://www.akretion.com). All Rights Reserved
|
||||
# @author Alexis de Lattre <alexis.delattre@akretion.com>
|
||||
# Copyright 2012-2013 Camptocamp SA
|
||||
# @author Guewen Baconnier (Camptocamp)
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
# published by the Free Software Foundation, either version 3 of the
|
||||
# License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
# Copyright 2011 Alexis de Lattre <alexis.delattre@akretion.com>
|
||||
# Copyright 2012-2013 Guewen Baconnier (Camptocamp)
|
||||
# Copyright 2016 Antonio Espinosa <antonio.espinosa@tecnativa.com>
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||
|
||||
{
|
||||
'name': 'Account Reversal',
|
||||
'version': '8.0.1.1.0',
|
||||
'category': 'Generic Modules/Accounting',
|
||||
'license': 'AGPL-3',
|
||||
'author': "Akretion,Camptocamp,Odoo Community Association (OCA)",
|
||||
'website': 'http://www.akretion.com/',
|
||||
'depends': ['account'],
|
||||
'data': [
|
||||
'account_view.xml',
|
||||
'wizard/account_move_reverse_view.xml'
|
||||
],
|
||||
'installable': False,
|
||||
'active': False,
|
||||
"name": "Account Reversal",
|
||||
"summary": "Wizard for creating a reversal account move",
|
||||
"version": "9.0.1.0.0",
|
||||
"category": "Accounting & Finance",
|
||||
"website": "https://odoo-community.org/",
|
||||
"author": "Akretion,"
|
||||
"Camptocamp,"
|
||||
"ACSONE SA/NV,"
|
||||
"Tecnativa,"
|
||||
"Odoo Community Association (OCA)",
|
||||
"license": "AGPL-3",
|
||||
"application": False,
|
||||
"installable": True,
|
||||
"depends": [
|
||||
"account"
|
||||
],
|
||||
"data": [
|
||||
"wizard/account_move_reverse_view.xml",
|
||||
"views/account_move_view.xml",
|
||||
],
|
||||
}
|
||||
|
||||
@@ -1,167 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
# Account reversal module for OpenERP
|
||||
# Copyright (C) 2011 Akretion (http://www.akretion.com). All Rights Reserved
|
||||
# @author Alexis de Lattre <alexis.delattre@akretion.com>
|
||||
# with the kind advice of Nicolas Bessi from Camptocamp
|
||||
# Copyright (C) 2012-2013 Camptocamp SA (http://www.camptocamp.com)
|
||||
# @author Guewen Baconnier <guewen.baconnier@camptocamp.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
# published by the Free Software Foundation, either version 3 of the
|
||||
# License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
from openerp import fields, models, api, _
|
||||
|
||||
|
||||
class account_move(models.Model):
|
||||
_inherit = "account.move"
|
||||
|
||||
to_be_reversed = fields.Boolean(
|
||||
'To Be Reversed',
|
||||
help='Check this box if your entry has to be'
|
||||
'reversed at the end of period.')
|
||||
reversal_id = fields.Many2one(
|
||||
'account.move',
|
||||
'Reversal Entry',
|
||||
ondelete='set null',
|
||||
readonly=True)
|
||||
|
||||
@api.multi
|
||||
def validate(self):
|
||||
# TODO: remove this method if and when
|
||||
# https://github.com/odoo/odoo/pull/7735 is merged
|
||||
if self.env.context.get('novalidate'):
|
||||
return
|
||||
return super(account_move, self).validate()
|
||||
|
||||
@api.multi
|
||||
def _move_reversal(self, reversal_date,
|
||||
reversal_period_id=False, reversal_journal_id=False,
|
||||
move_prefix=False, move_line_prefix=False,
|
||||
reconcile=False):
|
||||
"""
|
||||
Create the reversal of a move
|
||||
|
||||
:param move: browse instance of the move to reverse
|
||||
:param reversal_date: when the reversal must be input
|
||||
:param reversal_period_id: facultative period to write on the move
|
||||
(use the period of the date if empty
|
||||
:param reversal_journal_id: facultative journal on which create
|
||||
the move
|
||||
:param move_prefix: prefix for the move's name
|
||||
:param move_line_prefix: prefix for the move line's names
|
||||
|
||||
:return: Returns the id of the created reversal move
|
||||
"""
|
||||
self.ensure_one()
|
||||
period_obj = self.env['account.period']
|
||||
amlo = self.env['account.move.line']
|
||||
|
||||
if reversal_period_id:
|
||||
reversal_period = period_obj.browse([reversal_period_id])[0]
|
||||
else:
|
||||
reversal_period = period_obj.with_context(
|
||||
company_id=self.company_id.id,
|
||||
account_period_prefer_normal=True).find(reversal_date)[0]
|
||||
if not reversal_journal_id:
|
||||
reversal_journal_id = self.journal_id.id
|
||||
|
||||
if self.env['account.journal'].browse([
|
||||
reversal_journal_id]).company_id != self.company_id:
|
||||
raise Warning(_('Wrong company Journal is %s but we have %s') % (
|
||||
reversal_journal_id.company_id.name, self.company_id.name))
|
||||
if reversal_period.company_id != self.company_id:
|
||||
raise Warning(_('Wrong company Period is %s but we have %s') % (
|
||||
reversal_journal_id.company_id.name, self.company_id.name))
|
||||
|
||||
reversal_ref = ''.join([x for x in [move_prefix, self.ref] if x])
|
||||
reversal_move = self.copy(default={
|
||||
'company_id': self.company_id.id,
|
||||
'date': reversal_date,
|
||||
'period_id': reversal_period.id,
|
||||
'ref': reversal_ref,
|
||||
'journal_id': reversal_journal_id,
|
||||
'to_be_reversed': False,
|
||||
})
|
||||
|
||||
self.with_context(novalidate=True).write({
|
||||
'reversal_id': reversal_move.id,
|
||||
'to_be_reversed': False,
|
||||
})
|
||||
|
||||
rec_dict = {}
|
||||
for rev_move_line in reversal_move.line_id:
|
||||
rev_ml_name = ' '.join(
|
||||
[x for x
|
||||
in [move_line_prefix, rev_move_line.name]
|
||||
if x]
|
||||
)
|
||||
rev_move_line.write(
|
||||
{'debit': rev_move_line.credit,
|
||||
'credit': rev_move_line.debit,
|
||||
'amount_currency': rev_move_line.amount_currency * -1,
|
||||
'name': rev_ml_name},
|
||||
check=True,
|
||||
update_check=True)
|
||||
|
||||
if reconcile and rev_move_line.account_id.reconcile:
|
||||
rec_dict.setdefault(
|
||||
(rev_move_line.account_id, rev_move_line.partner_id),
|
||||
amlo.browse(False))
|
||||
rec_dict[(rev_move_line.account_id, rev_move_line.partner_id)]\
|
||||
+= rev_move_line
|
||||
|
||||
reversal_move.validate()
|
||||
if reconcile:
|
||||
for mline in self.line_id:
|
||||
if mline.account_id.reconcile:
|
||||
rec_dict[(mline.account_id, mline.partner_id)] += mline
|
||||
|
||||
for to_rec_move_lines in rec_dict.itervalues():
|
||||
to_rec_move_lines.reconcile()
|
||||
|
||||
return reversal_move.id
|
||||
|
||||
@api.multi
|
||||
def create_reversals(self, reversal_date, reversal_period_id=False,
|
||||
reversal_journal_id=False,
|
||||
move_prefix=False, move_line_prefix=False,
|
||||
reconcile=False):
|
||||
"""
|
||||
Create the reversal of one or multiple moves
|
||||
|
||||
:param reversal_date: when the reversal must be input
|
||||
:param reversal_period_id: facultative period to write on the move
|
||||
(use the period of the date if empty
|
||||
:param reversal_journal_id: facultative journal on which create
|
||||
the move
|
||||
:param move_prefix: prefix for the move's name
|
||||
:param move_line_prefix: prefix for the move line's names
|
||||
|
||||
:return: Returns a list of ids of the created reversal moves
|
||||
"""
|
||||
return [
|
||||
move._move_reversal(
|
||||
reversal_date,
|
||||
reversal_period_id=reversal_period_id,
|
||||
reversal_journal_id=reversal_journal_id,
|
||||
move_prefix=move_prefix,
|
||||
move_line_prefix=move_line_prefix,
|
||||
reconcile=reconcile
|
||||
)
|
||||
for move in self
|
||||
if not move.reversal_id
|
||||
]
|
||||
@@ -1,62 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Account reversal module for OpenERP
|
||||
Copyright (C) 2011 Akretion (http://www.akretion.com). All Rights Reserved
|
||||
@author Alexis de Lattre <alexis.delattre@akretion.com>
|
||||
The licence is in the file __openerp__.py
|
||||
-->
|
||||
|
||||
<openerp>
|
||||
<data>
|
||||
|
||||
<record id="view_move_reversal_tree" model="ir.ui.view">
|
||||
<field name="name">account.move.reversal.tree</field>
|
||||
<field name="model">account.move</field>
|
||||
<field name="inherit_id" ref="account.view_move_tree" />
|
||||
<field name="arch" type="xml">
|
||||
<field name="to_check" position="after">
|
||||
<field name="to_be_reversed"/>
|
||||
</field>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="view_move_reversal_form" model="ir.ui.view">
|
||||
<field name="name">account.move.reversal.form</field>
|
||||
<field name="model">account.move</field>
|
||||
<field name="inherit_id" ref="account.view_move_form" />
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//field[@name='company_id']" position="after">
|
||||
<field name="to_be_reversed"/>
|
||||
<field name="reversal_id" attrs="{'invisible': [('to_be_reversed', '=', False), ('reversal_id', '=', False)]}"/>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="view_account_move_reversal_filter" model="ir.ui.view">
|
||||
<field name="name">account.move.reversal.select</field>
|
||||
<field name="model">account.move</field>
|
||||
<field name="inherit_id" ref="account.view_account_move_filter" />
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="/search/group[1]/filter[3]" position="after">
|
||||
<filter name="to_be_reversed"
|
||||
string="To Be Reversed"
|
||||
domain="[('to_be_reversed', '=', True), ('reversal_id', '=', False)]"
|
||||
help="Journal Entries to be Reversed"/>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<act_window
|
||||
context="{'search_default_to_be_reversed':1}"
|
||||
id="action_move_to_be_reversed" name="Journal Entries to be Reversed"
|
||||
res_model="account.move"
|
||||
view_id="account.view_move_tree"/>
|
||||
|
||||
<menuitem
|
||||
name="Journal Entries to be Reversed" icon="STOCK_EXECUTE"
|
||||
action="action_move_to_be_reversed"
|
||||
id="menu_move_to_be_reversed"
|
||||
parent="account.menu_account_end_year_treatments"/>
|
||||
|
||||
</data>
|
||||
</openerp>
|
||||
4
account_reversal/models/__init__.py
Normal file
4
account_reversal/models/__init__.py
Normal file
@@ -0,0 +1,4 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||
|
||||
from . import account_move
|
||||
116
account_reversal/models/account_move.py
Normal file
116
account_reversal/models/account_move.py
Normal file
@@ -0,0 +1,116 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2011 Alexis de Lattre <alexis.delattre@akretion.com>
|
||||
# Copyright 2011 Nicolas Bessi (Camptocamp)
|
||||
# Copyright 2012-2013 Guewen Baconnier (Camptocamp)
|
||||
# Copyright 2016 Antonio Espinosa <antonio.espinosa@tecnativa.com>
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||
|
||||
from openerp import api, fields, models, _
|
||||
from openerp.exceptions import UserError
|
||||
|
||||
|
||||
class AccountMove(models.Model):
|
||||
_inherit = "account.move"
|
||||
|
||||
to_be_reversed = fields.Boolean(
|
||||
string="To Be Reversed",
|
||||
help="Check this box if your entry has to be reversed at the end "
|
||||
"of period.")
|
||||
reversal_id = fields.Many2one(
|
||||
comodel_name='account.move', ondelete='set null', readonly=True,
|
||||
string="Reversal Entry")
|
||||
|
||||
def _move_lines_reverse_prepare(self, move, date=False, journal=False,
|
||||
line_prefix=False):
|
||||
for line in move.get('line_ids', []):
|
||||
date = date or line[2].get('date', False)
|
||||
journal_id = journal and journal.id
|
||||
journal_id = journal_id or line[2].get('journal_id', False)
|
||||
name = line[2].get('name', False) or line_prefix
|
||||
debit = line[2].get('debit', 0.)
|
||||
credit = line[2].get('credit', 0.)
|
||||
amount_currency = line[2].get('amount_currency', 0.)
|
||||
if line_prefix and line_prefix != name:
|
||||
name = ' '.join([line_prefix, name])
|
||||
line[2].update({
|
||||
'name': name,
|
||||
'date': date,
|
||||
'journal_id': journal_id,
|
||||
'debit': credit,
|
||||
'credit': debit,
|
||||
'amount_currency': -amount_currency,
|
||||
})
|
||||
return move
|
||||
|
||||
def _move_reverse_prepare(self, date=False, journal=False,
|
||||
move_prefix=False):
|
||||
self.ensure_one()
|
||||
journal = journal or self.journal_id
|
||||
if journal.company_id != self.company_id:
|
||||
raise UserError(
|
||||
_("Wrong company Journal is '%s' but we have '%s'") % (
|
||||
journal.company_id.name, self.company_id.name))
|
||||
ref = self.ref or move_prefix
|
||||
if move_prefix and move_prefix != ref:
|
||||
ref = ' '.join([move_prefix, ref])
|
||||
date = date or self.date
|
||||
move = self.copy_data()[0]
|
||||
move.update({
|
||||
'journal_id': journal.id,
|
||||
'date': date,
|
||||
'ref': ref,
|
||||
'to_be_reversed': False,
|
||||
'state': 'draft',
|
||||
})
|
||||
return move
|
||||
|
||||
@api.multi
|
||||
def move_reverse_reconcile(self):
|
||||
for move in self.filtered('reversal_id'):
|
||||
rec = {}
|
||||
lines = move.reversal_id.line_ids.filtered('account_id.reconcile')
|
||||
for line in lines:
|
||||
rec.setdefault((line.account_id, line.partner_id),
|
||||
self.env['account.move.line'])
|
||||
rec[(line.account_id, line.partner_id)] += line
|
||||
lines = move.line_ids.filtered('account_id.reconcile')
|
||||
for line in lines:
|
||||
rec[(line.account_id, line.partner_id)] += line
|
||||
for lines in rec.itervalues():
|
||||
lines.reconcile()
|
||||
return True
|
||||
|
||||
@api.multi
|
||||
def create_reversals(self, date=False, journal=False, move_prefix=False,
|
||||
line_prefix=False, post=False, reconcile=False):
|
||||
"""
|
||||
Create the reversal of one or multiple moves
|
||||
|
||||
:param self: moves to reverse
|
||||
:param date: when the reversal must be input
|
||||
(use original if empty)
|
||||
:param journal: journal on which create the move
|
||||
(use original if empty)
|
||||
:param move_prefix: prefix for the move's name
|
||||
:param line_prefix: prefix for the move line's names
|
||||
:param reconcile: reconcile lines (if account with reconcile = True)
|
||||
|
||||
:return: Returns a recordset of the created reversal moves
|
||||
"""
|
||||
moves = self.env['account.move']
|
||||
for orig in self:
|
||||
data = orig._move_reverse_prepare(
|
||||
date=date, journal=journal, move_prefix=move_prefix)
|
||||
data = orig._move_lines_reverse_prepare(
|
||||
data, date=date, journal=journal, line_prefix=line_prefix)
|
||||
reversal_move = self.create(data)
|
||||
moves |= reversal_move
|
||||
orig.write({
|
||||
'reversal_id': reversal_move.id,
|
||||
'to_be_reversed': False,
|
||||
})
|
||||
if moves and post:
|
||||
moves.post()
|
||||
if reconcile:
|
||||
moves.move_reverse_reconcile()
|
||||
return moves
|
||||
@@ -1,23 +1,4 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
# Account partner required module for OpenERP
|
||||
# Copyright (C) 2014 Acsone (http://acsone.eu).
|
||||
# @author Stéphane Bidoul <stephane.bidoul@acsone.eu>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
# published by the Free Software Foundation, either version 3 of the
|
||||
# License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
# -*- coding: utf-8 -*-
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||
|
||||
from . import test_account_reversal
|
||||
|
||||
@@ -1,120 +1,73 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
# #############################################################################
|
||||
#
|
||||
# Account partner required module for OpenERP
|
||||
# Copyright (C) 2014 Acsone (http://acsone.eu).
|
||||
# @author Stéphane Bidoul <stephane.bidoul@acsone.eu>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
# published by the Free Software Foundation, either version 3 of the
|
||||
# License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2014 Stéphane Bidoul <stephane.bidoul@acsone.eu>
|
||||
# Copyright 2016 Antonio Espinosa <antonio.espinosa@tecnativa.com>
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||
|
||||
import time
|
||||
from datetime import datetime
|
||||
|
||||
from openerp.tests import common
|
||||
from openerp import fields
|
||||
from openerp.tests.common import TransactionCase
|
||||
|
||||
|
||||
class test_account_reversal(common.TransactionCase):
|
||||
class TestAccountReversal(TransactionCase):
|
||||
|
||||
def setUp(self):
|
||||
super(test_account_reversal, self).setUp()
|
||||
super(TestAccountReversal, self).setUp()
|
||||
self.move_obj = self.env['account.move']
|
||||
self.move_line_obj = self.env['account.move.line']
|
||||
|
||||
def _create_move(self, with_partner, amount=100, period=None):
|
||||
date = datetime.now()
|
||||
company_id = self.env.ref('base.main_company').id
|
||||
period = period or self.env.ref('account.period_0')
|
||||
|
||||
journal = self.env['account.journal'].create({
|
||||
self.company_id = self.env.ref('base.main_company').id
|
||||
self.partner = self.env['res.partner'].create({
|
||||
'name': 'Test partner',
|
||||
})
|
||||
self.journal = self.env['account.journal'].create({
|
||||
'name': 'Test journal',
|
||||
'code': 'COD',
|
||||
'type': 'sale',
|
||||
'sequence_id': self.env.ref('account.sequence_sale_journal').id,
|
||||
'company_id': company_id})
|
||||
'company_id': self.company_id
|
||||
})
|
||||
type_revenue = self.env.ref('account.data_account_type_revenue')
|
||||
type_payable = self.env.ref('account.data_account_type_payable')
|
||||
self.account_sale = self.env['account.account'].create({
|
||||
'name': 'Test sale',
|
||||
'code': 'XX_700',
|
||||
'user_type_id': type_revenue.id,
|
||||
})
|
||||
self.account_customer = self.env['account.account'].create({
|
||||
'name': 'Test customer',
|
||||
'code': 'XX_430',
|
||||
'user_type_id': type_payable.id,
|
||||
'reconcile': True,
|
||||
})
|
||||
|
||||
def _create_move(self, with_partner=True, amount=100):
|
||||
move_vals = {
|
||||
'journal_id': journal.id,
|
||||
'period_id': period.id,
|
||||
'date': date,
|
||||
'company_id': company_id,
|
||||
}
|
||||
|
||||
# Why this doesn't work I don't know:
|
||||
# acct = self.ref('account.a_sale'
|
||||
account1, account2 = self.env['account.account'].search(
|
||||
[('company_id', '=', company_id), ('type', '=', 'other')])[:2]
|
||||
|
||||
move_id = self.move_obj.create(move_vals)
|
||||
self.move_line_obj.create({
|
||||
'move_id': move_id.id,
|
||||
'name': '/',
|
||||
'debit': 0,
|
||||
'credit': amount,
|
||||
'company_id': company_id,
|
||||
'account_id': account1.id})
|
||||
move_line_id = self.move_line_obj.create(
|
||||
{
|
||||
'move_id': move_id.id,
|
||||
'journal_id': self.journal.id,
|
||||
'company_id': self.company_id,
|
||||
'line_ids': [(0, 0, {
|
||||
'name': '/',
|
||||
'debit': amount,
|
||||
'credit': 0,
|
||||
'account_id': account2.id,
|
||||
'company_id': company_id,
|
||||
'partner_id': self.ref('base.res_partner_1')
|
||||
if with_partner else False
|
||||
}
|
||||
)
|
||||
return move_line_id.move_id
|
||||
'account_id': self.account_customer.id,
|
||||
'company_id': self.company_id,
|
||||
'partner_id': with_partner and self.partner.id
|
||||
}), (0, 0, {
|
||||
'name': '/',
|
||||
'debit': 0,
|
||||
'credit': amount,
|
||||
'company_id': self.company_id,
|
||||
'account_id': self.account_sale.id,
|
||||
})]
|
||||
}
|
||||
return self.move_obj.create(move_vals)
|
||||
|
||||
def _close_period(self, period_id):
|
||||
self.env.cr.execute('update account_journal_period '
|
||||
'set state=%s where period_id=%s',
|
||||
('done', period_id))
|
||||
self.env.cr.execute('update account_period '
|
||||
'set state=%s where id=%s',
|
||||
('done', period_id))
|
||||
self.env.invalidate_all()
|
||||
def _move_str(self, move):
|
||||
return ''.join(['%.2f%.2f%s' % (
|
||||
x.debit, x.credit, x.account_id == self.account_sale and
|
||||
':SALE_' or ':CUSTOMER_')
|
||||
for x in move.line_ids.sorted(key=lambda r: r.account_id.id)])
|
||||
|
||||
def test_reverse(self):
|
||||
move = self._create_move(with_partner=False)
|
||||
company_id = self.env.ref('base.main_company').id
|
||||
account1 = self.env['account.account'].search(
|
||||
[('company_id', '=', company_id), ('type', '=', 'other')])[0]
|
||||
movestr = ''.join(['%.2f%.2f%s' % (x.debit, x.credit,
|
||||
x.account_id == account1 and
|
||||
'aaaa' or 'bbbb')
|
||||
for x in move.line_id])
|
||||
self.assertEqual(movestr, '100.000.00bbbb0.00100.00aaaa')
|
||||
yesterday_date = datetime(
|
||||
year=time.localtime().tm_year, month=3, day=3
|
||||
)
|
||||
yesterday = fields.Date.to_string(yesterday_date)
|
||||
reversed_move_ids = move.create_reversals(yesterday)
|
||||
reversed_moves = self.env['account.move'].browse(reversed_move_ids)
|
||||
movestr_reversed = ''.join(
|
||||
['%.2f%.2f%s' % (x.debit, x.credit,
|
||||
x.account_id == account1 and 'aaaa' or 'bbbb')
|
||||
for x in reversed_moves.line_id])
|
||||
self.assertEqual(movestr_reversed, '0.00100.00bbbb100.000.00aaaa')
|
||||
|
||||
def test_reverse_closed_period(self):
|
||||
move_period = self.env.ref('account.period_0')
|
||||
move = self._create_move(with_partner=False, period=move_period)
|
||||
self._close_period(move_period.id)
|
||||
reversal_period = self.env.ref('account.period_1')
|
||||
move.create_reversals(reversal_date=reversal_period.date_start,
|
||||
reversal_period_id=reversal_period.id)
|
||||
move = self._create_move()
|
||||
self.assertEqual(
|
||||
self._move_str(move), '0.00100.00:SALE_100.000.00:CUSTOMER_')
|
||||
rev = move.create_reversals()
|
||||
self.assertEqual(len(rev), 1)
|
||||
self.assertEqual(
|
||||
self._move_str(rev), '100.000.00:SALE_0.00100.00:CUSTOMER_')
|
||||
|
||||
59
account_reversal/views/account_move_view.xml
Normal file
59
account_reversal/views/account_move_view.xml
Normal file
@@ -0,0 +1,59 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright 2011 Alexis de Lattre <alexis.delattre@akretion.com>
|
||||
Copyright 2016 Antonio Espinosa <antonio.espinosa@tecnativa.com>
|
||||
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -->
|
||||
<odoo>
|
||||
|
||||
<record id="view_move_tree" model="ir.ui.view">
|
||||
<field name="name">Add to_be_reversed column</field>
|
||||
<field name="model">account.move</field>
|
||||
<field name="inherit_id" ref="account.view_move_tree" />
|
||||
<field name="arch" type="xml">
|
||||
<field name="state" position="after">
|
||||
<field name="to_be_reversed"/>
|
||||
</field>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="view_move_form" model="ir.ui.view">
|
||||
<field name="name">Add to_be_reversed and reversal_id fields</field>
|
||||
<field name="model">account.move</field>
|
||||
<field name="inherit_id" ref="account.view_move_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<button name="%(account.action_view_account_move_reversal)d" position="attributes">
|
||||
<attribute name="name">%(act_account_move_reverse)d</attribute>
|
||||
</button>
|
||||
<xpath expr="//field[@name='company_id']" position="after">
|
||||
<field name="to_be_reversed"/>
|
||||
<field name="reversal_id"
|
||||
attrs="{'invisible': [('to_be_reversed', '=', False), ('reversal_id', '=', False)]}"/>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="view_account_move_filter" model="ir.ui.view">
|
||||
<field name="name">Add to_be_reversed filter</field>
|
||||
<field name="model">account.move</field>
|
||||
<field name="inherit_id" ref="account.view_account_move_filter" />
|
||||
<field name="arch" type="xml">
|
||||
<field name="date" position="after">
|
||||
<filter name="to_be_reversed"
|
||||
string="To Be Reversed"
|
||||
domain="[('to_be_reversed', '=', True), ('reversal_id', '=', False)]"
|
||||
help="Journal Entries to be Reversed"/>
|
||||
</field>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<act_window
|
||||
context="{'search_default_to_be_reversed':1}"
|
||||
id="action_move_to_be_reversed" name="Journal Entries to be Reversed"
|
||||
res_model="account.move"
|
||||
view_id="account.view_move_tree"/>
|
||||
|
||||
<menuitem id="menu_move_to_be_reversed"
|
||||
parent="account.menu_finance_entries" sequence="80"
|
||||
name="Journal Entries to be Reversed"
|
||||
action="action_move_to_be_reversed"/>
|
||||
|
||||
</odoo>
|
||||
@@ -1,2 +1,4 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||
|
||||
from . import account_move_reverse
|
||||
|
||||
@@ -1,122 +1,78 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
# Account reversal module for OpenERP
|
||||
# Copyright (C) 2011 Akretion (http://www.akretion.com). All Rights Reserved
|
||||
# @author Alexis de Lattre <alexis.delattre@akretion.com>
|
||||
# Copyright (c) 2012-2013 Camptocamp SA (http://www.camptocamp.com)
|
||||
# @author Guewen Baconnier
|
||||
#
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
# published by the Free Software Foundation, either version 3 of the
|
||||
# License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
# Copyright 2011 Alexis de Lattre <alexis.delattre@akretion.com>
|
||||
# Copyright 2012-2013 Guewen Baconnier (Camptocamp)
|
||||
# Copyright 2016 Antonio Espinosa <antonio.espinosa@tecnativa.com>
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||
|
||||
from openerp.osv import orm, fields
|
||||
from openerp.tools.translate import _
|
||||
from openerp import api, fields, models, _
|
||||
|
||||
|
||||
class account_move_reversal(orm.TransientModel):
|
||||
class AccountMoveReverse(models.TransientModel):
|
||||
_name = "account.move.reverse"
|
||||
_description = "Create reversal of account moves"
|
||||
|
||||
_columns = {
|
||||
'date': fields.date(
|
||||
'Reversal Date',
|
||||
required=True,
|
||||
help="Enter the date of the reversal account entries. "
|
||||
"By default, OpenERP proposes the first day of "
|
||||
"the period following the period of the move to reverse."),
|
||||
'period_id': fields.many2one(
|
||||
'account.period',
|
||||
'Reversal Period',
|
||||
help="If empty, take the period of the date."),
|
||||
'journal_id': fields.many2one(
|
||||
'account.journal',
|
||||
'Reversal Journal',
|
||||
help='If empty, uses the journal of the journal entry '
|
||||
'to be reversed.'),
|
||||
'move_prefix': fields.char(
|
||||
'Entries Ref. Prefix',
|
||||
help="Prefix that will be added to the 'Ref' of the journal "
|
||||
"entry to be reversed to create the 'Ref' of the "
|
||||
"reversal journal entry (no space added after the prefix)."),
|
||||
'move_line_prefix': fields.char(
|
||||
'Items Name Prefix',
|
||||
help="Prefix that will be added to the name of the journal "
|
||||
"item to be reversed to create the name of the reversal "
|
||||
"journal item (a space is added after the prefix)."),
|
||||
'reconcile': fields.boolean('Reconcile'),
|
||||
}
|
||||
def _default_date(self):
|
||||
active_id = (self.env.context.get('active_id') or
|
||||
self.env.context.get('active_ids', [None])[0])
|
||||
move = self.env['account.move'].browse(active_id)
|
||||
return move.date or fields.Date.today()
|
||||
|
||||
def _next_period_first_date(self, cr, uid, context=None):
|
||||
if context is None:
|
||||
context = {}
|
||||
res = False
|
||||
period_ctx = context.copy()
|
||||
period_ctx['account_period_prefer_normal'] = True
|
||||
period_obj = self.pool.get('account.period')
|
||||
assert context['active_model'] == 'account.move'
|
||||
to_reverse_move = self.pool['account.move'].browse(
|
||||
cr, uid, context['active_id'], context=context)
|
||||
next_period_id = period_obj.next(
|
||||
cr, uid, to_reverse_move.period_id, 1, context=context)
|
||||
if next_period_id:
|
||||
next_period = period_obj.browse(
|
||||
cr, uid, next_period_id, context=context)
|
||||
res = next_period.date_start
|
||||
return res
|
||||
def _default_journal_id(self):
|
||||
active_id = (self.env.context.get('active_id') or
|
||||
self.env.context.get('active_ids', [None])[0])
|
||||
move = self.env['account.move'].browse(active_id)
|
||||
return move.journal_id.id
|
||||
|
||||
_defaults = {
|
||||
'date': _next_period_first_date,
|
||||
'move_line_prefix': 'REV -',
|
||||
'reconcile': True,
|
||||
}
|
||||
date = fields.Date(
|
||||
string="Reversal Date", required=True, default=_default_date,
|
||||
help="Enter the date of the reversal account entries. "
|
||||
"By default, Odoo proposes the same date of the move to reverse.")
|
||||
journal_id = fields.Many2one(
|
||||
comodel_name='account.journal', string="Reversal Journal",
|
||||
default=_default_journal_id,
|
||||
help="Enter the date of the reversal account entries. "
|
||||
"If empty, Odoo uses the same journal of the move to reverse.")
|
||||
move_prefix = fields.Char(
|
||||
string="Entries Ref. Prefix", default="REV:",
|
||||
help="Prefix that will be added to the 'Ref' of the reversal account "
|
||||
"entries. If empty, Odoo uses the Ref of the move to reverse. "
|
||||
"(NOTE: A space is added after the prefix).")
|
||||
line_prefix = fields.Char(
|
||||
string="Items Name Prefix", default="REV:",
|
||||
help="Prefix that will be added to the 'Name' of the reversal account "
|
||||
"entrie items. If empty, Odoo uses the same name of the move "
|
||||
"line to reverse. (NOTE: A space is added after the prefix).")
|
||||
post = fields.Boolean(
|
||||
string="Post", default=True,
|
||||
help="Mark this if you want to post reversal move")
|
||||
reconcile = fields.Boolean(
|
||||
string="Reconcile", default=True,
|
||||
help="Mark this if you want to reconcile items of both moves.")
|
||||
|
||||
def action_reverse(self, cr, uid, ids, context=None):
|
||||
if context is None:
|
||||
context = {}
|
||||
assert 'active_ids' in context, "active_ids missing in context"
|
||||
|
||||
form = self.read(cr, uid, ids, context=context)[0]
|
||||
|
||||
move_obj = self.pool.get('account.move')
|
||||
move_ids = context['active_ids']
|
||||
|
||||
period_id = form['period_id'][0] if form.get('period_id') else False
|
||||
journal_id = form['journal_id'][0] if form.get('journal_id') else False
|
||||
reconcile = form['reconcile'] if form.get('reconcile') else False
|
||||
reversed_move_ids = move_obj.create_reversals(
|
||||
cr, uid,
|
||||
move_ids,
|
||||
form['date'],
|
||||
reversal_period_id=period_id,
|
||||
reversal_journal_id=journal_id,
|
||||
move_prefix=form['move_prefix'],
|
||||
move_line_prefix=form['move_line_prefix'],
|
||||
reconcile=reconcile,
|
||||
context=context)
|
||||
|
||||
action = self.pool['ir.actions.act_window'].for_xml_id(
|
||||
cr, uid, 'account', 'action_move_journal_line')
|
||||
action['name'] = _('Reversal Entries')
|
||||
action['context'] = unicode({'search_default_to_be_reversed': 0})
|
||||
if len(reversed_move_ids) == 1:
|
||||
action['res_id'] = reversed_move_ids[0]
|
||||
action['view_mode'] = 'form,tree'
|
||||
action['views'] = False
|
||||
action['view_id'] = False
|
||||
@api.multi
|
||||
def action_reverse(self):
|
||||
moves = self.env['account.move']
|
||||
for wizard in self:
|
||||
orig = moves.browse(self.env.context.get('active_ids'))
|
||||
moves |= orig.create_reversals(
|
||||
date=wizard.date, journal=wizard.journal_id,
|
||||
move_prefix=wizard.move_prefix, line_prefix=wizard.line_prefix,
|
||||
post=wizard.post, reconcile=wizard.reconcile)
|
||||
action = {
|
||||
'name': _('Reverse moves'),
|
||||
'type': 'ir.actions.act_window',
|
||||
'view_type': 'form',
|
||||
'res_model': 'account.move',
|
||||
'context': {'search_default_to_be_reversed': 0},
|
||||
}
|
||||
if len(moves) == 1:
|
||||
action.update({
|
||||
'view_mode': 'form,tree',
|
||||
'res_id': moves.id,
|
||||
})
|
||||
else:
|
||||
action['domain'] = unicode([('id', 'in', reversed_move_ids)])
|
||||
action.update({
|
||||
'view_mode': 'tree,form',
|
||||
'domain': [('id', 'in', moves.ids)],
|
||||
})
|
||||
return action
|
||||
|
||||
@@ -1,47 +1,51 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<openerp>
|
||||
<data>
|
||||
<!-- Copyright 2011 Alexis de Lattre <alexis.delattre@akretion.com>
|
||||
Copyright 2016 Antonio Espinosa <antonio.espinosa@tecnativa.com>
|
||||
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -->
|
||||
|
||||
<record id="view_account_move_reverse" model="ir.ui.view">
|
||||
<field name="name">account.move.reverse.form</field>
|
||||
<field name="model">account.move.reverse</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Create reversal journal entries">
|
||||
<label string="This will create reversal for all selected entries whether checked 'to be reversed' or not."/>
|
||||
<group>
|
||||
<field name="date"/>
|
||||
<field name="period_id"/>
|
||||
<field name="journal_id"/>
|
||||
<newline/>
|
||||
<field name="move_prefix" />
|
||||
<field name="move_line_prefix" />
|
||||
<field name="reconcile"/>
|
||||
</group>
|
||||
<odoo>
|
||||
|
||||
<footer>
|
||||
<button name="action_reverse" string="Reverse Entries"
|
||||
type="object" class="oe_highlight"/>
|
||||
or
|
||||
<button string="Cancel" class="oe_link" special="cancel"/>
|
||||
</footer>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
<record id="view_account_move_reverse" model="ir.ui.view">
|
||||
<field name="name">account.move.reverse.form</field>
|
||||
<field name="model">account.move.reverse</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Create reversal journal entries">
|
||||
<label string="This will create reversal for all selected entries whether checked 'to be reversed' or not."/>
|
||||
<group>
|
||||
<group>
|
||||
<field name="date"/>
|
||||
<field name="journal_id"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="move_prefix" />
|
||||
<field name="line_prefix" />
|
||||
<field name="post"/>
|
||||
<field name="reconcile"
|
||||
attrs="{'invisible': [('post', '=', False)]}"/>
|
||||
</group>
|
||||
</group>
|
||||
|
||||
<record id="act_account_move_reverse" model="ir.actions.act_window">
|
||||
<field name="name">Reverse Entries</field>
|
||||
<field name="res_model">account.move.reverse</field>
|
||||
<field name="view_mode">form</field>
|
||||
<field name="view_id" ref="view_account_move_reverse"/>
|
||||
<field name="target">new</field>
|
||||
</record>
|
||||
<footer>
|
||||
<button name="action_reverse" string="Reverse Entries"
|
||||
type="object" class="oe_highlight"/>
|
||||
or
|
||||
<button string="Cancel" class="oe_link" special="cancel"/>
|
||||
</footer>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="ir_account_move_reverse" model="ir.values">
|
||||
<field name="name">Reverse Entries</field>
|
||||
<field name="key2">client_action_multi</field>
|
||||
<field name="model">account.move</field>
|
||||
<field name="value" eval="'ir.actions.act_window,%d'%act_account_move_reverse" />
|
||||
</record>
|
||||
<record id="act_account_move_reverse" model="ir.actions.act_window">
|
||||
<field name="name">Reverse Entries</field>
|
||||
<field name="res_model">account.move.reverse</field>
|
||||
<field name="view_mode">form</field>
|
||||
<field name="view_id" ref="view_account_move_reverse"/>
|
||||
<field name="target">new</field>
|
||||
</record>
|
||||
|
||||
</data>
|
||||
</openerp>
|
||||
<record id="account.action_account_move_reversal" model="ir.values">
|
||||
<field name="value"
|
||||
eval="'ir.actions.act_window,' + str(ref('act_account_move_reverse'))" />
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
|
||||
Reference in New Issue
Block a user