[ADD] account_full_reconcile: module that reintroduce the concept of full reconciliation, to have a named reference on journal items

This commit is contained in:
qdp-odoo
2016-02-26 11:23:26 +01:00
parent 89df4aa5ec
commit 395c4bdc69
6 changed files with 239 additions and 0 deletions
+97
View File
@@ -0,0 +1,97 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import models
from openerp import SUPERUSER_ID
from collections import OrderedDict
from openerp.tools import float_compare
def _migrate_full_reconcile(cr, registry):
#avoid doing anything if the table has already something in it (already migrated)
cr.execute("""SELECT count(id) FROM account_full_reconcile""")
res = cr.fetchone()[0]
if res:
return
#check column reconcile_id exists on account.move.line
cr.execute("""
SELECT 1 FROM information_schema.columns
WHERE table_name = 'account_move_line'
AND column_name = 'reconcile_id'
""")
if not cr.fetchone():
#if no, it's a fresh v9 database and we cannot migrate anything. Rebuild the whole table based on partial
all_partial_rec_ids = registry['account.partial.reconcile'].search(cr, SUPERUSER_ID, [])
already_processed = {}
for partial in registry['account.partial.reconcile'].browse(cr, SUPERUSER_ID, all_partial_rec_ids):
partial_rec_set = OrderedDict.fromkeys([partial])
aml_set = set()
total_debit = 0
total_credit = 0
for partial_rec in partial_rec_set:
if partial_rec in already_processed:
continue
for aml in [partial_rec.debit_move_id, partial_rec.credit_move_id]:
if aml not in aml_set:
total_debit += aml.debit
total_credit += aml.credit
aml_set |= set([aml])
for x in aml.matched_debit_ids | aml.matched_credit_ids:
partial_rec_set[x] = None
partial_rec_ids = []
for x in partial_rec_set.keys():
partial_rec_ids.append(x.id)
already_processed[x] = None
aml_ids = [x.id for x in aml_set]
if aml_ids and partial_rec_ids:
#then, if the total debit and credit are equal, the reconciliation is full
digits_rounding_precision = aml.company_id.currency_id.rounding
if float_compare(total_debit, total_credit, precision_rounding=digits_rounding_precision) == 0:
#in that case, mark the reference on the partial reconciliations and the entries
registry['account.full.reconcile'].create(cr, SUPERUSER_ID, {
'partial_reconcile_ids': [(6, 0, partial_rec_ids)],
'reconciled_line_ids': [(6, 0, aml_ids)]
}, context={'check_move_validity': False})
return
#otherwise, use the table account_move_reconcile if it exists (not dropped by migration)
cr.execute("SELECT 1 FROM information_schema.tables WHERE table_name = 'account_move_reconcile'")
if cr.fetchone():
#account_move_reconcile exists
#copy old table
cr.execute("""
INSERT INTO account_full_reconcile (id, name, create_date)
SELECT id, name, create_date
FROM account_move_reconcile
WHERE id IN (
SELECT DISTINCT reconcile_id FROM account_move_line WHERE reconcile_id IS NOT NULL)
""")
else:
#account_move_reconcile was dropped during migration, rebuild that table
cr.execute("""
INSERT INTO account_full_reconcile (id, name)
SELECT DISTINCT reconcile_id, reconcile_ref FROM account_move_line WHERE reconcile_id IS NOT NULL
""")
#update the index of account_full_reconcile
cr.execute("SELECT setval('account_full_reconcile_id_seq', (SELECT MAX(id) FROM account_full_reconcile))")
#copy values on account.move.line
cr.execute("UPDATE account_move_line SET full_reconcile_id = reconcile_id WHERE reconcile_id IS NOT NULL")
#copy the full_reconcile_id existing of account.move.line to their account.partial.reconcile
cr.execute("""
WITH tmp_table AS (
SELECT rec.id AS partial_id, aml.reconcile_id AS full_id
FROM account_move_line aml
RIGHT JOIN account_partial_reconcile rec
ON aml.id = rec.debit_move_id
WHERE aml.reconcile_id IS NOT NULL
UNION
SELECT rec.id AS partial_id, aml.reconcile_id AS full_id
FROM account_move_line aml
RIGHT JOIN account_partial_reconcile rec
ON aml.id = rec.credit_move_id
WHERE aml.reconcile_id IS NOT NULL)
UPDATE account_partial_reconcile p
SET full_reconcile_id = (SELECT full_id FROM tmp_table tmp WHERE p.id = tmp.partial_id)
WHERE p.id IN (SELECT partial_id FROM tmp_table)
""")
@@ -0,0 +1,21 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
{
'name': 'Full Reconciliation Concept',
'version': '1.1',
'category': 'Accounting & Finance',
'description': """
Add the concept of full reconciliation back into the accounting.
""",
'website': 'https://www.odoo.com/page/accounting',
'depends': ['account'],
'data': [
'security/ir.model.access.csv',
'views/account_full_reconcile_view.xml',
],
'demo': [],
'installable': True,
'auto_install': True,
'post_init_hook': '_migrate_full_reconcile',
}
@@ -0,0 +1,4 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import account_full_reconcile
@@ -0,0 +1,64 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from collections import OrderedDict
from openerp.tools import float_compare
from openerp import api, fields, models
class AccountMoveLine(models.Model):
_inherit = "account.move.line"
full_reconcile_id = fields.Many2one('account.full.reconcile', string="Matching Number")
class AccountPartialReconcile(models.Model):
_inherit = "account.partial.reconcile"
full_reconcile_id = fields.Many2one('account.full.reconcile', string="Full Reconcile")
@api.model
def create(self, vals):
res = super(AccountPartialReconcile, self).create(vals)
#check if the reconcilation is full
#first, gather all journal items involved in the reconciliation just created
partial_rec_set = OrderedDict.fromkeys([x for x in res])
aml_set = self.env['account.move.line']
total_debit = 0
total_credit = 0
for partial_rec in partial_rec_set:
for aml in [partial_rec.debit_move_id, partial_rec.credit_move_id]:
if aml not in aml_set:
total_debit += aml.debit
total_credit += aml.credit
aml_set |= aml
for x in aml.matched_debit_ids | aml.matched_credit_ids:
partial_rec_set[x] = None
partial_rec_ids = [x.id for x in partial_rec_set.keys()]
aml_ids = [x.id for x in aml_set]
#then, if the total debit and credit are equal, the reconciliation is full
digits_rounding_precision = aml_set[0].company_id.currency_id.rounding
if float_compare(total_debit, total_credit, precision_rounding=digits_rounding_precision) == 0:
#in that case, mark the reference on the partial reconciliations and the entries
self.env['account.full.reconcile'].with_context(check_move_validity=False).create({
'partial_reconcile_ids': [(6, 0, partial_rec_ids)],
'reconciled_line_ids': [(6, 0, aml_ids)]})
return res
@api.multi
def unlink(self):
"""When removing a partial reconciliation, also unlink its full reconciliation if it exists"""
full_rec = self.env['account.full.reconcile']
for rec in self:
if rec.full_reconcile_id:
full_rec |= rec.full_reconcile_id
full_rec.unlink()
return super(AccountPartialReconcile, self).unlink()
class AccountFullReconcile(models.Model):
_name = "account.full.reconcile"
_description = "Full Reconcile"
name = fields.Char(string='Number', required=True, copy=False, default=lambda self: self.env['ir.sequence'].next_by_code('account.reconcile'))
partial_reconcile_ids = fields.One2many('account.partial.reconcile', 'full_reconcile_id', string='Reconciliation Parts')
reconciled_line_ids = fields.One2many('account.move.line', 'full_reconcile_id', string='Matched Journal Items')
@@ -0,0 +1,3 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_account_full_reconcile_group_invoice,account.full.reconcile.group.invoice,model_account_full_reconcile,account.group_account_invoice,1,1,1,0
access_account_full_reconcile,account.full.reconcile,model_account_full_reconcile,account.group_account_user,1,1,1,1
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_account_full_reconcile_group_invoice account.full.reconcile.group.invoice model_account_full_reconcile account.group_account_invoice 1 1 1 0
3 access_account_full_reconcile account.full.reconcile model_account_full_reconcile account.group_account_user 1 1 1 1
@@ -0,0 +1,50 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<!-- Full Reconcile -->
<record id="view_full_reconcile_form" model="ir.ui.view">
<field name="name">account.full.reconcile.form</field>
<field name="model">account.full.reconcile</field>
<field name="arch" type="xml">
<form string="Matching">
<group>
<div class="oe_title" colspan="4">
<h1><field name="name" readonly="1"/></h1>
</div>
<separator string="Matched Journal Items" colspan="4"/>
<field name="reconciled_line_ids" readonly="1" colspan="4" nolabel="1"/>
</group>
</form>
</field>
</record>
<record id="view_move_line_form" model="ir.ui.view">
<field name="name">account.move.line.form.full.reconcile</field>
<field name="model">account.move.line</field>
<field name="inherit_id" ref="account.view_move_line_form"/>
<field name="arch" type="xml">
<data>
<xpath expr="//label[@for='reconciled']" position="replace">
<label for="full_reconcile_id"/>
</xpath>
<xpath expr="//field[@name='reconciled']" position="attributes">
<attribute name="invisible">1</attribute>
</xpath>
<xpath expr="//field[@name='reconciled']" position="after">
<field name="full_reconcile_id"/>
</xpath>
<xpath expr="//button[@name='open_reconcile_view']" position="replace">
<button name="open_reconcile_view"
class="oe_link"
type="object"
string="-> View partially reconciled entries"
attrs="{'invisible': ['|', ('reconciled', '=', True), '&amp;', ('matched_debit_ids', '=', []),('matched_credit_ids', '=', [])]}">
</button>
</xpath>
</data>
</field>
</record>
</data>
</openerp>