Browse Source

[ADD] backport mis_builder from 8.0

pull/90/head
Laurent Mignon (ACSONE) 9 years ago
committed by Stéphane Bidoul
parent
commit
495693dacc
  1. 9
      mis_builder/__openerp__.py
  2. 161
      mis_builder/models/aep.py
  3. 820
      mis_builder/models/mis_builder.py
  4. 1
      mis_builder/report/__init__.py
  5. 66
      mis_builder/report/report_mis_report_instance.py
  6. 55
      mis_builder/report/report_mis_report_instance.xml
  7. 4
      mis_builder/tests/__init__.py
  8. 18
      mis_builder/views/mis_builder.xml

9
mis_builder/__openerp__.py

@ -29,6 +29,8 @@
'summary': """
Build 'Management Information System' Reports and Dashboards
""",
'description': """
""",
'author': 'ACSONE SA/NV',
'website': 'http://acsone.eu',
'depends': [
@ -40,7 +42,6 @@
'views/mis_builder.xml',
'security/ir.model.access.csv',
'security/mis_builder_security.xml',
'report/report_mis_report_instance.xml',
],
'test': [
],
@ -51,6 +52,12 @@
'tests/mis.report.instance.period.csv',
'tests/mis.report.instance.csv',
],
'js': [
'static/src/js/*.js'
],
'css': [
'static/src/css/*.css'
],
'qweb': [
'static/src/xml/*.xml'
],

161
mis_builder/models/aep.py

