[MERGE] staging branch with fixes on slow analysis reports, and contracts managements

bzr revid: qdp-launchpad@openerp.com-20121129170332-mao60oyc3by1zzs8
This commit is contained in:
Quentin (OpenERP) 2012-11-29 18:03:32 +01:00
commit 79286ac47d
17 changed files with 191 additions and 140 deletions

View File

@ -1381,8 +1381,8 @@ class account_invoice_line(osv.osv):
'origin': fields.char('Source Document', size=256, help="Reference of the document that produced this invoice."),
'sequence': fields.integer('Sequence', help="Gives the sequence of this line when displaying the invoice."),
'invoice_id': fields.many2one('account.invoice', 'Invoice Reference', ondelete='cascade', select=True),
'uos_id': fields.many2one('product.uom', 'Unit of Measure', ondelete='set null'),
'product_id': fields.many2one('product.product', 'Product', ondelete='set null'),
'uos_id': fields.many2one('product.uom', 'Unit of Measure', ondelete='set null', select=True),
'product_id': fields.many2one('product.product', 'Product', ondelete='set null', select=True),
'account_id': fields.many2one('account.account', 'Account', required=True, domain=[('type','<>','view'), ('type', '<>', 'closed')], help="The income or expense account related to the selected product."),
'price_unit': fields.float('Unit Price', required=True, digits_compute= dp.get_precision('Product Price')),
'price_subtotal': fields.function(_amount_line, string='Subtotal', type="float",

View File

@ -1238,7 +1238,7 @@
<separator/>
<filter string="Next Partner to Reconcile" help="Next Partner Entries to reconcile" name="next_partner" context="{'next_partner_only': 1}" icon="terp-gtk-jump-to-ltr" domain="[('account_id.reconcile','=',True),('reconcile_id','=',False)]"/>
<field name="move_id" string="Number (Move)"/>
<field name="account_id"/>
<field name="account_id" filter_domain="['|', ('name', 'ilike', self), ('code', 'ilike', self)]"/>
<field name="partner_id"/>
<field name="journal_id" context="{'journal_id':self}" widget="selection"/> <!-- it's important to keep widget='selection' in this filter viewbecause without that the value passed in the context is not the ID but the textual value (name) of the selected journal -->
<field name="period_id" context="{'period_id':self}" widget="selection"/> <!-- it's important to keep the widget='selection' in this field, for the same reason as explained above -->

View File

@ -98,150 +98,124 @@ class account_invoice_report(osv.osv):
'partner_bank_id': fields.many2one('res.partner.bank', 'Bank Account',readonly=True),
'residual': fields.float('Total Residual', readonly=True),
'user_currency_residual': fields.function(_compute_amounts_in_user_currency, string="Total Residual", type='float', digits_compute=dp.get_precision('Account'), multi="_compute_amounts"),
'delay_to_pay': fields.float('Avg. Delay To Pay', readonly=True, group_operator="avg"),
'due_delay': fields.float('Avg. Due Delay', readonly=True, group_operator="avg"),
}
_order = 'date desc'
def _select(self):
select_str = """
SELECT min(ail.id) as id,
ai.date_invoice as date,
to_char(ai.date_invoice, 'YYYY') as year,
to_char(ai.date_invoice, 'MM') as month,
to_char(ai.date_invoice, 'YYYY-MM-DD') as day,
ail.product_id,
ai.partner_id as partner_id,
ai.payment_term as payment_term,
ai.period_id as period_id,
(case when u.uom_type not in ('reference') then
(select name from product_uom where uom_type='reference' and active and category_id=u.category_id LIMIT 1)
else
u.name
end) as uom_name,
ai.currency_id as currency_id,
ai.journal_id as journal_id,
ai.fiscal_position as fiscal_position,
ai.user_id as user_id,
ai.company_id as company_id,
count(ail.*) as nbr,
ai.type as type,
ai.state,
pt.categ_id,
ai.date_due as date_due,
ai.account_id as account_id,
ail.account_id as account_line_id,
ai.partner_bank_id as partner_bank_id,
sum(case when ai.type in ('out_refund','in_invoice') then
-ail.quantity / u.factor
else
ail.quantity / u.factor
end) as product_qty,
sum(case when ai.type in ('out_refund','in_invoice') then
-ail.price_subtotal
else
ail.price_subtotal
end) / cr.rate as price_total,
(case when ai.type in ('out_refund','in_invoice') then
sum(-ail.price_subtotal)
else
sum(ail.price_subtotal)
end) / (CASE WHEN sum(ail.quantity/u.factor) <> 0
THEN
(case when ai.type in ('out_refund','in_invoice')
then sum(-ail.quantity/u.factor)
else sum(ail.quantity/u.factor) end)
ELSE 1
END)
/ cr.rate as price_average,
cr.rate as currency_rate,
sum((select extract(epoch from avg(date_trunc('day',aml.date_created)-date_trunc('day',l.create_date)))/(24*60*60)::decimal(16,2)
from account_move_line as aml
left join account_invoice as a ON (a.move_id=aml.move_id)
left join account_invoice_line as l ON (a.id=l.invoice_id)
where a.id=ai.id)) as delay_to_pay,
sum((select extract(epoch from avg(date_trunc('day',a.date_due)-date_trunc('day',a.date_invoice)))/(24*60*60)::decimal(16,2)
from account_move_line as aml
left join account_invoice as a ON (a.move_id=aml.move_id)
left join account_invoice_line as l ON (a.id=l.invoice_id)
where a.id=ai.id)) as due_delay,
(case when ai.type in ('out_refund','in_invoice') then
-ai.residual
else
ai.residual
end)/ (CASE WHEN
(select count(l.id) from account_invoice_line as l
left join account_invoice as a ON (a.id=l.invoice_id)
where a.id=ai.id) <> 0
THEN
(select count(l.id) from account_invoice_line as l
left join account_invoice as a ON (a.id=l.invoice_id)
where a.id=ai.id)
ELSE 1
END) / cr.rate as residual
SELECT sub.id, sub.date, sub.year, sub.month, sub.day, sub.product_id, sub.partner_id,
sub.payment_term, sub.period_id, sub.uom_name, sub.currency_id, sub.journal_id,
sub.fiscal_position, sub.user_id, sub.company_id, sub.nbr, sub.type, sub.state,
sub.categ_id, sub.date_due, sub.account_id, sub.account_line_id, sub.partner_bank_id,
sub.product_qty, sub.price_total / cr.rate as price_total, sub.price_average /cr.rate as price_average,
cr.rate as currency_rate, sub.residual / cr.rate as residual
"""
return select_str
def _where(self):
where_str = """
WHERE cr.id in (select id from res_currency_rate cr2 where (cr2.currency_id = ai.currency_id)
and ((ai.date_invoice is not null and cr.name <= ai.date_invoice) or (ai.date_invoice is null and cr.name <= NOW())) order by name desc limit 1)
def _sub_select(self):
select_str = """
SELECT min(ail.id) AS id,
ai.date_invoice AS date,
to_char(ai.date_invoice::timestamp with time zone, 'YYYY'::text) AS year,
to_char(ai.date_invoice::timestamp with time zone, 'MM'::text) AS month,
to_char(ai.date_invoice::timestamp with time zone, 'YYYY-MM-DD'::text) AS day,
ail.product_id, ai.partner_id, ai.payment_term, ai.period_id,
CASE
WHEN u.uom_type::text <> 'reference'::text
THEN ( SELECT product_uom.name
FROM product_uom
WHERE product_uom.uom_type::text = 'reference'::text
AND product_uom.active
AND product_uom.category_id = u.category_id LIMIT 1)
ELSE u.name
END AS uom_name,
ai.currency_id, ai.journal_id, ai.fiscal_position, ai.user_id, ai.company_id,
count(ail.*) AS nbr,
ai.type, ai.state, pt.categ_id, ai.date_due, ai.account_id, ail.account_id AS account_line_id,
ai.partner_bank_id,
SUM(CASE
WHEN ai.type::text = ANY (ARRAY['out_refund'::character varying::text, 'in_invoice'::character varying::text])
THEN (- ail.quantity) / u.factor
ELSE ail.quantity / u.factor
END) AS product_qty,
SUM(CASE
WHEN ai.type::text = ANY (ARRAY['out_refund'::character varying::text, 'in_invoice'::character varying::text])
THEN - ail.price_subtotal
ELSE ail.price_subtotal
END) AS price_total,
CASE
WHEN ai.type::text = ANY (ARRAY['out_refund'::character varying::text, 'in_invoice'::character varying::text])
THEN SUM(- ail.price_subtotal)
ELSE SUM(ail.price_subtotal)
END / CASE
WHEN SUM(ail.quantity / u.factor) <> 0::numeric
THEN CASE
WHEN ai.type::text = ANY (ARRAY['out_refund'::character varying::text, 'in_invoice'::character varying::text])
THEN SUM((- ail.quantity) / u.factor)
ELSE SUM(ail.quantity / u.factor)
END
ELSE 1::numeric
END AS price_average,
CASE
WHEN ai.type::text = ANY (ARRAY['out_refund'::character varying::text, 'in_invoice'::character varying::text])
THEN - ai.residual
ELSE ai.residual
END / CASE
WHEN (( SELECT count(l.id) AS count
FROM account_invoice_line l
LEFT JOIN account_invoice a ON a.id = l.invoice_id
WHERE a.id = ai.id)) <> 0
THEN ( SELECT count(l.id) AS count
FROM account_invoice_line l
LEFT JOIN account_invoice a ON a.id = l.invoice_id
WHERE a.id = ai.id)
ELSE 1::bigint
END::numeric AS residual
"""
return where_str
return select_str
def _from(self):
from_str = """
FROM account_invoice_line as ail
left join account_invoice as ai ON (ai.id=ail.invoice_id)
left join product_product pr on (pr.id=ail.product_id)
left join product_template pt on (pt.id=pr.product_tmpl_id)
left join product_uom u on (u.id=ail.uos_id),
res_currency_rate cr
FROM account_invoice_line ail
JOIN account_invoice ai ON ai.id = ail.invoice_id
LEFT JOIN product_product pr ON pr.id = ail.product_id
left JOIN product_template pt ON pt.id = pr.product_tmpl_id
LEFT JOIN product_uom u ON u.id = ail.uos_id
"""
return from_str
def _group_by(self):
group_by_str = """
GROUP BY ail.product_id,
ai.date_invoice,
ai.id,
cr.rate,
to_char(ai.date_invoice, 'YYYY'),
to_char(ai.date_invoice, 'MM'),
to_char(ai.date_invoice, 'YYYY-MM-DD'),
ai.partner_id,
ai.payment_term,
ai.period_id,
u.name,
ai.currency_id,
ai.journal_id,
ai.fiscal_position,
ai.user_id,
ai.company_id,
ai.type,
ai.state,
pt.categ_id,
ai.date_due,
ai.account_id,
ail.account_id,
ai.partner_bank_id,
ai.residual,
ai.amount_total,
u.uom_type,
u.category_id
GROUP BY ail.product_id, ai.date_invoice, ai.id,
to_char(ai.date_invoice::timestamp with time zone, 'YYYY'::text),
to_char(ai.date_invoice::timestamp with time zone, 'MM'::text),
to_char(ai.date_invoice::timestamp with time zone, 'YYYY-MM-DD'::text),
ai.partner_id, ai.payment_term, ai.period_id, u.name, ai.currency_id, ai.journal_id,
ai.fiscal_position, ai.user_id, ai.company_id, ai.type, ai.state, pt.categ_id,
ai.date_due, ai.account_id, ail.account_id, ai.partner_bank_id, ai.residual,
ai.amount_total, u.uom_type, u.category_id
"""
return group_by_str
def init(self, cr):
# self._table = account_invoice_report
tools.drop_view_if_exists(cr, self._table)
cr.execute("CREATE or REPLACE VIEW %s as (%s %s %s %s)" % (
cr.execute("""CREATE or REPLACE VIEW %s as (
%s
FROM (
%s %s %s
) AS sub
JOIN res_currency_rate cr ON (cr.currency_id = sub.currency_id)
WHERE
cr.id IN (SELECT id
FROM res_currency_rate cr2
WHERE (cr2.currency_id = sub.currency_id)
AND ((sub.date IS NOT NULL AND cr.name <= sub.date)
OR (sub.date IS NULL AND cr.name <= NOW()))
ORDER BY name DESC LIMIT 1)
)""" % (
self._table,
self._select(), self._from(), self._where(), self._group_by()))
self._select(), self._sub_select(), self._from(), self._group_by()))
account_invoice_report()

View File

@ -30,8 +30,6 @@
<!-- <field name="reconciled" sum="# Reconciled"/> -->
<field name="user_currency_price_total" sum="Total Without Tax"/>
<field name="user_currency_residual" sum="Total Residual" invisible="context.get('residual_invisible',False)"/>
<field name="due_delay" sum="Avg. Due Delay" invisible="context.get('residual_invisible',False)"/>
<field name="delay_to_pay" sum="Avg. Delay To Pay" invisible="context.get('residual_invisible',False)"/>
</tree>
</field>
</record>
@ -93,7 +91,7 @@
<field name="view_mode">tree,graph</field>
<field name="context">{'search_default_period':1,'search_default_current':1, 'search_default_year': 1, 'search_default_category_product':1, 'search_default_customer':1, 'group_by':[], 'group_by_no_leaf':1,}</field>
<field name="search_view_id" ref="view_account_invoice_report_search"/>
<field name="help">From this report, you can have an overview of the amount invoiced to your customer as well as payment delays. The tool search can also be used to personalise your Invoices reports and so, match this analysis to your needs.</field>
<field name="help">From this report, you can have an overview of the amount invoiced to your customer. The tool search can also be used to personalise your Invoices reports and so, match this analysis to your needs.</field>
</record>

View File

@ -21,6 +21,7 @@
import account_analytic_analysis
import cron_account_analytic_account
import res_config
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -40,6 +40,7 @@ Adds menu to show relevant information to each manager.You can also view the rep
'account_analytic_analysis_view.xml',
'account_analytic_analysis_menu.xml',
'account_analytic_analysis_cron.xml',
'res_config_view.xml',
],
'css': [
'static/src/css/analytic.css'

View File

@ -147,6 +147,18 @@
</field>
</record>
<record id="view_account_analytic_account_template_required" model="ir.ui.view">
<field name="name">account.analytic.account.form.template.required</field>
<field name="model">account.analytic.account</field>
<field name="groups_id" eval="[(6, 0, [ref('group_template_required')])]"/>
<field name="inherit_id" ref="analytic.view_account_analytic_account_form"/>
<field name="arch" type="xml">
<field name="template_id" position="attributes">
<attribute name="attrs">{'required': [('type','=','contract')], 'invisible': [('type','in',['view', 'normal','template'])]}</attribute>
</field>
</field>
</record>
<record id="template_of_contract_action" model="ir.actions.act_window">
<field name="name">Template of Contract</field>
<field name="type">ir.actions.act_window</field>

View File

@ -0,0 +1,31 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Business Applications
# Copyright (C) 2004-2012 OpenERP S.A. (<http://openerp.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 osv import fields, osv
class sale_configuration(osv.osv_memory):
_inherit = 'sale.config.settings'
_columns = {
'group_template_required': fields.boolean("Mandatory use of templates.",
implied_group='account_analytic_analysis.group_template_required',
help="Allows you to set the template field as required when creating an analytic account or a contract."),
}

View File

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<record id="view_sales_config" model="ir.ui.view">
<field name="name">sale settings</field>
<field name="model">sale.config.settings</field>
<field name="inherit_id" ref="sale.view_sales_config"/>
<field name="arch" type="xml">
<xpath expr="//div[@name='module_analytic_user_function']" position="inside">
<div>
<field name="group_template_required" class="oe_inline"/>
<label for="group_template_required"/>
</div>
</xpath>
</field>
</record>
</data>
</openerp>

View File

@ -6,5 +6,12 @@
<field eval="[(4,ref('sale.group_analytic_accounting'))]" name="groups_id"/>
</record>
<record id="group_template_required" model="res.groups">
<field name="name">Mandatory use of templates in contracts</field>
<field name="category_id" ref="base.module_category_sales_management"/>
<field name="comment">the field template of the analytic accounts and contracts will be required.</field>
<field name="implied_ids" eval="[(4, ref('base.group_user'))]"/>
</record>
</data>
</openerp>
</openerp>

View File

@ -20,8 +20,10 @@
##############################################################################
import time
from datetime import datetime
from osv import fields, osv
import tools
from tools.translate import _
import decimal_precision as dp
@ -199,9 +201,14 @@ class account_analytic_account(osv.osv):
return {}
res = {'value':{}}
template = self.browse(cr, uid, template_id, context=context)
res['value']['date_start'] = template.date_start
res['value']['date'] = template.date
if template.date_start and template.date:
from_dt = datetime.strptime(template.date_start, tools.DEFAULT_SERVER_DATE_FORMAT)
to_dt = datetime.strptime(template.date, tools.DEFAULT_SERVER_DATE_FORMAT)
timedelta = to_dt - from_dt
res['value']['date'] = datetime.strftime(datetime.now() + timedelta, tools.DEFAULT_SERVER_DATE_FORMAT)
res['value']['date_start'] = fields.date.today()
res['value']['quantity_max'] = template.quantity_max
res['value']['parent_id'] = template.parent_id and template.parent_id.id or False
res['value']['description'] = template.description
return res

View File

@ -26,10 +26,10 @@
<field name="currency_id" attrs="{'invisible': ['|',('type', '&lt;&gt;', 'view'), ('company_id', '&lt;&gt;', False)]}"/>
</group>
<group>
<field name="code"/>
<field name="type" invisible="context.get('default_type', False)"/>
<field name="parent_id" on_change="on_change_parent(parent_id)" attrs="{'invisible': [('type','in',['contract','template'])]}"/>
<field name="template_id" on_change="on_change_template(template_id,context)" domain="[('type','=','template')]" attrs="{'invisible': [('type','in',['view', 'normal','template'])]}" context="{'default_type' : 'template'}"/>
<field name="code"/>
<field name="parent_id" on_change="on_change_parent(parent_id)" attrs="{'invisible': [('type','in',['contract'])]}"/>
<field name="company_id" on_change="on_change_company(company_id)" widget="selection" groups="base.group_multi_company" attrs="{'required': [('type','&lt;&gt;','view')]}"/>
</group>
</group>

View File

@ -81,7 +81,6 @@ class crm_lead_report(osv.osv):
'type_id':fields.many2one('crm.case.resource.type', 'Campaign', readonly=True),
'state': fields.selection(AVAILABLE_STATES, 'Status', size=16, readonly=True),
'company_id': fields.many2one('res.company', 'Company', readonly=True),
'email': fields.integer('# Emails', size=128, readonly=True),
'probability': fields.float('Probability',digits=(16,2),readonly=True, group_operator="avg"),
'planned_revenue': fields.float('Planned Revenue',digits=(16,2),readonly=True),
'probable_revenue': fields.float('Probable Revenue', digits=(16,2),readonly=True),
@ -137,7 +136,6 @@ class crm_lead_report(osv.osv):
c.planned_revenue,
c.planned_revenue*(c.probability/100) as probable_revenue,
1 as nbr,
(SELECT count(id) FROM mail_message WHERE model='crm.lead' AND res_id=c.id AND email_from is not null) AS email,
date_trunc('day',c.create_date) as create_date,
extract('epoch' from (c.date_closed-c.create_date))/(3600*24) as delay_close,
abs(extract('epoch' from (c.date_deadline - c.date_closed))/(3600*24)) as delay_expected,

View File

@ -25,7 +25,6 @@
<field name="partner_id" invisible="1"/>
<field name="country_id" invisible="1"/>
<field name="nbr" sum="# Leads"/>
<field name="email" sum="# Mails"/>
<field name="delay_open"/>
<field name="delay_close"/>
<field name="planned_revenue"/>
@ -148,7 +147,6 @@
<field name="company_id" invisible="1" groups="base.group_multi_company"/>
<field name="nbr" string="#Opportunities" sum="#Opportunities"/>
<field name="planned_revenue" sum="Planned Revenues"/>
<field name="email" sum="# of Emails"/>
<field name="delay_open" sum='Delay to open'/>
<field name="delay_close" sum='Delay to close'/>
<field name="delay_expected"/>
@ -167,7 +165,7 @@
<field name="context">{'search_default_year': 1,'search_default_lead': 1, "search_default_user":1, "search_default_this_month":1, 'group_by_no_leaf':1, 'group_by':[]}</field>
<field name="view_mode">tree,graph</field>
<field name="domain">[]</field>
<field name="help">Leads Analysis allows you to check different CRM related information. Check for treatment delays, number of responses given and emails sent. You can sort out your leads analysis by different groups to get accurate grained analysis.</field>
<field name="help">Leads Analysis allows you to check different CRM related information like the treatment delays or number of leads per state. You can sort out your leads analysis by different groups to get accurate grained analysis.</field>
</record>
<record model="ir.actions.act_window.view" id="action_report_crm_lead_tree">
<field name="sequence" eval="1"/>

View File

@ -546,7 +546,7 @@ class product_product(osv.osv):
'default_code' : fields.char('Internal Reference', size=64, select=True),
'active': fields.boolean('Active', help="If unchecked, it will allow you to hide the product without removing it."),
'variants': fields.char('Variants', size=64),
'product_tmpl_id': fields.many2one('product.template', 'Product Template', required=True, ondelete="cascade"),
'product_tmpl_id': fields.many2one('product.template', 'Product Template', required=True, ondelete="cascade", select=True),
'ean13': fields.char('EAN13 Barcode', size=13, help="International Article Number used for product identification."),
'packaging' : fields.one2many('product.packaging', 'product_id', 'Logistical Units', help="Gives the different ways to package the same product. This has no impact on the picking order and is mainly used if you use the EDI module."),
'price_extra': fields.float('Variant Price Extra', digits_compute=dp.get_precision('Product Price')),

View File

@ -86,13 +86,13 @@
your customer.
</p>
<group>
<label for="id" string="Contract Feature"/>
<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"/>
</div>
<div attrs="{'invisible':[('module_account_analytic_analysis','=',False)]}">
<div name="module_analytic_user_function" attrs="{'invisible':[('module_account_analytic_analysis','=',False)]}">
<field name="module_analytic_user_function" class="oe_inline"/>
<label for="module_analytic_user_function"/>
</div>

View File

@ -27,7 +27,10 @@ class account_invoice_report(osv.osv):
}
def _select(self):
return super(account_invoice_report, self)._select() + ", ai.section_id as section_id"
return super(account_invoice_report, self)._select() + ", sub.section_id as section_id"
def _sub_select(self):
return super(account_invoice_report, self)._sub_select() + ", ai.section_id as section_id"
def _group_by(self):
return super(account_invoice_report, self)._group_by() + ", ai.section_id"