diff options
| author | Christophe Simonis <chs@openerp.com> | 2013-11-25 16:00:54 +0100 |
|---|---|---|
| committer | Christophe Simonis <chs@openerp.com> | 2013-11-25 16:00:54 +0100 |
| commit | bb1e256f5b24bb4fc4b64e1fa973f86a30e4bace (patch) | |
| tree | 95f1de1c67b507b0d10786d04aa0f98bcd22243a /addons | |
| parent | f148061b9455e39d3b3e6f316a5c7d586e1aad88 (diff) | |
| parent | 7baee2822180db919e6754f04c0fa9bc8aaafe92 (diff) | |
[MERGE] forward port of branch saas-2 up to revid 9010 dle@openerp.com-20131125143003-vjtzj1c5051pdnk3
bzr revid: chs@openerp.com-20131125150054-b135qshkx6icakgk
Diffstat (limited to 'addons')
130 files changed, 813 insertions, 732 deletions
diff --git a/addons/account/account.py b/addons/account/account.py index 7f08538b97d..3c7c14737c1 100644 --- a/addons/account/account.py +++ b/addons/account/account.py @@ -1447,6 +1447,8 @@ class account_move(osv.osv): def unlink(self, cr, uid, ids, context=None, check=True): if context is None: context = {} + if isinstance(ids, (int, long)): + ids = [ids] toremove = [] obj_move_line = self.pool.get('account.move.line') for move in self.browse(cr, uid, ids, context=context): diff --git a/addons/account/account_move_line.py b/addons/account/account_move_line.py index 0fc9d9e2227..ed8fb8185e7 100644 --- a/addons/account/account_move_line.py +++ b/addons/account/account_move_line.py @@ -311,13 +311,13 @@ class account_move_line(osv.osv): context = {} c = context.copy() c['initital_bal'] = True - sql = """SELECT l2.id, SUM(l1.debit-l1.credit) - FROM account_move_line l1, account_move_line l2 - WHERE l2.account_id = l1.account_id - AND l1.id <= l2.id - AND l2.id IN %s AND """ + \ - self._query_get(cr, uid, obj='l1', context=c) + \ - " GROUP BY l2.id" + sql = """SELECT l1.id, COALESCE(SUM(l2.debit-l2.credit), 0) + FROM account_move_line l1 LEFT JOIN account_move_line l2 + ON (l1.account_id = l2.account_id + AND l2.id <= l1.id + AND """ + \ + self._query_get(cr, uid, obj='l2', context=c) + \ + ") WHERE l1.id IN %s GROUP BY l1.id" cr.execute(sql, [tuple(ids)]) return dict(cr.fetchall()) diff --git a/addons/account/data/account_data.xml b/addons/account/data/account_data.xml index 9d2e65a3a4c..8119e9f1002 100644 --- a/addons/account/data/account_data.xml +++ b/addons/account/data/account_data.xml @@ -16,7 +16,6 @@ </record> <record id="account_payment_term_line_immediate" model="account.payment.term.line"> - <field name="name">Immediate Payment</field> <field name="value">balance</field> <field eval="0" name="days"/> <field eval="0" name="days2"/> diff --git a/addons/account/edi/invoice_action_data.xml b/addons/account/edi/invoice_action_data.xml index b3fe8362b8b..417169fa8cb 100644 --- a/addons/account/edi/invoice_action_data.xml +++ b/addons/account/edi/invoice_action_data.xml @@ -22,7 +22,7 @@ <!--Email template --> <record id="email_template_edi_invoice" model="email.template"> <field name="name">Invoice - Send by Email</field> - <field name="email_from">${object.user_id.email or object.company_id.email or 'noreply@localhost'}</field> + <field name="email_from">${(object.user_id.email or object.company_id.email or 'noreply@localhost')|safe}</field> <field name="subject">${object.company_id.name} Invoice (Ref ${object.number or 'n/a'})</field> <field name="partner_to">${object.partner_id.id}</field> <field name="model_id" ref="account.model_account_invoice"/> diff --git a/addons/account/partner.py b/addons/account/partner.py index d0db2ed4f6c..f24ffcf55b4 100644 --- a/addons/account/partner.py +++ b/addons/account/partner.py @@ -107,14 +107,15 @@ class res_partner(osv.osv): _description = 'Partner' def _credit_debit_get(self, cr, uid, ids, field_names, arg, context=None): - query = self.pool.get('account.move.line')._query_get(cr, uid, context=context) + ctx = context.copy() + ctx['all_fiscalyear'] = True + query = self.pool.get('account.move.line')._query_get(cr, uid, context=ctx) cr.execute("""SELECT l.partner_id, a.type, SUM(l.debit-l.credit) FROM account_move_line l LEFT JOIN account_account a ON (l.account_id=a.id) WHERE a.type IN ('receivable','payable') AND l.partner_id IN %s - AND (l.reconcile_id IS NULL OR - reconcile_id in (SELECT id FROM account_move_reconcile WHERE opening_reconciliation is TRUE)) + AND l.reconcile_id IS NULL AND """ + query + """ GROUP BY l.partner_id, a.type """, diff --git a/addons/account/report/account_balance.rml b/addons/account/report/account_balance.rml index 6ddfc238bff..08c05c65fa8 100644 --- a/addons/account/report/account_balance.rml +++ b/addons/account/report/account_balance.rml @@ -170,6 +170,7 @@ <paraStyle name="Heading 9" fontName="Helvetica-Bold" fontSize="75%" leading="NaN" spaceBefore="12.0" spaceAfter="6.0"/> <paraStyle name="terp_tblheader_General" fontName="Helvetica-Bold" fontSize="8.0" leading="10" alignment="LEFT" spaceBefore="6.0" spaceAfter="6.0"/> <paraStyle name="terp_tblheader_Details" fontName="Helvetica-Bold" fontSize="9.0" leading="11" alignment="LEFT" spaceBefore="6.0" spaceAfter="6.0"/> + <paraStyle name="terp_default_Centre_7" fontName="Helvetica" fontSize="7.0" leading="9" alignment="CENTER" spaceBefore="0.0" spaceAfter="0.0"/> <paraStyle name="terp_default_8" fontName="Helvetica" fontSize="8.0" leading="10" alignment="LEFT" spaceBefore="0.0" spaceAfter="0.0"/> <paraStyle name="terp_default_Bold_8" fontName="Helvetica-Bold" fontSize="8.0" leading="10" alignment="LEFT" spaceBefore="0.0" spaceAfter="0.0"/> <paraStyle name="terp_tblheader_General_Centre" fontName="Helvetica-Bold" fontSize="8.0" leading="10" alignment="CENTER" spaceBefore="6.0" spaceAfter="6.0"/> diff --git a/addons/account/wizard/account_invoice_refund.py b/addons/account/wizard/account_invoice_refund.py index 4b7a7fd5314..8a583b79383 100644 --- a/addons/account/wizard/account_invoice_refund.py +++ b/addons/account/wizard/account_invoice_refund.py @@ -52,10 +52,19 @@ class account_invoice_refund(osv.osv_memory): journal = obj_journal.search(cr, uid, [('type', '=', type), ('company_id','=',company_id)], limit=1, context=context) return journal and journal[0] or False + def _get_reason(self, cr, uid, context=None): + active_id = context and context.get('active_id', False) + if active_id: + inv = self.pool.get('account.invoice').browse(cr, uid, active_id, context=context) + return inv.name + else: + return '' + _defaults = { 'date': lambda *a: time.strftime('%Y-%m-%d'), 'journal_id': _get_journal, 'filter_refund': 'refund', + 'description': _get_reason, } def fields_view_get(self, cr, uid, view_id=None, view_type=False, context=None, toolbar=False, submenu=False): diff --git a/addons/account_analytic_analysis/account_analytic_analysis_cron.xml b/addons/account_analytic_analysis/account_analytic_analysis_cron.xml index 970ab275ef9..492278a87b5 100644 --- a/addons/account_analytic_analysis/account_analytic_analysis_cron.xml +++ b/addons/account_analytic_analysis/account_analytic_analysis_cron.xml @@ -4,9 +4,9 @@ <record id="account_analytic_cron_email_template" model="email.template"> <field name="name">Contract expiration reminder</field> - <field name="email_from">${object.email or ''}</field> + <field name="email_from">${(object.email or '')|safe}</field> <field name="subject">Contract expiration reminder ${user.company_id.name}</field> - <field name="email_to">${object.email}</field> + <field name="email_to">${object.email|safe}</field> <field name="lang">${object.lang}</field> <field name="model_id" ref="base.model_res_users"/> <field name="auto_delete" eval="True"/> diff --git a/addons/account_budget/report/analytic_account_budget_report.rml b/addons/account_budget/report/analytic_account_budget_report.rml index 837825aa4e3..15fddebd02d 100644 --- a/addons/account_budget/report/analytic_account_budget_report.rml +++ b/addons/account_budget/report/analytic_account_budget_report.rml @@ -182,19 +182,19 @@ <blockTable colWidths="221.0,82.0,83.0,85.0,57.0" style="Table4"> <tr> <td> - <para style="terp_default_9"><font face="Helvetica" size="9.0" color="white">[['.....' *(a['status']-1) ]]</font><font face="Helvetica" size="8.0"> [[ (a['status']==1 and (setTag('para','para',{'fontName':'Helvetica-bold'}))) or removeParentNode('font') ]]</font> [[ a['name'] ]]</para> + <para style="terp_default_9"><font face="Helvetica" size="9.0" color="white">[['.....' *(a['status']-1) ]]</font><font face="Helvetica" size="8.0"> [[ (a['status']==1 and (setTag('para','para',{'fontName':'Helvetica-Bold'}))) or removeParentNode('font') ]]</font> [[ a['name'] ]]</para> </td> <td> - <para style="terp_default_Right_9"><font face="Helvetica" size="8.0">[[ (a['status']==1 and ( setTag('para','para',{'fontName':'Helvetica-bold'}))) or removeParentNode('font') ]]</font> [[ formatLang(a['theo'], currency_obj=company.currency_id) ]]</para> + <para style="terp_default_Right_9"><font face="Helvetica" size="8.0">[[ (a['status']==1 and ( setTag('para','para',{'fontName':'Helvetica-Bold'}))) or removeParentNode('font') ]]</font> [[ formatLang(a['theo'], currency_obj=company.currency_id) ]]</para> </td> <td> - <para style="terp_default_Right_9"><font face="Helvetica" size="8.0">[[ (a['status']==1 and ( setTag('para','para',{'fontName':'Helvetica-bold'}))) or removeParentNode('font') ]]</font> [[ formatLang(a['pln'], currency_obj=company.currency_id) ]]</para> + <para style="terp_default_Right_9"><font face="Helvetica" size="8.0">[[ (a['status']==1 and ( setTag('para','para',{'fontName':'Helvetica-Bold'}))) or removeParentNode('font') ]]</font> [[ formatLang(a['pln'], currency_obj=company.currency_id) ]]</para> </td> <td> - <para style="terp_default_Right_9"><font face="Helvetica" size="8.0">[[ (a['status']==1 and ( setTag('para','para',{'fontName':'Helvetica-bold'}))) or removeParentNode('font') ]]</font> [[ formatLang(a['prac'], currency_obj=company.currency_id) ]]</para> + <para style="terp_default_Right_9"><font face="Helvetica" size="8.0">[[ (a['status']==1 and ( setTag('para','para',{'fontName':'Helvetica-Bold'}))) or removeParentNode('font') ]]</font> [[ formatLang(a['prac'], currency_obj=company.currency_id) ]]</para> </td> <td> - <para style="terp_default_Centre_9"><font face="Helvetica" size="8.0">[[ (a['status']==1 and ( setTag('para','para',{'fontName':'Helvetica-bold'}))) or removeParentNode('font') ]]</font> [[ formatLang(a['perc']) ]]%</para> + <para style="terp_default_Centre_9"><font face="Helvetica" size="8.0">[[ (a['status']==1 and ( setTag('para','para',{'fontName':'Helvetica-Bold'}))) or removeParentNode('font') ]]</font> [[ formatLang(a['perc']) ]]%</para> </td> </tr> </blockTable> diff --git a/addons/account_budget/report/budget_report.rml b/addons/account_budget/report/budget_report.rml index 8b9835b6615..789c3ed1ffb 100644 --- a/addons/account_budget/report/budget_report.rml +++ b/addons/account_budget/report/budget_report.rml @@ -145,19 +145,19 @@ <blockTable colWidths="185.0,79.0,78.0,81.0,59.0" style="Table4"> <tr> <td> - <para style="terp_default_9"><font color="white">[['.....' *(a['status']-1) ]]</font><font face="Helvetica">[[ (a['status']==1 and (setTag('para','para',{'fontName':'Helvetica-bold'}))) or removeParentNode('font') ]] </font>[[ a['name'] ]]</para> + <para style="terp_default_9"><font color="white">[['.....' *(a['status']-1) ]]</font><font face="Helvetica">[[ (a['status']==1 and (setTag('para','para',{'fontName':'Helvetica-Bold'}))) or removeParentNode('font') ]] </font>[[ a['name'] ]]</para> </td> <td> - <para style="terp_default_Right_9"><font face="Helvetica">[[ (a['status']==1 and ( setTag('para','para',{'fontName':'Helvetica-bold'}))) or removeParentNode('font') ]]</font> [[ formatLang(a['theo'], digits=get_digits(dp='Account'), currency_obj=company.currency_id) ]]</para> + <para style="terp_default_Right_9"><font face="Helvetica">[[ (a['status']==1 and ( setTag('para','para',{'fontName':'Helvetica-Bold'}))) or removeParentNode('font') ]]</font> [[ formatLang(a['theo'], digits=get_digits(dp='Account'), currency_obj=company.currency_id) ]]</para> </td> <td> - <para style="terp_default_Right_9"><font face="Helvetica">[[ (a['status']==1 and ( setTag('para','para',{'fontName':'Helvetica-bold'}))) or removeParentNode('font') ]] </font>[[ formatLang(a['pln'], digits=get_digits(dp='Account'), currency_obj=company.currency_id) ]]</para> + <para style="terp_default_Right_9"><font face="Helvetica">[[ (a['status']==1 and ( setTag('para','para',{'fontName':'Helvetica-Bold'}))) or removeParentNode('font') ]] </font>[[ formatLang(a['pln'], digits=get_digits(dp='Account'), currency_obj=company.currency_id) ]]</para> </td> <td> - <para style="terp_default_Right_9"><font face="Helvetica">[[ (a['status']==1 and ( setTag('para','para',{'fontName':'Helvetica-bold'}))) or removeParentNode('font') ]] </font>[[ formatLang(a['prac'], digits=get_digits(dp='Account'), currency_obj=company.currency_id) ]]</para> + <para style="terp_default_Right_9"><font face="Helvetica">[[ (a['status']==1 and ( setTag('para','para',{'fontName':'Helvetica-Bold'}))) or removeParentNode('font') ]] </font>[[ formatLang(a['prac'], digits=get_digits(dp='Account'), currency_obj=company.currency_id) ]]</para> </td> <td> - <para style="terp_default_Right_9"><font face="Helvetica">[[ (a['status']==1 and ( setTag('para','para',{'fontName':'Helvetica-bold'}))) or removeParentNode('font') ]] </font>[[ formatLang(a['perc'], digits=2) ]]%</para> + <para style="terp_default_Right_9"><font face="Helvetica">[[ (a['status']==1 and ( setTag('para','para',{'fontName':'Helvetica-Bold'}))) or removeParentNode('font') ]] </font>[[ formatLang(a['perc'], digits=2) ]]%</para> </td> </tr> </blockTable> diff --git a/addons/account_budget/report/crossovered_budget_report.rml b/addons/account_budget/report/crossovered_budget_report.rml index 50b7d267125..a63920a4c9f 100644 --- a/addons/account_budget/report/crossovered_budget_report.rml +++ b/addons/account_budget/report/crossovered_budget_report.rml @@ -160,19 +160,19 @@ <blockTable colWidths="205.0,88.0,87.0,90.0,66.0" style="Table4"> <tr> <td> - <para style="terp_default_9"><font face="Helvetica" size="10.0" color="white">[['.....' *(a['status']-1) ]]</font><font face="Helvetica" size="8.0">[[ (a['status']==1 and (setTag('para','para',{'fontName':'Helvetica-bold'}))) or removeParentNode('font') ]]</font> [[ a['name'] ]]</para> + <para style="terp_default_9"><font face="Helvetica" size="10.0" color="white">[['.....' *(a['status']-1) ]]</font><font face="Helvetica" size="8.0">[[ (a['status']==1 and (setTag('para','para',{'fontName':'Helvetica-Bold'}))) or removeParentNode('font') ]]</font> [[ a['name'] ]]</para> </td> <td> - <para style="terp_default_Right_9"><font face="Helvetica" size="8.0">[[ (a['status']==1 and ( setTag('para','para',{'fontName':'Helvetica-bold'}))) or removeParentNode('font') ]]</font> [[ formatLang(a['theo'], dp='Account', currency_obj=company.currency_id) ]]</para> + <para style="terp_default_Right_9"><font face="Helvetica" size="8.0">[[ (a['status']==1 and ( setTag('para','para',{'fontName':'Helvetica-Bold'}))) or removeParentNode('font') ]]</font> [[ formatLang(a['theo'], dp='Account', currency_obj=company.currency_id) ]]</para> </td> <td> - <para style="terp_default_Right_9"><font face="Helvetica" size="8.0">[[ (a['status']==1 and ( setTag('para','para',{'fontName':'Helvetica-bold'}))) or removeParentNode('font') ]]</font> [[ formatLang(a['pln'], dp='Account', currency_obj=company.currency_id) ]]</para> + <para style="terp_default_Right_9"><font face="Helvetica" size="8.0">[[ (a['status']==1 and ( setTag('para','para',{'fontName':'Helvetica-Bold'}))) or removeParentNode('font') ]]</font> [[ formatLang(a['pln'], dp='Account', currency_obj=company.currency_id) ]]</para> </td> <td> - <para style="terp_default_Right_9"><font face="Helvetica" size="8.0">[[ (a['status']==1 and ( setTag('para','para',{'fontName':'Helvetica-bold'}))) or removeParentNode('font') ]]</font> [[ formatLang(a['prac'], dp='Account', currency_obj=company.currency_id) ]]</para> + <para style="terp_default_Right_9"><font face="Helvetica" size="8.0">[[ (a['status']==1 and ( setTag('para','para',{'fontName':'Helvetica-Bold'}))) or removeParentNode('font') ]]</font> [[ formatLang(a['prac'], dp='Account', currency_obj=company.currency_id) ]]</para> </td> <td> - <para style="terp_default_Centre_9"><font face="Helvetica" size="8.0">[[ (a['status']==1 and ( setTag('para','para',{'fontName':'Helvetica-bold'}))) or removeParentNode('font') ]]</font> [[ formatLang(a['perc'],digits=2) ]]%</para> + <para style="terp_default_Centre_9"><font face="Helvetica" size="8.0">[[ (a['status']==1 and ( setTag('para','para',{'fontName':'Helvetica-Bold'}))) or removeParentNode('font') ]]</font> [[ formatLang(a['perc'],digits=2) ]]%</para> </td> </tr> </blockTable> diff --git a/addons/account_followup/account_followup_data.xml b/addons/account_followup/account_followup_data.xml index ec18e0d2b0c..21c638fa513 100644 --- a/addons/account_followup/account_followup_data.xml +++ b/addons/account_followup/account_followup_data.xml @@ -6,9 +6,9 @@ <!--Mail template level 0--> <record id="email_template_account_followup_level0" model="email.template"> <field name="name">First polite payment follow-up reminder email</field> - <field name="email_from">${user.email or ''}</field> + <field name="email_from">${(user.email or '')|safe}</field> <field name="subject">${user.company_id.name} Payment Reminder</field> - <field name="email_to">${object.email}</field> + <field name="email_to">${object.email|safe}</field> <field name="lang">${object.lang}</field> <field name="model_id" ref="base.model_res_partner"/> <field name="auto_delete" eval="True"/> @@ -45,9 +45,9 @@ ${object.get_followup_table_html() | safe} <!--Mail template level 1 --> <record id="email_template_account_followup_level1" model="email.template"> <field name="name">A bit urging second payment follow-up reminder email</field> - <field name="email_from">${user.email or ''}</field> + <field name="email_from">${(user.email or '')|safe}</field> <field name="subject">${user.company_id.name} Payment Reminder</field> - <field name="email_to">${object.email}</field> + <field name="email_to">${object.email|safe}</field> <field name="lang">${object.lang}</field> <field name="model_id" ref="base.model_res_partner"/> <field name="auto_delete" eval="True"/> @@ -85,9 +85,9 @@ ${object.get_followup_table_html() | safe} <!--Mail template level 2 --> <record id="email_template_account_followup_level2" model="email.template"> <field name="name">Urging payment follow-up reminder email</field> - <field name="email_from">${user.email or ''}</field> + <field name="email_from">${(user.email or '')|safe}</field> <field name="subject">${user.company_id.name} Payment Reminder</field> - <field name="email_to">${object.email}</field> + <field name="email_to">${object.email|safe}</field> <field name="lang">${object.lang}</field> <field name="model_id" ref="base.model_res_partner"/> <field name="auto_delete" eval="True"/> @@ -122,9 +122,9 @@ ${object.get_followup_table_html() | safe} <!-- Default follow up message --> <record id="email_template_account_followup_default" model="email.template"> <field name="name">Default payment follow-up reminder e-mail</field> - <field name="email_from">${user.email or ''}</field> + <field name="email_from">${(user.email or '')|safe}</field> <field name="subject">${user.company_id.name} Payment Reminder</field> - <field name="email_to">${object.email}</field> + <field name="email_to">${object.email|safe}</field> <field name="lang">${object.lang}</field> <field name="model_id" ref="base.model_res_partner"/> <field name="auto_delete" eval="True"/> @@ -162,7 +162,7 @@ ${object.get_followup_table_html() | safe} <field name="sequence">0</field> <field name="delay">15</field> <field name="followup_id" ref="demo_followup1"/> - <field name="send-email">True</field> + <field name="send_email">True</field> <field name="description"> Dear %(partner_name)s, diff --git a/addons/auth_oauth/controllers/main.py b/addons/auth_oauth/controllers/main.py index bedf32ae776..af8b36d2556 100644 --- a/addons/auth_oauth/controllers/main.py +++ b/addons/auth_oauth/controllers/main.py @@ -19,7 +19,7 @@ _logger = logging.getLogger(__name__) #---------------------------------------------------------- def fragment_to_query_string(func): @functools.wraps(func) - def wrapper(self, **kw): + def wrapper(self, *a, **kw): if not kw: return """<html><head><script> var l = window.location; @@ -31,7 +31,7 @@ def fragment_to_query_string(func): } window.location = r; </script></head><body></body></html>""" - return func(self, **kw) + return func(self, *a, **kw) return wrapper diff --git a/addons/auth_signup/auth_signup_data.xml b/addons/auth_signup/auth_signup_data.xml index 1b4e3e05502..65ada8f39db 100644 --- a/addons/auth_signup/auth_signup_data.xml +++ b/addons/auth_signup/auth_signup_data.xml @@ -22,8 +22,8 @@ <record id="reset_password_email" model="email.template"> <field name="name">Reset Password</field> <field name="model_id" ref="base.model_res_users"/> - <field name="email_from"><![CDATA[${object.company_id.name} <${object.company_id.email or user.email}>]]></field> - <field name="email_to">${object.email}</field> + <field name="email_from"><![CDATA[${object.company_id.name} <${(object.company_id.email or user.email)|safe}>]]></field> + <field name="email_to">${object.email|safe}</field> <field name="subject">Password reset</field> <field name="body_html"><![CDATA[ <p>A password reset was requested for the OpenERP account linked to this email.</p> @@ -37,8 +37,8 @@ <record id="set_password_email" model="email.template"> <field name="name">OpenERP Enterprise Connection</field> <field name="model_id" ref="base.model_res_users"/> - <field name="email_from"><![CDATA[${object.company_id.name} <${object.company_id.email or user.email}>]]></field> - <field name="email_to">${object.email}</field> + <field name="email_from"><![CDATA[${object.company_id.name} <${(object.company_id.email or user.email)|safe}>]]></field> + <field name="email_to">${object.email|safe}</field> <field name="subject"><![CDATA[${object.company_id.name} invitation to connect on OpenERP]]></field> <field name="body_html"> <![CDATA[ diff --git a/addons/auth_signup/static/src/js/auth_signup.js b/addons/auth_signup/static/src/js/auth_signup.js index b825d683099..d9edb44d65f 100644 --- a/addons/auth_signup/static/src/js/auth_signup.js +++ b/addons/auth_signup/static/src/js/auth_signup.js @@ -39,12 +39,6 @@ openerp.auth_signup = function(instance) { delete self.params.error_message; } - // in case of a signup, retrieve the user information from the token - if (dbname && self.params.token) { - self.rpc("/auth_signup/retrieve", {dbname: dbname, token: self.params.token}) - .done(self.on_token_loaded) - .fail(self.on_token_failed); - } if (dbname && self.params.login) { self.$("form input[name=login]").val(self.params.login); } @@ -53,7 +47,7 @@ openerp.auth_signup = function(instance) { self.$('a.oe_signup_reset_password').click(self.do_reset_password); if (dbname) { - self.rpc("/auth_signup/get_config", {dbname: dbname}).done(function(result) { + self.rpc("/auth_signup/get_config", {dbname: dbname}).then(function(result) { self.signup_enabled = result.signup; self.reset_password_enabled = result.reset_password; if (!self.signup_enabled || self.$("form input[name=login]").val()){ @@ -61,6 +55,13 @@ openerp.auth_signup = function(instance) { } else { self.set('login_mode', 'signup'); } + + // in case of a signup, retrieve the user information from the token + if (self.params.token) { + self.rpc("/auth_signup/retrieve", {dbname: dbname, token: self.params.token}) + .then(self.on_token_loaded, self.on_token_failed); + } + }); } else { // TODO: support multiple database mode diff --git a/addons/base_action_rule/security/ir.model.access.csv b/addons/base_action_rule/security/ir.model.access.csv index 92d3d55649f..b509ec62944 100644 --- a/addons/base_action_rule/security/ir.model.access.csv +++ b/addons/base_action_rule/security/ir.model.access.csv @@ -1,3 +1,4 @@ id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_base_action_rule,base.action.rule,model_base_action_rule,,1,0,0,0
access_base_action_rule_config,base.action.rule config,model_base_action_rule,base.group_system,1,1,1,1
+access_base_action_rule_lead_test,access_base_action_rule_lead_test,model_base_action_rule_lead_test,base.group_system,1,1,1,1
diff --git a/addons/base_import/static/src/js/import.js b/addons/base_import/static/src/js/import.js index 3c029335fb1..0c3acd98265 100644 --- a/addons/base_import/static/src/js/import.js +++ b/addons/base_import/static/src/js/import.js @@ -51,7 +51,12 @@ openerp.base_import = function (instance) { type: 'ir.actions.client', tag: 'import', params: { - model: self.dataset.model + model: self.dataset.model, + // self.dataset.get_context() could be a compound? + // not sure. action's context should be evaluated + // so safer bet. Odd that timezone & al in it + // though + context: self.getParent().action.context, } }, { on_reverse_breadcrumb: function () { @@ -127,6 +132,7 @@ openerp.base_import = function (instance) { var self = this; this._super.apply(this, arguments); this.res_model = action.params.model; + this.parent_context = action.params.context || {}; // import object id this.id = null; this.Import = new instance.web.Model('base_import.import'); @@ -353,11 +359,12 @@ openerp.base_import = function (instance) { }, //- import itself - call_import: function (options) { + call_import: function (kwargs) { var fields = this.$('.oe_import_fields input.oe_import_match_field').map(function (index, el) { return $(el).select2('val') || false; }).get(); - return this.Import.call('do', [this.id, fields, this.import_options()], options) + kwargs.context = this.parent_context; + return this.Import.call('do', [this.id, fields, this.import_options()], kwargs) .then(undefined, function (error, event) { // In case of unexpected exception, convert // "JSON-RPC error" to an import failure, and diff --git a/addons/board/board.py b/addons/board/board.py index ba5da0db6e1..fafb0eae290 100644 --- a/addons/board/board.py +++ b/addons/board/board.py @@ -3,7 +3,7 @@ # # OpenERP, Open Source Management Solution # Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>). -# Copyright (C) 2010-2012 OpenERP s.a. (<http://openerp.com>). +# Copyright (C) 2010-2013 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 @@ -49,6 +49,7 @@ class board_board(osv.osv): ('value', 'in', refs), ], context=context) menu_ids = map(itemgetter('res_id'), IrValues.read(cr, uid, irv_ids, ['res_id'], context=context)) + menu_ids = Menus._filter_visible_menus(cr, uid, menu_ids, context=context) menu_names = Menus.name_get(cr, uid, menu_ids, context=context) return [dict(id=m[0], name=m[1]) for m in menu_names] diff --git a/addons/board/controllers.py b/addons/board/controllers.py index 50148977216..93634627352 100644 --- a/addons/board/controllers.py +++ b/addons/board/controllers.py @@ -1,22 +1,22 @@ # -*- coding: utf-8 -*- from xml.etree import ElementTree -import openerp from openerp.addons.web.controllers.main import load_actions_from_ir_values +from openerp.addons.web.http import Controller, route, request -class Board(openerp.http.Controller): - @openerp.http.route('/board/add_to_dashboard', type='json', auth='user') +class Board(Controller): + @route('/board/add_to_dashboard', type='json', auth='user') def add_to_dashboard(self, menu_id, action_id, context_to_save, domain, view_mode, name=''): - req = openerp.http.request # FIXME move this method to board.board model - dashboard_action = load_actions_from_ir_values('action', 'tree_but_open', [('ir.ui.menu', menu_id)], False) + dashboard_action = load_actions_from_ir_values('action', 'tree_but_open', + [('ir.ui.menu', menu_id)], False) if dashboard_action: action = dashboard_action[0][2] if action['res_model'] == 'board.board' and action['views'][0][1] == 'form': # Maybe should check the content instead of model board.board ? view_id = action['views'][0][0] - board = req.session.model(action['res_model']).fields_view_get(view_id, 'form') + board = request.session.model(action['res_model']).fields_view_get(view_id, 'form') if board and 'arch' in board: xml = ElementTree.fromstring(board['arch']) column = xml.find('./board/column') @@ -30,10 +30,10 @@ class Board(openerp.http.Controller): }) column.insert(0, new_action) arch = ElementTree.tostring(xml, 'utf-8') - return req.session.model('ir.ui.view.custom').create({ - 'user_id': req.session._uid, + return request.session.model('ir.ui.view.custom').create({ + 'user_id': request.session.uid, 'ref_id': view_id, 'arch': arch - }, req.context) + }, request.context) return False diff --git a/addons/crm/board_crm_view.xml b/addons/crm/board_crm_view.xml index 6bbd450ca38..8bbbece18cb 100644 --- a/addons/crm/board_crm_view.xml +++ b/addons/crm/board_crm_view.xml @@ -20,10 +20,7 @@ <field name="view_mode">graph,tree,form</field> <field name="view_id" ref="view_crm_opportunity_stage_graph"/> <!-- avoid done / cancelled --> - <field name="domain">['|', - '!', '&', ('probability', '=', 100), ('stage_id.on_change', '=', 1), - '!', '&', ('probability', '=', 0), ('stage_id.sequence', '!=', 1), - ('type', '=', 'opportunity')]</field> + <field name="domain">['&', ('stage_id.fold', '=', False), ('type', '=', 'opportunity')]</field> <field name="context">{'search_default_Stage':1}</field> </record> @@ -48,7 +45,7 @@ <field name="view_mode">graph,tree,form</field> <field name="view_id" ref="view_crm_opportunity_user_stage_graph"/> <!-- avoid cancelled --> - <field name="domain">['!', '&', ('probability', '=', 0), ('stage_id.sequence', '!=', 1)]</field> + <field name="domain">['|', ('stage_id.fold', '=', False), ('stage_id.probability', '=', 100)]</field> <field name="context">{'search_default_user': 1, 'search_default_Stage': 1}</field> </record> diff --git a/addons/crm/crm.py b/addons/crm/crm.py index 98581b91666..a98fcadb959 100644 --- a/addons/crm/crm.py +++ b/addons/crm/crm.py @@ -68,8 +68,9 @@ class crm_case_stage(osv.osv): help="Link between stages and sales teams. When set, this limitate the current stage to the selected sales teams."), 'case_default': fields.boolean('Default to New Sales Team', help="If you check this field, this stage will be proposed by default on each sales team. It will not assign this stage to existing teams."), - 'fold': fields.boolean('Fold by Default', - help="This stage is not visible, for example in status bar or kanban view, when there are no records in that stage to display."), + 'fold': fields.boolean('Folded in Kanban View', + help='This stage is folded in the kanban view when' + 'there are no records in that stage to display.'), 'type': fields.selection([('lead', 'Lead'), ('opportunity', 'Opportunity'), ('both', 'Both')], @@ -78,8 +79,8 @@ class crm_case_stage(osv.osv): } _defaults = { - 'sequence': lambda *args: 1, - 'probability': lambda *args: 0.0, + 'sequence': 1, + 'probability': 0.0, 'on_change': True, 'fold': False, 'type': 'both', diff --git a/addons/crm/crm_lead.py b/addons/crm/crm_lead.py index c580fd728e2..eaf51293355 100644 --- a/addons/crm/crm_lead.py +++ b/addons/crm/crm_lead.py @@ -72,10 +72,11 @@ class crm_lead(format_address, osv.osv): _track = { 'stage_id': { - 'crm.mt_lead_create': lambda self, cr, uid, obj, ctx=None: obj.probability == 0 and obj.stage_id and obj.stage_id.sequence == 1, - 'crm.mt_lead_stage': lambda self, cr, uid, obj, ctx=None: (obj.stage_id and obj.stage_id.sequence != 1) and obj.probability < 100, - 'crm.mt_lead_won': lambda self, cr, uid, obj, ctx=None: obj.probability == 100 and obj.stage_id and obj.stage_id.on_change, - 'crm.mt_lead_lost': lambda self, cr, uid, obj, ctx=None: obj.probability == 0 and obj.stage_id and obj.stage_id.sequence != 1, + # this is only an heuristics; depending on your particular stage configuration it may not match all 'new' stages + 'crm.mt_lead_create': lambda self, cr, uid, obj, ctx=None: obj.probability == 0 and obj.stage_id and obj.stage_id.sequence <= 1, + 'crm.mt_lead_stage': lambda self, cr, uid, obj, ctx=None: (obj.stage_id and obj.stage_id.sequence > 1) and obj.probability < 100, + 'crm.mt_lead_won': lambda self, cr, uid, obj, ctx=None: obj.probability == 100 and obj.stage_id and obj.stage_id.fold, + 'crm.mt_lead_lost': lambda self, cr, uid, obj, ctx=None: obj.probability == 0 and obj.stage_id and obj.stage_id.fold and obj.stage_id.sequence > 1, }, } @@ -93,7 +94,7 @@ class crm_lead(format_address, osv.osv): def _get_default_stage_id(self, cr, uid, context=None): """ Gives default stage_id """ section_id = self._get_default_section_id(cr, uid, context=context) - return self.stage_find(cr, uid, [], section_id, [('sequence', '=', '1')], context=context) + return self.stage_find(cr, uid, [], section_id, [('fold', '=', False)], context=context) def _resolve_section_id_from_context(self, cr, uid, context=None): """ Returns ID of section based on the value of 'section_id' @@ -382,34 +383,52 @@ class crm_lead(format_address, osv.osv): # AND with the domain in parameter search_domain += list(domain) # perform search, return the first found - stage_ids = self.pool.get('crm.case.stage').search(cr, uid, search_domain, order=order, context=context) + stage_ids = self.pool.get('crm.case.stage').search(cr, uid, search_domain, order=order, limit=1, context=context) if stage_ids: return stage_ids[0] return False def case_mark_lost(self, cr, uid, ids, context=None): - """ Mark the case as lost: state=cancel and probability=0 """ - for lead in self.browse(cr, uid, ids): - stage_id = self.stage_find(cr, uid, [lead], lead.section_id.id or False, [('probability', '=', 0.0), ('on_change', '=', True), ('sequence', '>', 1)], context=context) + """ Mark the case as lost: state=cancel and probability=0 + :deprecated: this method will be removed in OpenERP v8. + """ + stages_leads = {} + for lead in self.browse(cr, uid, ids, context=context): + stage_id = self.stage_find(cr, uid, [lead], lead.section_id.id or False, [('probability', '=', 0.0), ('fold', '=', True), ('sequence', '>', 1)], context=context) if stage_id: - return self.write(cr, uid, [lead.id], {'stage_id': stage_id}, context=context) + if stages_leads.get(stage_id): + stages_leads[stage_id].append(lead.id) + else: + stages_leads[stage_id] = [lead.id] else: raise osv.except_osv(_('Warning!'), _('To relieve your sales pipe and group all Lost opportunities, configure one of your sales stage as follow:\n' 'probability = 0 %, select "Change Probability Automatically".\n' 'Create a specific stage or edit an existing one by editing columns of your opportunity pipe.')) + for stage_id, lead_ids in stages_leads.items(): + self.write(cr, uid, lead_ids, {'stage_id': stage_id}, context=context) + return True def case_mark_won(self, cr, uid, ids, context=None): - """ Mark the case as won: state=done and probability=100 """ - for lead in self.browse(cr, uid, ids): - stage_id = self.stage_find(cr, uid, [lead], lead.section_id.id or False, [('probability', '=', 100.0), ('on_change', '=', True), ('sequence', '>', 1)], context=context) + """ Mark the case as won: state=done and probability=100 + :deprecated: this method will be removed in OpenERP v8. + """ + stages_leads = {} + for lead in self.browse(cr, uid, ids, context=context): + stage_id = self.stage_find(cr, uid, [lead], lead.section_id.id or False, [('probability', '=', 100.0), ('fold', '=', True)], context=context) if stage_id: - return self.write(cr, uid, [lead.id], {'stage_id': stage_id}, context=context) + if stages_leads.get(stage_id): + stages_leads[stage_id].append(lead.id) + else: + stages_leads[stage_id] = [lead.id] else: raise osv.except_osv(_('Warning!'), _('To relieve your sales pipe and group all Won opportunities, configure one of your sales stage as follow:\n' 'probability = 100 % and select "Change Probability Automatically".\n' 'Create a specific stage or edit an existing one by editing columns of your opportunity pipe.')) + for stage_id, lead_ids in stages_leads.items(): + self.write(cr, uid, lead_ids, {'stage_id': stage_id}, context=context) + return True def case_escalate(self, cr, uid, ids, context=None): """ Escalates case to parent level """ @@ -617,7 +636,7 @@ class crm_lead(format_address, osv.osv): # An Opportunity always has higher confidence level than a lead, unless its stage probability is 0.0 for opportunity in opportunities: sequence = -1 - if opportunity.stage_id and (opportunity.stage_id.probability != 0 or opportunity.stage_id.sequence == 1): + if opportunity.stage_id and not opportunity.stage_id.fold: sequence = opportunity.stage_id.sequence sequenced_opps.append(((int(sequence != -1 and opportunity.type == 'opportunity'), sequence, -opportunity.id), opportunity)) @@ -678,7 +697,7 @@ class crm_lead(format_address, osv.osv): 'phone': customer and customer.phone or lead.phone, } if not lead.stage_id or lead.stage_id.type=='lead': - val['stage_id'] = self.stage_find(cr, uid, [lead], section_id, [('sequence', '=', '1'), ('type', 'in', ('opportunity','both'))], context=context) + val['stage_id'] = self.stage_find(cr, uid, [lead], section_id, [('type', 'in', ('opportunity', 'both'))], context=context) return val def convert_opportunity(self, cr, uid, ids, partner_id, user_ids=False, section_id=False, context=None): @@ -688,7 +707,7 @@ class crm_lead(format_address, osv.osv): customer = partner.browse(cr, uid, partner_id, context=context) for lead in self.browse(cr, uid, ids, context=context): # TDE: was if lead.state in ('done', 'cancel'): - if (lead.probability == '100') or (lead.probability == '0' and lead.stage_id.sequence != '1'): + if lead.probability == 100 or (lead.probability == 0 and lead.stage_id.fold): continue vals = self._convert_opportunity_data(cr, uid, lead, customer, section_id, context=context) self.write(cr, uid, [lead.id], vals, context=context) @@ -818,9 +837,11 @@ class crm_lead(format_address, osv.osv): model_data = self.pool.get('ir.model.data') phonecall_dict = {} if not categ_id: - res_id = model_data._get_id(cr, uid, 'crm', 'categ_phone2') - if res_id: + try: + res_id = model_data._get_id(cr, uid, 'crm', 'categ_phone2') categ_id = model_data.browse(cr, uid, res_id, context=context).res_id + except ValueError: + pass for lead in self.browse(cr, uid, ids, context=context): if not section_id: section_id = lead.section_id and lead.section_id.id or False @@ -927,6 +948,23 @@ class crm_lead(format_address, osv.osv): vals.update(onchange_stage_values) return super(crm_lead, self).write(cr, uid, ids, vals, context=context) + def copy(self, cr, uid, id, default=None, context=None): + if not default: + default = {} + if not context: + context = {} + lead = self.browse(cr, uid, id, context=context) + local_context = dict(context) + local_context.setdefault('default_type', lead.type) + local_context.setdefault('default_section_id', lead.section_id) + if lead.type == 'opportunity': + default['date_open'] = fields.datetime.now() + else: + default['date_open'] = False + default['date_closed'] = False + default['stage_id'] = self._get_default_stage_id(cr, uid, local_context) + return super(crm_lead, self).copy(cr, uid, id, default, context=context) + # ---------------------------------------- # Mail Gateway # ---------------------------------------- diff --git a/addons/crm/crm_lead_data.xml b/addons/crm/crm_lead_data.xml index 3c299fef704..6bb8e2700d4 100644 --- a/addons/crm/crm_lead_data.xml +++ b/addons/crm/crm_lead_data.xml @@ -44,6 +44,7 @@ <record model="crm.case.stage" id="stage_lead6"> <field name="name">Won</field> <field name="case_default">1</field> + <field name="fold">1</field> <field name="probability">100</field> <field name="on_change">1</field> <field name="sequence">70</field> @@ -203,7 +204,7 @@ <field name="model_id" ref="crm.model_crm_lead"/> <field name="auto_delete" eval="True"/> <field name="partner_to">${object.partner_id != False and object.partner_id.id}</field> - <field name="email_to">${not object.partner_id and object.email_from}</field> + <field name="email_to">${(not object.partner_id and object.email_from)|safe}</field> <field name="body_html"></field> </record> <record id="email_template_opportunity_reminder_mail" model="email.template"> @@ -211,7 +212,7 @@ <field name="model_id" ref="crm.model_crm_lead"/> <field name="auto_delete" eval="True"/> <field name="email_from">admin@example.com</field> - <field name="email_to">${object.user_id != False and object.user_id.email}</field> + <field name="email_to">${(object.user_id != False and object.user_id.email)|safe}</field> <field name="subject">Reminder on Lead: ${object.id} from ${object.partner_id != False and object.partner_id.name or object.contact_name}</field> <field name="body_html"><![CDATA[<p>This opportunity did not have any activity since at least 5 days. Here are some details:</p> <ul> diff --git a/addons/crm/crm_lead_view.xml b/addons/crm/crm_lead_view.xml index c0eefcc8d4c..f3e424e3e37 100644 --- a/addons/crm/crm_lead_view.xml +++ b/addons/crm/crm_lead_view.xml @@ -323,7 +323,7 @@ <field name="model">crm.lead</field> <field name="arch" type="xml"> <search string="Search Leads"> - <field name="name" string="Lead / Customer" filter_domain="['|','|',('partner_name','ilike',self),('email_from','ilike',self),('name','ilike',self)]"/> + <field name="name" string="Lead / Customer" filter_domain="['|','|','|',('partner_name','ilike',self),('email_from','ilike',self),('contact_name','ilike',self),('name','ilike',self)]"/> <field name="categ_ids" string="Category" filter_domain="[('categ_ids','ilike',self)]"/> <field name="section_id" context="{'invisible_section': False}" groups="base.group_multi_salesteams"/> <field name="user_id"/> @@ -334,14 +334,14 @@ <filter string="Unassigned" name="unassigned" domain="[('user_id','=', False)]" help="No salesperson"/> - <filter string="Assigned to Me" + <filter string="My Leads" domain="[('user_id','=',uid)]" context="{'invisible_section': False}" help="Leads that are assigned to me"/> - <filter string="Assigned to My Team(s)" groups="base.group_multi_salesteams" + <filter string="My Team(s)" groups="base.group_multi_salesteams" domain="[('section_id.member_ids', 'in', [uid])]" context="{'invisible_section': False}" help="Leads that are assigned to any sales teams I am member of"/> <filter string="Dead" name="dead" - domain="[('probability', '=', '0'), ('stage_id.sequence', '!=', 1)]"/> + domain="[('probability', '=', '0'), ('stage_id.fold', '=', True)]"/> <separator /> <filter string="Available for mass mailing" name='not_opt_out' domain="[('opt_out', '=', False)]" @@ -379,10 +379,12 @@ <field name="arch" type="xml"> <form string="Opportunities" version="7.0"> <header> + <!-- :deprecated: this button will be removed from the view with OpenERP v8 --> <button name="case_mark_won" string="Mark Won" type="object" class="oe_highlight" - attrs="{'invisible': [('probability', '=', 100)]}"/> + invisible="True"/> + <!-- :deprecated: this button will be removed from the view with OpenERP v8 --> <button name="case_mark_lost" string="Mark Lost" type="object" class="oe_highlight" - attrs="{'invisible': ['|', ('probability', '=', 0), ('probability', '=', 100)]}"/> + invisible="True"/> <field name="stage_id" widget="statusbar" clickable="True" domain="['&', ('section_ids', '=', section_id), '|', ('type', '=', type), ('type', '=', 'both')]"/> </header> @@ -550,18 +552,16 @@ <field name="stage_id" domain="[]"/> <field name="probability"/> <separator/> - <filter string="New" name="new" - domain="[('probability', '=', 0), ('stage_id.sequence', '=', 1)]"/> <filter string="Won" name="won" - domain="[('probability', '=', 100), ('stage_id.on_change', '=', 1)]"/> + domain="[('probability', '=', 100), ('stage_id.fold', '=', True)]"/> <filter string="Lost" name="lost" - domain="[('probability', '=', 0), ('stage_id.sequence', '!=', 1)]"/> + domain="[('probability', '=', 0), ('stage_id.fold', '=', True)]"/> <filter string="Unassigned" name="unassigned" domain="[('user_id','=', False)]" help="No salesperson"/> <filter string="My Opportunities" name="assigned_to_me" domain="[('user_id', '=', uid)]" context="{'invisible_section': False}" help="Opportunities that are assigned to me"/> - <filter string="Assigned to My Team(s)" + <filter string="My Team(s)" domain="[('section_id.member_ids', 'in', [uid])]" context="{'invisible_section': False}" help="Opportunities that are assigned to any sales teams I am member of"/> <separator/> @@ -600,7 +600,7 @@ id="crm.action_lead_mass_mail" context="{ 'default_composition_mode': 'mass_mail', - 'default_email_to':'{$object.email or \'\'}', + 'default_email_to':'{($object.email or \'\')|safe}', 'default_use_template': True, 'default_template_id': ref('crm.email_template_opportunity_mail'), }" diff --git a/addons/crm/crm_phonecall.py b/addons/crm/crm_phonecall.py index 1e67c50a2c3..e74fecc9bdf 100644 --- a/addons/crm/crm_phonecall.py +++ b/addons/crm/crm_phonecall.py @@ -117,9 +117,11 @@ class crm_phonecall(osv.osv): model_data = self.pool.get('ir.model.data') phonecall_dict = {} if not categ_id: - res_id = model_data._get_id(cr, uid, 'crm', 'categ_phone2') - if res_id: + try: + res_id = model_data._get_id(cr, uid, 'crm', 'categ_phone2') categ_id = model_data.browse(cr, uid, res_id, context=context).res_id + except ValueError: + pass for call in self.browse(cr, uid, ids, context=context): if not section_id: section_id = call.section_id and call.section_id.id or False diff --git a/addons/crm/crm_phonecall_view.xml b/addons/crm/crm_phonecall_view.xml index 585286f5456..a027a910520 100644 --- a/addons/crm/crm_phonecall_view.xml +++ b/addons/crm/crm_phonecall_view.xml @@ -120,7 +120,7 @@ <field name="date"/> <field name="name"/> <field name="partner_id" - on_change="onchange_partner_id(partner_id)"/> + on_change="on_change_partner_id(partner_id)"/> <field name="partner_phone" invisible="1"/> <field name="user_id" context="{'default_groups_ref': ['base.group_user', 'base.group_partner_manager', 'base.group_sale_salesman_all_leads']}"/> diff --git a/addons/crm/crm_view.xml b/addons/crm/crm_view.xml index d0dcc07fcb0..8cc4b03cb41 100644 --- a/addons/crm/crm_view.xml +++ b/addons/crm/crm_view.xml @@ -93,14 +93,19 @@ <field name="priority" eval="1"/> <field name="arch" type="xml"> <form string="Stage" version="7.0"> - <group col="4"> - <field name="name"/> - <field name="probability"/> - <field name="type"/> - <field name="on_change"/> - <field name="sequence"/> - <field name="case_default" groups="base.group_multi_salesteams"/> - <field name="fold"/> + <group> + <group> + <field name="name"/> + <field name="sequence"/> + <field name="type"/> + <field name="probability"/> + </group> + <group> + <field name="fold"/> + <field name="on_change"/> + <field name="case_default" groups="base.group_multi_salesteams"/> + </group> + </group> <separator string="Requirements"/> <field name="requirements" nolabel="1"/> diff --git a/addons/crm/wizard/crm_lead_to_opportunity.py b/addons/crm/wizard/crm_lead_to_opportunity.py index 48097944ec4..f239ad739c1 100644 --- a/addons/crm/wizard/crm_lead_to_opportunity.py +++ b/addons/crm/wizard/crm_lead_to_opportunity.py @@ -59,11 +59,11 @@ class crm_lead2opportunity_partner(osv.osv_memory): if partner_id: # Search for opportunities that have the same partner and that arent done or cancelled - ids = lead_obj.search(cr, uid, [('partner_id', '=', partner_id), ('probability', '<', '100')]) + ids = lead_obj.search(cr, uid, [('partner_id', '=', partner_id), '|', ('probability', '=', False), ('probability', '<', '100')]) for id in ids: tomerge.add(id) if email: - ids = lead_obj.search(cr, uid, [('email_from', '=ilike', email[0]), ('probability', '<', '100')]) + ids = lead_obj.search(cr, uid, [('email_from', '=ilike', email[0]), '|', ('probability', '=', False), ('probability', '<', '100')]) for id in ids: tomerge.add(id) diff --git a/addons/crm/wizard/crm_opportunity_to_phonecall.py b/addons/crm/wizard/crm_opportunity_to_phonecall.py index bb1ac2fdce6..5183773b618 100644 --- a/addons/crm/wizard/crm_opportunity_to_phonecall.py +++ b/addons/crm/wizard/crm_opportunity_to_phonecall.py @@ -34,9 +34,11 @@ class crm_opportunity2phonecall(osv.osv_memory): opp_obj = self.pool.get('crm.lead') categ_id = False data_obj = self.pool.get('ir.model.data') - res_id = data_obj._get_id(cr, uid, 'crm', 'categ_phone2') - if res_id: + try: + res_id = data_obj._get_id(cr, uid, 'crm', 'categ_phone2') categ_id = data_obj.browse(cr, uid, res_id, context=context).res_id + except ValueError: + pass record_ids = context and context.get('active_ids', []) or [] res = {} diff --git a/addons/crm/wizard/crm_phonecall_to_phonecall.py b/addons/crm/wizard/crm_phonecall_to_phonecall.py index 7e1da41a29f..804b3c7cb0f 100644 --- a/addons/crm/wizard/crm_phonecall_to_phonecall.py +++ b/addons/crm/wizard/crm_phonecall_to_phonecall.py @@ -78,9 +78,11 @@ class crm_phonecall2phonecall(osv.osv_memory): categ_id = False data_obj = self.pool.get('ir.model.data') - res_id = data_obj._get_id(cr, uid, 'crm', 'categ_phone2') - if res_id: + try: + res_id = data_obj._get_id(cr, uid, 'crm', 'categ_phone2') categ_id = data_obj.browse(cr, uid, res_id, context=context).res_id + except ValueError: + pass if 'name' in fields: res.update({'name': phonecall.name}) diff --git a/addons/crm_claim/crm_claim.py b/addons/crm_claim/crm_claim.py index 1b9cb0b0c50..50cbe4a6c44 100644 --- a/addons/crm_claim/crm_claim.py +++ b/addons/crm_claim/crm_claim.py @@ -173,7 +173,8 @@ class crm_claim(osv.osv): through message_process. This override updates the document according to the email. """ - if custom_values is None: custom_values = {} + if custom_values is None: + custom_values = {} desc = html2plaintext(msg.get('body')) if msg.get('body') else '' defaults = { 'name': msg.get('subject') or _("No Subject"), @@ -185,33 +186,7 @@ class crm_claim(osv.osv): if msg.get('priority'): defaults['priority'] = msg.get('priority') defaults.update(custom_values) - return super(crm_claim,self).message_new(cr, uid, msg, custom_values=defaults, context=context) - - def message_update(self, cr, uid, ids, msg, update_vals=None, context=None): - """ Overrides mail_thread message_update that is called by the mailgateway - through message_process. - This method updates the document according to the email. - """ - if isinstance(ids, (str, int, long)): - ids = [ids] - if update_vals is None: update_vals = {} - - if msg.get('priority') in dict(crm.AVAILABLE_PRIORITIES): - update_vals['priority'] = msg.get('priority') - - maps = { - 'cost':'planned_cost', - 'revenue': 'planned_revenue', - 'probability':'probability' - } - for line in msg['body'].split('\n'): - line = line.strip() - res = tools.command_re.match(line) - if res and maps.get(res.group(1).lower()): - key = maps.get(res.group(1).lower()) - update_vals[key] = res.group(2).lower() - - return super(crm_claim,self).message_update(cr, uid, ids, msg, update_vals=update_vals, context=context) + return super(crm_claim, self).message_new(cr, uid, msg, custom_values=defaults, context=context) class res_partner(osv.osv): _inherit = 'res.partner' diff --git a/addons/crm_helpdesk/crm_helpdesk.py b/addons/crm_helpdesk/crm_helpdesk.py index 5f84d45f974..c1cd6d2ff91 100644 --- a/addons/crm_helpdesk/crm_helpdesk.py +++ b/addons/crm_helpdesk/crm_helpdesk.py @@ -127,7 +127,8 @@ class crm_helpdesk(osv.osv): through message_process. This override updates the document according to the email. """ - if custom_values is None: custom_values = {} + if custom_values is None: + custom_values = {} desc = html2plaintext(msg.get('body')) if msg.get('body') else '' defaults = { 'name': msg.get('subject') or _("No Subject"), @@ -138,32 +139,6 @@ class crm_helpdesk(osv.osv): 'partner_id': msg.get('author_id', False), } defaults.update(custom_values) - return super(crm_helpdesk,self).message_new(cr, uid, msg, custom_values=defaults, context=context) - - def message_update(self, cr, uid, ids, msg, update_vals=None, context=None): - """ Overrides mail_thread message_update that is called by the mailgateway - through message_process. - This method updates the document according to the email. - """ - if isinstance(ids, (str, int, long)): - ids = [ids] - if update_vals is None: update_vals = {} - - if msg.get('priority') in dict(crm.AVAILABLE_PRIORITIES): - update_vals['priority'] = msg.get('priority') - - maps = { - 'cost':'planned_cost', - 'revenue': 'planned_revenue', - 'probability':'probability' - } - for line in msg['body'].split('\n'): - line = line.strip() - res = tools.command_re.match(line) - if res and maps.get(res.group(1).lower()): - key = maps.get(res.group(1).lower()) - update_vals[key] = res.group(2).lower() - - return super(crm_helpdesk,self).message_update(cr, uid, ids, msg, update_vals=update_vals, context=context) + return super(crm_helpdesk, self).message_new(cr, uid, msg, custom_values=defaults, context=context) # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/crm_partner_assign/__openerp__.py b/addons/crm_partner_assign/__openerp__.py index e0f1d78558a..3c75ed35911 100644 --- a/addons/crm_partner_assign/__openerp__.py +++ b/addons/crm_partner_assign/__openerp__.py @@ -21,20 +21,17 @@ { - 'name': 'Partners Geo-Localization', + 'name': 'CRM Geolocation', 'version': '1.0', 'category': 'Customer Relationship Management', 'description': """ -This is the module used by OpenERP SA to redirect customers to its partners, based on geolocalization. +This is the module used by OpenERP SA to redirect customers to its partners, based on geolocation. ====================================================================================================== -You can geolocalize your opportunities by using this module. +This modules lets you geolocate Leads, Opportunities and Partners based on their address. -Use geolocalization when assigning opportunities to partners. -Determine the GPS coordinates according to the address of the partner. - -The most appropriate partner can be assigned. -You can also use the geolocalization without using the GPS coordinates. +Once the coordinates of the Lead/Opportunity is known, they can be automatically assigned +to an appropriate local partner, based on the distance and the weight that was assigned to the partner. """, 'author': 'OpenERP SA', 'depends': ['crm', 'account', 'portal'], diff --git a/addons/crm_partner_assign/crm_lead.py b/addons/crm_partner_assign/crm_lead.py index 37f4b9aedff..d7463355271 100644 --- a/addons/crm_partner_assign/crm_lead.py +++ b/addons/crm_partner_assign/crm_lead.py @@ -42,3 +42,17 @@ class crm_lead(osv.osv): def case_disinterested(self, cr, uid, ids, context=None): return self.get_interested_action(cr, uid, False, context=context) + + def assign_salesman_of_assigned_partner(self, cr, uid, ids, context=None): + salesmans_leads = {} + for lead in self.browse(cr, uid, ids, context=context): + if (lead.stage_id.probability > 0 and lead.stage_id.probability < 100) or lead.stage_id.sequence == 1: + if lead.partner_assigned_id and lead.partner_assigned_id.user_id and lead.partner_assigned_id.user_id != lead.user_id: + salesman_id = lead.partner_assigned_id.user_id.id + if salesmans_leads.get(salesman_id): + salesmans_leads[salesman_id].append(lead.id) + else: + salesmans_leads[salesman_id] = [lead.id] + for salesman_id, lead_ids in salesmans_leads.items(): + salesteam_id = self.on_change_user(cr, uid, lead_ids, salesman_id, context=None)['value'].get('section_id') + self.write(cr, uid, lead_ids, {'user_id': salesman_id, 'section_id': salesteam_id}, context=context) diff --git a/addons/crm_partner_assign/crm_lead_view.xml b/addons/crm_partner_assign/crm_lead_view.xml index 7550396e886..80d82c6c2bf 100644 --- a/addons/crm_partner_assign/crm_lead_view.xml +++ b/addons/crm_partner_assign/crm_lead_view.xml @@ -65,6 +65,9 @@ domain="[]" context="{'group_by':'date_assign'}"/> </filter> + <filter string="My Team(s)" position="after"> + <filter string="My Assigned Partners" domain="[('partner_assigned_id.user_id', '=', uid)]"/> + </filter> <field name="partner_id" position="after"> <field name="partner_assigned_id"/> </field> @@ -131,11 +134,40 @@ <filter string="Team" position="after"> <filter string="Assigned Partner" icon="terp-personal" domain="[]" context="{'group_by':'partner_assigned_id'}"/> </filter> + <filter string="My Team(s)" position="after"> + <filter string="My Assigned Partners" domain="[('partner_assigned_id.user_id', '=', uid)]"/> + </filter> <field name="partner_id" position="after"> <field name="partner_assigned_id"/> </field> </field> </record> + <record id="action_assign_salesman_according_assigned_partner" model="ir.actions.server"> + <field name="name">Assign salesman of assigned partner</field> + <field name="model_id" ref="model_crm_lead"/> + <field name="state">code</field> + <field name="code"> + if context.get('active_model') == 'crm.lead': + ids = [] + if context.get('active_domain'): + ids = self.search(cr, uid, context['active_domain'], context=context) + elif context.get('active_ids'): + ids = context['active_ids'] + if ids: + self.assign_salesman_of_assigned_partner(cr, uid, ids, context=context) + </field> + <field name="groups_id" eval="[(4,ref('base.group_sale_manager'))]"/> + </record> + + <record model="ir.values" id="ir_assign_salesman_according_assigned_partner"> + <field name="model_id" ref="model_crm_lead" /> + <field name="name">Assign salesman of assigned partner</field> + <field name="key2">client_action_multi</field> + <field eval="'ir.actions.server,%d'%action_assign_salesman_according_assigned_partner" name="value"/> + <field name="key">action</field> + <field name="model">crm.lead</field> + </record> + </data> </openerp> diff --git a/addons/crm_partner_assign/crm_partner_assign.py b/addons/crm_partner_assign/crm_partner_assign.py index daf3d844fe6..c5329696b32 100644 --- a/addons/crm_partner_assign/crm_partner_assign.py +++ b/addons/crm_partner_assign/crm_partner_assign.py @@ -179,23 +179,27 @@ class crm_lead(osv.osv): return res def assign_geo_localize(self, cr, uid, ids, latitude=False, longitude=False, context=None): - # Don't pass context to browse()! We need country name in english below - for lead in self.browse(cr, uid, ids): - if not lead.country_id: - continue - result = geo_find(geo_query_address(street=lead.street, - zip=lead.zip, - city=lead.city, - state=lead.state_id.name, - country=lead.country_id.name)) - if not latitude and result: - latitude = result[0] - if not longitude and result: - longitude = result[1] - self.write(cr, uid, [lead.id], { + if latitude and longitude: + self.write(cr, uid, ids, { 'partner_latitude': latitude, 'partner_longitude': longitude }, context=context) + return True + # Don't pass context to browse()! We need country name in english below + for lead in self.browse(cr, uid, ids): + if lead.partner_latitude and lead.partner_longitude: + continue + if lead.country_id: + result = geo_find(geo_query_address(street=lead.street, + zip=lead.zip, + city=lead.city, + state=lead.state_id.name, + country=lead.country_id.name)) + if result: + self.write(cr, uid, [lead.id], { + 'partner_latitude': result[0], + 'partner_longitude': result[1] + }, context=context) return True def search_geo_partner(self, cr, uid, ids, context=None): diff --git a/addons/crm_partner_assign/crm_partner_assign_data.xml b/addons/crm_partner_assign/crm_partner_assign_data.xml index b4347b1621b..284476cb6ee 100644 --- a/addons/crm_partner_assign/crm_partner_assign_data.xml +++ b/addons/crm_partner_assign/crm_partner_assign_data.xml @@ -4,14 +4,14 @@ <record model="crm.case.stage" id="stage_portal_lead_assigned"> <field name="name">Assigned</field> <field eval="1" name="case_default"/> - <field eval="0" name="probability"/> + <field eval="10" name="probability"/> <field eval="12" name="sequence"/> <field name="type">lead</field> </record> <record model="crm.case.stage" id="stage_portal_lead_recycle"> <field name="name">To Recycle</field> <field eval="1" name="case_default"/> - <field eval="0" name="probability"/> + <field eval="5" name="probability"/> <field eval="11" name="sequence"/> <field name="type">lead</field> </record> @@ -19,8 +19,8 @@ <field name="name">Lead Mass Mail</field> <field name="model_id" model="ir.model" search="[('name', '=', 'crm.lead.forward.to.partner')]"></field> <field name="auto_delete" eval="True"/> - <field name="email_to">${ctx['partner_id'].email}</field> - <field name="email_from">${user.email or ''}</field> + <field name="email_to">${ctx['partner_id'].email|safe}</field> + <field name="email_from">${(user.email or '')|safe}</field> <field name="subject">Fwd: Lead: ${ctx['partner_id'].name}</field> <field name="body_html"><![CDATA[ @@ -31,7 +31,7 @@ <ol> % for lead in ctx['partner_leads']: - <li><a href="${lead.lead_link}">${lead.lead_id.name or 'Subject Undefined'}</a>, ${lead.lead_id.contact_name or 'Contact Name Undefined'}, ${lead.lead_id.country_id and lead.lead_id.country_id.name or 'Country Undefined' }, ${lead.lead_id.email_from or 'Email Undefined'}, ${lead.lead_id.phone or ''} </li></br> + <li><a href="${lead.lead_link}">${lead.lead_id.name or 'Subject Undefined'}</a>, ${lead.lead_id.partner_name or lead.lead_id.contact_name or 'Contact Name Undefined'}, ${lead.lead_id.country_id and lead.lead_id.country_id.name or 'Country Undefined' }, ${lead.lead_id.email_from or 'Email Undefined'}, ${lead.lead_id.phone or ''} </li></br> % endfor </ol> @@ -56,4 +56,4 @@ PS: It looks like you do not have an account manager assigned to you, please con ]]></field> </record> </data> -</openerp>
\ No newline at end of file +</openerp> diff --git a/addons/delivery/stock.py b/addons/delivery/stock.py index 95e87eb45ab..42c24b4a27d 100644 --- a/addons/delivery/stock.py +++ b/addons/delivery/stock.py @@ -187,6 +187,9 @@ class stock_move(osv.osv): # Redefinition of the new fields in order to update the model stock.picking.out in the orm # FIXME: this is a temporary workaround because of a framework bug (ref: lp996816). It should be removed as soon as # the bug is fixed + +# TODO in trunk: Remove the duplication below using a mixin class! + class stock_picking_out(osv.osv): _inherit = 'stock.picking.out' @@ -212,6 +215,7 @@ class stock_picking_out(osv.osv): }), 'carrier_tracking_ref': fields.char('Carrier Tracking Ref', size=32), 'number_of_packages': fields.integer('Number of Packages'), + 'weight_uom_id': fields.many2one('product.uom', 'Unit of Measure', required=True,readonly="1",help="Unit of measurement for Weight",), } class stock_picking_in(osv.osv): @@ -224,6 +228,8 @@ class stock_picking_in(osv.osv): return self.pool.get('stock.picking')._get_picking_line(cr, uid, ids, context=context) _columns = { + 'carrier_id':fields.many2one("delivery.carrier","Carrier"), + 'volume': fields.float('Volume'), 'weight': fields.function(_cal_weight, type='float', string='Weight', digits_compute= dp.get_precision('Stock Weight'), multi='_cal_weight', store={ 'stock.picking': (lambda self, cr, uid, ids, c={}: ids, ['move_lines'], 20), @@ -234,6 +240,9 @@ class stock_picking_in(osv.osv): 'stock.picking': (lambda self, cr, uid, ids, c={}: ids, ['move_lines'], 20), 'stock.move': (_get_picking_line, ['product_id','product_qty','product_uom','product_uos_qty'], 20), }), + 'carrier_tracking_ref': fields.char('Carrier Tracking Ref', size=32), + 'number_of_packages': fields.integer('Number of Packages'), + 'weight_uom_id': fields.many2one('product.uom', 'Unit of Measure', required=True,readonly="1",help="Unit of measurement for Weight",), } # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/document/security/ir.model.access.csv b/addons/document/security/ir.model.access.csv index e0513a0640c..8c5e4323831 100644 --- a/addons/document/security/ir.model.access.csv +++ b/addons/document/security/ir.model.access.csv @@ -15,3 +15,4 @@ access_report_document_user_group_document_manager,report.document.user document access_report_document_file_group_document_manager,report.document.file document manager,model_report_document_file,base.group_system,1,0,0,0
access_report_document_file_group_document,report.document.file document manager,model_report_document_file,base.group_document_user,1,0,0,0
access_report_document_user_knowledgeuser,report.document.user knowledgeuser,document.model_report_document_user,base.group_document_user,1,0,0,0
+access_document_storage,access_document_storage,model_document_storage,base.group_system,1,1,1,1
diff --git a/addons/document/static/src/js/document.js b/addons/document/static/src/js/document.js index 23f1375e8c8..e4bce2da76e 100644 --- a/addons/document/static/src/js/document.js +++ b/addons/document/static/src/js/document.js @@ -9,7 +9,7 @@ openerp.document = function (instance) { on_attachments_loaded: function(attachments) { //to display number in name if more then one attachment which has same name. var self = this; - _.chain(attachments.reverse()) + _.chain(attachments) .groupBy(function(attachment) { return attachment.name}) .each(function(attachment){ if(attachment.length > 1) diff --git a/addons/document_ftp/ftpserver/__init__.py b/addons/document_ftp/ftpserver/__init__.py index 3749a02a387..6474def3e99 100644 --- a/addons/document_ftp/ftpserver/__init__.py +++ b/addons/document_ftp/ftpserver/__init__.py @@ -34,6 +34,9 @@ def start_server(): if openerp.multi_process: _logger.info("FTP disabled in multiprocess mode") return + if openerp.evented: + _logger.info("FTP disabled in evented mode") + return HOST = config.get('ftp_server_host', '127.0.0.1') PORT = int(config.get('ftp_server_port', '8021')) PASSIVE_PORTS = None diff --git a/addons/email_template/email_template.py b/addons/email_template/email_template.py index ed35349f420..efa1387435a 100644 --- a/addons/email_template/email_template.py +++ b/addons/email_template/email_template.py @@ -378,7 +378,7 @@ class email_template(osv.osv): # Ensure report is rendered using template's language ctx = context.copy() if template.lang: - ctx['lang'] = self.render_template_batch(cr, uid, template.lang, template.model, res_id, context) # take 0 ? + ctx['lang'] = self.render_template_batch(cr, uid, template.lang, template.model, [res_id], context)[res_id] # take 0 ? result, format = openerp.report.render_report(cr, uid, [res_id], report_service, {'model': template.model}, ctx) result = base64.b64encode(result) if not report_name: diff --git a/addons/event/email_template.xml b/addons/event/email_template.xml index 95ffbf46d8d..d99774acd3c 100644 --- a/addons/event/email_template.xml +++ b/addons/event/email_template.xml @@ -4,8 +4,8 @@ <record id="confirmation_event" model="email.template"> <field name="name">Confirmation of the Event</field> <field name="model_id" ref="event.model_event_registration"/> - <field name="email_from" >${object.user_id.email or object.company_id.email or 'noreply@' + object.company_id.name + '.com'}</field> - <field name="email_to" >${object.email}</field> + <field name="email_from" >${(object.user_id.email or object.company_id.email or 'noreply@' + object.company_id.name + '.com')|safe}</field> + <field name="email_to" >${object.email|safe}</field> <field name="subject">Your registration at ${object.event_id.name}</field> <field name="body_html"><![CDATA[ <p>Hello ${object.name},</p> @@ -21,8 +21,8 @@ <record id="confirmation_registration" model="email.template"> <field name="name">Confirmation of the Registration</field> <field name="model_id" ref="event.model_event_registration"/> - <field name="email_from" >${object.user_id.email or object.company_id.email or 'noreply@' + object.company_id.name + '.com'}</field> - <field name="email_to" >${object.email}</field> + <field name="email_from" >${(object.user_id.email or object.company_id.email or 'noreply@' + object.company_id.name + '.com')|safe}</field> + <field name="email_to" >${object.email|safe}</field> <field name="subject">Your registration at ${object.event_id.name}</field> <field name="body_html"><![CDATA[ <p>Hello ${object.name},</p> diff --git a/addons/fleet/fleet_demo.xml b/addons/fleet/fleet_demo.xml index a550ae78da5..2f9b71b3bb5 100644 --- a/addons/fleet/fleet_demo.xml +++ b/addons/fleet/fleet_demo.xml @@ -1,8 +1,9 @@ <?xml version="1.0" encoding="utf-8"?> <openerp> <data> + <!--Users--> <record id="base.user_demo" model="res.users"> - <field name="groups_id" eval="[(4, ref('fleet.group_fleet_user'))]" /> + <field name="groups_id" eval="[(4, ref('fleet.group_fleet_manager'))]" /> </record> <record id="vehicle_state_inshop" model="fleet.vehicle.state"> diff --git a/addons/fleet/fleet_view.xml b/addons/fleet/fleet_view.xml index 1258e49aa2d..a0ced74a509 100644 --- a/addons/fleet/fleet_view.xml +++ b/addons/fleet/fleet_view.xml @@ -43,6 +43,7 @@ </form> </field> </record> + <record model='ir.ui.view' id='fleet_vehicle_model_tree'> <field name="name">fleet.vehicle.model.tree</field> <field name="model">fleet.vehicle.model</field> @@ -565,7 +566,7 @@ </tree> </field> </record> - <!-- + <record model='ir.ui.view' id='fleet_vehicle_odometer_search'> <field name="name">fleet.vehicle.odometer.search</field> <field name="model">fleet.vehicle.odometer</field> @@ -575,11 +576,12 @@ <field name="value"/> <field name="unit"/> <field name="date"/> - <filter name="groupby_vehicle" context="{'group_by' : 'vehicle_id'}" string="Vehicle"/> + <group expand="0" string="Group By..."> + <filter name="groupby_vehicle" context="{'group_by' : 'vehicle_id'}" string="Vehicle"/> + </group> </search> </field> </record> - --> <record model="ir.ui.view" id="fleet_vehicle_odometer_graph"> <field name="name">fleet.vehicle.odometer.graph</field> @@ -671,6 +673,23 @@ </tree> </field> </record> + + <record model='ir.ui.view' id='fleet_vehicle_log_fuel_search'> + <field name="name">fleet.vehicle.log.fuel.search</field> + <field name="model">fleet.vehicle.log.fuel</field> + <field name="arch" type="xml"> + <search string="Vehicles Fuel Logs" > + <field name="vehicle_id" /> + <field name="date"/> + <field name="purchaser_id" /> + <field name="liter" /> + <field name="amount" sum="Price"/> + <group expand="0" string="Group By..."> + <filter name="groupby_vehicle" context="{'group_by' : 'vehicle_id'}" string="Vehicle"/> + </group> + </search> + </field> + </record> <record model="ir.ui.view" id="fleet_vehicle_log_fuel_graph"> <field name="name">fleet.vehicle.log.fuel.graph</field> @@ -690,6 +709,7 @@ <field name="res_model">fleet.vehicle.log.fuel</field> <field name="view_type">form</field> <field name="view_mode">tree,form,graph</field> + <field name="context">{"search_default_groupby_vehicle" : True}</field> <field name="help" type="html"> <p class="oe_view_nocontent_create"> Click to create a new fuel log. diff --git a/addons/hr/hr.py b/addons/hr/hr.py index bf58fdd76f8..37d1bae7fea 100644 --- a/addons/hr/hr.py +++ b/addons/hr/hr.py @@ -236,11 +236,13 @@ class hr_employee(osv.osv): employee_id = super(hr_employee, self).create(cr, uid, data, context=create_ctx) employee = self.browse(cr, uid, employee_id, context=context) if employee.user_id: + res_users = self.pool['res.users'] # send a copy to every user of the company - company_id = employee.user_id.partner_id.company_id.id - partner_ids = self.pool.get('res.partner').search(cr, uid, [ - ('company_id', '=', company_id), - ('user_ids', '!=', False)], context=context) + # TODO: post to the `Whole Company` mail.group when we'll be able to link to the employee record + _model, group_id = self.pool['ir.model.data'].get_object_reference(cr, uid, 'base', 'group_user') + user_ids = res_users.search(cr, uid, [('company_id', '=', employee.user_id.company_id.id), + ('groups_id', 'in', group_id)]) + partner_ids = list(set(u.partner_id.id for u in res_users.browse(cr, uid, user_ids, context=context))) else: partner_ids = [] self.message_post(cr, uid, [employee_id], diff --git a/addons/hr/hr_view.xml b/addons/hr/hr_view.xml index 4922a53bcbf..e9a859bf44d 100644 --- a/addons/hr/hr_view.xml +++ b/addons/hr/hr_view.xml @@ -117,7 +117,7 @@ <field name="model">hr.employee</field> <field name="arch" type="xml"> <search string="Employees"> - <field name="name" string="Employees"/> + <field name="name" string="Employees" filter_domain="['|',('work_email','ilike',self),('name','ilike',self)]"/> <field name="department_id" /> <field name="category_ids" groups="base.group_hr_user"/> <group expand="0" string="Group By..."> diff --git a/addons/hr_expense/hr_expense_view.xml b/addons/hr_expense/hr_expense_view.xml index 7505717c672..33f737aa66e 100644 --- a/addons/hr_expense/hr_expense_view.xml +++ b/addons/hr_expense/hr_expense_view.xml @@ -157,7 +157,7 @@ <field name="date"/> <filter icon="terp-document-new" domain="[('state','=','draft')]" string="New" help="New Expense"/> <filter icon="terp-camera_test" domain="[('state','=','confirm')]" string="To Approve" help="Confirmed Expenses"/> - <filter icon="terp-dolar" domain="[('state','=','accepted')]" string="To Pay" help="Expenses to Invoice"/> + <filter icon="terp-dolar" domain="['|',('state','=','accepted'),('state','=','done')]" string="To Pay" help="Expenses to Invoice"/> <separator/> <filter domain="[('user_id', '=', uid)]" string="My Expenses"/> <field name="employee_id"/> diff --git a/addons/hr_holidays/hr_holidays.py b/addons/hr_holidays/hr_holidays.py index 1af2fc00193..682508e0ca0 100644 --- a/addons/hr_holidays/hr_holidays.py +++ b/addons/hr_holidays/hr_holidays.py @@ -145,7 +145,9 @@ class hr_holidays(osv.osv): def _check_date(self, cr, uid, ids): for holiday in self.browse(cr, uid, ids): - holiday_ids = self.search(cr, uid, [('date_from', '<=', holiday.date_to), ('date_to', '>=', holiday.date_from), ('employee_id', '=', holiday.employee_id.id), ('id', '<>', holiday.id)]) + holiday_ids = self.search(cr, uid, [('date_from', '<=', holiday.date_to), ('date_to', '>=', holiday.date_from), + ('employee_id', '=', holiday.employee_id.id), ('id', '<>', holiday.id), + ('state', 'not in', ['cancel', 'refuse'])]) if holiday_ids: return False return True diff --git a/addons/hr_holidays/hr_holidays_workflow.xml b/addons/hr_holidays/hr_holidays_workflow.xml index b563dbf68d4..c539ff6de41 100644 --- a/addons/hr_holidays/hr_holidays_workflow.xml +++ b/addons/hr_holidays/hr_holidays_workflow.xml @@ -55,6 +55,7 @@ <record model="workflow.activity" id="act_refuse"> <!-- refused --> <field name="wkf_id" ref="wkf_holidays" /> <field name="name">refuse</field> + <field name="flow_stop" eval="False"/> <field name="kind">function</field> <field name="action">holidays_refuse()</field> </record> diff --git a/addons/hr_recruitment/hr_recruitment.py b/addons/hr_recruitment/hr_recruitment.py index 0f0f2785fc1..9d94b079fb5 100644 --- a/addons/hr_recruitment/hr_recruitment.py +++ b/addons/hr_recruitment/hr_recruitment.py @@ -52,12 +52,13 @@ class hr_recruitment_stage(osv.osv): 'name': fields.char('Name', size=64, required=True, translate=True), 'sequence': fields.integer('Sequence', help="Gives the sequence order when displaying a list of stages."), 'department_id':fields.many2one('hr.department', 'Specific to a Department', help="Stages of the recruitment process may be different per department. If this stage is common to all departments, keep this field empty."), - 'fold': fields.boolean('Hide in views if empty', help="This stage is not visible, for example in status bar or kanban view, when there are no records in that stage to display."), 'requirements': fields.text('Requirements'), + 'fold': fields.boolean('Folded in Kanban View', + help='This stage is folded in the kanban view when' + 'there are no records in that stage to display.'), } _defaults = { 'sequence': 1, - 'fold': False, } class hr_recruitment_degree(osv.osv): @@ -82,8 +83,9 @@ class hr_applicant(osv.Model): _inherit = ['mail.thread', 'ir.needaction_mixin'] _track = { 'stage_id': { - 'hr_recruitment.mt_applicant_new': lambda self, cr, uid, obj, ctx=None: obj.stage_id and obj.stage_id.sequence == 1, - 'hr_recruitment.mt_applicant_stage_changed': lambda self, cr, uid, obj, ctx=None: obj.stage_id and obj.stage_id.sequence != 1, + # this is only an heuristics; depending on your particular stage configuration it may not match all 'new' stages + 'hr_recruitment.mt_applicant_new': lambda self, cr, uid, obj, ctx=None: obj.stage_id and obj.stage_id.sequence <= 1, + 'hr_recruitment.mt_applicant_stage_changed': lambda self, cr, uid, obj, ctx=None: obj.stage_id and obj.stage_id.sequence > 1, }, } @@ -94,7 +96,7 @@ class hr_applicant(osv.Model): def _get_default_stage_id(self, cr, uid, context=None): """ Gives default stage_id """ department_id = self._get_default_department_id(cr, uid, context=context) - return self.stage_find(cr, uid, [], department_id, [('sequence', '=', '1')], context=context) + return self.stage_find(cr, uid, [], department_id, [('fold', '=', False)], context=context) def _resolve_department_id_from_context(self, cr, uid, context=None): """ Returns ID of department based on the value of 'default_department_id' @@ -241,7 +243,7 @@ class hr_applicant(osv.Model): def onchange_department_id(self, cr, uid, ids, department_id=False, stage_id=False, context=None): if not stage_id: - stage_id = self.stage_find(cr, uid, [], department_id, [('sequence', '=', '1')], context=context) + stage_id = self.stage_find(cr, uid, [], department_id, [('fold', '=', False)], context=context) return {'value': {'stage_id': stage_id}} def onchange_partner_id(self, cr, uid, ids, partner_id, context=None): @@ -331,7 +333,8 @@ class hr_applicant(osv.Model): through message_process. This override updates the document according to the email. """ - if custom_values is None: custom_values = {} + if custom_values is None: + custom_values = {} val = msg.get('from').split('<')[0] defaults = { 'name': msg.get('subject') or _("No Subject"), @@ -344,38 +347,7 @@ class hr_applicant(osv.Model): if msg.get('priority'): defaults['priority'] = msg.get('priority') defaults.update(custom_values) - return super(hr_applicant,self).message_new(cr, uid, msg, custom_values=defaults, context=context) - - def message_update(self, cr, uid, ids, msg, update_vals=None, context=None): - """ Override mail_thread message_update that is called by the mailgateway - through message_process. - This method updates the document according to the email. - """ - if isinstance(ids, (str, int, long)): - ids = [ids] - if update_vals is None: - update_vals = {} - - update_vals.update({ - 'email_from': msg.get('from'), - 'email_cc': msg.get('cc'), - }) - if msg.get('priority'): - update_vals['priority'] = msg.get('priority') - - maps = { - 'cost': 'planned_cost', - 'revenue': 'planned_revenue', - 'probability': 'probability', - } - for line in msg.get('body', '').split('\n'): - line = line.strip() - res = tools.command_re.match(line) - if res and maps.get(res.group(1).lower(), False): - key = maps.get(res.group(1).lower()) - update_vals[key] = res.group(2).lower() - - return super(hr_applicant, self).message_update(cr, uid, ids, msg, update_vals=update_vals, context=context) + return super(hr_applicant, self).message_new(cr, uid, msg, custom_values=defaults, context=context) def create(self, cr, uid, vals, context=None): if context is None: diff --git a/addons/hr_recruitment/hr_recruitment_data.xml b/addons/hr_recruitment/hr_recruitment_data.xml index 854dea0548f..219f36405b5 100644 --- a/addons/hr_recruitment/hr_recruitment_data.xml +++ b/addons/hr_recruitment/hr_recruitment_data.xml @@ -71,12 +71,14 @@ <record model="hr.recruitment.stage" id="stage_job5"> <field name="name">Contract Signed</field> <field name="sequence">5</field> + <field name="fold" eval="True"/> </record> <record model="hr.recruitment.stage" id="stage_job6"> <field name="name">Refused</field> <field name="sequence">6</field> <field name="fold" eval="True"/> </record> + <record id="survey_job_0" model="survey"> <field name="title">Job Survey</field> <field name="max_response_limit">20</field> diff --git a/addons/hr_recruitment/hr_recruitment_view.xml b/addons/hr_recruitment/hr_recruitment_view.xml index 3bf09d21a17..7553a24ff3c 100644 --- a/addons/hr_recruitment/hr_recruitment_view.xml +++ b/addons/hr_recruitment/hr_recruitment_view.xml @@ -342,6 +342,7 @@ <field name="sequence" invisible="1"/> <field name="name"/> <field name="department_id"/> + <field name="fold"/> </tree> </field> </record> diff --git a/addons/hr_recruitment/report/hr_recruitment_report.py b/addons/hr_recruitment/report/hr_recruitment_report.py index 0292875a2c7..908b701d928 100644 --- a/addons/hr_recruitment/report/hr_recruitment_report.py +++ b/addons/hr_recruitment/report/hr_recruitment_report.py @@ -90,7 +90,7 @@ class hr_recruitment_report(osv.Model): (sum(salary_proposed)/count(*)) as salary_prop_avg, sum(salary_expected) as salary_exp, (sum(salary_expected)/count(*)) as salary_exp_avg, - extract('epoch' from (s.date_closed-s.create_date))/(3600*24) as delay_close, + extract('epoch' from (s.write_date-s.create_date))/(3600*24) as delay_close, count(*) as nbr from hr_applicant s group by @@ -101,6 +101,7 @@ class hr_recruitment_report(osv.Model): date_trunc('day',s.date_closed), s.date_open, s.create_date, + s.write_date, s.date_closed, s.date_last_stage_update, s.partner_id, diff --git a/addons/hr_timesheet/hr_timesheet_view.xml b/addons/hr_timesheet/hr_timesheet_view.xml index 3d9e39e986f..0cd8a880a68 100644 --- a/addons/hr_timesheet/hr_timesheet_view.xml +++ b/addons/hr_timesheet/hr_timesheet_view.xml @@ -81,7 +81,8 @@ </record> <record model="ir.actions.act_window" id="act_analytic_cost_revenue"> - <field name="context">{'search_default_account_id': [active_id], 'default_account_id': active_id, 'search_default_group_date': 1, 'search_default_group_journal': 1}</field> + <field name="context">{'search_default_group_date': 1, 'search_default_group_journal': 1}</field> + <field name="domain">[('account_id','child_of', active_id)]</field> <field name="name">Costs & Revenues</field> <field name="res_model">account.analytic.line</field> <field name="src_model">account.analytic.account</field> diff --git a/addons/hr_timesheet_sheet/report/timesheet_report.py b/addons/hr_timesheet_sheet/report/timesheet_report.py index 16889253a68..6d284ac08c8 100644 --- a/addons/hr_timesheet_sheet/report/timesheet_report.py +++ b/addons/hr_timesheet_sheet/report/timesheet_report.py @@ -95,7 +95,7 @@ class timesheet_report(osv.osv): htss.state from account_analytic_line as aal left join hr_analytic_timesheet as hat ON (hat.line_id=aal.id) - left join hr_timesheet_sheet_sheet as htss ON (hat.line_id=htss.id) + left join hr_timesheet_sheet_sheet as htss ON (hat.sheet_id=htss.id) group by aal.account_id, aal.date, diff --git a/addons/im/static/src/js/im.js b/addons/im/static/src/js/im.js index 6fbf452f974..f4b74adebf6 100644 --- a/addons/im/static/src/js/im.js +++ b/addons/im/static/src/js/im.js @@ -62,16 +62,12 @@ this.user_search_dm = new instance.web.DropMisordered(); }, start: function() { + var self = this; this.$el.css("right", -this.$el.outerWidth()); $(window).scroll(_.bind(this.calc_box, this)); $(window).resize(_.bind(this.calc_box, this)); this.calc_box(); - this.on("change:current_search", this, this.search_changed); - this.search_changed(); - - var self = this; - return this.c_manager.start_polling().then(function() { self.c_manager.on("new_conversation", self, function(conv) { conv.$el.droppable({ @@ -80,6 +76,7 @@ } }); }); + self.search_changed(); }); }, calc_box: function() { @@ -174,4 +171,4 @@ openerp.webclient.to_kitten(); }; -})();
\ No newline at end of file +})(); diff --git a/addons/l10n_be/__openerp__.py b/addons/l10n_be/__openerp__.py index cd822045e64..8e607ba3120 100644 --- a/addons/l10n_be/__openerp__.py +++ b/addons/l10n_be/__openerp__.py @@ -65,6 +65,7 @@ Wizards provided by this module: 'account_pcmn_belgium.xml', 'account_tax_code_template.xml', 'account_chart_template.xml', + 'account_chart_template.yml', 'account_tax_template.xml', 'wizard/l10n_be_account_vat_declaration_view.xml', 'wizard/l10n_be_vat_intra_view.xml', diff --git a/addons/l10n_be/account_chart_template.xml b/addons/l10n_be/account_chart_template.xml index 48e506ded79..1de39382624 100644 --- a/addons/l10n_be/account_chart_template.xml +++ b/addons/l10n_be/account_chart_template.xml @@ -12,7 +12,6 @@ <field name="property_account_payable" ref="a_pay"/> <field name="property_account_expense_categ" ref="a_expense"/> <field name="property_account_income_categ" ref="a_sale"/> - <field name="spoken_languages" eval="'nl_BE'"/> <field name="currency_id" ref="base.EUR"/> </record> diff --git a/addons/l10n_be/account_chart_template.yml b/addons/l10n_be/account_chart_template.yml new file mode 100644 index 00000000000..0028576f964 --- /dev/null +++ b/addons/l10n_be/account_chart_template.yml @@ -0,0 +1,4 @@ +- + !python {model: account.chart.template}: | + if 'spoken_languages' in self._all_columns: + self.write(cr, uid, [ref('l10nbe_chart_template')], {'spoken_languages': 'nl_BE'}) diff --git a/addons/l10n_be/wizard/l10n_be_account_vat_declaration.py b/addons/l10n_be/wizard/l10n_be_account_vat_declaration.py index 0576e1d5f8f..2ef1a81fef2 100644 --- a/addons/l10n_be/wizard/l10n_be_account_vat_declaration.py +++ b/addons/l10n_be/wizard/l10n_be_account_vat_declaration.py @@ -185,7 +185,7 @@ class l10n_be_vat_declaration(osv.osv_memory): for item in cases_list: grid_amount_data = { 'code': str(int(item['code'])), - 'amount': str(abs(item['sum_period'])), + 'amount': '%.2f' % abs(item['sum_period']), } data_of_file += '\n\t\t\t<ns2:Amount GridNumber="%(code)s">%(amount)s</ns2:Amount''>' % (grid_amount_data) diff --git a/addons/l10n_ch/sterchi_chart/account.xml b/addons/l10n_ch/sterchi_chart/account.xml index acee99cad4a..11198c43b9c 100644 --- a/addons/l10n_ch/sterchi_chart/account.xml +++ b/addons/l10n_ch/sterchi_chart/account.xml @@ -177,14 +177,12 @@ <record model="account.account.type" id="account_type_receivable"> <field name="name">Bilan : Debiteurs</field> <field name="code">receivable</field> - <field name="partner_account" eval="True"/> <field name="close_method">unreconciled</field> <field name="report_type">none</field> </record> <record model="account.account.type" id="account_type_payable"> <field name="name">Bilan : Fournisseurs</field> <field name="code">payable</field> - <field name="partner_account" eval="True"/> <field name="close_method">unreconciled</field> <field name="report_type">none</field> </record> @@ -11809,7 +11807,6 @@ <field name="property_account_income" ref="ch_3200"/> <field name="property_account_income_opening" ref="ch_2990"/> <field name="property_account_expense_opening" ref="ch_2990"/> - <field name="property_reserve_and_surplus_account" ref="ch_2991"/> <field name="code_digits" eval="0"/> <field name="spoken_languages" eval="'it_IT;de_DE'"/> <field name="currency_id" ref="base.CHF"/> diff --git a/addons/l10n_es/taxes_data_assoc.xml b/addons/l10n_es/taxes_data_assoc.xml index e2ebb47fb59..152b27363af 100644 --- a/addons/l10n_es/taxes_data_assoc.xml +++ b/addons/l10n_es/taxes_data_assoc.xml @@ -44,7 +44,6 @@ <!-- Exentas IVA --> <record id="base_assoc_sop_ex" model="account.tax.code.template"> <field name="name">Base adquisiciones exentas</field> - <field name="company_id" model="res.company" search="[]"/> <field name="parent_id" ref="vat_code_chart_assoc_root"/> <field name="code">--</field> <field name="sign">1.0</field> @@ -53,7 +52,6 @@ <record id="base_assoc_rep_ex" model="account.tax.code.template"> <field name="name">Base ventas exentas</field> <field name="code">--</field> - <field name="company_id" model="res.company" search="[]"/> <field name="parent_id" ref="vat_code_chart_assoc_root"/> <field name="sign">1.0</field> </record> diff --git a/addons/l10n_fr/fr_tax.xml b/addons/l10n_fr/fr_tax.xml index 7c75136bdd3..8633cca7141 100644 --- a/addons/l10n_fr/fr_tax.xml +++ b/addons/l10n_fr/fr_tax.xml @@ -219,8 +219,8 @@ <field name="tax_code_id" ref="tax_acq_196"/> <field name="tax_sign" eval="-1"/> - <field name="account_collected_id" ref="pcg_445662"/> - <field name="account_paid_id" ref="pcg_445662"/> + <field name="account_collected_id" ref="pcg_44566"/> + <field name="account_paid_id" ref="pcg_44566"/> <field name="ref_base_code_id" ref="tax_acq_196_ht"/> <field name="ref_base_sign" eval="1"/> diff --git a/addons/l10n_in_hr_payroll/data/hr.salary.rule.csv b/addons/l10n_in_hr_payroll/data/hr.salary.rule.csv index 2fe1ba739f4..043ba8f0522 100644 --- a/addons/l10n_in_hr_payroll/data/hr.salary.rule.csv +++ b/addons/l10n_in_hr_payroll/data/hr.salary.rule.csv @@ -1,5 +1,5 @@ -"id","amount_select","condition_range_min","condition_range_max","amount_percentage","amount_fix","name","category_id","sequence","code","parent_rule_id/id","condition_select","condition_range","amount_percentage_base" -1,"fix",1,1,,100,"Education Allowance For One Child","Allowance",23,"CHEAONE","hr_payroll_rule_child1","range","employee.children", -2,"fix",2,10,,200,"Education Allowance For Two Child","Allowance",24,"CHEATWO","hr_payroll_rule_child1","range","employee.children", -3,"fix",1,1,,300,"Child Hostel Allowance For One Child","Allowance",26,"CHOONE","hr_payroll_rule_child2","range","employee.children", -4,"fix",2,10,,600,"Child Hostel Allowance For Two Child","Allowance",27,"CHOTWO","hr_payroll_rule_child2","range","employee.children", +"id","amount_select","condition_range_min","condition_range_max","amount_percentage","amount_fix","name","category_id/id","sequence","code","parent_rule_id/id","condition_select","condition_range","amount_percentage_base" +1,"fix",1,1,,100,"Education Allowance For One Child",hr_payroll.ALW,23,"CHEAONE","hr_payroll_rule_child1","range","employee.children", +2,"fix",2,10,,200,"Education Allowance For Two Child",hr_payroll.ALW,24,"CHEATWO","hr_payroll_rule_child1","range","employee.children", +3,"fix",1,1,,300,"Child Hostel Allowance For One Child",hr_payroll.ALW,26,"CHOONE","hr_payroll_rule_child2","range","employee.children", +4,"fix",2,10,,600,"Child Hostel Allowance For Two Child",hr_payroll.ALW,27,"CHOTWO","hr_payroll_rule_child2","range","employee.children", diff --git a/addons/l10n_multilang/l10n_multilang.py b/addons/l10n_multilang/l10n_multilang.py index b7564a9960a..937ba332694 100644 --- a/addons/l10n_multilang/l10n_multilang.py +++ b/addons/l10n_multilang/l10n_multilang.py @@ -33,6 +33,7 @@ class wizard_multi_charts_accounts(osv.osv_memory): """ _inherit = 'wizard.multi.charts.accounts' + # FIXME: in trunk, drop the force_write param entirely def process_translations(self, cr, uid, langs, in_obj, in_field, in_ids, out_obj, out_ids, force_write=False, context=None): """ This method copies translations values of templates into new Accounts/Taxes/Journals for languages selected @@ -45,8 +46,7 @@ class wizard_multi_charts_accounts(osv.osv_memory): :param in_ids: List of ids of source object :param out_obj: Destination object for which translation is to be copied :param out_ids: List of ids of destination object - :param force_write: boolean that depicts if we need to create a translation OR simply replace the actual value - with the translation in the uid's language by doing a write (in case it's TRUE) + :param force_write: Deprecated as of 7.0, do not use :param context: usual context information. May contain the key 'lang', which is the language of the user running the wizard, that will be used if force_write is True @@ -65,26 +65,25 @@ class wizard_multi_charts_accounts(osv.osv_memory): for j in range(len(in_ids)): in_id = in_ids[j] if value[in_id]: - if not force_write: - #copy Translation from Source to Destination object - xlat_obj.create(cr, uid, { - 'name': out_obj._name + ',' + in_field, - 'type': 'model', - 'res_id': out_ids[j], - 'lang': lang, - 'src': src[in_id], - 'value': value[in_id], + #copy Translation from Source to Destination object + xlat_obj.create(cr, uid, { + 'name': out_obj._name + ',' + in_field, + 'type': 'model', + 'res_id': out_ids[j], + 'lang': lang, + 'src': src[in_id], + 'value': value[in_id], }) - else: - #replace the value in the destination object only if it's the user lang - if context.get('lang') == lang: - self.pool[out_obj._name].write(cr, uid, out_ids[j], {in_field: value[in_id]}) else: _logger.info('Language: %s. Translation from template: there is no translation available for %s!' %(lang, src[in_id]))#out_obj._name)) return True def execute(self, cr, uid, ids, context=None): - res = super(wizard_multi_charts_accounts, self).execute(cr, uid, ids, context=context) + if not context: + context = {} + # remove the lang to get the untranslated value + ctx = dict(context, lang=None) + res = super(wizard_multi_charts_accounts, self).execute(cr, uid, ids, context=ctx) obj_multi = self.browse(cr, uid, ids[0], context=context) company_id = obj_multi.company_id.id @@ -125,7 +124,7 @@ class wizard_multi_charts_accounts(osv.osv_memory): acc_root_id = obj_acc.search(cr, uid, [('company_id', '=', company_id), ('parent_id', '=', None)])[0] in_ids = obj_acc_template.search(cr, uid, [('id', 'child_of', [acc_template_root_id])], order='id')[1:] out_ids = obj_acc.search(cr, uid, [('id', 'child_of', [acc_root_id])], order='id')[1:] - return self.process_translations(cr, uid, langs, obj_acc_template, field, in_ids, obj_acc, out_ids, force_write=True, context=context) + return self.process_translations(cr, uid, langs, obj_acc_template, field, in_ids, obj_acc, out_ids, context=context) def _process_tax_codes_translations(self, cr, uid, obj_multi, company_id, langs, field, context=None): obj_tax_code_template = self.pool.get('account.tax.code.template') @@ -134,21 +133,21 @@ class wizard_multi_charts_accounts(osv.osv_memory): tax_code_root_id = obj_tax_code.search(cr, uid, [('company_id', '=', company_id), ('parent_id', '=', None)])[0] in_ids = obj_tax_code_template.search(cr, uid, [('id', 'child_of', [tax_code_template_root_id])], order='id')[1:] out_ids = obj_tax_code.search(cr, uid, [('id', 'child_of', [tax_code_root_id])], order='id')[1:] - return self.process_translations(cr, uid, langs, obj_tax_code_template, field, in_ids, obj_tax_code, out_ids, force_write=False, context=context) + return self.process_translations(cr, uid, langs, obj_tax_code_template, field, in_ids, obj_tax_code, out_ids, context=context) def _process_taxes_translations(self, cr, uid, obj_multi, company_id, langs, field, context=None): obj_tax_template = self.pool.get('account.tax.template') obj_tax = self.pool.get('account.tax') in_ids = sorted([x.id for x in obj_multi.chart_template_id.tax_template_ids]) out_ids = obj_tax.search(cr, uid, [('company_id', '=', company_id)], order='id') - return self.process_translations(cr, uid, langs, obj_tax_template, field, in_ids, obj_tax, out_ids, force_write=False, context=context) + return self.process_translations(cr, uid, langs, obj_tax_template, field, in_ids, obj_tax, out_ids, context=context) def _process_fiscal_pos_translations(self, cr, uid, obj_multi, company_id, langs, field, context=None): obj_fiscal_position_template = self.pool.get('account.fiscal.position.template') obj_fiscal_position = self.pool.get('account.fiscal.position') in_ids = obj_fiscal_position_template.search(cr, uid, [('chart_template_id', '=', obj_multi.chart_template_id.id)], order='id') out_ids = obj_fiscal_position.search(cr, uid, [('company_id', '=', company_id)], order='id') - return self.process_translations(cr, uid, langs, obj_fiscal_position_template, field, in_ids, obj_fiscal_position, out_ids, force_write=False, context=context) + return self.process_translations(cr, uid, langs, obj_fiscal_position_template, field, in_ids, obj_fiscal_position, out_ids, context=context) # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/l10n_mx/data/account_chart.xml b/addons/l10n_mx/data/account_chart.xml index daa564fd10e..c28d8da8a44 100644 --- a/addons/l10n_mx/data/account_chart.xml +++ b/addons/l10n_mx/data/account_chart.xml @@ -3693,8 +3693,6 @@ Cuentas del plan <field name="property_account_payable" ref="cuenta2122001000"/> <field name="property_account_expense_categ" ref="cuenta6111001000"/> <field name="property_account_income_categ" ref="cuenta5111001000"/> - <field name="property_stock_account_input_categ" ref="cuenta4111003000"/> - <field name="property_stock_account_output_categ" ref="cuenta4511003000"/> <field name="currency_id" ref="base.MXN"/> </record> diff --git a/addons/l10n_pt/account_chart_template.xml b/addons/l10n_pt/account_chart_template.xml index 49adfa393ef..08984b8d2af 100644 --- a/addons/l10n_pt/account_chart_template.xml +++ b/addons/l10n_pt/account_chart_template.xml @@ -15,7 +15,6 @@ <field name="property_account_income" ref="chart_711"/> <field name="property_account_income_categ" ref="chart_711"/> <field name="property_account_expense_categ" ref="chart_311"/> - <field name="property_reserve_and_surplus_account" ref="chart_811"/> <field name="currency_id" ref="base.EUR"/> </record> diff --git a/addons/l10n_us/account_chart_template.xml b/addons/l10n_us/account_chart_template.xml index 704afbad152..0adeddc5856 100644 --- a/addons/l10n_us/account_chart_template.xml +++ b/addons/l10n_us/account_chart_template.xml @@ -6,7 +6,6 @@ <field name="name">Basic Chart of Account</field> <field name="visible" eval="False" /> <field name="complete_tax_set" eval="False" /> - <field name="currency_id" ref="base.USD"/> </record> <record id="account_chart_template_cogs" model="account.chart.template"> diff --git a/addons/l10n_ve/data/account_tax.xml b/addons/l10n_ve/data/account_tax.xml index 7acbda3e2af..8fb1aa4b819 100644 --- a/addons/l10n_ve/data/account_tax.xml +++ b/addons/l10n_ve/data/account_tax.xml @@ -7,8 +7,8 @@ <field name="amount">0.00000</field> <field name="type">percent</field> <field name="type_tax_use">all</field> - <field name="collected_id" ref="account_activa_account_2172003"/> - <field name="paid_id" ref="account_activa_account_2172003"/> + <field name="account_collected_id" ref="account_activa_account_2172003"/> + <field name="account_paid_id" ref="account_activa_account_2172003"/> <field name="base_code_id" ref="vat_code_base_due"/> <field name="tax_code_id" ref="vat_code_due_tva"/> <field name="ref_base_code_id" ref="vat_code_receivable_net"/> @@ -21,8 +21,8 @@ <field name="amount">0.120000</field> <field name="type">percent</field> <field name="type_tax_use">sale</field> - <field name="collected_id" ref="account_activa_account_2172003"/> - <field name="paid_id" ref="account_activa_account_2172003"/> + <field name="account_collected_id" ref="account_activa_account_2172003"/> + <field name="account_paid_id" ref="account_activa_account_2172003"/> <field name="base_code_id" ref="vat_code_base_due"/> <field name="tax_code_id" ref="vat_code_due_tva"/> <field name="ref_base_code_id" ref="vat_code_receivable_net"/> @@ -34,8 +34,8 @@ <field name="amount">0.080000</field> <field name="type">percent</field> <field name="type_tax_use">sale</field> - <field name="collected_id" ref="account_activa_account_2172003"/> - <field name="paid_id" ref="account_activa_account_2172003"/> + <field name="account_collected_id" ref="account_activa_account_2172003"/> + <field name="account_paid_id" ref="account_activa_account_2172003"/> <field name="base_code_id" ref="vat_code_base_due"/> <field name="tax_code_id" ref="vat_code_due_tva"/> <field name="ref_base_code_id" ref="vat_code_receivable_net"/> @@ -47,8 +47,8 @@ <field name="amount">0.220000</field> <field name="type">percent</field> <field name="type_tax_use">sale</field> - <field name="collected_id" ref="account_activa_account_2172003"/> - <field name="paid_id" ref="account_activa_account_2172003"/> + <field name="account_collected_id" ref="account_activa_account_2172003"/> + <field name="account_paid_id" ref="account_activa_account_2172003"/> <field name="base_code_id" ref="vat_code_base_due"/> <field name="tax_code_id" ref="vat_code_due_tva"/> <field name="ref_base_code_id" ref="vat_code_receivable_net"/> @@ -60,8 +60,8 @@ <field name="amount">0.120000</field> <field name="type">percent</field> <field name="type_tax_use">purchase</field> - <field name="collected_id" ref="account_activa_account_1151004"/> - <field name="paid_id" ref="account_activa_account_1151004"/> + <field name="account_collected_id" ref="account_activa_account_1151004"/> + <field name="account_paid_id" ref="account_activa_account_1151004"/> <field name="base_code_id" ref="vat_code_base_due"/> <field name="tax_code_id" ref="vat_code_due_tva"/> <field name="ref_base_code_id" ref="vat_code_receivable_net"/> @@ -73,8 +73,8 @@ <field name="amount">0.080000</field> <field name="type">percent</field> <field name="type_tax_use">purchase</field> - <field name="collected_id" ref="account_activa_account_1151004"/> - <field name="paid_id" ref="account_activa_account_1151004"/> + <field name="account_collected_id" ref="account_activa_account_1151004"/> + <field name="account_paid_id" ref="account_activa_account_1151004"/> <field name="base_code_id" ref="vat_code_base_due"/> <field name="tax_code_id" ref="vat_code_due_tva"/> <field name="ref_base_code_id" ref="vat_code_receivable_net"/> @@ -86,8 +86,8 @@ <field name="amount">0.220000</field> <field name="type">percent</field> <field name="type_tax_use">purchase</field> - <field name="collected_id" ref="account_activa_account_1151004"/> - <field name="paid_id" ref="account_activa_account_1151004"/> + <field name="account_collected_id" ref="account_activa_account_1151004"/> + <field name="account_paid_id" ref="account_activa_account_1151004"/> <field name="base_code_id" ref="vat_code_base_due"/> <field name="tax_code_id" ref="vat_code_due_tva"/> <field name="ref_base_code_id" ref="vat_code_receivable_net"/> diff --git a/addons/mail/mail_message.py b/addons/mail/mail_message.py index 72ccdc83bb6..de8e4c68fda 100644 --- a/addons/mail/mail_message.py +++ b/addons/mail/mail_message.py @@ -698,8 +698,9 @@ class mail_message(osv.Model): """ model_record_ids = {} for id in msg_ids: - if msg_val[id]['model'] and msg_val[id]['res_id']: - model_record_ids.setdefault(msg_val[id]['model'], dict()).setdefault(msg_val[id]['res_id'], set()).add(msg_val[id]['res_id']) + vals = msg_val.get(id, {}) + if vals.get('model') and vals.get('res_id'): + model_record_ids.setdefault(vals['model'], set()).add(vals['res_id']) return model_record_ids if uid == SUPERUSER_ID: @@ -711,7 +712,7 @@ class mail_message(osv.Model): partner_id = self.pool['res.users'].browse(cr, SUPERUSER_ID, uid, context=None).partner_id.id # Read mail_message.ids to have their values - message_values = dict.fromkeys(ids) + message_values = dict.fromkeys(ids, {}) cr.execute('SELECT DISTINCT id, model, res_id, author_id, parent_id FROM "%s" WHERE id = ANY (%%s)' % self._table, (ids,)) for id, rmod, rid, author_id, parent_id in cr.fetchall(): message_values[id] = {'model': rmod, 'res_id': rid, 'author_id': author_id, 'parent_id': parent_id} @@ -745,10 +746,10 @@ class mail_message(osv.Model): ], context=context) notified_ids = [notification.message_id.id for notification in not_obj.browse(cr, SUPERUSER_ID, not_ids, context=context)] elif operation == 'create': - for doc_model, doc_dict in model_record_ids.items(): + for doc_model, doc_ids in model_record_ids.items(): fol_ids = fol_obj.search(cr, SUPERUSER_ID, [ ('res_model', '=', doc_model), - ('res_id', 'in', list(doc_dict.keys())), + ('res_id', 'in', list(doc_ids)), ('partner_id', '=', partner_id), ], context=context) fol_mids = [follower.res_id for follower in fol_obj.browse(cr, SUPERUSER_ID, fol_ids, context=context)] @@ -759,9 +760,9 @@ class mail_message(osv.Model): other_ids = other_ids.difference(set(notified_ids)) model_record_ids = _generate_model_record_ids(message_values, other_ids) document_related_ids = [] - for model, doc_dict in model_record_ids.items(): + for model, doc_ids in model_record_ids.items(): model_obj = self.pool[model] - mids = model_obj.exists(cr, uid, doc_dict.keys()) + mids = model_obj.exists(cr, uid, list(doc_ids)) if hasattr(model_obj, 'check_mail_message_access'): model_obj.check_mail_message_access(cr, uid, mids, operation, context=context) else: diff --git a/addons/mail/mail_thread.py b/addons/mail/mail_thread.py index a5d6d9a3614..dfee2a04d4d 100644 --- a/addons/mail/mail_thread.py +++ b/addons/mail/mail_thread.py @@ -38,6 +38,7 @@ from openerp import tools from openerp import SUPERUSER_ID from openerp.addons.mail.mail_message import decode from openerp.osv import fields, osv, orm +from openerp.osv.orm import browse_record, browse_null from openerp.tools.safe_eval import safe_eval as eval from openerp.tools.translate import _ @@ -355,11 +356,11 @@ class mail_thread(osv.AbstractModel): if not context.get('mail_create_nosubscribe'): self.message_subscribe_users(cr, uid, [thread_id], [uid], context=context) # auto_subscribe: take values and defaults into account - create_values = set(values.keys()) + create_values = dict(values) for key, val in context.iteritems(): if key.startswith('default_'): - create_values.add(key[8:]) - self.message_auto_subscribe(cr, uid, [thread_id], list(create_values), context=context) + create_values[key[8:]] = val + self.message_auto_subscribe(cr, uid, [thread_id], create_values.keys(), context=context, values=create_values) # track values tracked_fields = self._get_tracked_fields(cr, uid, values.keys(), context=context) @@ -380,7 +381,7 @@ class mail_thread(osv.AbstractModel): # Perform write, update followers result = super(mail_thread, self).write(cr, uid, ids, values, context=context) - self.message_auto_subscribe(cr, uid, ids, values.keys(), context=context) + self.message_auto_subscribe(cr, uid, ids, values.keys(), context=context, values=values) # Perform the tracking if tracked_fields: @@ -604,7 +605,8 @@ class mail_thread(osv.AbstractModel): return action if msg_id and not (model and res_id): msg = self.pool.get('mail.message').browse(cr, uid, msg_id, context=context) - model, res_id = msg.model, msg.res_id + if msg.exists(): + model, res_id = msg.model, msg.res_id # if model + res_id found: try to redirect to the document or fallback on the Inbox if model and res_id: @@ -1565,75 +1567,100 @@ class mail_thread(osv.AbstractModel): user_field_lst.append(name) return user_field_lst - def message_auto_subscribe(self, cr, uid, ids, updated_fields, context=None): - """ - 1. fetch project subtype related to task (parent_id.res_model = 'project.task') - 2. for each project subtype: subscribe the follower to the task + def message_auto_subscribe(self, cr, uid, ids, updated_fields, context=None, values=None): + """ Handle auto subscription. Two methods for auto subscription exist: + + - tracked res.users relational fields, such as user_id fields. Those fields + must be relation fields toward a res.users record, and must have the + track_visilibity attribute set. + - using subtypes parent relationship: check if the current model being + modified has an header record (such as a project for tasks) whose followers + can be added as followers of the current records. Example of structure + with project and task: + + - st_project_1.parent_id = st_task_1 + - st_project_1.res_model = 'project.project' + - st_project_1.relation_field = 'project_id' + - st_task_1.model = 'project.task' + + :param list updated_fields: list of updated fields to track + :param dict values: updated values; if None, the first record will be browsed + to get the values. Added after releasing 7.0, therefore + not merged with updated_fields argumment. """ subtype_obj = self.pool.get('mail.message.subtype') follower_obj = self.pool.get('mail.followers') + new_followers = dict() - # fetch auto_follow_fields + # fetch auto_follow_fields: res.users relation fields whose changes are tracked for subscription user_field_lst = self._message_get_auto_subscribe_fields(cr, uid, updated_fields, context=context) - # fetch related record subtypes - related_subtype_ids = subtype_obj.search(cr, uid, ['|', ('res_model', '=', False), ('parent_id.res_model', '=', self._name)], context=context) - subtypes = subtype_obj.browse(cr, uid, related_subtype_ids, context=context) - default_subtypes = [subtype for subtype in subtypes if subtype.res_model == False] - related_subtypes = [subtype for subtype in subtypes if subtype.res_model != False] - relation_fields = set([subtype.relation_field for subtype in subtypes if subtype.relation_field != False]) - if (not related_subtypes or not any(relation in updated_fields for relation in relation_fields)) and not user_field_lst: + # fetch header subtypes + header_subtype_ids = subtype_obj.search(cr, uid, ['|', ('res_model', '=', False), ('parent_id.res_model', '=', self._name)], context=context) + subtypes = subtype_obj.browse(cr, uid, header_subtype_ids, context=context) + + # if no change in tracked field or no change in tracked relational field: quit + relation_fields = set([subtype.relation_field for subtype in subtypes if subtype.relation_field is not False]) + if not any(relation in updated_fields for relation in relation_fields) and not user_field_lst: return True - for record in self.browse(cr, uid, ids, context=context): - new_followers = dict() - parent_res_id = False - parent_model = False - for subtype in related_subtypes: - if not subtype.relation_field or not subtype.parent_id: - continue - if not subtype.relation_field in self._columns or not getattr(record, subtype.relation_field, False): - continue - parent_res_id = getattr(record, subtype.relation_field).id - parent_model = subtype.res_model - follower_ids = follower_obj.search(cr, SUPERUSER_ID, [ - ('res_model', '=', parent_model), - ('res_id', '=', parent_res_id), - ('subtype_ids', 'in', [subtype.id]) - ], context=context) - for follower in follower_obj.browse(cr, SUPERUSER_ID, follower_ids, context=context): - new_followers.setdefault(follower.partner_id.id, set()).add(subtype.parent_id.id) - - if parent_res_id and parent_model: - for subtype in default_subtypes: - follower_ids = follower_obj.search(cr, SUPERUSER_ID, [ - ('res_model', '=', parent_model), - ('res_id', '=', parent_res_id), - ('subtype_ids', 'in', [subtype.id]) - ], context=context) - for follower in follower_obj.browse(cr, SUPERUSER_ID, follower_ids, context=context): - new_followers.setdefault(follower.partner_id.id, set()).add(subtype.id) - - # add followers coming from res.users relational fields that are tracked - user_ids = [getattr(record, name).id for name in user_field_lst if getattr(record, name)] - user_id_partner_ids = [user.partner_id.id for user in self.pool.get('res.users').browse(cr, SUPERUSER_ID, user_ids, context=context)] - for partner_id in user_id_partner_ids: - new_followers.setdefault(partner_id, None) - - for pid, subtypes in new_followers.items(): - subtypes = list(subtypes) if subtypes is not None else None - self.message_subscribe(cr, uid, [record.id], [pid], subtypes, context=context) - - # find first email message, set it as unread for auto_subscribe fields for them to have a notification - if user_id_partner_ids: - msg_ids = self.pool.get('mail.message').search(cr, uid, [ - ('model', '=', self._name), - ('res_id', '=', record.id), - ('type', '=', 'email')], limit=1, context=context) - if not msg_ids and record.message_ids: - msg_ids = [record.message_ids[-1].id] + # legacy behavior: if values is not given, compute the values by browsing + # @TDENOTE: remove me in 8.0 + if values is None: + record = self.browse(cr, uid, ids[0], context=context) + for updated_field in updated_fields: + field_value = getattr(record, updated_field) + if isinstance(field_value, browse_record): + field_value = field_value.id + elif isinstance(field_value, browse_null): + field_value = False + values[updated_field] = field_value + + # find followers of headers, update structure for new followers + headers = set() + for subtype in subtypes: + if subtype.relation_field and values.get(subtype.relation_field): + headers.add((subtype.res_model, values.get(subtype.relation_field))) + if headers: + header_domain = ['|'] * (len(headers) - 1) + for header in headers: + header_domain += ['&', ('res_model', '=', header[0]), ('res_id', '=', header[1])] + header_follower_ids = follower_obj.search( + cr, SUPERUSER_ID, + header_domain, + context=context + ) + for header_follower in follower_obj.browse(cr, SUPERUSER_ID, header_follower_ids, context=context): + for subtype in header_follower.subtype_ids: + if subtype.parent_id and subtype.parent_id.res_model == self._name: + new_followers.setdefault(header_follower.partner_id.id, set()).add(subtype.parent_id.id) + elif subtype.res_model is False: + new_followers.setdefault(header_follower.partner_id.id, set()).add(subtype.id) + + # add followers coming from res.users relational fields that are tracked + user_ids = [values[name] for name in user_field_lst if values.get(name)] + user_pids = [user.partner_id.id for user in self.pool.get('res.users').browse(cr, SUPERUSER_ID, user_ids, context=context)] + for partner_id in user_pids: + new_followers.setdefault(partner_id, None) + + for pid, subtypes in new_followers.items(): + subtypes = list(subtypes) if subtypes is not None else None + self.message_subscribe(cr, uid, ids, [pid], subtypes, context=context) + + # find first email message, set it as unread for auto_subscribe fields for them to have a notification + if user_pids: + for record_id in ids: + message_obj = self.pool.get('mail.message') + msg_ids = message_obj.search(cr, SUPERUSER_ID, [ + ('model', '=', self._name), + ('res_id', '=', record_id), + ('type', '=', 'email')], limit=1, context=context) + if not msg_ids: + msg_ids = message_obj.search(cr, SUPERUSER_ID, [ + ('model', '=', self._name), + ('res_id', '=', record_id)], limit=1, context=context) if msg_ids: - self.pool.get('mail.notification')._notify(cr, uid, msg_ids[0], partners_to_notify=user_id_partner_ids, context=context) + self.pool.get('mail.notification')._notify(cr, uid, msg_ids[0], partners_to_notify=user_pids, context=context) return True diff --git a/addons/mail/tests/test_mail_features.py b/addons/mail/tests/test_mail_features.py index 62c76db5a40..c7f1056c59a 100644 --- a/addons/mail/tests/test_mail_features.py +++ b/addons/mail/tests/test_mail_features.py @@ -294,9 +294,9 @@ class test_mail(TestMail): 'notification email: link should contain the user login') self.assertIn('message_id=%s' % mail.mail_message_id.id, url, 'notification email: link based on message should contain the mail_message id') - self.assertNotIn('model', url, + self.assertNotIn('model=mail.group', url, 'notification email: link based on message should not contain model') - self.assertNotIn('res_id', url, + self.assertNotIn('res_id=%s' % group_pigs.id, url, 'notification email: link based on message should not contain res_id') @mute_logger('openerp.addons.mail.mail_thread', 'openerp.osv.orm') diff --git a/addons/marketing_campaign/marketing_campaign_demo.xml b/addons/marketing_campaign/marketing_campaign_demo.xml index 89a299e3a85..a7b1a8bce0c 100644 --- a/addons/marketing_campaign/marketing_campaign_demo.xml +++ b/addons/marketing_campaign/marketing_campaign_demo.xml @@ -5,7 +5,7 @@ <field name="name">welcome new partner</field> <field name="email_from">info@openerp.com</field> <field name="subject">Welcome to the OpenERP Partner Channel!</field> - <field name="email_to">${object.email or ''}</field> + <field name="email_to">${(object.email or '')|safe}</field> <field name="model_id" ref="base.model_res_partner"/> <field name="body_html">Hello, you will receive your welcome pack via email shortly.</field> </record> @@ -13,7 +13,7 @@ <field name="name">congrats silver partner</field> <field name="email_from">info@openerp.com</field> <field name="subject">Congratulations! You are now a Silver Partner!</field> - <field name="email_to">${object.email or ''}</field> + <field name="email_to">${(object.email or '')|safe}</field> <field name="model_id" ref="base.model_res_partner"/> <field name="body_html">Hi, we are delighted to welcome you among our Silver Partners as of today!</field> </record> @@ -22,7 +22,7 @@ <field name="name">congrats gold partner</field> <field name="email_from">info@openerp.com</field> <field name="subject">Congratulations! You are now one of our Gold Partners!</field> - <field name="email_to">${object.email or ''}</field> + <field name="email_to">${(object.email or '')|safe}</field> <field name="model_id" ref="base.model_res_partner"/> <field name="body_html">Hi, we are delighted to let you know that you have entered the select circle of our Gold Partners</field> </record> diff --git a/addons/mrp/mrp.py b/addons/mrp/mrp.py index 1d0a0c98d48..4ef2a6a4f4c 100644 --- a/addons/mrp/mrp.py +++ b/addons/mrp/mrp.py @@ -965,7 +965,10 @@ class mrp_production(osv.osv): partner_id = routing_loc.partner_id and routing_loc.partner_id.id or False # Take next Sequence number of shipment base on type - pick_name = ir_sequence.get(cr, uid, 'stock.picking.' + pick_type) + if pick_type!='internal': + pick_name = ir_sequence.get(cr, uid, 'stock.picking.' + pick_type) + else: + pick_name = ir_sequence.get(cr, uid, 'stock.picking') picking_id = stock_picking.create(cr, uid, { 'name': pick_name, diff --git a/addons/mrp_byproduct/mrp_byproduct.py b/addons/mrp_byproduct/mrp_byproduct.py index 2425c2d8fea..38058abf071 100644 --- a/addons/mrp_byproduct/mrp_byproduct.py +++ b/addons/mrp_byproduct/mrp_byproduct.py @@ -80,11 +80,11 @@ class mrp_production(osv.osv): _inherit= 'mrp.production' - def action_confirm(self, cr, uid, ids): + def action_confirm(self, cr, uid, ids, context=None): """ Confirms production order and calculates quantity based on subproduct_type. @return: Newly generated picking Id. """ - picking_id = super(mrp_production,self).action_confirm(cr, uid, ids) + picking_id = super(mrp_production,self).action_confirm(cr, uid, ids, context=context) product_uom_obj = self.pool.get('product.uom') for production in self.browse(cr, uid, ids): source = production.product_id.property_stock_production.id diff --git a/addons/pad/pad.py b/addons/pad/pad.py index 4cd9e91b76e..474321edb1d 100644 --- a/addons/pad/pad.py +++ b/addons/pad/pad.py @@ -39,7 +39,11 @@ class pad_common(osv.osv_memory): #if create with content if "field_name" in context and "model" in context and "object_id" in context: myPad = EtherpadLiteClient( pad["key"], pad["server"]+'/api') - myPad.createPad(path) + try: + myPad.createPad(path) + except urllib2.URLError: + raise osv.except_osv(_("Error"), _("Pad creation failed, \ + either there is a problem with your pad server URL or with your connection.")) #get attr on the field model model = self.pool[context["model"]] diff --git a/addons/pad/pad_demo.xml b/addons/pad/pad_demo.xml index 99c4b38481c..5fd76f65e21 100644 --- a/addons/pad/pad_demo.xml +++ b/addons/pad/pad_demo.xml @@ -2,7 +2,7 @@ <data> <record id="base.main_company" model="res.company"> - <field name="pad_server">pad.openerp.com</field> + <field name="pad_server">https://pad.openerp.com</field> </record> <record id="base.main_company" model="res.company"> diff --git a/addons/point_of_sale/point_of_sale.py b/addons/point_of_sale/point_of_sale.py index d32b8e91d3a..a7bc2f5f59e 100644 --- a/addons/point_of_sale/point_of_sale.py +++ b/addons/point_of_sale/point_of_sale.py @@ -694,7 +694,7 @@ class pos_order(osv.osv): def create_picking(self, cr, uid, ids, context=None): """Create a picking for each order and validate it.""" - picking_obj = self.pool.get('stock.picking') + picking_obj = self.pool.get('stock.picking.out') partner_obj = self.pool.get('res.partner') move_obj = self.pool.get('stock.move') diff --git a/addons/point_of_sale/point_of_sale_view.xml b/addons/point_of_sale/point_of_sale_view.xml index 7202b57a1eb..0f26a7c85b9 100644 --- a/addons/point_of_sale/point_of_sale_view.xml +++ b/addons/point_of_sale/point_of_sale_view.xml @@ -56,7 +56,7 @@ <div class="oe_subtotal_footer_separator oe_inline"> <label for="amount_total" /> <button name="button_dummy" - states="draft" string="(update)" type="object" class="oe_edit_only oe_link"/> + states="draft" string="(update)" class="oe_edit_only oe_link"/> </div> <field name="amount_total" nolabel="1" class="oe_subtotal_footer_separator"/> </group> diff --git a/addons/point_of_sale/static/src/img/open-cashbox.png.moved b/addons/point_of_sale/static/src/img/open-cashbox.png.moved Binary files differnew file mode 100644 index 00000000000..1e07b0a9282 --- /dev/null +++ b/addons/point_of_sale/static/src/img/open-cashbox.png.moved diff --git a/addons/portal/mail_message.py b/addons/portal/mail_message.py index 9699b3a9ef6..4429df3be8b 100644 --- a/addons/portal/mail_message.py +++ b/addons/portal/mail_message.py @@ -19,6 +19,7 @@ # ############################################################################## +from openerp import SUPERUSER_ID from openerp.osv import osv, orm from openerp.tools.translate import _ @@ -32,6 +33,9 @@ class mail_message(osv.Model): """ Override that adds specific access rights of mail.message, to remove all internal notes if uid is a non-employee """ + if uid == SUPERUSER_ID: + return super(mail_message, self)._search(cr, uid, args, offset=offset, limit=limit, order=order, + context=context, count=False, access_rights_uid=access_rights_uid) group_ids = self.pool.get('res.users').browse(cr, uid, uid, context=context).groups_id group_user_id = self.pool.get("ir.model.data").get_object_reference(cr, uid, 'base', 'group_user')[1] if group_user_id not in [group.id for group in group_ids]: @@ -45,6 +49,8 @@ class mail_message(osv.Model): - read: - raise if the type is comment and subtype NULL (internal note) """ + if uid == SUPERUSER_ID: + return super(mail_message, self).check_access_rule(cr, uid, ids=ids, operation=operation, context=context) group_ids = self.pool.get('res.users').browse(cr, uid, uid, context=context).groups_id group_user_id = self.pool.get("ir.model.data").get_object_reference(cr, uid, 'base', 'group_user')[1] if group_user_id not in [group.id for group in group_ids]: diff --git a/addons/portal/tests/test_portal.py b/addons/portal/tests/test_portal.py index f5eeba0aa62..12c64d7588c 100644 --- a/addons/portal/tests/test_portal.py +++ b/addons/portal/tests/test_portal.py @@ -73,6 +73,9 @@ class test_portal(TestMail): for message in chell_pigs.message_ids: trigger_read = message.subject for partner in chell_pigs.message_follower_ids: + if partner.id == self.partner_chell_id: + # Chell can read her own partner record + continue with self.assertRaises(except_orm): trigger_read = partner.name diff --git a/addons/portal_claim/security/portal_security.xml b/addons/portal_claim/security/portal_security.xml index af5261c408b..1f96da4ef9e 100644 --- a/addons/portal_claim/security/portal_security.xml +++ b/addons/portal_claim/security/portal_security.xml @@ -1,17 +1,15 @@ <?xml version="1.0" encoding="utf-8"?> <openerp> - <data noupdate="1"> - + <data> <record id="portal_claim_rule" model="ir.rule"> <field name="name">Portal Personal Claims</field> <field ref="crm_claim.model_crm_claim" name="model_id"/> - <field name="domain_force">[('message_follower_ids','in', [user.partner_id.id])]</field> + <field name="domain_force">['|', ('message_follower_ids','in', [user.partner_id.id]), ('partner_id','=', user.partner_id.id)]</field> <field name="groups" eval="[(4, ref('portal.group_portal'))]"/> - <field eval="1" name="perm_unlink"/> - <field eval="1" name="perm_write"/> + <field eval="0" name="perm_unlink"/> + <field eval="0" name="perm_write"/> <field eval="1" name="perm_read"/> - <field eval="0" name="perm_create"/> + <field eval="1" name="perm_create"/> </record> - </data> </openerp> diff --git a/addons/portal_hr_employees/hr_employee.py b/addons/portal_hr_employees/hr_employee.py index 52d01170a2a..b6d3ed1a7b5 100644 --- a/addons/portal_hr_employees/hr_employee.py +++ b/addons/portal_hr_employees/hr_employee.py @@ -24,7 +24,6 @@ from openerp.osv import fields, osv class crm_contact_us(osv.TransientModel): """ Add employees list to the portal's contact page """ _inherit = 'portal_crm.crm_contact_us' - _description = 'Contact form for the portal' _columns = { 'employee_ids' : fields.many2many('hr.employee', string='Employees', readonly=True), } @@ -40,7 +39,6 @@ class crm_contact_us(osv.TransientModel): } class hr_employee(osv.osv): - _description = 'Portal employee' _inherit = 'hr.employee' """ diff --git a/addons/portal_sale/portal_sale_data.xml b/addons/portal_sale/portal_sale_data.xml index 2cd7ca8b501..750ae12995f 100644 --- a/addons/portal_sale/portal_sale_data.xml +++ b/addons/portal_sale/portal_sale_data.xml @@ -6,7 +6,7 @@ <!--Email template --> <record id="email_template_edi_sale" model="email.template"> <field name="name">Sales Order - Send by Email (Portal)</field> - <field name="email_from">${object.user_id.email or ''}</field> + <field name="email_from">${(object.user_id.email or '')|safe}</field> <field name="subject">${object.company_id.name} ${object.state in ('draft', 'sent') and 'Quotation' or 'Order'} (Ref ${object.name or 'n/a' })</field> <field name="partner_to">${object.partner_invoice_id.id}</field> <field name="model_id" ref="sale.model_sale_order"/> @@ -95,7 +95,7 @@ <record id="email_template_edi_invoice" model="email.template"> <field name="name">Invoice - Send by Email (Portal)</field> - <field name="email_from">${object.user_id.email or object.company_id.email or 'noreply@localhost'}</field> + <field name="email_from">${(object.user_id.email or object.company_id.email or 'noreply@localhost')|safe}</field> <field name="subject">${object.company_id.name} Invoice (Ref ${object.number or 'n/a' })</field> <field name="partner_to">${object.partner_id.id}</field> <field name="model_id" ref="account.model_account_invoice"/> diff --git a/addons/procurement/procurement.py b/addons/procurement/procurement.py index 032952aa511..c2878df69cb 100644 --- a/addons/procurement/procurement.py +++ b/addons/procurement/procurement.py @@ -181,10 +181,6 @@ class procurement_order(osv.osv): """ return all(procurement.move_id.state == 'cancel' for procurement in self.browse(cr, uid, ids, context=context)) - #This Function is create to avoid a server side Error Like 'ERROR:tests.mrp:name 'check_move' is not defined' - def check_move(self, cr, uid, ids, context=None): - pass - def check_move_done(self, cr, uid, ids, context=None): """ Checks if move is done or not. @return: True or False. @@ -297,6 +293,12 @@ class procurement_order(osv.osv): """ return False + def check_move(self, cr, uid, ids, context=None): + """ Check whether the given procurement can be satisfied by an internal move, + typically a pulled flow. By default, it's False. Overwritten by the `stock_location` module. + """ + return False + def check_conditions_confirm2wait(self, cr, uid, ids): """ condition on the transition to go from 'confirm' activity to 'confirm_wait' activity """ return not self.test_cancel(cr, uid, ids) diff --git a/addons/procurement/procurement_workflow.xml b/addons/procurement/procurement_workflow.xml index 76b54370ef2..d4068a6eada 100644 --- a/addons/procurement/procurement_workflow.xml +++ b/addons/procurement/procurement_workflow.xml @@ -147,13 +147,15 @@ <record id="trans_confirm_mto_make_done" model="workflow.transition"> <!-- This transition is there to unblock products that would be in MTO with a supply method that would be - produce or buy without MRP or purchase module installed. These modules overwrite the check_produce() + produce or buy, and without MRP or Purchase modules installed. These modules overwrite the check_produce() and check_buy() methods -so that it invalidates their part of this 'bypass transition'-, and define their own workflow paths. + The stock_location module also introduces a check_move() alternative, for pulled flows that are + satisfied with an internal product move. This yields a threefold test for the bypass transition. --> <field name="act_from" ref="act_confirm_mto"/> <field name="act_to" ref="act_make_done"/> - <field name="condition">not check_produce() and not check_buy()</field> + <field name="condition">not check_produce() and not check_buy() and not check_move()</field> </record> <record id="trans_make_to_stock_make_done" model="workflow.transition"> diff --git a/addons/procurement/schedulers.py b/addons/procurement/schedulers.py index d82fa94c132..0828b4f10df 100644 --- a/addons/procurement/schedulers.py +++ b/addons/procurement/schedulers.py @@ -173,6 +173,7 @@ class procurement_order(osv.osv): 'company_id': orderpoint.company_id.id, 'product_uom': orderpoint.product_uom.id, 'location_id': orderpoint.location_id.id, + 'warehouse_id': orderpoint.warehouse_id.id, 'procure_method': 'make_to_order', 'origin': orderpoint.name} diff --git a/addons/product_visible_discount/product_visible_discount.py b/addons/product_visible_discount/product_visible_discount.py index a2f0a41847f..bd43028b36a 100644 --- a/addons/product_visible_discount/product_visible_discount.py +++ b/addons/product_visible_discount/product_visible_discount.py @@ -86,78 +86,13 @@ class sale_order_line(osv.osv): pricelists = pricelist_obj.read(cr,uid,[pricelist],['visible_discount']) new_list_price = get_real_price(list_price, product.id, qty, uom, pricelist) - if(len(pricelists)>0 and pricelists[0]['visible_discount'] and list_price[pricelist] != 0): + if len(pricelists)>0 and pricelists[0]['visible_discount'] and list_price[pricelist] != 0 and new_list_price != 0: discount = (new_list_price - price) / new_list_price * 100 - result['price_unit'] = new_list_price - result['discount'] = discount - else: - result['discount'] = 0.0 - return res - - -class account_invoice_line(osv.osv): - _inherit = "account.invoice.line" - - def product_id_change(self, cr, uid, ids, product, uom_id, qty=0, name='', type='out_invoice', partner_id=False, fposition_id=False, price_unit=False, currency_id=False, context=None, company_id=None): - res = super(account_invoice_line, self).product_id_change(cr, uid, ids, product, uom_id, qty, name, type, partner_id, fposition_id, price_unit,currency_id, context=context, company_id=company_id) - - def get_real_price(res_dict, product_id, qty, uom_id, pricelist): - item_obj = self.pool.get('product.pricelist.item') - price_type_obj = self.pool.get('product.price.type') - product_obj = self.pool.get('product.product') - field_name = 'list_price' - - if res_dict.get('item_id',False) and res_dict['item_id'].get(pricelist,False): - item = res_dict['item_id'].get(pricelist,False) - item_read = item_obj.read(cr, uid, [item], ['base']) - if item_read: - item_base = item_read[0]['base'] - if item_base > 0: - field_name = price_type_obj.browse(cr, uid, item_base).field - - product = product_obj.browse(cr, uid, product_id, context) - product_read = product_obj.read(cr, uid, product_id, [field_name], context=context) - - factor = 1.0 - if uom_id and uom_id != product.uom_id.id: - product_uom_obj = self.pool.get('product.uom') - uom_data = product_uom_obj.browse(cr, uid, product.uom_id.id) - factor = uom_data.factor - return product_read[field_name] * factor - - if product: - pricelist_obj = self.pool.get('product.pricelist') - partner_obj = self.pool.get('res.partner') - product = self.pool.get('product.product').browse(cr, uid, product, context=context) - result = res['value'] - pricelist = False - real_price = 0.00 - if type in ('in_invoice', 'in_refund'): - if not price_unit and partner_id: - pricelist =partner_obj.browse(cr, uid, partner_id).property_product_pricelist_purchase.id - if not pricelist: - raise osv.except_osv(_('No Purchase Pricelist Found!'),_("You must first define a pricelist on the supplier form!")) - price_unit_res = pricelist_obj.price_get(cr, uid, [pricelist], product.id, qty or 1.0, partner_id, {'uom': uom_id}) - price_unit = price_unit_res[pricelist] - real_price = get_real_price(price_unit_res, product.id, qty, uom_id, pricelist) - else: - if partner_id: - pricelist = partner_obj.browse(cr, uid, partner_id).property_product_pricelist.id - if not pricelist: - raise osv.except_osv(_('No Sale Pricelist Found!'),_("You must first define a pricelist on the customer form!")) - price_unit_res = pricelist_obj.price_get(cr, uid, [pricelist], product.id, qty or 1.0, partner_id, {'uom': uom_id}) - price_unit = price_unit_res[pricelist] - - real_price = get_real_price(price_unit_res, product.id, qty, uom_id, pricelist) - if pricelist: - pricelists=pricelist_obj.read(cr,uid,[pricelist],['visible_discount']) - if(len(pricelists)>0 and pricelists[0]['visible_discount'] and real_price != 0): - discount=(real_price-price_unit) / real_price * 100 - result['price_unit'] = real_price + if discount > 0: + result['price_unit'] = new_list_price result['discount'] = discount else: - result['discount']=0.0 + result['discount'] = 0.0 + else: + result['discount'] = 0.0 return res - - -# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/project/project.py b/addons/project/project.py index 4e2b6c6b17b..ea9c4492fa4 100644 --- a/addons/project/project.py +++ b/addons/project/project.py @@ -41,14 +41,13 @@ class project_task_type(osv.osv): 'case_default': fields.boolean('Default for New Projects', help="If you check this field, this stage will be proposed by default on each new project. It will not assign this stage to existing projects."), 'project_ids': fields.many2many('project.project', 'project_task_type_rel', 'type_id', 'project_id', 'Projects'), - 'fold': fields.boolean('Folded by Default', - help="This stage is not visible, for example in status bar or kanban view, when there are no records in that stage to display."), + 'fold': fields.boolean('Folded in Kanban View', + help='This stage is folded in the kanban view when' + 'there are no records in that stage to display.'), } _defaults = { 'sequence': 1, - 'fold': False, - 'case_default': False, 'project_ids': lambda self, cr, uid, ctx=None: self.pool['project.task']._get_default_project_id(cr, uid, context=ctx), } _order = 'sequence' @@ -189,6 +188,8 @@ class project(osv.osv): return res def _task_count(self, cr, uid, ids, field_name, arg, context=None): + """ :deprecated: this method will be removed with OpenERP v8. Use task_ids + fields instead. """ if context is None: context = {} res = dict.fromkeys(ids, 0) @@ -265,7 +266,10 @@ class project(osv.osv): }), 'resource_calendar_id': fields.many2one('resource.calendar', 'Working Time', help="Timetable working hours to adjust the gantt diagram report", states={'close':[('readonly',True)]} ), 'type_ids': fields.many2many('project.task.type', 'project_task_type_rel', 'project_id', 'type_id', 'Tasks Stages', states={'close':[('readonly',True)], 'cancelled':[('readonly',True)]}), - 'task_count': fields.function(_task_count, type='integer', string="Open Tasks"), + 'task_count': fields.function(_task_count, type='integer', string="Open Tasks", + deprecated="This field will be removed in OpenERP v8. Use task_ids one2many field instead."), + 'task_ids': fields.one2many('project.task', 'project_id', + domain=[('stage_id.fold', '=', False)]), 'color': fields.integer('Color Index'), 'alias_id': fields.many2one('mail.alias', 'Alias', ondelete="restrict", required=True, help="Internal email associated with this project. Incoming emails are automatically synchronized" @@ -556,8 +560,9 @@ class task(osv.osv): _mail_post_access = 'read' _track = { 'stage_id': { - 'project.mt_task_new': lambda self, cr, uid, obj, ctx=None: obj.stage_id and obj.stage_id.sequence == 1, - 'project.mt_task_stage': lambda self, cr, uid, obj, ctx=None: obj.stage_id.sequence != 1, + # this is only an heuristics; depending on your particular stage configuration it may not match all 'new' stages + 'project.mt_task_new': lambda self, cr, uid, obj, ctx=None: obj.stage_id and obj.stage_id.sequence <= 1, + 'project.mt_task_stage': lambda self, cr, uid, obj, ctx=None: obj.stage_id.sequence > 1, }, 'user_id': { 'project.mt_task_assigned': lambda self, cr, uid, obj, ctx=None: obj.user_id and obj.user_id.id, @@ -582,7 +587,7 @@ class task(osv.osv): def _get_default_stage_id(self, cr, uid, context=None): """ Gives default stage_id """ project_id = self._get_default_project_id(cr, uid, context=context) - return self.stage_find(cr, uid, [], project_id, [('sequence', '=', '1')], context=context) + return self.stage_find(cr, uid, [], project_id, [('fold', '=', False)], context=context) def _resolve_project_id_from_context(self, cr, uid, context=None): """ Returns ID of project based on the value of 'default_project_id' @@ -994,7 +999,7 @@ class task(osv.osv): def set_remaining_time(self, cr, uid, ids, remaining_time=1.0, context=None): for task in self.browse(cr, uid, ids, context=context): - if (task.stage_id and task.stage_id.sequence == 1) or (task.planned_hours == 0.0): + if (task.stage_id and task.stage_id.sequence <= 1) or (task.planned_hours == 0.0): self.write(cr, uid, [task.id], {'planned_hours': remaining_time}, context=context) self.write(cr, uid, ids, {'remaining_hours': remaining_time}, context=context) return True @@ -1127,7 +1132,8 @@ class task(osv.osv): def message_new(self, cr, uid, msg, custom_values=None, context=None): """ Override to updates the document according to the email. """ - if custom_values is None: custom_values = {} + if custom_values is None: + custom_values = {} defaults = { 'name': msg.get('subject'), 'planned_hours': 0.0, @@ -1137,10 +1143,10 @@ class task(osv.osv): def message_update(self, cr, uid, ids, msg, update_vals=None, context=None): """ Override to update the task according to the email. """ - if update_vals is None: update_vals = {} - act = False + if update_vals is None: + update_vals = {} maps = { - 'cost':'planned_hours', + 'cost': 'planned_hours', } for line in msg['body'].split('\n'): line = line.strip() @@ -1153,9 +1159,7 @@ class task(osv.osv): update_vals[field] = float(res.group(2).lower()) except (ValueError, TypeError): pass - if act: - getattr(self,act)(cr, uid, ids, context=context) - return super(task,self).message_update(cr, uid, ids, msg, update_vals=update_vals, context=context) + return super(task, self).message_update(cr, uid, ids, msg, update_vals=update_vals, context=context) class project_work(osv.osv): _name = "project.task.work" @@ -1243,6 +1247,8 @@ class account_analytic_account(osv.osv): return analytic_account_id def write(self, cr, uid, ids, vals, context=None): + if isinstance(ids, (int, long)): + ids = [ids] vals_for_project = vals.copy() for account in self.browse(cr, uid, ids, context=context): if not vals.get('name'): diff --git a/addons/project/project_data.xml b/addons/project/project_data.xml index 0f48dfe9a2b..bc53c4a436f 100644 --- a/addons/project/project_data.xml +++ b/addons/project/project_data.xml @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="utf-8"?> <openerp> - <data> + <data noupdate="1"> <!-- This will set the unit of measure used in projects and tasks.--> <record id="base.main_company" model="res.company"> <field name="project_time_mode_id" ref="product.product_uom_hour"></field> @@ -63,14 +63,18 @@ <field name="name">Done</field> <field name="case_default" eval="True"/> <field name="fold" eval="True"/> + <field name="closed" eval="True"/> </record> <record id="project_tt_cancel" model="project.task.type"> <field name="sequence">30</field> <field name="name">Cancelled</field> <field name="case_default" eval="True"/> <field name="fold" eval="True"/> + <field name="closed" eval="True"/> </record> + </data> + <data> <!-- Task-related subtypes for messaging / Chatter --> <record id="mt_task_new" model="mail.message.subtype"> <field name="name">Task Created</field> diff --git a/addons/project/project_demo.xml b/addons/project/project_demo.xml index c58c61448c2..ff225c46e10 100644 --- a/addons/project/project_demo.xml +++ b/addons/project/project_demo.xml @@ -4,7 +4,7 @@ <!-- Users --> <record id="base.user_demo" model="res.users"> - <field name="groups_id" eval="[(4, ref('base.group_sale_salesman')),(4, ref('group_project_user'))]"/> + <field name="groups_id" eval="[(4, ref('base.group_sale_salesman')),(4, ref('group_project_manager'))]"/> </record> <!-- Categories --> diff --git a/addons/project/project_view.xml b/addons/project/project_view.xml index 5015d80e01f..0f507c604f2 100644 --- a/addons/project/project_view.xml +++ b/addons/project/project_view.xml @@ -19,7 +19,7 @@ <field name="name" string="Tasks"/> <field name="categ_ids"/> <filter string="Unassigned" name="unassigned" domain="[('user_id', '=', False)]"/> - <filter string="New" name="draft" domain="[('stage_id.sequence', '=', 1)]"/> + <filter string="New" name="draft" domain="[('stage_id.sequence', '<=', 1)]"/> <separator/> <filter string="My Tasks" domain="[('user_id','=',uid)]"/> <separator/> @@ -239,6 +239,7 @@ <field name="date"/> <field name="color"/> <field name="task_count"/> + <field name="task_ids"/> <field name="alias_id"/> <field name="doc_count"/> <templates> @@ -258,9 +259,10 @@ <span class="oe_e oe_e_alias">%%</span><small><field name="alias_id"/></small> </div> <div class="oe_kanban_project_list"> - <a t-if="record.use_tasks.raw_value" name="%(act_project_project_2_project_task_all)d" type="action" style="margin-right: 10px"> - <span t-if="record.task_count.raw_value gt 1"><field name="task_count"/> Tasks</span> - <span t-if="record.task_count.raw_value lt 2"><field name="task_count"/> Task</span> + <a t-if="record.use_tasks.raw_value" name="%(act_project_project_2_project_task_all)d" type="action" style="margin-right: 10px"> + <t t-raw="record.task_ids.raw_value.length"/> + <span t-if="record.task_ids.raw_value.length == 1">Task</span> + <span t-if="record.task_ids.raw_value.length > 1">Tasks</span> </a> </div> <div class="oe_kanban_project_list"> @@ -683,10 +685,10 @@ <group> <group> <field name="name"/> - <field name="case_default"/> + <field name="sequence"/> </group> <group> - <field name="sequence"/> + <field name="case_default"/> <field name="fold"/> </group> </group> diff --git a/addons/project/report/project_report.py b/addons/project/report/project_report.py index 15b26507ff3..ed086b4318d 100644 --- a/addons/project/report/project_report.py +++ b/addons/project/report/project_report.py @@ -75,7 +75,7 @@ class report_project_task_user(osv.osv): date_trunc('day',t.date_last_stage_update) as date_last_stage_update, to_date(to_char(t.date_deadline, 'dd-MM-YYYY'),'dd-MM-YYYY') as date_deadline, -- sum(cast(to_char(date_trunc('day',t.date_end) - date_trunc('day',t.date_start),'DD') as int)) as no_of_days, - abs((extract('epoch' from (t.date_end-t.date_start)))/(3600*24)) as no_of_days, + abs((extract('epoch' from (t.write_date-t.date_start)))/(3600*24)) as no_of_days, t.user_id, progress as progress, t.project_id, @@ -89,9 +89,9 @@ class report_project_task_user(osv.osv): total_hours as total_hours, t.delay_hours as hours_delay, planned_hours as hours_planned, - (extract('epoch' from (t.date_end-t.create_date)))/(3600*24) as closing_days, + (extract('epoch' from (t.write_date-t.create_date)))/(3600*24) as closing_days, (extract('epoch' from (t.date_start-t.create_date)))/(3600*24) as opening_days, - abs((extract('epoch' from (t.date_deadline-t.date_end)))/(3600*24)) as delay_endings_days + abs((extract('epoch' from (t.date_deadline-t.write_date)))/(3600*24)) as delay_endings_days FROM project_task t WHERE t.active = 'true' GROUP BY @@ -106,6 +106,7 @@ class report_project_task_user(osv.osv): month, day, create_date, + write_date, date_start, date_end, date_deadline, diff --git a/addons/project/report/project_report.xml b/addons/project/report/project_report.xml index 59d0fd28220..4ff7e852620 100644 --- a/addons/project/report/project_report.xml +++ b/addons/project/report/project_report.xml @@ -17,7 +17,6 @@ <tasks> <task type="zoom" name="tasks"> <task_name type="field" name="name"/> - <task_status type="field" name="state" /> <task_descritpion type="field" name="description"/> <task_sequence type="field" name="sequence"/> <task_priority type="field" name="priority"/> diff --git a/addons/project_issue/project_issue.py b/addons/project_issue/project_issue.py index dc4d5cf5ff5..8e789e03658 100644 --- a/addons/project_issue/project_issue.py +++ b/addons/project_issue/project_issue.py @@ -415,18 +415,20 @@ class project_issue(osv.Model): return stage_ids[0] return False - def case_escalate(self, cr, uid, ids, context=None): - cases = self.browse(cr, uid, ids) - for case in cases: + def case_escalate(self, cr, uid, ids, context=None): # FIXME rename this method to issue_escalate + for issue in self.browse(cr, uid, ids, context=context): data = {} - if case.project_id.project_escalation_id: - data['project_id'] = case.project_id.project_escalation_id.id - if case.project_id.project_escalation_id.user_id: - data['user_id'] = case.project_id.project_escalation_id.user_id.id - if case.task_id: - self.pool.get('project.task').write(cr, uid, [case.task_id.id], {'project_id': data['project_id'], 'user_id': False}) - else: + esc_proj = issue.project_id.project_escalation_id + if not esc_proj: raise osv.except_osv(_('Warning!'), _('You cannot escalate this issue.\nThe relevant Project has not configured the Escalation Project!')) + + data['project_id'] = esc_proj.id + if esc_proj.user_id: + data['user_id'] = esc_proj.user_id.id + issue.write(data) + + if issue.task_id: + issue.task_id.write({'project_id': esc_proj.id, 'user_id': False}) return True # ------------------------------------------------------- @@ -467,40 +469,10 @@ class project_issue(osv.Model): 'partner_id': msg.get('author_id', False), 'user_id': False, } - if msg.get('priority'): - defaults['priority'] = msg.get('priority') - defaults.update(custom_values) res_id = super(project_issue, self).message_new(cr, uid, msg, custom_values=defaults, context=context) return res_id - def message_update(self, cr, uid, ids, msg, update_vals=None, context=None): - """ Overrides mail_thread message_update that is called by the mailgateway - through message_process. - This method updates the document according to the email. - """ - if isinstance(ids, (str, int, long)): - ids = [ids] - if update_vals is None: update_vals = {} - - # Update doc values according to the message - if msg.get('priority'): - update_vals['priority'] = msg.get('priority') - # Parse 'body' to find values to update - maps = { - 'cost': 'planned_cost', - 'revenue': 'planned_revenue', - 'probability': 'probability', - } - for line in msg.get('body', '').split('\n'): - line = line.strip() - res = tools.command_re.match(line) - if res and maps.get(res.group(1).lower(), False): - key = maps.get(res.group(1).lower()) - update_vals[key] = res.group(2).lower() - - return super(project_issue, self).message_update(cr, uid, ids, msg, update_vals=update_vals, context=context) - def message_post(self, cr, uid, thread_id, body='', subject=None, type='notification', subtype=None, parent_id=False, attachments=None, context=None, content_subtype='html', **kwargs): """ Overrides mail_thread message_post so that we can set the date of last action field when a new message is posted on the issue. @@ -520,6 +492,8 @@ class project(osv.Model): return [('project.task', "Tasks"), ("project.issue", "Issues")] def _issue_count(self, cr, uid, ids, field_name, arg, context=None): + """ :deprecated: this method will be removed with OpenERP v8. Use issue_ids + fields instead. """ res = dict.fromkeys(ids, 0) issue_ids = self.pool.get('project.issue').search(cr, uid, [('project_id', 'in', ids)]) for issue in self.pool.get('project.issue').browse(cr, uid, issue_ids, context): @@ -531,7 +505,10 @@ class project(osv.Model): 'project_escalation_id': fields.many2one('project.project', 'Project Escalation', help='If any issue is escalated from the current Project, it will be listed under the project selected here.', states={'close': [('readonly', True)], 'cancelled': [('readonly', True)]}), - 'issue_count': fields.function(_issue_count, type='integer', string="Unclosed Issues"), + 'issue_count': fields.function(_issue_count, type='integer', string="Unclosed Issues", + deprecated="This field will be removed in OpenERP v8. Use issue_ids one2many field instead."), + 'issue_ids': fields.one2many('project.issue', 'project_id', + domain=[('stage_id.fold', '=', False)]) } def _check_escalation(self, cr, uid, ids, context=None): diff --git a/addons/project_issue/project_issue_view.xml b/addons/project_issue/project_issue_view.xml index 445cdd17ee6..2e99b75046c 100644 --- a/addons/project_issue/project_issue_view.xml +++ b/addons/project_issue/project_issue_view.xml @@ -325,13 +325,14 @@ <field name="arch" type="xml"> <field name="use_tasks" position="after"> <field name="use_issues"/> - <field name="issue_count" invisible="1"/> + <field name="issue_ids" invisible="1"/> </field> <xpath expr="//div[contains(@class, 'oe_kanban_project_list')]" position="inside"> <a t-if="record.use_issues.raw_value" style="margin-right: 10px" name="%(act_project_project_2_project_issue_all)d" type="action"> - <span t-if="record.issue_count.raw_value gt 1"><field name="issue_count"/> Issues</span> - <span t-if="record.issue_count.raw_value lt 2"><field name="issue_count"/> Issue</span> + <t t-raw="record.issue_ids.raw_value.length"/> + <span t-if="record.issue_ids.raw_value.length == 1">Issue</span> + <span t-if="record.issue_ids.raw_value.length > 1">Issues</span> </a> </xpath> </field> diff --git a/addons/project_mrp/process/project_mrp_process.xml b/addons/project_mrp/process/project_mrp_process.xml index b147740758a..de2b2c1b4c8 100644 --- a/addons/project_mrp/process/project_mrp_process.xml +++ b/addons/project_mrp/process/project_mrp_process.xml @@ -50,10 +50,8 @@ `project_mrp` implements a basic procurement system for services without actually using the full-fledged procurement process from sale_stock, and without the dependency. So it stil represents a "procurement system". - TODO: To cleanup this invalid foreign external ID, the node should probably be moved to the - `sale` module directly, and removed from both `sale_stock` and `project_mrp`. --> - <record id="sale_stock.process_node_saleprocurement0" model="process.node"> + <record id="procurement.process_node_saleprocurement0" model="process.node"> <field name="menu_id" ref="procurement.menu_stock_procurement_action"/> <field name="model_id" ref="procurement.model_procurement_order"/> <field name="kind">subflow</field> @@ -68,7 +66,7 @@ <field eval=""""Procurement Task"""" name="name"/> <field eval=""""if product type is 'service' then it creates the task."""" name="note"/> <field name="target_node_id" ref="process_node_procuretasktask0"/> - <field name="source_node_id" ref="sale_stock.process_node_saleprocurement0"/> + <field name="source_node_id" ref="procurement.process_node_saleprocurement0"/> </record> <record id="process_transition_createtask0" model="process.transition"> diff --git a/addons/purchase/edi/purchase_order_action_data.xml b/addons/purchase/edi/purchase_order_action_data.xml index b9c3c3ab95d..5bb56e8ab15 100644 --- a/addons/purchase/edi/purchase_order_action_data.xml +++ b/addons/purchase/edi/purchase_order_action_data.xml @@ -19,7 +19,7 @@ <!--Email template --> <record id="email_template_edi_purchase" model="email.template"> <field name="name">Purchase Order - Send by mail</field> - <field name="email_from">${object.validator.email or ''}</field> + <field name="email_from">${(object.validator.email or '')|safe}</field> <field name="subject">${object.company_id.name} Order (Ref ${object.name or 'n/a' })</field> <field name="partner_to">${object.partner_id.id}</field> <field name="model_id" ref="purchase.model_purchase_order"/> diff --git a/addons/purchase/purchase.py b/addons/purchase/purchase.py index e551eb40396..fb838e8b3cd 100644 --- a/addons/purchase/purchase.py +++ b/addons/purchase/purchase.py @@ -1234,17 +1234,27 @@ class account_invoice(osv.Model): def invoice_validate(self, cr, uid, ids, context=None): res = super(account_invoice, self).invoice_validate(cr, uid, ids, context=context) purchase_order_obj = self.pool.get('purchase.order') - po_ids = purchase_order_obj.search(cr, uid, [('invoice_ids', 'in', ids)], context=context) + # read access on purchase.order object is not required + if not purchase_order_obj.check_access_rights(cr, uid, 'read', raise_exception=False): + user_id = SUPERUSER_ID + else: + user_id = uid + po_ids = purchase_order_obj.search(cr, user_id, [('invoice_ids', 'in', ids)], context=context) for po_id in po_ids: - purchase_order_obj.message_post(cr, uid, po_id, body=_("Invoice received"), context=context) + purchase_order_obj.message_post(cr, user_id, po_id, body=_("Invoice received"), context=context) return res def confirm_paid(self, cr, uid, ids, context=None): res = super(account_invoice, self).confirm_paid(cr, uid, ids, context=context) purchase_order_obj = self.pool.get('purchase.order') - po_ids = purchase_order_obj.search(cr, uid, [('invoice_ids', 'in', ids)], context=context) + # read access on purchase.order object is not required + if not purchase_order_obj.check_access_rights(cr, uid, 'read', raise_exception=False): + user_id = SUPERUSER_ID + else: + user_id = uid + po_ids = purchase_order_obj.search(cr, user_id, [('invoice_ids', 'in', ids)], context=context) if po_ids: - purchase_order_obj.message_post(cr, uid, po_ids, body=_("Invoice paid"), context=context) + purchase_order_obj.message_post(cr, user_id, po_ids, body=_("Invoice paid"), context=context) return res class account_invoice_line(osv.Model): diff --git a/addons/purchase/purchase_view.xml b/addons/purchase/purchase_view.xml index 6bed8bcbf9c..4a301d3fa7b 100644 --- a/addons/purchase/purchase_view.xml +++ b/addons/purchase/purchase_view.xml @@ -36,6 +36,9 @@ <menuitem id="menu_purchase_config_pricelist" name="Pricelists" parent="menu_purchase_config_purchase" sequence="50" groups="product.group_purchase_pricelist"/> + <menuitem + action="product.product_pricelist_action_for_purchase" id="menu_product_pricelist_action2_purchase" + parent="menu_purchase_config_pricelist" sequence="1" groups="product.group_purchase_pricelist" /> <menuitem action="product.product_pricelist_action_for_purchase" id="menu_product_pricelist_action2_purchase" diff --git a/addons/purchase/report/purchase_report.py b/addons/purchase/report/purchase_report.py index 5af74b23495..f6a37bb37a7 100644 --- a/addons/purchase/report/purchase_report.py +++ b/addons/purchase/report/purchase_report.py @@ -94,23 +94,20 @@ class purchase_report(osv.osv): extract(epoch from age(s.date_approve,s.date_order))/(24*60*60)::decimal(16,2) as delay, extract(epoch from age(l.date_planned,s.date_order))/(24*60*60)::decimal(16,2) as delay_pass, count(*) as nbr, - (l.price_unit*l.product_qty)::decimal(16,2) as price_total, + sum(l.price_unit*l.product_qty)::decimal(16,2) as price_total, avg(100.0 * (l.price_unit*l.product_qty) / NULLIF(t.standard_price*l.product_qty/u.factor*u2.factor, 0.0))::decimal(16,2) as negociation, - sum(t.standard_price*l.product_qty/u.factor*u2.factor)::decimal(16,2) as price_standard, (sum(l.product_qty*l.price_unit)/NULLIF(sum(l.product_qty/u.factor*u2.factor),0.0))::decimal(16,2) as price_average - from purchase_order s - left join purchase_order_line l on (s.id=l.order_id) + from purchase_order_line l + join purchase_order s on (l.order_id=s.id) left join product_product p on (l.product_id=p.id) left join product_template t on (p.product_tmpl_id=t.id) left join product_uom u on (u.id=l.product_uom) left join product_uom u2 on (u2.id=t.uom_id) - where l.product_id is not null group by s.company_id, s.create_uid, s.partner_id, - l.product_qty, u.factor, s.location_id, l.price_unit, diff --git a/addons/resource/resource.py b/addons/resource/resource.py index 449fa422ab4..212a7dd6828 100644 --- a/addons/resource/resource.py +++ b/addons/resource/resource.py @@ -177,8 +177,8 @@ class resource_calendar(osv.osv): if leave_flag: break else: - d1 = datetime(dt_from.year, dt_from.month, dt_from.day, int(math.floor(m)), int((m%1) * 60)) - d2 = datetime(dt_from.year, dt_from.month, dt_from.day, int(math.floor(hour_to)), int((hour_to%1) * 60)) + d1 = datetime(dt_from.year, dt_from.month, dt_from.day) + timedelta(hours=int(math.floor(m)), minutes=int((m%1) * 60)) + d2 = datetime(dt_from.year, dt_from.month, dt_from.day) + timedelta(hours=int(math.floor(hour_to)), minutes=int((hour_to%1) * 60)) result.append((d1, d2)) current_hour = hour_to todo -= (hour_to - m) diff --git a/addons/sale/edi/sale_order_action_data.xml b/addons/sale/edi/sale_order_action_data.xml index a7f597f4339..4ad657d7f82 100644 --- a/addons/sale/edi/sale_order_action_data.xml +++ b/addons/sale/edi/sale_order_action_data.xml @@ -20,7 +20,7 @@ <!--Email template --> <record id="email_template_edi_sale" model="email.template"> <field name="name">Sales Order - Send by Email</field> - <field name="email_from">${object.user_id.email or ''}</field> + <field name="email_from">${(object.user_id.email or '')|safe}</field> <field name="subject">${object.company_id.name} ${object.state in ('draft', 'sent') and 'Quotation' or 'Order'} (Ref ${object.name or 'n/a' })</field> <field name="partner_to">${object.partner_invoice_id.id}</field> <field name="model_id" ref="sale.model_sale_order"/> diff --git a/addons/sale/report/sale_report.py b/addons/sale/report/sale_report.py index d5f07f863b9..2368a9849de 100644 --- a/addons/sale/report/sale_report.py +++ b/addons/sale/report/sale_report.py @@ -70,7 +70,7 @@ class sale_report(osv.osv): t.uom_id as product_uom, sum(l.product_uom_qty / u.factor * u2.factor) as product_uom_qty, sum(l.product_uom_qty * l.price_unit * (100.0-l.discount) / 100.0) as price_total, - 1 as nbr, + count(*) as nbr, s.date_order as date, s.date_confirm as date_confirm, to_char(s.date_order, 'YYYY') as year, @@ -85,15 +85,14 @@ class sale_report(osv.osv): s.pricelist_id as pricelist_id, s.project_id as analytic_account_id from - sale_order s - join sale_order_line l on (s.id=l.order_id) + sale_order_line l + join sale_order s on (l.order_id=s.id) left join product_product p on (l.product_id=p.id) left join product_template t on (p.product_tmpl_id=t.id) left join product_uom u on (u.id=l.product_uom) left join product_uom u2 on (u2.id=t.uom_id) group by l.product_id, - l.product_uom_qty, l.order_id, t.uom_id, t.categ_id, diff --git a/addons/sale/sale.py b/addons/sale/sale.py index ad048e505e5..7a32b919eae 100644 --- a/addons/sale/sale.py +++ b/addons/sale/sale.py @@ -977,9 +977,9 @@ class mail_compose_message(osv.Model): def send_mail(self, cr, uid, ids, context=None): context = context or {} - if context.get('default_model') == 'sale.order' and context.get('default_res_id') and context.get('mark_so_as_sent'): + if context.get('active_model') == 'sale.order' and context.get('active_ids') and context.get('mark_so_as_sent'): context = dict(context, mail_post_autofollow=True) - self.pool.get('sale.order').signal_quotation_sent(cr, uid, [context['default_res_id']]) + self.pool.get('sale.order').signal_quotation_sent(cr, uid, context['active_ids']) return super(mail_compose_message, self).send_mail(cr, uid, ids, context=context) diff --git a/addons/sale/sale_view.xml b/addons/sale/sale_view.xml index 53c4a166977..60e5c83ecf0 100644 --- a/addons/sale/sale_view.xml +++ b/addons/sale/sale_view.xml @@ -122,7 +122,6 @@ </header> <group> <group> - <field name="state" invisible="1" /> <field name="product_id" context="{'partner_id':parent.partner_id, 'quantity':product_uom_qty, 'pricelist':parent.pricelist_id, 'uom':product_uom}" groups="base.group_user" diff --git a/addons/sale/test/edi_sale_order.yml b/addons/sale/test/edi_sale_order.yml index 24fdfdf2786..65b6eb76835 100644 --- a/addons/sale/test/edi_sale_order.yml +++ b/addons/sale/test/edi_sale_order.yml @@ -170,7 +170,6 @@ "__import_module": "sale", "__import_model": "sale.order.line", "name": "Basic PC", - "date_planned": "2011-09-30", "price_unit": 150.0, "product_id": ["product:5af1272e-dd26-11e0-b65e-701a04e25543.product_product_pc1", "[PC1] Basic PC"], "product_qty": 1.0, @@ -183,7 +182,6 @@ "__import_module": "sale", "__import_model": "sale.order.line", "name": "Medium PC", - "date_planned": "2011-09-15", "price_unit": 100.0, "product_id": ["product:5af1272e-dd26-11e0-b65e-701a04e25543.product_product_pc3", "[PC3] Medium PC"], "product_qty": 2.0, @@ -214,4 +212,4 @@ assert sale_line.price_unit == 100 , "unit price is not same, got %s, expected 100"%(sale_line.price_unit,) assert sale_line.product_uom_qty == 2 , "product qty is not same" else: - raise AssertionError('unknown order line: %s' % sale_line)
\ No newline at end of file + raise AssertionError('unknown order line: %s' % sale_line) diff --git a/addons/sale_crm/sale_crm_view.xml b/addons/sale_crm/sale_crm_view.xml index 34f32e0b58b..8c008339903 100644 --- a/addons/sale_crm/sale_crm_view.xml +++ b/addons/sale_crm/sale_crm_view.xml @@ -8,7 +8,7 @@ <field name="inherit_id" ref="crm.crm_case_form_view_oppor"/> <field name="arch" type="xml"> <data> - <xpath expr="/form/header/button[@name='case_mark_lost']" position="after"> + <xpath expr="//field[@name='stage_id']" position="before"> <button attrs="{'invisible': [('probability', '<', 100)]}" string="Create Quotation" name="%(action_crm_make_sale)d" type="action"/> <button attrs="{'invisible': [('probability', '=', 100)]}" string="Convert to Quotation" name="%(action_crm_make_sale)d" type="action" class="oe_highlight"/> </xpath> diff --git a/addons/sale_crm/wizard/crm_make_sale.py b/addons/sale_crm/wizard/crm_make_sale.py index 829468e684f..69a01f90dbb 100644 --- a/addons/sale_crm/wizard/crm_make_sale.py +++ b/addons/sale_crm/wizard/crm_make_sale.py @@ -46,8 +46,8 @@ class crm_make_sale(osv.osv_memory): if not active_id: return False - lead = lead_obj.read(cr, uid, active_id, ['partner_id']) - return lead['partner_id'] + lead = lead_obj.read(cr, uid, active_id, ['partner_id'], context=context) + return lead['partner_id'][0] def view_init(self, cr, uid, fields_list, context=None): return super(crm_make_sale, self).view_init(cr, uid, fields_list, context=context) diff --git a/addons/sale_order_dates/sale_order_dates_view.xml b/addons/sale_order_dates/sale_order_dates_view.xml index b2863f644e0..68419cd29bb 100644 --- a/addons/sale_order_dates/sale_order_dates_view.xml +++ b/addons/sale_order_dates/sale_order_dates_view.xml @@ -8,7 +8,7 @@ <field name="inherit_id" ref="sale.view_order_form"/> <field name="arch" type="xml"> <group name="sale_pay" position="after"> - <group colspan="2" col="2" groups="base.group_no_one"> + <group colspan="2" col="2" > <separator string="Dates" colspan="2"/> <field name="requested_date"/> <field name="commitment_date"/> diff --git a/addons/sale_stock/process/sale_stock_process.xml b/addons/sale_stock/process/sale_stock_process.xml index 1ebebec7809..45979fa9b70 100644 --- a/addons/sale_stock/process/sale_stock_process.xml +++ b/addons/sale_stock/process/sale_stock_process.xml @@ -15,7 +15,8 @@ </record> - <record id="process_node_saleprocurement0" model="process.node"> + <!-- NOTE: as this node is also define in project_mrp project, we must attach it to the nearest common module --> + <record id="procurement.process_node_saleprocurement0" model="process.node"> <field name="menu_id" ref="procurement.menu_stock_procurement_action"/> <field name="model_id" ref="procurement.model_procurement_order"/> <field eval=""""subflow"""" name="kind"/> @@ -72,7 +73,7 @@ <field eval="[(6,0,[])]" name="transition_ids"/> <field eval=""""Create Procurement Order"""" name="name"/> <field eval=""""A procurement order is automatically created as soon as a sales order is confirmed or as the invoice is paid. It drives the purchasing and the production of products regarding to the rules and to the sales order's parameters. """" name="note"/> - <field name="target_node_id" ref="process_node_saleprocurement0"/> + <field name="target_node_id" ref="procurement.process_node_saleprocurement0"/> <field name="source_node_id" ref="sale.process_node_saleorder0"/> </record> @@ -81,7 +82,7 @@ <field eval=""""Create Pick List"""" name="name"/> <field eval=""""The Pick List form is created as soon as the sales order is confirmed, in the same time as the procurement order. It represents the assignment of parts to the sales order. There is 1 pick list by sales order line which evolves with the availability of parts."""" name="note"/> <field name="target_node_id" ref="process_node_packinglist0"/> - <field name="source_node_id" ref="process_node_saleprocurement0"/> + <field name="source_node_id" ref="procurement.process_node_saleprocurement0"/> </record> <record id="process_transition_deliver0" model="process.transition"> diff --git a/addons/sale_stock/report/sale_report.py b/addons/sale_stock/report/sale_report.py index 0feb3ee2392..bdcce410a5f 100644 --- a/addons/sale_stock/report/sale_report.py +++ b/addons/sale_stock/report/sale_report.py @@ -42,6 +42,8 @@ class sale_report(osv.osv): def init(self, cr): tools.drop_view_if_exists(cr, 'sale_report') + # TODO: make parent view extensible similarly to invoice analysis and + # remove the duplication cr.execute(""" create or replace view sale_report as ( select @@ -50,7 +52,7 @@ class sale_report(osv.osv): t.uom_id as product_uom, sum(l.product_uom_qty / u.factor * u2.factor) as product_uom_qty, sum(l.product_uom_qty * l.price_unit * (100.0-l.discount) / 100.0) as price_total, - 1 as nbr, + count(*) as nbr, s.date_order as date, s.date_confirm as date_confirm, to_char(s.date_order, 'YYYY') as year, @@ -68,15 +70,14 @@ class sale_report(osv.osv): s.pricelist_id as pricelist_id, s.project_id as analytic_account_id from - sale_order s - join sale_order_line l on (s.id=l.order_id) + sale_order_line l + join sale_order s on (l.order_id=s.id) left join product_product p on (l.product_id=p.id) left join product_template t on (p.product_tmpl_id=t.id) left join product_uom u on (u.id=l.product_uom) left join product_uom u2 on (u2.id=t.uom_id) group by l.product_id, - l.product_uom_qty, l.order_id, t.uom_id, t.categ_id, diff --git a/addons/sale_stock/sale_stock.py b/addons/sale_stock/sale_stock.py index b8f6249d744..3f7ac28c900 100644 --- a/addons/sale_stock/sale_stock.py +++ b/addons/sale_stock/sale_stock.py @@ -312,7 +312,6 @@ class sale_order(osv.osv): 'move_id': move_id, 'company_id': order.company_id.id, 'note': line.name, - 'property_ids': [(6, 0, [x.id for x in line.property_ids])], } def _prepare_order_line_move(self, cr, uid, order, line, picking_id, date_planned, context=None): diff --git a/addons/sale_stock/stock.py b/addons/sale_stock/stock.py index ad19684c4cf..ccece846390 100644 --- a/addons/sale_stock/stock.py +++ b/addons/sale_stock/stock.py @@ -62,6 +62,15 @@ class stock_picking(osv.osv): return picking.note or picking.sale_id.note return super(stock_picking, self)._get_comment_invoice(cursor, user, picking) + def _prepare_invoice_group(self, cr, uid, picking, partner, invoice, context=None): + """ Inherit the original function of the 'stock' module in order to override name field + to pass the customer reference form the sales order + """ + invoice_vals = super(stock_picking, self)._prepare_invoice_group(cr, uid, picking, partner, invoice, context) + if picking.sale_id: + invoice_vals['name'] = (invoice.name or '') + ', ' + (picking.sale_id.client_order_ref or '') + return invoice_vals + def _prepare_invoice(self, cr, uid, picking, partner, inv_type, journal_id, context=None): """ Inherit the original function of the 'stock' module in order to override some values if the picking has been generated by a sales order diff --git a/addons/stock/product_view.xml b/addons/stock/product_view.xml index 5388e76fe6d..7c0d3ac201b 100644 --- a/addons/stock/product_view.xml +++ b/addons/stock/product_view.xml @@ -224,12 +224,18 @@ <field name="delivery_count"/> </field> <xpath expr="//div[@name='tags']" position="inside"> - <a name="%(action_receive_move)d" type="action" t-if="record.reception_count.raw_value>0"> + <a name="%(action_receive_move)d" type="action" t-if="record.reception_count.raw_value>1"> <t t-esc="record.reception_count.value"/> Receptions </a> - <a name="%(action_deliver_move)d" type="action" t-if="record.delivery_count.raw_value>0"> + <a name="%(action_receive_move)d" type="action" t-if="record.reception_count.raw_value==1"> + <t t-esc="record.reception_count.value"/> Reception + </a> + <a name="%(action_deliver_move)d" type="action" t-if="record.delivery_count.raw_value>1"> <t t-esc="record.delivery_count.value"/> Deliveries </a> + <a name="%(action_deliver_move)d" type="action" t-if="record.delivery_count.raw_value==1"> + <t t-esc="record.delivery_count.value"/> Delivery + </a> </xpath> <ul position="inside"> <li t-if="record.type.raw_value != 'service'">On hand: <field name="qty_available"/> <field name="uom_id"/></li> diff --git a/addons/stock/report/picking.rml b/addons/stock/report/picking.rml index 0a17f56fc71..381b7eb4c28 100644 --- a/addons/stock/report/picking.rml +++ b/addons/stock/report/picking.rml @@ -285,9 +285,9 @@ <para style="terp_default_Centre_9">[[ (move_lines.prodlot_id and move_lines.prodlot_id.name) or '' ]]</para> </td> <td> - <para style="terp_default_9">[[ (picking.type == 'in' or removeParentNode('para')) ]][[ move_lines.state == 'done' and 'Received' or move_lines.state]]</para> - <para style="terp_default_9">[[ (picking.type == 'out' or removeParentNode('para')) ]][[ move_lines.state == 'done' and 'Delivered' or move_lines.state]]</para> - <para style="terp_default_9">[[ (picking.type == 'internal' or removeParentNode('para')) ]][[ move_lines.state == 'done' and 'Transferred' or move_lines.state]]</para> + <para style="terp_default_9">[[ (picking.type == 'in' or removeParentNode('para')) and '' ]][[ move_lines.state == 'done' and 'Received' or move_lines.state]]</para> + <para style="terp_default_9">[[ (picking.type == 'out' or removeParentNode('para')) and '']][[ move_lines.state == 'done' and 'Delivered' or move_lines.state]]</para> + <para style="terp_default_9">[[ (picking.type == 'internal' or removeParentNode('para')) and '' ]][[ move_lines.state == 'done' and 'Transferred' or move_lines.state]]</para> </td> <td> <para style="terp_default_Right_9">[[ (move_lines.location_id and move_lines.location_id.name) or '' ]] </para> diff --git a/addons/stock/report/report_stock_move.py b/addons/stock/report/report_stock_move.py index b8c22062fa5..db9ea187260 100644 --- a/addons/stock/report/report_stock_move.py +++ b/addons/stock/report/report_stock_move.py @@ -61,66 +61,49 @@ class report_stock_move(osv.osv): cr.execute(""" CREATE OR REPLACE view report_stock_move AS ( SELECT - min(sm_id) as id, - date_trunc('day',al.dp) as date, - al.curr_year as year, - al.curr_month as month, - al.curr_day as day, - al.curr_day_diff as day_diff, - al.curr_day_diff1 as day_diff1, - al.curr_day_diff2 as day_diff2, - al.location_id as location_id, - al.picking_id as picking_id, - al.company_id as company_id, - al.location_dest_id as location_dest_id, - al.product_qty, - al.out_qty as product_qty_out, - al.in_qty as product_qty_in, - al.partner_id as partner_id, - al.product_id as product_id, - al.state as state , - al.product_uom as product_uom, - al.categ_id as categ_id, - coalesce(al.type, 'other') as type, - al.stock_journal as stock_journal, - sum(al.in_value - al.out_value) as value - FROM (SELECT - CASE WHEN sp.type in ('out') THEN - sum(sm.product_qty * pu.factor / pu2.factor) - ELSE 0.0 - END AS out_qty, - CASE WHEN sp.type in ('in') THEN - sum(sm.product_qty * pu.factor / pu2.factor) - ELSE 0.0 - END AS in_qty, - CASE WHEN sp.type in ('out') THEN - sum(sm.product_qty * pu.factor / pu2.factor) * pt.standard_price - ELSE 0.0 - END AS out_value, - CASE WHEN sp.type in ('in') THEN - sum(sm.product_qty * pu.factor / pu2.factor) * pt.standard_price - ELSE 0.0 - END AS in_value, - min(sm.id) as sm_id, - sm.date as dp, - to_char(date_trunc('day',sm.date), 'YYYY') as curr_year, - to_char(date_trunc('day',sm.date), 'MM') as curr_month, - to_char(date_trunc('day',sm.date), 'YYYY-MM-DD') as curr_day, - avg(date(sm.date)-date(sm.create_date)) as curr_day_diff, - avg(date(sm.date_expected)-date(sm.create_date)) as curr_day_diff1, - avg(date(sm.date)-date(sm.date_expected)) as curr_day_diff2, + min(sm.id) as id, + date_trunc('day', sm.date) as date, + to_char(date_trunc('day',sm.date), 'YYYY') as year, + to_char(date_trunc('day',sm.date), 'MM') as month, + to_char(date_trunc('day',sm.date), 'YYYY-MM-DD') as day, + avg(date(sm.date)-date(sm.create_date)) as day_diff, + avg(date(sm.date_expected)-date(sm.create_date)) as day_diff1, + avg(date(sm.date)-date(sm.date_expected)) as day_diff2, sm.location_id as location_id, + sm.picking_id as picking_id, + sm.company_id as company_id, sm.location_dest_id as location_dest_id, sum(sm.product_qty) as product_qty, - pt.categ_id as categ_id , + sum( + (CASE WHEN sp.type in ('out') THEN + (sm.product_qty * pu.factor / pu2.factor) + ELSE 0.0 + END) + ) as product_qty_out, + sum( + (CASE WHEN sp.type in ('in') THEN + (sm.product_qty * pu.factor / pu2.factor) + ELSE 0.0 + END) + ) as product_qty_in, sm.partner_id as partner_id, sm.product_id as product_id, - sm.picking_id as picking_id, - sm.company_id as company_id, - sm.state as state, - sm.product_uom as product_uom, - sp.type as type, - sp.stock_journal_id AS stock_journal + sm.state as state, + sm.product_uom as product_uom, + pt.categ_id as categ_id , + coalesce(sp.type, 'other') as type, + sp.stock_journal_id AS stock_journal, + sum( + (CASE WHEN sp.type in ('in') THEN + (sm.product_qty * pu.factor / pu2.factor) * pt.standard_price + ELSE 0.0 + END) + - + (CASE WHEN sp.type in ('out') THEN + (sm.product_qty * pu.factor / pu2.factor) * pt.standard_price + ELSE 0.0 + END) + ) as value FROM stock_move sm LEFT JOIN stock_picking sp ON (sm.picking_id=sp.id) @@ -129,16 +112,11 @@ class report_stock_move(osv.osv): LEFT JOIN product_uom pu2 ON (sm.product_uom=pu2.id) LEFT JOIN product_template pt ON (pp.product_tmpl_id=pt.id) GROUP BY - sm.id,sp.type, sm.date,sm.partner_id, - sm.product_id,sm.state,sm.product_uom,sm.date_expected, - sm.product_id,pt.standard_price, sm.picking_id, sm.product_qty, - sm.company_id,sm.product_qty, sm.location_id,sm.location_dest_id,pu.factor,pt.categ_id, sp.stock_journal_id) - AS al - GROUP BY - al.out_qty,al.in_qty,al.curr_year,al.curr_month, - al.curr_day,al.curr_day_diff,al.curr_day_diff1,al.curr_day_diff2,al.dp,al.location_id,al.location_dest_id, - al.partner_id,al.product_id,al.state,al.product_uom, - al.picking_id,al.company_id,al.type,al.product_qty, al.categ_id, al.stock_journal + coalesce(sp.type, 'other'), date_trunc('day', sm.date), sm.partner_id, + sm.state, sm.product_uom, sm.date_expected, + sm.product_id, pt.standard_price, sm.picking_id, + sm.company_id, sm.location_id, sm.location_dest_id, pu.factor, pt.categ_id, sp.stock_journal_id, + year, month, day ) """) diff --git a/addons/stock/stock.py b/addons/stock/stock.py index d818ca794d5..1c3e8d41062 100644 --- a/addons/stock/stock.py +++ b/addons/stock/stock.py @@ -159,6 +159,10 @@ class stock_location(osv.osv): \n* Production: Virtual counterpart location for production operations: this location consumes the raw material and produces finished products """, select = True), # temporarily removed, as it's unused: 'allocation_method': fields.selection([('fifo', 'FIFO'), ('lifo', 'LIFO'), ('nearest', 'Nearest')], 'Allocation Method', required=True), + + # as discussed on bug 765559, the main purpose of this field is to allow sorting the list of locations + # according to the displayed names, and reversing that sort by clicking on a column. It does not work for + # translated values though - so it needs fixing. 'complete_name': fields.function(_complete_name, type='char', size=256, string="Location Name", store={'stock.location': (_get_sublocations, ['name', 'location_id'], 10)}), @@ -239,6 +243,9 @@ class stock_location(osv.osv): if location.chained_location_type == 'customer': if partner: result = partner.property_stock_customer + else: + loc_id = self.pool['res.partner'].default_get(cr, uid, ['property_stock_customer'], context=context)['property_stock_customer'] + result = self.pool['stock.location'].browse(cr, uid, loc_id, context=context) elif location.chained_location_type == 'fixed': result = location.chained_location_id if result: @@ -1263,17 +1270,17 @@ class stock_picking(osv.osv): context['currency_id'] = move_currency_id qty = uom_obj._compute_qty(cr, uid, product_uom, product_qty, product.uom_id.id) - if product.id in product_avail: - product_avail[product.id] += qty - else: + if product.id not in product_avail: + # keep track of stock on hand including processed lines not yet marked as done product_avail[product.id] = product.qty_available if qty > 0: new_price = currency_obj.compute(cr, uid, product_currency, - move_currency_id, product_price) + move_currency_id, product_price, round=False) new_price = uom_obj._compute_price(cr, uid, product_uom, new_price, product.uom_id.id) - if product.qty_available <= 0: + if product_avail[product.id] <= 0: + product_avail[product.id] = 0 new_std_price = new_price else: # Get the standard price @@ -1289,6 +1296,9 @@ class stock_picking(osv.osv): {'price_unit': product_price, 'price_currency_id': product_currency}) + product_avail[product.id] += qty + + for move in too_few: product_qty = move_product_qty[move.id] @@ -1705,7 +1715,8 @@ class stock_move(osv.osv): if location_xml_id: try: location_model, location_id = mod_obj.get_object_reference(cr, uid, 'stock', location_xml_id) - self.pool.get('stock.location').check_access_rule(cr, uid, [location_id], 'read', context=context) + with tools.mute_logger('openerp.osv.orm'): + self.pool.get('stock.location').check_access_rule(cr, uid, [location_id], 'read', context=context) except (orm.except_orm, ValueError): location_id = False @@ -1739,7 +1750,8 @@ class stock_move(osv.osv): if location_xml_id: try: location_model, location_id = mod_obj.get_object_reference(cr, uid, 'stock', location_xml_id) - self.pool.get('stock.location').check_access_rule(cr, uid, [location_id], 'read', context=context) + with tools.mute_logger('openerp.osv.orm'): + self.pool.get('stock.location').check_access_rule(cr, uid, [location_id], 'read', context=context) except (orm.except_orm, ValueError): location_id = False @@ -1963,12 +1975,14 @@ class stock_move(osv.osv): location_dest_id = 'stock_location_customers' try: source_location = mod_obj.get_object_reference(cr, uid, 'stock', location_source_id) - self.pool.get('stock.location').check_access_rule(cr, uid, [source_location[1]], 'read', context=context) + with tools.mute_logger('openerp.osv.orm'): + self.pool.get('stock.location').check_access_rule(cr, uid, [source_location[1]], 'read', context=context) except (orm.except_orm, ValueError): source_location = False try: dest_location = mod_obj.get_object_reference(cr, uid, 'stock', location_dest_id) - self.pool.get('stock.location').check_access_rule(cr, uid, [dest_location[1]], 'read', context=context) + with tools.mute_logger('openerp.osv.orm'): + self.pool.get('stock.location').check_access_rule(cr, uid, [dest_location[1]], 'read', context=context) except (orm.except_orm, ValueError): dest_location = False return {'value':{'location_id': source_location and source_location[1] or False, 'location_dest_id': dest_location and dest_location[1] or False}} @@ -2182,6 +2196,7 @@ class stock_move(osv.osv): while res: r = res.pop(0) + product_uos_qty = self.pool.get('stock.move').onchange_quantity(cr, uid, ids, move.product_id.id, r[0], move.product_id.uom_id.id, move.product_id.uos_id.id)['value']['product_uos_qty'] move_id = self.copy(cr, uid, move.id, {'product_uos_qty': product_uos_qty, 'product_qty': r[0], 'location_id': r[1]}) done.append(move_id) if done: @@ -2355,7 +2370,7 @@ class stock_move(osv.osv): { 'journal_id': j_id, 'line_id': move_lines, - 'ref': move.picking_id and move.picking_id.name}) + 'ref': move.picking_id and move.picking_id.name}, context=context) def action_done(self, cr, uid, ids, context=None): """ Makes the move done and if all moves are done, it will finish the picking. @@ -2693,7 +2708,7 @@ class stock_move(osv.osv): qty = uom_obj._compute_qty(cr, uid, product_uom, product_qty, product.uom_id.id) if qty > 0: new_price = currency_obj.compute(cr, uid, product_currency, - move_currency_id, product_price) + move_currency_id, product_price, round=False) new_price = uom_obj._compute_price(cr, uid, product_uom, new_price, product.uom_id.id) if product.qty_available <= 0: @@ -2905,7 +2920,8 @@ class stock_inventory_line(osv.osv): def _default_stock_location(self, cr, uid, context=None): try: location_model, location_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'stock', 'stock_location_stock') - self.pool.get('stock.location').check_access_rule(cr, uid, [location_id], 'read', context=context) + with tools.mute_logger('openerp.osv.orm'): + self.pool.get('stock.location').check_access_rule(cr, uid, [location_id], 'read', context=context) except (orm.except_orm, ValueError): location_id = False return location_id @@ -2948,7 +2964,8 @@ class stock_warehouse(osv.osv): def _default_lot_input_stock_id(self, cr, uid, context=None): try: lot_input_stock_model, lot_input_stock_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'stock', 'stock_location_stock') - self.pool.get('stock.location').check_access_rule(cr, uid, [lot_input_stock_id], 'read', context=context) + with tools.mute_logger('openerp.osv.orm'): + self.pool.get('stock.location').check_access_rule(cr, uid, [lot_input_stock_id], 'read', context=context) except (ValueError, orm.except_orm): # the user does not have read access on the location or it does not exists lot_input_stock_id = False @@ -2957,7 +2974,8 @@ class stock_warehouse(osv.osv): def _default_lot_output_id(self, cr, uid, context=None): try: lot_output_model, lot_output_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'stock', 'stock_location_output') - self.pool.get('stock.location').check_access_rule(cr, uid, [lot_output_id], 'read', context=context) + with tools.mute_logger('openerp.osv.orm'): + self.pool.get('stock.location').check_access_rule(cr, uid, [lot_output_id], 'read', context=context) except (ValueError, orm.except_orm): # the user does not have read access on the location or it does not exists lot_output_id = False @@ -3027,6 +3045,13 @@ class stock_picking_in(osv.osv): """Send the unsubscribe action on stock.picking model to match with subscribe""" return self.pool.get('stock.picking').message_unsubscribe(*args, **kwargs) + def default_get(self, cr, uid, fields_list, context=None): + # merge defaults from stock.picking with possible defaults defined on stock.picking.in + defaults = self.pool['stock.picking'].default_get(cr, uid, fields_list, context=context) + in_defaults = super(stock_picking_in, self).default_get(cr, uid, fields_list, context=context) + defaults.update(in_defaults) + return defaults + _columns = { 'backorder_id': fields.many2one('stock.picking.in', 'Back Order of', states={'done':[('readonly', True)], 'cancel':[('readonly',True)]}, help="If this shipment was split, then this field links to the shipment which contains the already processed part.", select=True), 'state': fields.selection( @@ -3100,6 +3125,13 @@ class stock_picking_out(osv.osv): """Send the unsubscribe action on stock.picking model to match with subscribe""" return self.pool.get('stock.picking').message_unsubscribe(*args, **kwargs) + def default_get(self, cr, uid, fields_list, context=None): + # merge defaults from stock.picking with possible defaults defined on stock.picking.out + defaults = self.pool['stock.picking'].default_get(cr, uid, fields_list, context=context) + out_defaults = super(stock_picking_out, self).default_get(cr, uid, fields_list, context=context) + defaults.update(out_defaults) + return defaults + _columns = { 'backorder_id': fields.many2one('stock.picking.out', 'Back Order of', states={'done':[('readonly', True)], 'cancel':[('readonly',True)]}, help="If this shipment was split, then this field links to the shipment which contains the already processed part.", select=True), 'state': fields.selection( diff --git a/addons/stock/stock_view.xml b/addons/stock/stock_view.xml index c48a7cf0f2b..99f2e738709 100644 --- a/addons/stock/stock_view.xml +++ b/addons/stock/stock_view.xml @@ -1251,7 +1251,7 @@ <field name="company_id" groups="base.group_multi_company" widget="selection"/> </group> <group name="origin_grp" string="Origin"> - <field name="origin"/> + <field name="origin" attrs="{'invisible': [('picking_id', '=', False)]}"/> <field name="picking_id" domain="[('type','=',type)]"/> <field name="type" on_change="onchange_move_type(type)"/> <field name="location_id" groups="stock.group_locations"/> diff --git a/addons/stock/tests/test_multicompany.py b/addons/stock/tests/test_multicompany.py index fb840a846ea..f0175791b50 100644 --- a/addons/stock/tests/test_multicompany.py +++ b/addons/stock/tests/test_multicompany.py @@ -56,7 +56,7 @@ class TestStockMulticompany(common.TransactionCase): try: self.stock_location.check_access_rule(cr, uid, [defaults[field]], 'read', context) except Exception, exc: - assert False, "unreadable location %s: %s" % (field, exc) + self.fail("unreadable location %s: %s" % (field, exc)) self.assertEqual(defaults['type'], type, "wrong move type") @@ -71,7 +71,7 @@ class TestStockMulticompany(common.TransactionCase): try: self.stock_location.check_access_rule(cr, uid, [result[field]], 'read', context) except Exception, exc: - assert False, "unreadable location %s: %s" % (field, exc) + self.fail("unreadable location %s: %s" % (field, exc)) def test_20_multicompany_default_stock_fill_inventory(self): @@ -82,7 +82,7 @@ class TestStockMulticompany(common.TransactionCase): try: self.stock_location.check_access_rule(cr, uid, [defaults['location_id']], 'read', context) except Exception, exc: - assert False, "unreadable source location: %s" % exc + self.fail("unreadable source location: %s" % exc) def test_30_multicompany_default_warehouse_location(self): @@ -95,4 +95,4 @@ class TestStockMulticompany(common.TransactionCase): try: self.stock_location.check_access_rule(cr, uid, [defaults[field]], 'read', context) except Exception, exc: - assert False, "unreadable default %s: %s" % (field, exc) + self.fail("unreadable default %s: %s" % (field, exc)) diff --git a/addons/stock/wizard/stock_fill_inventory.py b/addons/stock/wizard/stock_fill_inventory.py index b367e6069d8..2071fddffc0 100644 --- a/addons/stock/wizard/stock_fill_inventory.py +++ b/addons/stock/wizard/stock_fill_inventory.py @@ -21,6 +21,7 @@ from openerp.osv import fields, osv, orm from openerp.tools.translate import _ +from openerp.tools import mute_logger class stock_fill_inventory(osv.osv_memory): _name = "stock.fill.inventory" @@ -29,7 +30,8 @@ class stock_fill_inventory(osv.osv_memory): def _default_location(self, cr, uid, ids, context=None): try: location = self.pool.get('ir.model.data').get_object(cr, uid, 'stock', 'stock_location_stock') - location.check_access_rule('read', context=context) + with mute_logger('openerp.osv.orm'): + location.check_access_rule('read', context=context) location_id = location.id except (ValueError, orm.except_orm), e: return False diff --git a/addons/stock/wizard/stock_partial_picking.py b/addons/stock/wizard/stock_partial_picking.py index 8c2c5839989..50ddc2b88ed 100644 --- a/addons/stock/wizard/stock_partial_picking.py +++ b/addons/stock/wizard/stock_partial_picking.py @@ -51,7 +51,7 @@ class stock_partial_picking_line(osv.TransientModel): 'move_id' : fields.many2one('stock.move', "Move", ondelete='CASCADE'), 'wizard_id' : fields.many2one('stock.partial.picking', string="Wizard", ondelete='CASCADE'), 'update_cost': fields.boolean('Need cost update'), - 'cost' : fields.float("Cost", help="Unit Cost for this product line"), + 'cost' : fields.float("Cost", help="Unit Cost for this product line", digits_compute=dp.get_precision('Product Price')), 'currency' : fields.many2one('res.currency', string="Currency", help="Currency in which Unit cost is expressed", ondelete='CASCADE'), 'tracking': fields.function(_tracking, string='Tracking', type='boolean'), } diff --git a/addons/stock_invoice_directly/wizard/stock_invoice.py b/addons/stock_invoice_directly/wizard/stock_invoice.py index ba41de626da..c8ca4cdd8fd 100644 --- a/addons/stock_invoice_directly/wizard/stock_invoice.py +++ b/addons/stock_invoice_directly/wizard/stock_invoice.py @@ -31,9 +31,14 @@ class invoice_directly(osv.osv_memory): if context is None: context = {} result = super(invoice_directly, self).do_partial(cr, uid, ids, context) partial = self.browse(cr, uid, ids[0], context) + if partial.picking_id.state != 'done' and partial.picking_id.backorder_id: + # delivery is not finished, opening invoice on backorder + picking = partial.picking_id.backorder_id + else: + picking = partial.picking_id context.update(active_model='stock.picking', - active_ids=[partial.picking_id.id]) - if partial.picking_id.invoice_state == '2binvoiced': + active_ids=[picking.id]) + if picking.invoice_state == '2binvoiced': return { 'name': 'Create Invoice', 'view_type': 'form', diff --git a/addons/survey/report/survey_analysis_report.py b/addons/survey/report/survey_analysis_report.py index 581f52d49be..9301e0d2a2b 100644 --- a/addons/survey/report/survey_analysis_report.py +++ b/addons/survey/report/survey_analysis_report.py @@ -96,20 +96,20 @@ class survey_analysis(report_rml): <initialize> <paraStyle name="all" alignment="justify"/> </initialize> - <paraStyle name="answer_right" alignment="RIGHT" fontName="helvetica" fontSize="09.0" leftIndent="2.0"/> - <paraStyle name="Standard1" fontName="helvetica-bold" alignment="RIGHT" fontSize="09.0"/> + <paraStyle name="answer_right" alignment="RIGHT" fontName="Helvetica" fontSize="09.0" leftIndent="2.0"/> + <paraStyle name="Standard1" fontName="Helvetica-Bold" alignment="RIGHT" fontSize="09.0"/> <paraStyle name="Standard" alignment="LEFT" fontName="Helvetica-Bold" fontSize="11.0"/> <paraStyle name="header1" fontName="Helvetica" fontSize="11.0"/> - <paraStyle name="response" fontName="Helvetica-oblique" fontSize="9.5"/> - <paraStyle name="response-bold" fontName="Helvetica-bold" fontSize="9" alignment="RIGHT" /> - <paraStyle name="page" fontName="helvetica" fontSize="11.0" leftIndent="0.0"/> - <paraStyle name="question" fontName="helvetica-boldoblique" fontSize="10.0" leftIndent="3.0"/> + <paraStyle name="response" fontName="Helvetica-Oblique" fontSize="9.5"/> + <paraStyle name="response-bold" fontName="Helvetica-Bold" fontSize="9" alignment="RIGHT" /> + <paraStyle name="page" fontName="Helvetica" fontSize="11.0" leftIndent="0.0"/> + <paraStyle name="question" fontName="Helvetica-BoldOblique" fontSize="10.0" leftIndent="3.0"/> <paraStyle name="answer_bold" fontName="Helvetica-Bold" fontSize="09.0" leftIndent="2.0"/> - <paraStyle name="answer" fontName="helvetica" fontSize="09.0" leftIndent="2.0"/> - <paraStyle name="Title" fontName="helvetica" fontSize="20.0" leading="15" spaceBefore="6.0" spaceAfter="6.0" alignment="CENTER"/> + <paraStyle name="answer" fontName="Helvetica" fontSize="09.0" leftIndent="2.0"/> + <paraStyle name="title" fontName="Helvetica" fontSize="20.0" leading="15" spaceBefore="6.0" spaceAfter="6.0" alignment="CENTER"/> <paraStyle name="terp_tblheader_General_Centre" fontName="Helvetica-Bold" fontSize="9.0" leading="10" alignment="CENTER" spaceBefore="6.0" spaceAfter="6.0"/> <paraStyle name="terp_default_Centre_8" fontName="Helvetica" fontSize="9.0" leading="10" alignment="CENTER" spaceBefore="0.0" spaceAfter="0.0"/> - <paraStyle name="terp_default_Center_heading" fontName="Helvetica-bold" fontSize="9.0" leading="10" alignment="CENTER" spaceBefore="0.0" spaceAfter="0.0"/> + <paraStyle name="terp_default_Center_heading" fontName="Helvetica-Bold" fontSize="9.0" leading="10" alignment="CENTER" spaceBefore="0.0" spaceAfter="0.0"/> <paraStyle name="P2" fontName="Helvetica" fontSize="14.0" leading="15" spaceBefore="6.0" spaceAfter="6.0"/> </stylesheet> <images/> @@ -120,7 +120,7 @@ class survey_analysis(report_rml): for survey in surv_obj.browse(cr, uid, ids): rml += """<story> - <para style="Title">Answers Summary</para> + <para style="title">Answers Summary</para> <para style="Standard"><font></font></para> <para style="P2"> <font color="white"> </font> diff --git a/addons/survey/report/survey_browse_response.py b/addons/survey/report/survey_browse_response.py index 344de523628..7c4a5e98aa1 100644 --- a/addons/survey/report/survey_browse_response.py +++ b/addons/survey/report/survey_browse_response.py @@ -164,18 +164,18 @@ class survey_browse_response(report_rml): <initialize> <paraStyle name="all" alignment="justify"/> </initialize> - <paraStyle name="title" fontName="helvetica-bold" fontSize="18.0" leftIndent="0.0" textColor="white"/> - <paraStyle name="answer_right" alignment="RIGHT" fontName="helvetica" fontSize="09.0" leftIndent="2.0"/> - <paraStyle name="Standard1" fontName="helvetica-bold" alignment="RIGHT" fontSize="09.0"/> + <paraStyle name="title" fontName="Helvetica-Bold" fontSize="18.0" leftIndent="0.0" textColor="white"/> + <paraStyle name="answer_right" alignment="RIGHT" fontName="Helvetica" fontSize="09.0" leftIndent="2.0"/> + <paraStyle name="Standard1" fontName="Helvetica-Bold" alignment="RIGHT" fontSize="09.0"/> <paraStyle name="Standard" alignment="LEFT" fontName="Helvetica-Bold" fontSize="11.0"/> <paraStyle name="header1" fontName="Helvetica" fontSize="11.0"/> - <paraStyle name="response" fontName="Helvetica-oblique" fontSize="9.5"/> - <paraStyle name="page" fontName="helvetica" fontSize="11.0" leftIndent="0.0"/> - <paraStyle name="question" fontName="helvetica-boldoblique" fontSize="10.0" leftIndent="3.0"/> - <paraStyle name="answer_bold" fontName="Helvetica-Bold" fontSize="09.0" leftIndent="2.0"/> - <paraStyle name="answer" fontName="helvetica" fontSize="09.0" leftIndent="2.0"/> - <paraStyle name="answer1" fontName="helvetica" fontSize="09.0" leftIndent="2.0"/> - <paraStyle name="Title" fontName="helvetica" fontSize="20.0" leading="15" spaceBefore="6.0" spaceAfter="6.0" alignment="CENTER"/> + <paraStyle name="response" fontName="Helvetica-Oblique" fontSize="9.5"/> + <paraStyle name="page" fontName="Helvetica" fontSize="11.0" leftIndent="0.0"/> + <paraStyle name="question" fontName="Helvetica-BoldOblique" fontSize="10.0" leftIndent="3.0"/> + <paraStyle name="answer_Bold" fontName="Helvetica-Bold" fontSize="09.0" leftIndent="2.0"/> + <paraStyle name="answer" fontName="Helvetica" fontSize="09.0" leftIndent="2.0"/> + <paraStyle name="answer1" fontName="Helvetica" fontSize="09.0" leftIndent="2.0"/> + <paraStyle name="Title" fontName="Helvetica" fontSize="20.0" leading="15" spaceBefore="6.0" spaceAfter="6.0" alignment="CENTER"/> <paraStyle name="P2" fontName="Helvetica" fontSize="14.0" leading="15" spaceBefore="6.0" spaceAfter="6.0"/> <paraStyle name="comment" fontName="Helvetica" fontSize="14.0" leading="50" spaceBefore="0.0" spaceAfter="0.0"/> <paraStyle name="P1" fontName="Helvetica" fontSize="9.0" leading="12" spaceBefore="0.0" spaceAfter="1.0"/> @@ -186,8 +186,8 @@ class survey_browse_response(report_rml): <paraStyle name="terp_tblheader_General_Centre" fontName="Helvetica-Bold" fontSize="10.0" leading="10" alignment="LEFT" spaceBefore="6.0" spaceAfter="6.0"/> <paraStyle name="terp_tblheader_General_right_simple" fontName="Helvetica" fontSize="10.0" leading="10" alignment="RIGHT" spaceBefore="6.0" spaceAfter="6.0"/> <paraStyle name="terp_tblheader_General_right" fontName="Helvetica-Bold" fontSize="10.0" leading="10" alignment="RIGHT" spaceBefore="6.0" spaceAfter="6.0"/> - <paraStyle name="descriptive_text" fontName="helvetica-bold" fontSize="18.0" leftIndent="0.0" textColor="white"/> - <paraStyle name="descriptive_text_heading" fontName="helvetica-bold" fontSize="18.0" alignment="RIGHT" leftIndent="0.0" textColor="white"/> + <paraStyle name="descriptive_text" fontName="Helvetica-Bold" fontSize="18.0" leftIndent="0.0" textColor="white"/> + <paraStyle name="descriptive_text_heading" fontName="Helvetica-Bold" fontSize="18.0" alignment="RIGHT" leftIndent="0.0" textColor="white"/> </stylesheet> <images/> <story>""" diff --git a/addons/survey/report/survey_form.py b/addons/survey/report/survey_form.py index 1868faeee03..6a4f5fa4795 100644 --- a/addons/survey/report/survey_form.py +++ b/addons/survey/report/survey_form.py @@ -133,13 +133,13 @@ class survey_form(report_rml): <initialize> <paraStyle name="all" alignment="justify"/> </initialize> - <paraStyle name="response" fontName="Helvetica-oblique" fontSize="9.5"/> - <paraStyle name="page" fontName="helvetica-bold" fontSize="15.0" leftIndent="0.0" textColor="white"/> - <paraStyle name="title" fontName="helvetica-bold" fontSize="18.0" leading="15" leftIndent="0.0" textColor="white"/> - <paraStyle name="question" fontName="helvetica-boldoblique" fontSize="10.0" leftIndent="3.0"/> - <paraStyle name="answer" fontName="helvetica" fontSize="09.0" leftIndent="0.0"/> - <paraStyle name="descriptive_text" fontName="helvetica" fontSize="10.0" leftIndent="0.0"/> - <paraStyle name="answer_left" alignment="LEFT" fontName="helvetica-bold" fontSize="8.0" leftIndent="0.0"/> + <paraStyle name="response" fontName="Helvetica-Oblique" fontSize="9.5"/> + <paraStyle name="page" fontName="Helvetica-Bold" fontSize="15.0" leftIndent="0.0" textColor="white"/> + <paraStyle name="title" fontName="Helvetica-Bold" fontSize="18.0" leading="15" leftIndent="0.0" textColor="white"/> + <paraStyle name="question" fontName="Helvetica-BoldOblique" fontSize="10.0" leftIndent="3.0"/> + <paraStyle name="answer" fontName="Helvetica" fontSize="09.0" leftIndent="0.0"/> + <paraStyle name="descriptive_text" fontName="Helvetica" fontSize="10.0" leftIndent="0.0"/> + <paraStyle name="answer_left" alignment="LEFT" fontName="Helvetica-Bold" fontSize="8.0" leftIndent="0.0"/> <paraStyle name="P2" fontName="Helvetica" fontSize="14.0" leading="15" spaceBefore="6.0" spaceAfter="6.0"/> <paraStyle name="comment" fontName="Helvetica" fontSize="14.0" leading="50" spaceBefore="0.0" spaceAfter="0.0"/> <paraStyle name="P1" fontName="Helvetica" fontSize="9.0" leading="12" spaceBefore="0.0" spaceAfter="1.0"/> diff --git a/addons/survey/survey_view.xml b/addons/survey/survey_view.xml index f6973a5bc30..2c43364871d 100644 --- a/addons/survey/survey_view.xml +++ b/addons/survey/survey_view.xml @@ -1127,7 +1127,7 @@ <field name="survey_id"/> <field name="user_id" on_change="on_change_user(user_id)" context="{'default_groups_ref': ['base.group_user', 'base.group_partner_manager', 'base.group_survey_user']}"/> <field name="email"/> - <field name="response" readonly="1"/> + <field name="response" readonly="1" attrs="{'invisible':[('response','=',False)]}"/> </group> </sheet> </form> diff --git a/addons/warning/warning.py b/addons/warning/warning.py index 8e1168e5d35..d582478113a 100644 --- a/addons/warning/warning.py +++ b/addons/warning/warning.py @@ -281,7 +281,7 @@ class purchase_order_line(osv.osv): name=False, price_unit=False, notes=False, context=None): warning = {} if not product: - return {'value': {'price_unit': 0.0, 'name':'','notes':'', 'product_uom' : False}, 'domain':{'product_uom':[]}} + return {'value': {'price_unit': price_unit or 0.0, 'name': name or '', 'notes': notes or '', 'product_uom' : uom or False}, 'domain':{'product_uom':[]}} product_obj = self.pool.get('product.product') product_info = product_obj.browse(cr, uid, product) title = False |