@ -26,6 +26,7 @@ import re
from collections import defaultdict
from openerp.exceptions import Warning
from openerp import pooler
from openerp.osv import expression
from openerp.tools.safe_eval import safe_eval
from openerp.tools.translate import _
@ -82,18 +83,16 @@ class AccountingExpressionProcessor(object):
r"(?P<accounts>_[a-zA-Z0-9]+|\[.*?\])"
r"(?P<domain>\[.*?\])?")
def __init__(self, env):
self.env = env
def __init__(self, cursor):
self.pool = pooler.get_pool(cursor.dbname)
# before done_parsing: {(domain, mode): set(account_codes)}
# after done_parsing: {(domain, mode): list(account_ids)}
self._map_account_ids = defaultdict(set)
self._account_ids_by_code = defaultdict(set)
def _load_account_codes(self, account_codes, root_account):
account_model = self.env['account.account']
# TODO: account_obj is necessary because _get_children_and_consol
# does not work in new API?
account_obj = self.env.registry('account.account')
def _load_account_codes(self, cr, uid, account_codes, root_account,
context=None):
account_obj = self.pool['account.account']
exact_codes = set()
like_codes = set()
for account_code in account_codes:
@ -109,9 +108,13 @@ class AccountingExpressionProcessor(object):
like_codes.add(account_code)
else:
exact_codes.add(account_code)
for account in account_model.\
search([('code', 'in', list(exact_codes)),
('parent_id', 'child_of', root_account.id)]):
account_ids = account_obj.search(
cr, uid,
[('code', 'in', list(exact_codes)),
('parent_id', 'child_of', root_account.id)],
context=context)
for account in account_obj.browse(
cr, uid, account_ids, context=context):
if account.code == root_account.code:
code = None
else:
@ -119,21 +122,26 @@ class AccountingExpressionProcessor(object):
if account.type in ('view', 'consolidation'):
self._account_ids_by_code[code].update(
account_obj._get_children_and_consol(
self.env.cr, self.env.uid,
cr, uid,
[account.id],
self.env.context))
context=context))
else:
self._account_ids_by_code[code].add(account.id)
for like_code in like_codes:
for account in account_model.\
search([('code', 'like', like_code),
('parent_id', 'child_of', root_account.id)]):
for account_id in account_obj.\
search(cr, uid,
[('code', 'like', like_code),
('parent_id', 'child_of', root_account.id)],
context=context):
account = account_obj.browse(cr, uid, account_id,
context=context)
if account.type in ('view', 'consolidation'):
self._account_ids_by_code[like_code].update(
account_obj._get_children_and_consol(
cr, uid,
self.env.cr, self.env.uid,
[account.id],
self.env.context))
context=context))
else:
self._account_ids_by_code[like_code].add(account.id)
@ -171,11 +179,12 @@ class AccountingExpressionProcessor(object):
key = (domain, mode)
self._map_account_ids[key].update(account_codes)
def done_parsing(self, root_account):
def done_parsing(self, cr, uid, root_account, context=None):
"""Load account codes and replace account codes by
account ids in map."""
for key, account_codes in self._map_account_ids.items():
self._load_account_codes(account_codes, root_account)
self._load_account_codes(cr, uid, account_codes, root_account,
context=context)
account_ids = set()
for account_code in account_codes:
account_ids.update(self._account_ids_by_code[account_code])
@ -219,78 +228,105 @@ class AccountingExpressionProcessor(object):
expression.OR(date_domain_by_mode.values())
def _period_has_moves(self, period):
move_model = self.env['account.move']
move_model = self.pool['account.move']
return bool(move_model.search([('period_id', '=', period.id)],
limit=1))
def _get_previous_opening_period(self, period, company_id):
period_model = self.env['account.period']
periods = period_model.search(
def _get_previous_opening_period(self, cr, uid, period, company_id,
context=None):
period_model = self.pool['account.period']
period_ids = period_model.search(
cr, uid,
[('date_start', '<=', period.date_start),
('special', '=', True),
('company_id', '=', company_id)],
order="date_start desc",
limit=1)
limit=1,
context=context)
periods = period_model.browse(cr, uid, period_ids, context=context)
return periods and periods[0]
def _get_previous_normal_period(self, period, company_id):
period_model = self.env['account.period']
periods = period_model.search(
def _get_previous_normal_period(self, cr, uid, period, company_id,
context=None):
period_model = self.pool['account.period']
period_ids = period_model.search(
cr, uid,
[('date_start', '<', period.date_start),
('special', '=', False),
('company_id', '=', company_id)],
order="date_start desc",
limit=1)
limit=1,
context=context)
periods = period_model.browse(cr, uid, period_ids, context=context)
return periods and periods[0]
def _get_first_normal_period(self, company_id):
period_model = self.env['account.period']
periods = period_model.search(
def _get_first_normal_period(self, cr, uid, company_id, context=None):
period_model = self.pool['account.period']
period_ids = period_model.search(
cr, uid,
[('special', '=', False),
('company_id', '=', company_id)],
order="date_start asc",
limit=1)
limit=1,
context=context)
periods = period_model.browse(cr, uid, period_ids, context=context)
return periods and periods[0]
def _get_period_ids_between(self, period_from, period_to, company_id):
period_model = self.env['account.period']
periods = period_model.search(
def _get_period_ids_between(self, cr, uid, period_from, period_to,
company_id, context=None):
period_model = self.pool['account.period']
period_ids = period_model.search(
cr, uid,
[('date_start', '>=', period_from.date_start),
('date_stop', '<=', period_to.date_stop),
('special', '=', False),
('company_id', '=', company_id)])
period_ids = [p.id for p in periods]
('company_id', '=', company_id)],
context=context)
if period_from.special:
period_ids.append(period_from.id)
return period_ids
def _get_period_company_ids(self, period_from, period_to):
period_model = self.env['account.period']
periods = period_model.search(
def _get_period_company_ids(self, cr, uid, period_from, period_to,
context=None):
period_model = self.pool['account.period']
period_ids = period_model.search(
cr, uid,
[('date_start', '>=', period_from.date_start),
('date_stop', '<=', period_to.date_stop),
('special', '=', False)])
('special', '=', False)],
context=context)
periods = period_model.browse(cr, uid, period_ids, context=context)
return set([p.company_id.id for p in periods])
def _get_period_ids_for_mode(self, period_from, period_to, mode):
def _get_period_ids_for_mode(self, cr, uid, period_from, period_to, mode,
context=None):
assert not period_from.special
assert not period_to.special
assert period_from.company_id == period_to.company_id
assert period_from.date_start <= period_to.date_start
period_ids = []
for company_id in self._get_period_company_ids(period_from, period_to):
for company_id in self._get_period_company_ids(cr, uid,
period_from, period_to,
context=context):
if mode == MODE_VARIATION:
period_ids.extend(self._get_period_ids_between(
period_from, period_to, company_id))
cr, uid,
period_from, period_to, company_id,
context=context))
else:
if mode == MODE_INITIAL:
period_to = self._get_previous_normal_period(
period_from, company_id)
cr, uid,
period_from, company_id,
context=context)
# look for opening period with moves
opening_period = self._get_previous_opening_period(
period_from, company_id)
cr, uid,
period_from, company_id,
context=context)
if opening_period and \
self._period_has_moves(opening_period[0]):
self._period_has_moves(cr, uid, opening_period[0],
context=context):
# found opening period with moves
if opening_period.date_start == period_from.date_start and \
mode == MODE_INITIAL:
@ -303,19 +339,25 @@ class AccountingExpressionProcessor(object):
else:
# no opening period with moves,
# use very first normal period
period_from = self._get_first_normal_period(company_id)
period_from = self._get_first_normal_period(
cr, uid, company_id, context=context)
if period_to:
period_ids.extend(self._get_period_ids_between(
period_from, period_to, company_id))
cr, uid,
period_from, period_to, company_id,
context=context))
return period_ids
def get_aml_domain_for_dates(self, date_from, date_to,
def get_aml_domain_for_dates(self, cr, uid, date_from, date_to,
period_from, period_to,
mode,
target_move):
target_move,
context=None):
if period_from and period_to:
period_ids = self._get_period_ids_for_mode(
period_from, period_to, mode)
cr, uid,
period_from, period_to, mode,
context=context)
domain = [('period_id', 'in', period_ids)]
else:
if mode == MODE_VARIATION:
@ -327,14 +369,14 @@ class AccountingExpressionProcessor(object):
domain.append(('move_id.state', '=', 'posted'))
return expression.normalize_domain(domain)
def do_queries(self, date_from, date_to, period_from, period_to,
target_move):
def do_queries(self, cr, uid, date_from, date_to, period_from, period_to,
target_move, context=None):
"""Query sums of debit and credit for all accounts and domains
used in expressions.
This method must be executed after done_parsing().
"""
aml_model = self.env['account.move.line']
aml_model = self.pool['account.move.line']
# {(domain, mode): {account_id: (debit, credit)}}
self._data = defaultdict(dict)
domain_by_mode = {}
@ -342,15 +384,18 @@ class AccountingExpressionProcessor(object):
domain, mode = key
if mode not in domain_by_mode:
domain_by_mode[mode] = \
self.get_aml_domain_for_dates(date_from, date_to,
self.get_aml_domain_for_dates(cr, uid,
date_from, date_to,
period_from, period_to,
mode, target_move)
mode, target_move,
context=context)
domain = list(domain) + domain_by_mode[mode]
domain.append(('account_id', 'in', self._map_account_ids[key]))
# fetch sum of debit/credit, grouped by account_id
accs = aml_model.read_group(domain,
accs = aml_model.read_group(cr, uid, domain,
['debit', 'credit', 'account_id'],
['account_id'])
['account_id'],
context=context)
for acc in accs:
self._data[key][acc['account_id'][0]] = \
(acc['debit'] or 0.0, acc['credit'] or 0.0)

