openerp-community-reviewer team mailing list archive
-
openerp-community-reviewer team
-
Mailing list archive
-
Message #04548
[Merge] lp:~akretion-team/account-closing/70-forecast-prepaid into lp:account-closing
Alexis de Lattre has proposed merging lp:~akretion-team/account-closing/70-forecast-prepaid into lp:account-closing.
Requested reviews:
Account Core Editors (account-core-editors)
For more details, see:
https://code.launchpad.net/~akretion-team/account-closing/70-forecast-prepaid/+merge/210066
The main change in this MP is the addition of one new feature : the ability to compute forecasts of prepaid expense and prepaid revenue. This is important when working on budgets ; it allows the Accountant to easily compute the amount of prepaid revenue and prepaid expense between 2 dates in the future (you can also use it for dates in the past...).
This feature used to be available in the module "account_prepaid_reporting" from lp:~akretion-team/+junk/70-prepaid-reporting, but this module had a lot of duplicate code with the module account_cutoff_prepaid and it was based on two TransientModels which caused some bugs in some scenarios when doing CSV exports. So I eventually decided to try to integrate this feature inside the module account_cutoff_prepaid ; I am happy with the result (it avoids code duplication and only introduce a few lines of additional code) so I propose it for inclusion in the official branch.
--
https://code.launchpad.net/~akretion-team/account-closing/70-forecast-prepaid/+merge/210066
Your team Account Core Editors is requested to review the proposed merge of lp:~akretion-team/account-closing/70-forecast-prepaid into lp:account-closing.
=== modified file 'account_cutoff_base/account_cutoff.py'
--- account_cutoff_base/account_cutoff.py 2013-12-24 15:31:15 +0000
+++ account_cutoff_base/account_cutoff.py 2014-03-09 00:20:56 +0000
@@ -61,7 +61,7 @@
_columns = {
'cutoff_date': fields.date(
- 'Cut-off Date', required=True, readonly=True,
+ 'Cut-off Date', readonly=True,
states={'draft': [('readonly', False)]},
track_visibility='always'),
'type': fields.selection([
@@ -75,7 +75,7 @@
'account.move', 'Cut-off Journal Entry', readonly=True),
'move_label': fields.char(
'Label of the Cut-off Journal Entry',
- size=64, required=True, readonly=True,
+ size=64, readonly=True,
states={'draft': [('readonly', False)]},
help="This label will be written in the 'Name' field of the "
"Cut-off Account Move Lines and in the 'Reference' field of "
@@ -83,10 +83,9 @@
'cutoff_account_id': fields.many2one(
'account.account', 'Cut-off Account',
domain=[('type', '<>', 'view'), ('type', '<>', 'closed')],
- required=True, readonly=True,
- states={'draft': [('readonly', False)]}),
+ readonly=True, states={'draft': [('readonly', False)]}),
'cutoff_journal_id': fields.many2one(
- 'account.journal', 'Cut-off Account Journal', required=True,
+ 'account.journal', 'Cut-off Account Journal',
readonly=True, states={'draft': [('readonly', False)]}),
'total_cutoff_amount': fields.function(
_compute_total_cutoff, type='float', string="Total Cut-off Amount",
@@ -165,12 +164,15 @@
)]
def cutoff_date_onchange(
- self, cr, uid, ids, type, cutoff_date, move_label):
+ self, cr, uid, ids, type, cutoff_date, move_label, context=None):
+ if context is None:
+ context = {}
res = {'value': {}}
if type and cutoff_date:
- context = {'type': type, 'cutoff_date': cutoff_date}
+ ctx = context.copy()
+ ctx.update({'type': type, 'cutoff_date': cutoff_date})
res['value']['move_label'] = self._default_move_label(
- cr, uid, context=context)
+ cr, uid, context=ctx)
return res
def back2draft(self, cr, uid, ids, context=None):
=== modified file 'account_cutoff_base/account_cutoff_view.xml'
--- account_cutoff_base/account_cutoff_view.xml 2013-10-15 19:35:48 +0000
+++ account_cutoff_base/account_cutoff_view.xml 2014-03-09 00:20:56 +0000
@@ -35,15 +35,15 @@
</div>
<group name="top">
<group name="general-params">
- <field name="cutoff_date" on_change="cutoff_date_onchange(type, cutoff_date, move_label)"/>
+ <field name="cutoff_date" on_change="cutoff_date_onchange(type, cutoff_date, move_label, context)"/>
<field name="total_cutoff_amount" widget="monetary" options="{'currency_field': 'company_currency_id'}"/>
<field name="company_id" groups="base.group_multi_company" widget="selection" />
<field name="company_currency_id" invisible="1"/>
</group>
<group name="accounting-params">
- <field name="cutoff_journal_id"/>
- <field name="cutoff_account_id"/>
- <field name="move_label"/>
+ <field name="cutoff_journal_id" required="1"/>
+ <field name="cutoff_account_id" required="1"/>
+ <field name="move_label" required="1"/>
<field name="move_id"/>
</group>
</group>
=== modified file 'account_cutoff_prepaid/__openerp__.py'
--- account_cutoff_prepaid/__openerp__.py 2013-09-09 21:28:50 +0000
+++ account_cutoff_prepaid/__openerp__.py 2014-03-09 00:20:56 +0000
@@ -23,7 +23,7 @@
{
'name': 'Account Cut-off Prepaid',
- 'version': '0.1',
+ 'version': '0.2',
'category': 'Accounting & Finance',
'license': 'AGPL-3',
'summary': 'Prepaid Expense, Prepaid Revenue',
=== modified file 'account_cutoff_prepaid/account_cutoff.py'
--- account_cutoff_prepaid/account_cutoff.py 2013-12-24 15:31:15 +0000
+++ account_cutoff_prepaid/account_cutoff.py 2014-03-09 00:20:56 +0000
@@ -23,6 +23,7 @@
from openerp.osv import orm, fields
from openerp.tools.translate import _
+from openerp.tools import DEFAULT_SERVER_DATE_FORMAT
from datetime import datetime
@@ -34,6 +35,13 @@
'account.journal', id1='cutoff_id', id2='journal_id',
string='Source Journals', readonly=True,
states={'draft': [('readonly', False)]}),
+ 'forecast': fields.boolean(
+ 'Forecast',
+ readonly=True, states={'draft': [('readonly', False)]},
+ help="The Forecast mode allows the user to compute "
+ "the prepaid revenue/expense between 2 dates in the future."),
+ 'start_date': fields.date('Start Date'),
+ 'end_date': fields.date('End Date'),
}
def _get_default_source_journals(self, cr, uid, context=None):
@@ -58,32 +66,71 @@
}
_sql_constraints = [(
- 'date_type_company_uniq',
- 'unique(cutoff_date, company_id, type)',
- 'A cut-off of the same type already exists with this cut-off date !'
+ 'date_type_forecast_company_uniq',
+ 'unique(cutoff_date, company_id, type, forecast, start_date, end_date)',
+ 'A cut-off of the same type already exists with the same date(s) !'
)]
+ def _check_start_end_dates(self, cr, uid, ids):
+ for prepaid in self.browse(cr, uid, ids):
+ if prepaid.forecast and prepaid.start_date and prepaid.end_date \
+ and prepaid.start_date > prepaid.end_date:
+ return False
+ return True
+
+ _constraints = [
+ (_check_start_end_dates, "The start date is after the end date!",
+ ['start_date', 'end_date', 'forecast']),
+ ]
+
+ def forecast_onchange(self, cr, uid, ids, forecast, context=None):
+ res = {'value': {}}
+ line_ids = self.pool['account.cutoff.line'].search(
+ cr, uid, [('parent_id', 'in', ids)], context=context)
+ self.pool['account.cutoff.line'].unlink(
+ cr, uid, line_ids, context=context)
+ if forecast:
+ res['value']['cutoff_date'] = False
+ else:
+ res['value']['start_date'] = False
+ res['value']['end_date'] = False
+ return res
+
def _prepare_prepaid_lines(
self, cr, uid, ids, aml, cur_cutoff, mapping, context=None):
- start_date = datetime.strptime(aml['start_date'], '%Y-%m-%d')
- end_date = datetime.strptime(aml['end_date'], '%Y-%m-%d')
- cutoff_date_str = cur_cutoff['cutoff_date']
- cutoff_date = datetime.strptime(cutoff_date_str, '%Y-%m-%d')
+ start_date = datetime.strptime(
+ aml['start_date'], DEFAULT_SERVER_DATE_FORMAT)
+ end_date = datetime.strptime(
+ aml['end_date'], DEFAULT_SERVER_DATE_FORMAT)
# Here, we compute the amount of the cutoff
# That's the important part !
total_days = (end_date - start_date).days + 1
- if aml['start_date'] > cutoff_date_str:
- after_cutoff_days = total_days
- cutoff_amount = -1 * (aml['credit'] - aml['debit'])
+ if cur_cutoff['forecast']:
+ out_days = 0
+ forecast_start_date = datetime.strptime(
+ cur_cutoff['start_date'], DEFAULT_SERVER_DATE_FORMAT)
+ forecast_end_date = datetime.strptime(
+ cur_cutoff['end_date'], DEFAULT_SERVER_DATE_FORMAT)
+ if aml['end_date'] > cur_cutoff['end_date']:
+ out_days += (end_date - forecast_end_date).days
+ if aml['start_date'] < cur_cutoff['start_date']:
+ out_days += (forecast_start_date - start_date).days
+ prepaid_days = total_days - out_days
else:
- after_cutoff_days = (end_date - cutoff_date).days
- if total_days:
- cutoff_amount = -1 * (aml['credit'] - aml['debit'])\
- * after_cutoff_days / total_days
+ cutoff_date_str = cur_cutoff['cutoff_date']
+ cutoff_date = datetime.strptime(
+ cutoff_date_str, DEFAULT_SERVER_DATE_FORMAT)
+ if aml['start_date'] > cutoff_date_str:
+ prepaid_days = total_days
else:
- raise orm.except_orm(
- _('Error:'),
- "Should never happen. Total days should always be > 0")
+ prepaid_days = (end_date - cutoff_date).days
+ if total_days:
+ cutoff_amount = (aml['debit'] - aml['credit'])\
+ * prepaid_days / float(total_days)
+ else:
+ raise orm.except_orm(
+ _('Error:'),
+ "Should never happen. Total days should always be > 0")
# we use account mapping here
if aml['account_id'][0] in mapping:
@@ -104,7 +151,7 @@
aml['analytic_account_id'] and aml['analytic_account_id'][0]
or False,
'total_days': total_days,
- 'after_cutoff_days': after_cutoff_days,
+ 'prepaid_days': prepaid_days,
'amount': aml['credit'] - aml['debit'],
'currency_id': cur_cutoff['company_currency_id'][0],
'cutoff_amount': cutoff_amount,
@@ -120,7 +167,8 @@
cur_cutoff = self.read(
cr, uid, ids[0], [
'line_ids', 'source_journal_ids', 'cutoff_date', 'company_id',
- 'type', 'company_currency_id'
+ 'type', 'company_currency_id', 'forecast', 'start_date',
+ 'end_date',
],
context=context)
src_journal_ids = cur_cutoff['source_journal_ids']
@@ -132,13 +180,22 @@
if cur_cutoff['line_ids']:
line_obj.unlink(cr, uid, cur_cutoff['line_ids'], context=context)
+ if cur_cutoff['forecast']:
+ domain = [
+ ('start_date', '<=', cur_cutoff['end_date']),
+ ('end_date', '>=', cur_cutoff['start_date']),
+ ('journal_id', 'in', src_journal_ids)
+ ]
+ else:
+ domain = [
+ ('start_date', '!=', False),
+ ('journal_id', 'in', src_journal_ids),
+ ('end_date', '>', cutoff_date_str),
+ ('date', '<=', cutoff_date_str)
+ ]
+
# Search for account move lines in the source journals
- aml_ids = aml_obj.search(cr, uid, [
- ('start_date', '!=', False),
- ('journal_id', 'in', src_journal_ids),
- ('end_date', '>', cutoff_date_str),
- ('date', '<=', cutoff_date_str)
- ], context=context)
+ aml_ids = aml_obj.search(cr, uid, domain, context=context)
# Create mapping dict
mapping = mapping_obj._get_mapping_dict(
cr, uid, cur_cutoff['company_id'][0], cur_cutoff['type'],
@@ -188,6 +245,9 @@
'start_date': fields.date('Start Date', readonly=True),
'end_date': fields.date('End Date', readonly=True),
'total_days': fields.integer('Total Number of Days', readonly=True),
- 'after_cutoff_days': fields.integer(
- 'Number of Days after Cut-off Date', readonly=True),
+ 'prepaid_days': fields.integer(
+ 'Prepaid Days', readonly=True,
+ help="In regular mode, this is the number of days after the "
+ "cut-off date. In forecast mode, this is the number of days "
+ "between the start date and the end date."),
}
=== modified file 'account_cutoff_prepaid/account_cutoff_view.xml'
--- account_cutoff_prepaid/account_cutoff_view.xml 2013-10-15 19:35:48 +0000
+++ account_cutoff_prepaid/account_cutoff_view.xml 2014-03-09 00:20:56 +0000
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
- Copyright (C) 2013 Akretion (http://www.akretion.com/)
+ Copyright (C) 2013-2014 Akretion (http://www.akretion.com/)
@author Alexis de Lattre <alexis.delattre@xxxxxxxxxxxx>
The licence is in the file __openerp__.py
-->
@@ -9,12 +9,57 @@
<openerp>
<data>
+<!-- Tree view -->
+<record id="account_cutoff_tree" model="ir.ui.view">
+ <field name="name">account.cutoff.prepaid.tree</field>
+ <field name="model">account.cutoff</field>
+ <field name="inherit_id" ref="account_cutoff_base.account_cutoff_tree"/>
+ <field name="arch" type="xml">
+ <field name="cutoff_date" position="after">
+ <field name="forecast" invisible="'prepaid' not in context.get('type', '-')"/>
+ <field name="start_date" invisible="'prepaid' not in context.get('type', '-')"/>
+ <field name="end_date" invisible="'prepaid' not in context.get('type', '-')"/>
+ </field>
+ </field>
+</record>
+
<!-- Form view -->
<record id="account_cutoff_form" model="ir.ui.view">
<field name="name">account.cutoff.prepaid.form</field>
<field name="model">account.cutoff</field>
<field name="inherit_id" ref="account_cutoff_base.account_cutoff_form"/>
<field name="arch" type="xml">
+ <field name="cutoff_date" position="before">
+ <field name="forecast"
+ invisible="'prepaid' not in context.get('type', '-')"
+ on_change="forecast_onchange(forecast, context)"/>
+ <field name="start_date"
+ attrs="{'invisible': [('forecast', '=', False)], 'required': [('forecast', '=', True)]}"/>
+ <field name="end_date"
+ attrs="{'invisible': [('forecast', '=', False)], 'required': [('forecast', '=', True)]}"/>
+ </field>
+ <field name="cutoff_date" position="attributes">
+ <attribute name="attrs">{'invisible': [('forecast', '=', True)], 'required': [('forecast', '=', False)]}</attribute>
+ <attribute name="required">0</attribute>
+ </field>
+ <group name="accounting-params" position="attributes">
+ <attribute name="attrs">{'invisible': [('forecast', '=', True)]}</attribute>
+ </group>
+ <field name="cutoff_journal_id" position="attributes">
+ <attribute name="required">0</attribute>
+ <attribute name="attrs">{'required': [('forecast', '=', False)]}</attribute>
+ </field>
+ <field name="cutoff_account_id" position="attributes">
+ <attribute name="required">0</attribute>
+ <attribute name="attrs">{'required': [('forecast', '=', False)]}</attribute>
+ </field>
+ <field name="move_label" position="attributes">
+ <attribute name="required">0</attribute>
+ <attribute name="attrs">{'required': [('forecast', '=', False)]}</attribute>
+ </field>
+ <button name="create_move" position="attributes">
+ <attribute name="attrs">{'invisible': [('forecast', '=', True)]}</attribute>
+ </button>
<button name="back2draft" position="after">
<button class="oe_highlight" name="get_prepaid_lines" string="Re-Generate Lines" type="object" states="draft" invisible="'prepaid' not in context.get('type', '-')"/>
</button>
@@ -41,7 +86,7 @@
</field>
<field name="cutoff_amount" position="before">
<field name="total_days" invisible="'prepaid' not in context.get('type', '-')"/>
- <field name="after_cutoff_days" invisible="'prepaid' not in context.get('type', '-')"/>
+ <field name="prepaid_days" invisible="'prepaid' not in context.get('type', '-')"/>
</field>
</field>
</record>
@@ -59,7 +104,7 @@
<field name="start_date" invisible="'prepaid' not in context.get('type', '-')"/>
<field name="end_date" invisible="'prepaid' not in context.get('type', '-')"/>
<field name="total_days" string="Days Total" invisible="'prepaid' not in context.get('type', '-')"/>
- <field name="after_cutoff_days" string="Days after Cut-off" invisible="'prepaid' not in context.get('type', '-')"/>
+ <field name="prepaid_days" string="Days after Cut-off" invisible="'prepaid' not in context.get('type', '-')"/>
</field>
</field>
</record>
=== modified file 'account_cutoff_prepaid/i18n/account_cutoff_prepaid.pot'
--- account_cutoff_prepaid/i18n/account_cutoff_prepaid.pot 2013-12-24 15:31:15 +0000
+++ account_cutoff_prepaid/i18n/account_cutoff_prepaid.pot 2014-03-09 00:20:56 +0000
@@ -6,8 +6,8 @@
msgstr ""
"Project-Id-Version: OpenERP Server 7.0\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2013-12-24 15:26+0000\n"
-"PO-Revision-Date: 2013-12-24 15:26+0000\n"
+"POT-Creation-Date: 2014-03-09 00:05+0000\n"
+"PO-Revision-Date: 2014-03-09 00:05+0000\n"
"Last-Translator: <>\n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
@@ -21,6 +21,7 @@
msgstr ""
#. module: account_cutoff_prepaid
+#: field:account.cutoff,end_date:0
#: field:account.cutoff.line,end_date:0
#: field:account.invoice.line,end_date:0
#: field:account.move.line,end_date:0
@@ -38,6 +39,16 @@
msgstr ""
#. module: account_cutoff_prepaid
+#: field:account.cutoff,forecast:0
+msgid "Forecast"
+msgstr ""
+
+#. module: account_cutoff_prepaid
+#: view:account.cutoff:0
+msgid "{'invisible': [('forecast', '=', True)], 'required': [('forecast', '=', False)]}"
+msgstr ""
+
+#. module: account_cutoff_prepaid
#: field:res.company,default_prepaid_expense_account_id:0
msgid "Default Account for Prepaid Expense"
msgstr ""
@@ -65,6 +76,11 @@
msgstr ""
#. module: account_cutoff_prepaid
+#: view:account.cutoff:0
+msgid "{'invisible': [('forecast', '=', True)]}"
+msgstr ""
+
+#. module: account_cutoff_prepaid
#: field:account.cutoff.line,total_days:0
msgid "Total Number of Days"
msgstr ""
@@ -104,6 +120,11 @@
msgstr ""
#. module: account_cutoff_prepaid
+#: help:account.cutoff,forecast:0
+msgid "The Forecast mode allows the user to compute the prepaid revenue/expense between 2 dates in the future."
+msgstr ""
+
+#. module: account_cutoff_prepaid
#: model:ir.model,name:account_cutoff_prepaid.model_account_cutoff
msgid "Account Cut-off"
msgstr ""
@@ -119,16 +140,6 @@
msgstr ""
#. module: account_cutoff_prepaid
-#: sql_constraint:account.cutoff:0
-msgid "A cut-off of the same type already exists with this cut-off date !"
-msgstr ""
-
-#. module: account_cutoff_prepaid
-#: field:account.cutoff.line,after_cutoff_days:0
-msgid "Number of Days after Cut-off Date"
-msgstr ""
-
-#. module: account_cutoff_prepaid
#: model:product.template,name:account_cutoff_prepaid.product_insurance_contrat_product_template
msgid "Car Insurance"
msgstr ""
@@ -141,8 +152,8 @@
#: code:addons/account_cutoff_prepaid/account.py:94
#: code:addons/account_cutoff_prepaid/account.py:100
#: code:addons/account_cutoff_prepaid/account.py:144
-#: code:addons/account_cutoff_prepaid/account_cutoff.py:85
-#: code:addons/account_cutoff_prepaid/account_cutoff.py:129
+#: code:addons/account_cutoff_prepaid/account_cutoff.py:132
+#: code:addons/account_cutoff_prepaid/account_cutoff.py:177
#, python-format
msgid "Error:"
msgstr ""
@@ -170,7 +181,7 @@
msgstr ""
#. module: account_cutoff_prepaid
-#: code:addons/account_cutoff_prepaid/account_cutoff.py:129
+#: code:addons/account_cutoff_prepaid/account_cutoff.py:177
#, python-format
msgid "You should set at least one Source Journal."
msgstr ""
@@ -182,17 +193,32 @@
msgstr ""
#. module: account_cutoff_prepaid
+#: constraint:account.cutoff:0
+msgid "The start date is after the end date!"
+msgstr ""
+
+#. module: account_cutoff_prepaid
#: code:addons/account_cutoff_prepaid/account.py:90
#, python-format
msgid "Missing End Date for move line with Name '%s'."
msgstr ""
#. module: account_cutoff_prepaid
+#: help:account.cutoff.line,prepaid_days:0
+msgid "In regular mode, this is the number of days after the cut-off date. In forecast mode, this is the number of days between the start date and the end date."
+msgstr ""
+
+#. module: account_cutoff_prepaid
#: model:ir.model,name:account_cutoff_prepaid.model_product_template
msgid "Product Template"
msgstr ""
#. module: account_cutoff_prepaid
+#: view:account.cutoff:0
+msgid "{'required': [('forecast', '=', False)]}"
+msgstr ""
+
+#. module: account_cutoff_prepaid
#: model:ir.model,name:account_cutoff_prepaid.model_account_invoice_line
msgid "Invoice Line"
msgstr ""
@@ -224,6 +250,12 @@
msgstr ""
#. module: account_cutoff_prepaid
+#: sql_constraint:account.cutoff:0
+msgid "A cut-off of the same type already exists with the same date(s) !"
+msgstr ""
+
+#. module: account_cutoff_prepaid
+#: field:account.cutoff,start_date:0
#: field:account.cutoff.line,start_date:0
#: field:account.invoice.line,start_date:0
#: field:account.move.line,start_date:0
@@ -236,6 +268,11 @@
msgstr ""
#. module: account_cutoff_prepaid
+#: field:account.cutoff.line,prepaid_days:0
+msgid "Prepaid Days"
+msgstr ""
+
+#. module: account_cutoff_prepaid
#: view:product.template:0
msgid "Sales Properties"
msgstr ""
=== added directory 'account_cutoff_prepaid/migrations'
=== added directory 'account_cutoff_prepaid/migrations/7.0.0.2'
=== added file 'account_cutoff_prepaid/migrations/7.0.0.2/pre-migration.py'
--- account_cutoff_prepaid/migrations/7.0.0.2/pre-migration.py 1970-01-01 00:00:00 +0000
+++ account_cutoff_prepaid/migrations/7.0.0.2/pre-migration.py 2014-03-09 00:20:56 +0000
@@ -0,0 +1,29 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# Copyright (C) 2014 Akretion (http://www.akretion.com/)
+# @author: Alexis de Lattre <alexis.delattre@xxxxxxxxxxxx>
+#
+# 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/>.
+#
+##############################################################################
+
+
+def migrate(cr, version):
+ if not version:
+ return
+
+ cr.execute(
+ 'ALTER TABLE "account_cutoff_line" RENAME "after_cutoff_days" '
+ 'TO "prepaid_days"')
Follow ups