[MERGE] Forward-port saas-5 up to d9cda97
This commit is contained in:
commit
34bfa1b44f
|
@ -49,7 +49,8 @@ class OAuthLogin(Home):
|
|||
def list_providers(self):
|
||||
try:
|
||||
provider_obj = request.registry.get('auth.oauth.provider')
|
||||
providers = provider_obj.search_read(request.cr, SUPERUSER_ID, [('enabled', '=', True)])
|
||||
providers = provider_obj.search_read(request.cr, SUPERUSER_ID, [('enabled', '=', True), ('auth_endpoint', '!=', False), ('validation_endpoint', '!=', False)])
|
||||
# TODO in forwardport: remove conditions on 'auth_endpoint' and 'validation_endpoint' when these fields will be 'required' in model
|
||||
except Exception:
|
||||
providers = []
|
||||
for provider in providers:
|
||||
|
|
|
@ -201,6 +201,14 @@ class res_users(osv.Model):
|
|||
partner.write({'signup_token': False, 'signup_type': False, 'signup_expiration': False})
|
||||
|
||||
partner_user = partner.user_ids and partner.user_ids[0] or False
|
||||
|
||||
# avoid overwriting existing (presumably correct) values with geolocation data
|
||||
if partner.country_id or partner.zip or partner.city:
|
||||
values.pop('city', None)
|
||||
values.pop('country_id', None)
|
||||
if partner.lang:
|
||||
values.pop('lang', None)
|
||||
|
||||
if partner_user:
|
||||
# user exists, modify it according to values
|
||||
values.pop('login', None)
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
import calendar
|
||||
from datetime import date
|
||||
from dateutil import relativedelta
|
||||
import json
|
||||
|
||||
from openerp import tools
|
||||
from openerp.osv import fields, osv
|
||||
|
@ -30,8 +33,8 @@ class crm_case_section(osv.Model):
|
|||
res[id] = dict()
|
||||
lead_domain = lead_pre_domain + [('section_id', '=', id)]
|
||||
opp_domain = opp_pre_domain + [('section_id', '=', id)]
|
||||
res[id]['monthly_open_leads'] = self.__get_bar_values(cr, uid, obj, lead_domain, ['create_date'], 'create_date_count', 'create_date', context=context)
|
||||
res[id]['monthly_planned_revenue'] = self.__get_bar_values(cr, uid, obj, opp_domain, ['planned_revenue', 'date_deadline'], 'planned_revenue', 'date_deadline', context=context)
|
||||
res[id]['monthly_open_leads'] = json.dumps(self.__get_bar_values(cr, uid, obj, lead_domain, ['create_date'], 'create_date_count', 'create_date', context=context))
|
||||
res[id]['monthly_planned_revenue'] = json.dumps(self.__get_bar_values(cr, uid, obj, opp_domain, ['planned_revenue', 'date_deadline'], 'planned_revenue', 'date_deadline', context=context))
|
||||
return res
|
||||
|
||||
_columns = {
|
||||
|
|
|
@ -235,18 +235,8 @@ class document_directory(osv.osv):
|
|||
_parent(dir_id, path)
|
||||
return path
|
||||
|
||||
def _check_recursion(self, cr, uid, ids, context=None):
|
||||
level = 100
|
||||
while len(ids):
|
||||
cr.execute('select distinct parent_id from document_directory where id in ('+','.join(map(str,ids))+')')
|
||||
ids = filter(None, map(lambda x:x[0], cr.fetchall()))
|
||||
if not level:
|
||||
return False
|
||||
level -= 1
|
||||
return True
|
||||
|
||||
_constraints = [
|
||||
(_check_recursion, 'Error! You cannot create recursive directories.', ['parent_id'])
|
||||
(osv.osv._check_recursion, 'Error! You cannot create recursive directories.', ['parent_id'])
|
||||
]
|
||||
|
||||
def onchange_content_id(self, cr, uid, ids, ressource_type_id):
|
||||
|
|
|
@ -522,8 +522,8 @@ class hr_employee(osv.osv):
|
|||
where
|
||||
h.state='validate' and
|
||||
s.limit=False and
|
||||
h.employee_id in (%s)
|
||||
group by h.employee_id"""% (','.join(map(str,ids)),) )
|
||||
h.employee_id in %s
|
||||
group by h.employee_id""", (tuple(ids),))
|
||||
res = cr.dictfetchall()
|
||||
remaining = {}
|
||||
for r in res:
|
||||
|
|
|
@ -32,7 +32,7 @@
|
|||
templates to target objects.
|
||||
""",
|
||||
'website': 'http://www.openerp.com',
|
||||
'depends' : ['account_accountant'],
|
||||
'depends' : ['account'],
|
||||
'data': [],
|
||||
'demo': [],
|
||||
'installable': True,
|
||||
|
|
|
@ -132,15 +132,9 @@
|
|||
border-radius: 3px;
|
||||
margin: 0px;
|
||||
padding-left: 3px;
|
||||
padding-right: 15px;
|
||||
padding-right: 5px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
.openerp .oe_mail .oe_mail_vote_count .oe_e{
|
||||
position: absolute;
|
||||
bottom: 1px;
|
||||
right: 2px;
|
||||
font-size: 26px;
|
||||
}
|
||||
|
||||
/* c) Message action icons */
|
||||
|
||||
|
|
|
@ -341,7 +341,7 @@
|
|||
<span t-name="mail.thread.message.vote">
|
||||
<span class="oe_mail_vote_count" t-if='widget.vote_nb > 0'>
|
||||
<t t-esc='widget.vote_nb' />
|
||||
<span class='oe_e'>8</span>
|
||||
<i class="fa fa-thumbs-o-up"></i>
|
||||
</span>
|
||||
<a href='#' class="oe_msg_vote">
|
||||
<t t-if="!widget.has_voted">like</t>
|
||||
|
|
|
@ -62,8 +62,7 @@
|
|||
<field name="search_view_id" ref="view_campaign_analysis_search"/>
|
||||
</record>
|
||||
|
||||
<menuitem name="Marketing" id="base.menu_report_marketing" parent="base.menu_reporting" sequence="45"/>
|
||||
<menuitem action="action_campaign_analysis_all" id="menu_action_campaign_analysis_all" parent="base.menu_report_marketing" sequence="2"/>
|
||||
<menuitem action="action_campaign_analysis_all" id="menu_action_campaign_analysis_all" parent="base.marketing_reporting_menu" sequence="20"/>
|
||||
|
||||
</data>
|
||||
</openerp>
|
||||
|
|
|
@ -113,26 +113,38 @@ class MassMailingCampaign(osv.Model):
|
|||
|
||||
def _get_statistics(self, cr, uid, ids, name, arg, context=None):
|
||||
""" Compute statistics of the mass mailing campaign """
|
||||
Statistics = self.pool['mail.mail.statistics']
|
||||
results = dict.fromkeys(ids, False)
|
||||
for cid in ids:
|
||||
stat_ids = Statistics.search(cr, uid, [('mass_mailing_campaign_id', '=', cid)], context=context)
|
||||
stats = Statistics.browse(cr, uid, stat_ids, context=context)
|
||||
results[cid] = {
|
||||
'total': len(stats),
|
||||
'failed': len([s for s in stats if not s.scheduled is False and s.sent is False and not s.exception is False]),
|
||||
'scheduled': len([s for s in stats if not s.scheduled is False and s.sent is False and s.exception is False]),
|
||||
'sent': len([s for s in stats if not s.sent is False]),
|
||||
'opened': len([s for s in stats if not s.opened is False]),
|
||||
'replied': len([s for s in stats if not s.replied is False]),
|
||||
'bounced': len([s for s in stats if not s.bounced is False]),
|
||||
}
|
||||
results[cid]['delivered'] = results[cid]['sent'] - results[cid]['bounced']
|
||||
results[cid]['received_ratio'] = 100.0 * results[cid]['delivered'] / (results[cid]['total'] or 1)
|
||||
results[cid]['opened_ratio'] = 100.0 * results[cid]['opened'] / (results[cid]['total'] or 1)
|
||||
results[cid]['replied_ratio'] = 100.0 * results[cid]['replied'] / (results[cid]['total'] or 1)
|
||||
results = {}
|
||||
cr.execute("""
|
||||
SELECT
|
||||
c.id as campaign_id,
|
||||
COUNT(s.id) AS total,
|
||||
COUNT(CASE WHEN s.sent is not null THEN 1 ELSE null END) AS sent,
|
||||
COUNT(CASE WHEN s.scheduled is not null AND s.sent is null AND s.exception is null THEN 1 ELSE null END) AS scheduled,
|
||||
COUNT(CASE WHEN s.scheduled is not null AND s.sent is null AND s.exception is not null THEN 1 ELSE null END) AS failed,
|
||||
COUNT(CASE WHEN s.id is not null AND s.bounced is null THEN 1 ELSE null END) AS delivered,
|
||||
COUNT(CASE WHEN s.opened is not null THEN 1 ELSE null END) AS opened,
|
||||
COUNT(CASE WHEN s.replied is not null THEN 1 ELSE null END) AS replied ,
|
||||
COUNT(CASE WHEN s.bounced is not null THEN 1 ELSE null END) AS bounced
|
||||
FROM
|
||||
mail_mail_statistics s
|
||||
RIGHT JOIN
|
||||
mail_mass_mailing_campaign c
|
||||
ON (c.id = s.mass_mailing_campaign_id)
|
||||
WHERE
|
||||
c.id IN %s
|
||||
GROUP BY
|
||||
c.id
|
||||
""", (tuple(ids), ))
|
||||
for row in cr.dictfetchall():
|
||||
results[row.pop('campaign_id')] = row
|
||||
total = row['total'] or 1
|
||||
row['delivered'] = row['sent'] - row['bounced']
|
||||
row['received_ratio'] = 100.0 * row['delivered'] / total
|
||||
row['opened_ratio'] = 100.0 * row['opened'] / total
|
||||
row['replied_ratio'] = 100.0 * row['replied'] / total
|
||||
return results
|
||||
|
||||
|
||||
_columns = {
|
||||
'name': fields.char('Name', required=True),
|
||||
'stage_id': fields.many2one('mail.mass_mailing.stage', 'Stage', required=True),
|
||||
|
@ -283,26 +295,38 @@ class MassMailing(osv.Model):
|
|||
|
||||
def _get_statistics(self, cr, uid, ids, name, arg, context=None):
|
||||
""" Compute statistics of the mass mailing campaign """
|
||||
Statistics = self.pool['mail.mail.statistics']
|
||||
results = dict.fromkeys(ids, False)
|
||||
for mid in ids:
|
||||
stat_ids = Statistics.search(cr, uid, [('mass_mailing_id', '=', mid)], context=context)
|
||||
stats = Statistics.browse(cr, uid, stat_ids, context=context)
|
||||
results[mid] = {
|
||||
'total': len(stats),
|
||||
'failed': len([s for s in stats if not s.scheduled is False and s.sent is False and not s.exception is False]),
|
||||
'scheduled': len([s for s in stats if not s.scheduled is False and s.sent is False and s.exception is False]),
|
||||
'sent': len([s for s in stats if not s.sent is False]),
|
||||
'opened': len([s for s in stats if not s.opened is False]),
|
||||
'replied': len([s for s in stats if not s.replied is False]),
|
||||
'bounced': len([s for s in stats if not s.bounced is False]),
|
||||
}
|
||||
results[mid]['delivered'] = results[mid]['sent'] - results[mid]['bounced']
|
||||
results[mid]['received_ratio'] = 100.0 * results[mid]['delivered'] / (results[mid]['total'] or 1)
|
||||
results[mid]['opened_ratio'] = 100.0 * results[mid]['opened'] / (results[mid]['total'] or 1)
|
||||
results[mid]['replied_ratio'] = 100.0 * results[mid]['replied'] / (results[mid]['total'] or 1)
|
||||
results = {}
|
||||
cr.execute("""
|
||||
SELECT
|
||||
m.id as mailing_id,
|
||||
COUNT(s.id) AS total,
|
||||
COUNT(CASE WHEN s.sent is not null THEN 1 ELSE null END) AS sent,
|
||||
COUNT(CASE WHEN s.scheduled is not null AND s.sent is null AND s.exception is null THEN 1 ELSE null END) AS scheduled,
|
||||
COUNT(CASE WHEN s.scheduled is not null AND s.sent is null AND s.exception is not null THEN 1 ELSE null END) AS failed,
|
||||
COUNT(CASE WHEN s.id is not null AND s.bounced is null THEN 1 ELSE null END) AS delivered,
|
||||
COUNT(CASE WHEN s.opened is not null THEN 1 ELSE null END) AS opened,
|
||||
COUNT(CASE WHEN s.replied is not null THEN 1 ELSE null END) AS replied ,
|
||||
COUNT(CASE WHEN s.bounced is not null THEN 1 ELSE null END) AS bounced
|
||||
FROM
|
||||
mail_mail_statistics s
|
||||
RIGHT JOIN
|
||||
mail_mass_mailing m
|
||||
ON (m.id = s.mass_mailing_id)
|
||||
WHERE
|
||||
m.id IN %s
|
||||
GROUP BY
|
||||
m.id
|
||||
""", (tuple(ids), ))
|
||||
for row in cr.dictfetchall():
|
||||
results[row.pop('mailing_id')] = row
|
||||
total = row['total'] or 1
|
||||
row['delivered'] = row['sent'] - row['bounced']
|
||||
row['received_ratio'] = 100.0 * row['delivered'] / total
|
||||
row['opened_ratio'] = 100.0 * row['opened'] / total
|
||||
row['replied_ratio'] = 100.0 * row['replied'] / total
|
||||
return results
|
||||
|
||||
|
||||
def _get_mailing_model(self, cr, uid, context=None):
|
||||
res = []
|
||||
for model_name in self.pool:
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
<field invisible="1" name="product_uom_id" on_change="on_change_unit_amount(product_id, unit_amount, False, product_uom_id,journal_id)"/>
|
||||
<field invisible="1" name="amount"/>
|
||||
<field invisible="1" name="general_account_id"/>
|
||||
<field invisible="1" name="to_invoice"/>
|
||||
</tree>
|
||||
</field>
|
||||
</page>
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
import calendar
|
||||
from datetime import date
|
||||
from dateutil import relativedelta
|
||||
import json
|
||||
|
||||
from openerp import tools
|
||||
from openerp.osv import fields, osv
|
||||
|
@ -20,9 +21,9 @@ class crm_case_section(osv.osv):
|
|||
for id in ids:
|
||||
res[id] = dict()
|
||||
created_domain = [('section_id', '=', id), ('state', '=', 'draft'), ('date_order', '>=', date_begin), ('date_order', '<=', date_end)]
|
||||
res[id]['monthly_quoted'] = self.__get_bar_values(cr, uid, obj, created_domain, ['amount_total', 'date_order'], 'amount_total', 'date_order', context=context)
|
||||
res[id]['monthly_quoted'] = json.dumps(self.__get_bar_values(cr, uid, obj, created_domain, ['amount_total', 'date_order'], 'amount_total', 'date_order', context=context))
|
||||
validated_domain = [('section_id', '=', id), ('state', 'not in', ['draft', 'sent', 'cancel']), ('date_order', '>=', date_begin), ('date_order', '<=', date_end)]
|
||||
res[id]['monthly_confirmed'] = self.__get_bar_values(cr, uid, obj, validated_domain, ['amount_total', 'date_order'], 'amount_total', 'date_order', context=context)
|
||||
res[id]['monthly_confirmed'] = json.dumps(self.__get_bar_values(cr, uid, obj, validated_domain, ['amount_total', 'date_order'], 'amount_total', 'date_order', context=context))
|
||||
return res
|
||||
|
||||
def _get_invoices_data(self, cr, uid, ids, field_name, arg, context=None):
|
||||
|
@ -33,11 +34,11 @@ class crm_case_section(osv.osv):
|
|||
date_end = month_begin.replace(day=calendar.monthrange(month_begin.year, month_begin.month)[1]).strftime(tools.DEFAULT_SERVER_DATE_FORMAT)
|
||||
for id in ids:
|
||||
created_domain = [('section_id', '=', id), ('state', 'not in', ['draft', 'cancel']), ('date', '>=', date_begin), ('date', '<=', date_end)]
|
||||
res[id] = self.__get_bar_values(cr, uid, obj, created_domain, ['price_total', 'date'], 'price_total', 'date', context=context)
|
||||
res[id] = json.dumps(self.__get_bar_values(cr, uid, obj, created_domain, ['price_total', 'date'], 'price_total', 'date', context=context))
|
||||
return res
|
||||
|
||||
_columns = {
|
||||
'use_quotations': fields.boolean('Opportunities', help="Check this box to manage quotations in this sales team."),
|
||||
'use_quotations': fields.boolean('Quotations', help="Check this box to manage quotations in this sales team."),
|
||||
'invoiced_forecast': fields.integer(string='Invoice Forecast',
|
||||
help="Forecast of the invoice revenue for the current month. This is the amount the sales \n"
|
||||
"team should invoice this month. It is used to compute the progression ratio \n"
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
|
||||
from datetime import date, datetime
|
||||
from dateutil import relativedelta
|
||||
|
||||
import json
|
||||
import time
|
||||
|
||||
from openerp.osv import fields, osv
|
||||
|
@ -1930,7 +1930,6 @@ class stock_move(osv.osv):
|
|||
result = {
|
||||
'product_uom_qty': 0.00
|
||||
}
|
||||
warning = {}
|
||||
|
||||
if (not product_id) or (product_uos_qty <= 0.0):
|
||||
result['product_uos_qty'] = 0.0
|
||||
|
@ -1939,21 +1938,14 @@ class stock_move(osv.osv):
|
|||
product_obj = self.pool.get('product.product')
|
||||
uos_coeff = product_obj.read(cr, uid, product_id, ['uos_coeff'])
|
||||
|
||||
# Warn if the quantity was decreased
|
||||
for move in self.read(cr, uid, ids, ['product_uos_qty']):
|
||||
if product_uos_qty < move['product_uos_qty']:
|
||||
warning.update({
|
||||
'title': _('Warning: No Back Order'),
|
||||
'message': _("By changing the quantity here, you accept the "
|
||||
"new quantity as complete: OpenERP will not "
|
||||
"automatically generate a Back Order.")})
|
||||
break
|
||||
# No warning if the quantity was decreased to avoid double warnings:
|
||||
# The clients should call onchange_quantity too anyway
|
||||
|
||||
if product_uos and product_uom and (product_uom != product_uos):
|
||||
result['product_uom_qty'] = product_uos_qty / uos_coeff['uos_coeff']
|
||||
else:
|
||||
result['product_uom_qty'] = product_uos_qty
|
||||
return {'value': result, 'warning': warning}
|
||||
return {'value': result}
|
||||
|
||||
def onchange_product_id(self, cr, uid, ids, prod_id=False, loc_id=False, loc_dest_id=False, partner_id=False):
|
||||
""" On change of product id, if finds UoM, UoS, quantity and UoS quantity.
|
||||
|
@ -4102,12 +4094,12 @@ class stock_picking_type(osv.osv):
|
|||
tristates = []
|
||||
for picking in picking_obj.browse(cr, uid, picking_ids, context=context):
|
||||
if picking.date_done > picking.date:
|
||||
tristates.insert(0, {'tooltip': picking.name or '' + _(': Late'), 'value': -1})
|
||||
tristates.insert(0, {'tooltip': picking.name or '' + ": " + _('Late'), 'value': -1})
|
||||
elif picking.backorder_id:
|
||||
tristates.insert(0, {'tooltip': picking.name or '' + _(': Backorder exists'), 'value': 0})
|
||||
tristates.insert(0, {'tooltip': picking.name or '' + ": " + _('Backorder exists'), 'value': 0})
|
||||
else:
|
||||
tristates.insert(0, {'tooltip': picking.name or '' + _(': OK'), 'value': 1})
|
||||
res[picking_type_id] = tristates
|
||||
tristates.insert(0, {'tooltip': picking.name or '' + ": " + _('OK'), 'value': 1})
|
||||
res[picking_type_id] = json.dumps(tristates)
|
||||
return res
|
||||
|
||||
def _get_picking_count(self, cr, uid, ids, field_names, arg, context=None):
|
||||
|
|
|
@ -365,7 +365,7 @@ class survey_survey(osv.Model):
|
|||
for cell in product(rows.keys(), answers.keys()):
|
||||
res[cell] = 0
|
||||
for input_line in question.user_input_line_ids:
|
||||
if input_line.answer_type == 'suggestion' and not(current_filters) or input_line.user_input_id.id in current_filters:
|
||||
if input_line.answer_type == 'suggestion' and (not(current_filters) or input_line.user_input_id.id in current_filters):
|
||||
res[(input_line.value_suggested_row.id, input_line.value_suggested.id)] += 1
|
||||
result_summary = {'answers': answers, 'rows': rows, 'result': res}
|
||||
|
||||
|
@ -998,6 +998,8 @@ class survey_user_input_line(osv.Model):
|
|||
def __get_mark(self, cr, uid, value_suggested, context=None):
|
||||
try:
|
||||
mark = self.pool.get('survey.label').browse(cr, uid, int(value_suggested), context=context).quizz_mark
|
||||
except AttributeError:
|
||||
mark = 0.0
|
||||
except KeyError:
|
||||
mark = 0.0
|
||||
except ValueError:
|
||||
|
@ -1145,7 +1147,7 @@ class survey_user_input_line(osv.Model):
|
|||
|
||||
comment_answer = post.pop(("%s_%s" % (answer_tag, 'comment')), '').strip()
|
||||
if comment_answer:
|
||||
vals.update({'answer_type': 'text', 'value_text': comment_answer})
|
||||
vals.update({'answer_type': 'text', 'value_text': comment_answer, 'skipped': False})
|
||||
self.create(cr, uid, vals, context=context)
|
||||
|
||||
return True
|
||||
|
|
|
@ -38,6 +38,9 @@ instance.web_kanban.GaugeWidget = instance.web_kanban.AbstractField.extend({
|
|||
var title = this.$node.html() || this.field.string;
|
||||
// current gauge value
|
||||
var val = this.field.value;
|
||||
if (_.isArray(JSON.parse(val))) {
|
||||
val = JSON.parse(val);
|
||||
}
|
||||
var value = _.isArray(val) && val.length ? val[val.length-1]['value'] : val;
|
||||
// displayed value under gauge
|
||||
var gauge_value = value;
|
||||
|
|
|
@ -11,8 +11,9 @@ instance.web_kanban.SparklineBarWidget = instance.web_kanban.AbstractField.exten
|
|||
var self = this;
|
||||
var title = this.$node.html() || this.field.string;
|
||||
setTimeout(function () {
|
||||
var value = _.pluck(self.field.value, 'value');
|
||||
var tooltips = _.pluck(self.field.value, 'tooltip');
|
||||
var field_value = JSON.parse(self.field.value);
|
||||
var value = _.pluck(field_value, 'value');
|
||||
var tooltips = _.pluck(field_value, 'tooltip');
|
||||
var suffix = self.options.tooltip_suffix || "";
|
||||
var tooltipFormat = self.options.type == 'tristate' && '{{offset:offset}}' + suffix || '{{offset:offset}} {{value:value}}' + suffix
|
||||
var sparkline_options = _.extend({
|
||||
|
|
|
@ -34,7 +34,7 @@ class Binary(openerp.http.Controller):
|
|||
_scheme, _netloc, path, params, query, fragment = urlparse(url)
|
||||
# media.linkedin.com is the master domain for LinkedIn media (replicated to CDNs),
|
||||
# so forcing it should always work and prevents abusing this method to load arbitrary URLs
|
||||
url = urlunparse(('http', 'media.linkedin.com', path, params, query, fragment))
|
||||
url = urlunparse(('http', 'media.licdn.com', path, params, query, fragment))
|
||||
bfile = urllib2.urlopen(url)
|
||||
return base64.b64encode(bfile.read())
|
||||
|
||||
|
|
|
@ -269,7 +269,7 @@ class website(osv.osv):
|
|||
# Compute Pager
|
||||
page_count = int(math.ceil(float(total) / step))
|
||||
|
||||
page = max(1, min(int(page), page_count))
|
||||
page = max(1, min(int(page if str(page).isdigit() else 1), page_count))
|
||||
scope -= 1
|
||||
|
||||
pmin = max(page - int(math.floor(scope/2)), 1)
|
||||
|
|
|
@ -393,7 +393,7 @@
|
|||
<table class="table js_kanban">
|
||||
<thead>
|
||||
<tr>
|
||||
<t t-set="width" t-valuef="{{ round(100.0 / len(objects), 2) }}%"/>
|
||||
<t t-set="width" t-valuef="{{ round(100.0 / (len(objects) if objects else 1), 2) }}%"/>
|
||||
<t t-foreach="objects" t-as="obj">
|
||||
<th t-att-width="width">
|
||||
<div t-field="obj['column_id'].name" class="text-center"></div>
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
import base64
|
||||
|
||||
from openerp.tools.translate import _
|
||||
|
||||
from openerp.addons.web import http
|
||||
from openerp.addons.web.http import request
|
||||
from openerp import SUPERUSER_ID
|
||||
|
@ -53,6 +52,8 @@ class contactus(http.Controller):
|
|||
elif field_name not in _TECHNICAL: # allow to add some free fields or blacklisted field like ID
|
||||
post_description.append("%s: %s" % (field_name, field_value))
|
||||
|
||||
if "name" not in kwargs and values.get("contact_name"): # if kwarg.name is empty, it's an error, we cannot copy the contact_name
|
||||
values["name"] = values.get("contact_name")
|
||||
# fields validation : Check that required field from model crm_lead exists
|
||||
error = set(field for field in _REQUIRED if not kwargs.get(field))
|
||||
|
||||
|
|
|
@ -24,6 +24,7 @@ from datetime import datetime, timedelta
|
|||
from dateutil.relativedelta import relativedelta
|
||||
|
||||
import werkzeug.urls
|
||||
from werkzeug.exceptions import NotFound
|
||||
|
||||
from openerp import http
|
||||
from openerp import tools
|
||||
|
@ -165,6 +166,16 @@ class website_event(http.Controller):
|
|||
'event': event,
|
||||
'main_object': event
|
||||
}
|
||||
|
||||
if '.' not in page:
|
||||
page = 'website_event.%s' % page
|
||||
|
||||
try:
|
||||
request.website.get_template(page)
|
||||
except ValueError, e:
|
||||
# page not found
|
||||
raise NotFound
|
||||
|
||||
return request.website.render(page, values)
|
||||
|
||||
@http.route(['/event/<model("event.event"):event>'], type='http', auth="public", website=True)
|
||||
|
|
|
@ -16,5 +16,5 @@ Delivery Costs
|
|||
],
|
||||
'demo': [],
|
||||
'qweb': [],
|
||||
'installable': False,
|
||||
'installable': True,
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@ class website_sale(openerp.addons.website_sale.controllers.main.website_sale):
|
|||
@http.route(['/shop/payment'], type='http', auth="public", website=True)
|
||||
def payment(self, **post):
|
||||
cr, uid, context = request.cr, request.uid, request.context
|
||||
order = self.get_order()
|
||||
order = request.website.sale_get_order(context=context)
|
||||
carrier_id = post.get('carrier_id')
|
||||
if carrier_id:
|
||||
carrier_id = int(carrier_id)
|
||||
|
|
|
@ -592,7 +592,7 @@ class res_partner(osv.osv, format_address):
|
|||
""" Supported syntax:
|
||||
- 'Raoul <raoul@grosbedon.fr>': will find name and email address
|
||||
- otherwise: default, everything is set as the name """
|
||||
emails = tools.email_split(text)
|
||||
emails = tools.email_split(text.replace(' ',','))
|
||||
if emails:
|
||||
email = emails[0]
|
||||
name = text[:text.index(email)].replace('"', '').replace('<', '').strip()
|
||||
|
|
|
@ -158,8 +158,8 @@
|
|||
<div class="oe_edit_only">
|
||||
<field name="use_parent_address" class="oe_inline"
|
||||
on_change="onchange_address(use_parent_address, parent_id)"
|
||||
attrs="{'invisible': [('parent_id','=', False),('use_parent_address','=',False)]}"/>
|
||||
<label for="use_parent_address" attrs="{'invisible': [('parent_id','=', False),('use_parent_address','=',False)]}"/>
|
||||
attrs="{'invisible': ['|', ('is_company', '=', True),('parent_id', '=', False)]}"/>
|
||||
<label for="use_parent_address" attrs="{'invisible': ['|', ('is_company', '=', True), ('parent_id', '=', False)]}"/>
|
||||
</div>
|
||||
<button name="open_parent" type="object" string="(edit company address)" class="oe_link oe_edit_only"
|
||||
attrs="{'invisible': ['|',('parent_id','=', False),('use_parent_address','=',False)]}"/>
|
||||
|
|
|
@ -379,7 +379,11 @@ class _rml_canvas(object):
|
|||
v = utils.attr_get(node, ['x','y'])
|
||||
text=self._textual(node, **v)
|
||||
text = utils.xml2str(text)
|
||||
self.canvas.drawString(text=text, **v)
|
||||
try:
|
||||
self.canvas.drawString(text=text, **v)
|
||||
except TypeError:
|
||||
_logger.error("Bad RML: <drawString> tag requires attributes 'x' and 'y'!")
|
||||
raise
|
||||
|
||||
def _drawCenteredString(self, node):
|
||||
v = utils.attr_get(node, ['x','y'])
|
||||
|
@ -1005,10 +1009,10 @@ class _rml_template(object):
|
|||
story_cnt = 0
|
||||
for node_story in node_stories:
|
||||
if story_cnt > 0:
|
||||
# Reset Page Number with new story tag
|
||||
fis.append(PageReset())
|
||||
fis.append(platypus.PageBreak())
|
||||
fis += r.render(node_story)
|
||||
# Reset Page Number with new story tag
|
||||
fis.append(PageReset())
|
||||
story_cnt += 1
|
||||
try:
|
||||
if self.localcontext and self.localcontext.get('internal_header',False):
|
||||
|
|
Loading…
Reference in New Issue