820
mis_builder/models/mis_builder.py
File diff suppressed because it is too large
View File

1
mis_builder/report/__init__.py

@ -23,4 +23,3 @@
##############################################################################
from . import mis_builder_xls
from . import report_mis_report_instance

66
mis_builder/report/report_mis_report_instance.py

@ -1,66 +0,0 @@
# -*- encoding: utf-8 -*-
##############################################################################
#
# mis_builder module for Odoo, Management Information System Builder
# Copyright (C) 2014-2015 ACSONE SA/NV (<http://acsone.eu>)
#
# This file is a part of mis_builder
#
# mis_builder is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License v3 or later
# as published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# mis_builder 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 v3 or later for more details.
#
# You should have received a copy of the GNU Affero General Public License
# v3 or later along with this program.
# If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
import logging
from openerp import api, models
_logger = logging.getLogger(__name__)
class ReportMisReportInstance(models.AbstractModel):
_name = 'report.mis_builder.report_mis_report_instance'
@api.multi
def render_html(self, data=None):
docs = self.env['mis.report.instance'].browse(self._ids)
docs_computed = {}
for doc in docs:
docs_computed[doc.id] = doc.compute()
docargs = {
'doc_ids': self._ids,
'doc_model': 'mis.report.instance',
'docs': docs,
'docs_computed': docs_computed,
}
return self.env['report'].\
render('mis_builder.report_mis_report_instance', docargs)
class Report(models.Model):
_inherit = "report"
@api.v7
def get_pdf(self, cr, uid, ids, report_name, html=None, data=None,
context=None):
report = self._get_report_from_name(cr, uid, report_name)
obj = self.pool[report.model].browse(cr, uid, ids,
context=context)[0]
context = context.copy()
if hasattr(obj, 'landscape_pdf') and obj.landscape_pdf:
context.update({'landscape': True})
return super(Report, self).get_pdf(cr, uid, ids, report_name,
html=html, data=data,
context=context)

