[RENAME] account_analytic_analysis -> sale_contract

This commit is contained in:
Damien Bouvy
2015-07-23 11:40:30 +02:00
parent 20dbd2675a
commit d8f0231afc
81 changed files with 237 additions and 156 deletions
@@ -12,7 +12,7 @@ This module is for modifying account analytic view to show some data related to
""",
'author': 'OpenERP S.A.',
'website': 'https://www.odoo.com/',
'depends': ['hr_expense','account_analytic_analysis'],
'depends': ['hr_expense','sale_contract'],
'data': ['analytic_contract_hr_expense_view.xml'],
'demo': [],
'installable': True,
@@ -5,7 +5,7 @@
<field name="name">account.analytic.account.form.expense.inherit</field>
<field name="model">account.analytic.account</field>
<field eval="60" name="priority"/>
<field name="inherit_id" ref="account_analytic_analysis.account_analytic_account_form_form"/>
<field name="inherit_id" ref="sale_contract.account_analytic_account_form_form"/>
<field name="arch" type="xml">
<xpath expr='//tr[@name="total"]' position='before'>
<tr>
+1 -1
View File
@@ -554,7 +554,7 @@
<br/>
<h4><b>Time &amp; material</b> <span class="small">(ex: consulting mission)</span></h4>
<span>
<li><a t-att-href="prepare_backend_url('account_analytic_analysis.action_account_analytic_overdue_all', 'list', 'sale')">Create your contracts</a> and configure your prices</li>
<li><a t-att-href="prepare_backend_url('sale_contract.action_account_analytic_overdue_all', 'list', 'sale')">Create your contracts</a> and configure your prices</li>
</span>
</div>
</div>
+2 -2
View File
@@ -26,8 +26,8 @@ class hr_config_settings(osv.osv_memory):
help ="""This installs the module hr_evaluation."""),
'module_hr_gamification': fields.boolean('Drive engagement with challenges and badges',
help ="""This installs the module hr_gamification."""),
'module_account_analytic_analysis': fields.boolean('Allow invoicing based on timesheets (the sale application will be installed)',
help ="""This installs the module account_analytic_analysis, which will install sales management too."""),
'module_sale_contract': fields.boolean('Allow invoicing based on timesheets (the sale application will be installed)',
help ="""This installs the module sale_contract, which will install sales management too."""),
'module_hr_payroll': fields.boolean('Manage payroll',
help ="""This installs the module hr_payroll."""),
'module_website_hr_recruitment': fields.boolean('Publish jobs on your website',
+1 -1
View File
@@ -16,7 +16,7 @@ with the effect of creating, editing and deleting either ways.
'author': 'OpenERP SA',
'website': 'https://www.odoo.com/page/project-management',
'images': ['images/invoice_task_work.jpeg', 'images/my_timesheet.jpeg', 'images/working_hour.jpeg'],
'depends': ['resource', 'project', 'account_analytic_analysis', 'procurement'],
'depends': ['resource', 'project', 'sale_contract', 'procurement'],
'data': [
'security/ir.model.access.csv',
'security/project_timesheet_security.xml',
@@ -126,7 +126,7 @@
<field name="view_mode">tree,form,graph</field>
<field name="context">{}</field>
<field name="domain">[('type','=','normal')]</field>
<field name="search_view_id" ref="account_analytic_analysis.view_account_analytic_account_overdue_search"/>
<field name="search_view_id" ref="sale_contract.view_account_analytic_account_overdue_search"/>
<field name="help" type="html">
<p class="oe_view_nocontent_create">
Click to add a customer contract.
@@ -136,6 +136,9 @@
</p>
</field>
</record>
<menuitem id="menu_invoicing_contracts" parent="menu_project_billing" sequence="4"
action="sale_contract.action_account_analytic_overdue"/>
<record model="ir.ui.view" id="view_task_form2_inherited">
<field name="name">project.task.form.inherited</field>
+4 -4
View File
@@ -16,13 +16,13 @@ class sale_configuration(osv.TransientModel):
'timesheet': fields.boolean('Prepare invoices based on timesheets',
help='For modifying account analytic view to show important data to project manager of services companies.'
'You can also view the report of account analytic summary user-wise as well as month wise.\n'
'-This installs the module account_analytic_analysis.'),
'module_account_analytic_analysis': fields.boolean('Use contracts management',
'-This installs the module sale_contract.'),
'module_sale_contract': fields.boolean('Use contracts management',
help='Allows to define your customer contracts conditions: invoicing '
'method (fixed price, on timesheet, advance invoice), the exact pricing '
'(650€/day for a developer), the duration (one year support contract).\n'
'You will be able to follow the progress of the contract and invoice automatically.\n'
'-It installs the account_analytic_analysis module.'),
'-It installs the sale_contract module.'),
'group_sale_pricelist':fields.boolean("Use pricelists to adapt your price per customers",
implied_group='product.group_sale_pricelist',
help="""Allows to manage different prices based on rules per category of customers.
@@ -79,7 +79,7 @@ Example: 10% for retailers, promotion of 5 EUR on this product, etc."""),
def onchange_timesheet(self, cr, uid, ids, timesheet, context=None):
return {'value': {
'timesheet': timesheet,
'module_account_analytic_analysis': timesheet,
'module_sale_contract': timesheet,
}}
class account_config_settings(osv.osv_memory):
+3 -3
View File
@@ -95,10 +95,10 @@
<label for="id" string="Contract Features"/>
<div>
<div>
<field name="module_account_analytic_analysis" on_change="onchange_timesheet(module_account_analytic_analysis)" class="oe_inline"/>
<label for="module_account_analytic_analysis"/>
<field name="module_sale_contract" on_change="onchange_timesheet(module_sale_contract)" class="oe_inline"/>
<label for="module_sale_contract"/>
</div>
<div name="module_analytic_user_function" attrs="{'invisible':[('module_account_analytic_analysis','=',False)]}">
<div name="module_analytic_user_function" attrs="{'invisible':[('module_sale_contract','=',False)]}">
<field name="module_analytic_user_function" class="oe_inline"/>
<label for="module_analytic_user_function"/>
</div>
@@ -1,5 +1,6 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import account_analytic_analysis
import res_config
import models
import report
import test
@@ -18,13 +18,14 @@ Adds menu to show relevant information to each manager.You can also view the rep
'data': [
'security/ir.model.access.csv',
'security/account_analytic_analysis_security.xml',
'account_analytic_analysis_view.xml',
'account_analytic_analysis_cron.xml',
'res_config_view.xml',
'views/sale_contract_view.xml',
'data/sale_contract_cron.xml',
'views/res_config_view.xml',
'views/account_analytic_analysis.xml',
'views/product_template_view.xml',
],
'demo': ['analytic_account_demo.xml', 'analytic_account_demo.yml'],
'test': ['../account/test/account_minimal_test.xml', 'test/account_analytic_analysis.yml'],
'demo': ['demo/sale_contract_demo.xml'],
'test': ['test/account_analytic_analysis.yml'],
'installable': True,
'auto_install': False,
}
+4
View File
@@ -0,0 +1,4 @@
import sale_contract
import sale
import res_config
@@ -10,7 +10,7 @@ class sale_configuration(osv.osv_memory):
_columns = {
'group_template_required': fields.boolean("Mandatory use of templates.",
implied_group='account_analytic_analysis.group_template_required',
implied_group='sale_contract.group_template_required',
help="Allows you to set the template field as required when creating an analytic account or a contract."),
'time_unit': fields.many2one('product.uom', 'The default working time unit.'),
}
@@ -25,7 +25,7 @@ class sale_configuration(osv.osv_memory):
product = ir_model_data.xmlid_to_object(cr, uid, 'product.product_product_consultant')
if product and product.exists():
res['time_unit'] = product.uom_id.id
res['timesheet'] = res.get('module_account_analytic_analysis')
res['timesheet'] = res.get('module_sale_contract')
return res
def set_sale_defaults(self, cr, uid, ids, context=None):
+37
View File
@@ -0,0 +1,37 @@
from openerp import models, fields, api
class product_template(models.Model):
""" Add recurrent_invoice field to product template if it is true,
it will add to related contract.
"""
_inherit = "product.template"
recurring_invoice = fields.Boolean(
string='Recurrent Invoice Product', default=False,
help="If selected, this product will be added to the "
"related contract (which must be associated with the SO). \n"
"It will be used as product for invoice lines and generate "
"the recurring invoices automatically")
class sale_order_line(models.Model):
_inherit = "sale.order.line"
@api.one
def button_confirm(self):
if self.product_id.recurring_invoice and self.order_id.project_id:
invoice_line_ids = [((0, 0, {
'product_id': self.product_id.id,
'analytic_account_id': self.order_id.project_id.id,
'name': self.name,
'quantity': self.product_uom_qty,
'uom_id': self.product_uom.id,
'price_unit': self.price_unit,
'price_subtotal': self.price_subtotal
}))]
analytic_values = {'recurring_invoices': True, 'recurring_invoice_line_ids': invoice_line_ids}
if not self.order_id.project_id.partner_id:
analytic_values['partner_id'] = self.order_id.partner_id.id
self.order_id.project_id.write(analytic_values)
return super(sale_order_line, self).button_confirm()
@@ -623,8 +623,8 @@ class account_analytic_account(osv.osv):
fill_remind("future", [('state', 'in', ['draft', 'open']), ('date', '!=', False), ('date', '<', (datetime.datetime.now() + datetime.timedelta(30)).strftime("%Y-%m-%d"))])
context['base_url'] = self.pool.get('ir.config_parameter').get_param(cr, uid, 'web.base.url')
context['action_id'] = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'account_analytic_analysis', 'action_account_analytic_overdue_all')[1]
template_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'account_analytic_analysis', 'account_analytic_cron_email_template')[1]
context['action_id'] = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'sale_contract', 'action_account_analytic_overdue_all')[1]
template_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'sale_contract', 'account_analytic_cron_email_template')[1]
for user_id, data in remind.items():
context["data"] = data
_logger.debug("Sending reminder to uid %s", user_id)
@@ -763,126 +763,3 @@ class account_analytic_account(osv.osv):
else:
raise
return invoice_ids
class account_analytic_account_summary_user(osv.osv):
_name = "account_analytic_analysis.summary.user"
_description = "Hours Summary by User"
_order='user'
_auto = False
_rec_name = 'user'
def _unit_amount(self, cr, uid, ids, name, arg, context=None):
res = {}
account_obj = self.pool.get('account.analytic.account')
cr.execute('SELECT MAX(id) FROM res_users')
max_user = cr.fetchone()[0]
account_ids = [int(str(x/max_user - (x%max_user == 0 and 1 or 0))) for x in ids]
user_ids = [int(str(x-((x/max_user - (x%max_user == 0 and 1 or 0)) *max_user))) for x in ids]
parent_ids = tuple(account_ids) #We don't want consolidation for each of these fields because those complex computation is resource-greedy.
if parent_ids:
cr.execute('SELECT id, unit_amount ' \
'FROM account_analytic_analysis_summary_user ' \
'WHERE account_id IN %s ' \
'AND "user" IN %s',(parent_ids, tuple(user_ids),))
for sum_id, unit_amount in cr.fetchall():
res[sum_id] = unit_amount
for id in ids:
res[id] = round(res.get(id, 0.0), 2)
return res
_columns = {
'account_id': fields.many2one('account.analytic.account', 'Analytic Account', readonly=True),
'unit_amount': fields.float('Total Time'),
'user': fields.many2one('res.users', 'User'),
}
_depends = {
'res.users': ['id'],
'account.analytic.line': ['account_id', 'journal_id', 'unit_amount', 'user_id'],
'account.analytic.journal': ['type'],
}
def init(self, cr):
openerp.tools.sql.drop_view_if_exists(cr, 'account_analytic_analysis_summary_user')
cr.execute('''CREATE OR REPLACE VIEW account_analytic_analysis_summary_user AS (
with mu as
(select max(id) as max_user from res_users)
, lu AS
(SELECT
l.account_id AS account_id,
coalesce(l.user_id, 0) AS user_id,
SUM(l.unit_amount) AS unit_amount
FROM account_analytic_line AS l,
account_analytic_journal AS j
WHERE (j.type = 'general' ) and (j.id=l.journal_id)
GROUP BY l.account_id, l.user_id
)
select (lu.account_id * mu.max_user) + lu.user_id as id,
lu.account_id as account_id,
lu.user_id as "user",
unit_amount
from lu, mu)''')
class account_analytic_account_summary_month(osv.osv):
_name = "account_analytic_analysis.summary.month"
_description = "Hours summary by month"
_auto = False
_rec_name = 'month'
_columns = {
'account_id': fields.many2one('account.analytic.account', 'Analytic Account', readonly=True),
'unit_amount': fields.float('Total Time'),
'month': fields.char('Month', size=32, readonly=True),
}
_depends = {
'account.analytic.line': ['account_id', 'date', 'journal_id', 'unit_amount'],
'account.analytic.journal': ['type'],
}
def init(self, cr):
openerp.tools.sql.drop_view_if_exists(cr, 'account_analytic_analysis_summary_month')
cr.execute('CREATE VIEW account_analytic_analysis_summary_month AS (' \
'SELECT ' \
'(TO_NUMBER(TO_CHAR(d.month, \'YYYYMM\'), \'999999\') + (d.account_id * 1000000::bigint))::bigint AS id, ' \
'd.account_id AS account_id, ' \
'TO_CHAR(d.month, \'Mon YYYY\') AS month, ' \
'TO_NUMBER(TO_CHAR(d.month, \'YYYYMM\'), \'999999\') AS month_id, ' \
'COALESCE(SUM(l.unit_amount), 0.0) AS unit_amount ' \
'FROM ' \
'(SELECT ' \
'd2.account_id, ' \
'd2.month ' \
'FROM ' \
'(SELECT ' \
'a.id AS account_id, ' \
'l.month AS month ' \
'FROM ' \
'(SELECT ' \
'DATE_TRUNC(\'month\', l.date) AS month ' \
'FROM account_analytic_line AS l, ' \
'account_analytic_journal AS j ' \
'WHERE j.type = \'general\' ' \
'GROUP BY DATE_TRUNC(\'month\', l.date) ' \
') AS l, ' \
'account_analytic_account AS a ' \
'GROUP BY l.month, a.id ' \
') AS d2 ' \
'GROUP BY d2.account_id, d2.month ' \
') AS d ' \
'LEFT JOIN ' \
'(SELECT ' \
'l.account_id AS account_id, ' \
'DATE_TRUNC(\'month\', l.date) AS month, ' \
'SUM(l.unit_amount) AS unit_amount ' \
'FROM account_analytic_line AS l, ' \
'account_analytic_journal AS j ' \
'WHERE (j.type = \'general\') and (j.id=l.journal_id) ' \
'GROUP BY l.account_id, DATE_TRUNC(\'month\', l.date) ' \
') AS l '
'ON (' \
'd.account_id = l.account_id ' \
'AND d.month = l.month' \
') ' \
'GROUP BY d.month, d.account_id ' \
')')
+2
View File
@@ -0,0 +1,2 @@
# -*- coding: utf-8 -*-
import account_analytic_analysis_summary
@@ -0,0 +1,137 @@
# -*- coding: utf-8 -*-
from dateutil.relativedelta import relativedelta
import datetime
import logging
import time
from openerp.osv import osv, fields
import openerp.tools
from openerp.tools.translate import _
from openerp.exceptions import UserError
from openerp.addons.decimal_precision import decimal_precision as dp
_logger = logging.getLogger(__name__)
class account_analytic_account_summary_user(osv.osv):
_name = "account_analytic_analysis.summary.user"
_description = "Hours Summary by User"
_order='user'
_auto = False
_rec_name = 'user'
def _unit_amount(self, cr, uid, ids, name, arg, context=None):
res = {}
account_obj = self.pool.get('account.analytic.account')
cr.execute('SELECT MAX(id) FROM res_users')
max_user = cr.fetchone()[0]
account_ids = [int(str(x/max_user - (x%max_user == 0 and 1 or 0))) for x in ids]
user_ids = [int(str(x-((x/max_user - (x%max_user == 0 and 1 or 0)) *max_user))) for x in ids]
parent_ids = tuple(account_ids) #We don't want consolidation for each of these fields because those complex computation is resource-greedy.
if parent_ids:
cr.execute('SELECT id, unit_amount ' \
'FROM account_analytic_analysis_summary_user ' \
'WHERE account_id IN %s ' \
'AND "user" IN %s',(parent_ids, tuple(user_ids),))
for sum_id, unit_amount in cr.fetchall():
res[sum_id] = unit_amount
for id in ids:
res[id] = round(res.get(id, 0.0), 2)
return res
_columns = {
'account_id': fields.many2one('account.analytic.account', 'Analytic Account', readonly=True),
'unit_amount': fields.float('Total Time'),
'user': fields.many2one('res.users', 'User'),
}
_depends = {
'res.users': ['id'],
'account.analytic.line': ['account_id', 'journal_id', 'unit_amount', 'user_id'],
'account.analytic.journal': ['type'],
}
def init(self, cr):
openerp.tools.sql.drop_view_if_exists(cr, 'account_analytic_analysis_summary_user')
cr.execute('''CREATE OR REPLACE VIEW account_analytic_analysis_summary_user AS (
with mu as
(select max(id) as max_user from res_users)
, lu AS
(SELECT
l.account_id AS account_id,
coalesce(l.user_id, 0) AS user_id,
SUM(l.unit_amount) AS unit_amount
FROM account_analytic_line AS l,
account_analytic_journal AS j
WHERE (j.type = 'general' ) and (j.id=l.journal_id)
GROUP BY l.account_id, l.user_id
)
select (lu.account_id * mu.max_user) + lu.user_id as id,
lu.account_id as account_id,
lu.user_id as "user",
unit_amount
from lu, mu)''')
class account_analytic_account_summary_month(osv.osv):
_name = "account_analytic_analysis.summary.month"
_description = "Hours summary by month"
_auto = False
_rec_name = 'month'
_columns = {
'account_id': fields.many2one('account.analytic.account', 'Analytic Account', readonly=True),
'unit_amount': fields.float('Total Time'),
'month': fields.char('Month', size=32, readonly=True),
}
_depends = {
'account.analytic.line': ['account_id', 'date', 'journal_id', 'unit_amount'],
'account.analytic.journal': ['type'],
}
def init(self, cr):
openerp.tools.sql.drop_view_if_exists(cr, 'account_analytic_analysis_summary_month')
cr.execute('CREATE VIEW account_analytic_analysis_summary_month AS (' \
'SELECT ' \
'(TO_NUMBER(TO_CHAR(d.month, \'YYYYMM\'), \'999999\') + (d.account_id * 1000000::bigint))::bigint AS id, ' \
'd.account_id AS account_id, ' \
'TO_CHAR(d.month, \'Mon YYYY\') AS month, ' \
'TO_NUMBER(TO_CHAR(d.month, \'YYYYMM\'), \'999999\') AS month_id, ' \
'COALESCE(SUM(l.unit_amount), 0.0) AS unit_amount ' \
'FROM ' \
'(SELECT ' \
'd2.account_id, ' \
'd2.month ' \
'FROM ' \
'(SELECT ' \
'a.id AS account_id, ' \
'l.month AS month ' \
'FROM ' \
'(SELECT ' \
'DATE_TRUNC(\'month\', l.date) AS month ' \
'FROM account_analytic_line AS l, ' \
'account_analytic_journal AS j ' \
'WHERE j.type = \'general\' ' \
'GROUP BY DATE_TRUNC(\'month\', l.date) ' \
') AS l, ' \
'account_analytic_account AS a ' \
'GROUP BY l.month, a.id ' \
') AS d2 ' \
'GROUP BY d2.account_id, d2.month ' \
') AS d ' \
'LEFT JOIN ' \
'(SELECT ' \
'l.account_id AS account_id, ' \
'DATE_TRUNC(\'month\', l.date) AS month, ' \
'SUM(l.unit_amount) AS unit_amount ' \
'FROM account_analytic_line AS l, ' \
'account_analytic_journal AS j ' \
'WHERE (j.type = \'general\') and (j.id=l.journal_id) ' \
'GROUP BY l.account_id, DATE_TRUNC(\'month\', l.date) ' \
') AS l '
'ON (' \
'd.account_id = l.account_id ' \
'AND d.month = l.month' \
') ' \
'GROUP BY d.month, d.account_id ' \
')')
@@ -19,14 +19,14 @@
-
!record {model: account.analytic.account, id: contract_main}:
partner_id: base.main_partner
template_id: account_analytic_analysis.contract_template
template_id: sale_contract.contract_template
type: contract
-
I check that the contract inherited from data of the template
-
!python {model: account.analytic.account}: |
contract = self.browse(cr, uid, ref('account_analytic_analysis.contract_main'))
template = self.browse(cr, uid, ref('account_analytic_analysis.contract_template'))
contract = self.browse(cr, uid, ref('sale_contract.contract_main'))
template = self.browse(cr, uid, ref('sale_contract.contract_template'))
assert template.recurring_interval == contract.recurring_interval, "The recurring interval of the contract does not match with the contract template"
assert template.recurring_invoices == contract.recurring_invoices, "Invoice method of contract does not match with the contract template"
-
@@ -39,8 +39,8 @@
-
!python {model: account.invoice}: |
from openerp.tools import float_compare
aid = ref('account_analytic_analysis.contract_main')
ids = self.search(cr, uid, [('invoice_line_ids.account_analytic_id','=',aid)], context=context)
aid = ref('sale_contract.contract_main')
ids = self.search(cr, uid, [('invoice_line.analytic_account_id','=',aid)], context=context)
assert len(ids)>=1, 'No invoice created for the contract'
for invoice in self.browse(cr, uid, ids,context=context):
assert float_compare(invoice.amount_untaxed, 150.0, precision_digits=2) == 0, "The invoice total is different than 150!"
@@ -3,7 +3,7 @@
<data>
<template id="assets_backend" name="account_analytic_analysis assets" inherit_id="web.assets_backend">
<xpath expr="." position="inside">
<link rel="stylesheet" href="/account_analytic_analysis/static/src/css/analytic.css"/>
<link rel="stylesheet" href="/sale_contract/static/src/css/analytic.css"/>
</xpath>
</template>
</data>
@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<record id="view_product_template_recurrent_form_inherit" model="ir.ui.view">
<field name="name">product_template_form_view.inherit</field>
<field name="model">product.template</field>
<field name="inherit_id" ref="product.product_template_form_view"/>
<field name="arch" type="xml">
<xpath expr="//group[@name='sale_condition']" position="after">
<group name="contract" string="Contract" colspan="3">
<field name="recurring_invoice" class="oe_inline"/>
</group>
</xpath>
</field>
</record>
</data>
</openerp>
@@ -17,7 +17,7 @@
<attribute name="invisible">0</attribute>
</group>
<xpath expr="//div[@name='div_default_options']" position="inside">
<div name='time_unit' attrs="{'required':[('module_account_analytic_analysis','=',True)]}">
<div name='time_unit' attrs="{'required':[('module_sale_contract','=',True)]}">
<label for="time_unit"/>
<field name="time_unit" domain="[('category_id.name','=','Working Time')]" class="oe_inline"/>
</div>
@@ -368,7 +368,7 @@
<menuitem action="template_of_contract_action" id="menu_template_of_contract_action" parent="base.menu_sales_config" sequence="3"/>
<record id="account_analytic_analysis_form_form" model="ir.ui.view">
<record id="sale_contract_form_form" model="ir.ui.view">
<field name="name">account.analytic.account.invoice.form.inherit</field>
<field name="model">account.analytic.account</field>
<field name="inherit_id" ref="analytic.view_account_analytic_account_form"/>