55
mis_builder/report/report_mis_report_instance.xml

@ -1,55 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<openerp>
<data>
<template id="report_mis_report_instance">
<t t-call="report.html_container">
<t t-foreach="docs" t-as="o">
<t t-call="report.internal_layout">
<div class="page">
<h2 t-field="o.name"></h2>
<table class="table table-condensed text-right">
<thead>
<tr>
<t t-foreach="docs_computed[o.id]['header']" t-as="h">
<th>
<div>
<t t-esc="h_value['kpi_name']"/>
</div>
</th>
<th t-foreach="h_value['cols']" t-as="col">
<div>
<t t-esc="col['name']"/>
</div>
<div>
<t t-esc="col['date']"/>
</div>
</th>
</t>
</tr>
</thead>
<tbody>
<tr t-foreach="docs_computed[o.id]['content']" t-as="c">
<td t-att-style="c_value['default_style']">
<div>
<t t-esc="c_value['kpi_name']"/>
</div>
</td>
<t t-foreach="c_value['cols']" t-as="value">
<td t-att-style="c_value['default_style']">
<div t-att-style="value_value.get('style')">
<t t-esc="value_value['val_r']"/>
</div>
</td>
</t>
</tr>
</tbody>
</table>
</div>
</t>
</t>
</t>
</template>
</data>
</openerp>

4
mis_builder/tests/__init__.py

@ -23,3 +23,7 @@
##############################################################################
from . import test_mis_builder
checks = [
test_mis_builder,
]

18
mis_builder/views/mis_builder.xml

@ -2,13 +2,6 @@
<openerp>
<data>
<template id="assets_backend" name="mis_builder" inherit_id="web.assets_backend">
<xpath expr="." position="inside">
<link rel="stylesheet" href="/mis_builder/static/src/css/custom.css"/>
<script type="text/javascript" src="/mis_builder/static/src/js/mis_builder.js"></script>
</xpath>
</template>
<record model="ir.ui.view" id="mis_report_view_tree">
<field name="name">mis.report.view.tree</field>
<field name="model">mis.report</field>
@ -107,15 +100,6 @@
<field name="auto" eval="False"/>
</record>
<record id="qweb_pdf_export" model="ir.actions.report.xml">
<field name="name">MIS report instance QWEB PDF report</field>
<field name="model">mis.report.instance</field>
<field name="type">ir.actions.report.xml</field>
<field name="report_name">mis_builder.report_mis_report_instance</field>
<field name="report_type">qweb-pdf</field>
<field name="auto" eval="False"/>
</record>
<record model="ir.ui.view" id="mis_report_instance_result_view_form">
<field name="name">mis.report.instance.result.view.form</field>
<field name="model">mis.report.instance</field>
@ -123,7 +107,6 @@
<field name="arch" type="xml">
<form string="MIS Report Result" version="7.0">
<widget type="mis_report"></widget>
<button icon="gtk-print" name="%(qweb_pdf_export)d" string="Print" type="action" colspan="2"/>
<button icon="gtk-execute" name="%(xls_export)d" string="Export" type="action" colspan="2"/>
</form>
</field>
@ -161,7 +144,6 @@
</div>
<div class="oe_right oe_button_box" name="buttons">
<button type="object" name="preview" string="Preview" icon="gtk-print-preview" />
<button type="action" name="%(qweb_pdf_export)d" string="Print" icon="gtk-print" />
<button type="action" name="%(xls_export)d" string="Export" icon="gtk-execute" />
<button type="action" name="%(mis_report_instance_add_to_dashboard_action)d" string="Add to dashboard" icon="gtk-add" />
</div>

Loading…
Cancel
Save