[MERGE] Forward-port 9.0 up to 9a3e9291ffa1509f4afa0f35f9d8d71b6e0932d1
This commit is contained in:
@@ -27,10 +27,6 @@ var JournalDashboardGraph = kanban_widgets.AbstractField.extend({
|
||||
margin: {'left': 0, 'right': 0, 'top': 0, 'bottom': 0},
|
||||
showYAxis: false,
|
||||
showLegend: false,
|
||||
tooltips: true,
|
||||
tooltipContent: function(key, x, y, e, graph) {
|
||||
return self.create_tooltip(x, y, e);
|
||||
},
|
||||
});
|
||||
self.chart.xAxis
|
||||
.tickFormat(function(d) {
|
||||
@@ -55,11 +51,7 @@ var JournalDashboardGraph = kanban_widgets.AbstractField.extend({
|
||||
.y(function(d) { return d.value })
|
||||
.showValues(false)
|
||||
.showYAxis(false)
|
||||
.margin({'left': 0, 'right': 0, 'top': 0, 'bottom': 40})
|
||||
.tooltips(true)
|
||||
.tooltipContent(function(key, x, y, e, graph) {
|
||||
return self.create_tooltip(x, y, e);
|
||||
});
|
||||
.margin({'left': 0, 'right': 0, 'top': 0, 'bottom': 40});
|
||||
|
||||
self.chart.xAxis.axisLabel(self.data[0].title);
|
||||
self.chart.yAxis.tickFormat(d3.format(',.2f'));
|
||||
@@ -94,23 +86,6 @@ var JournalDashboardGraph = kanban_widgets.AbstractField.extend({
|
||||
}
|
||||
},
|
||||
|
||||
create_tooltip: function(x, y, e){
|
||||
var header = _.findWhere(e.series.values, {x: x})
|
||||
header = header && header.name || x;
|
||||
|
||||
var $tooltip = $('<div>').addClass('o_tooltip');
|
||||
|
||||
$('<b>')
|
||||
.addClass('o_tooltip_title')
|
||||
.html(header)
|
||||
.appendTo($tooltip)
|
||||
$('<div>')
|
||||
.addClass('o_tooltip_content')
|
||||
.html('Balance ' + y)
|
||||
.appendTo($tooltip)
|
||||
return $tooltip[0].outerHTML;
|
||||
},
|
||||
|
||||
destroy: function(){
|
||||
nv.utils.offWindowResize(this.on_resize);
|
||||
this._super();
|
||||
|
||||
@@ -37,14 +37,6 @@
|
||||
|
||||
.o_kanban_graph_section {
|
||||
.o-position-absolute(auto, 0, 0, 0);
|
||||
|
||||
.nvtooltip {
|
||||
padding: 0 8px;
|
||||
.o_tooltip_content {
|
||||
color: @odoo-brand-primary;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
.o_graph_linechart {
|
||||
> svg {
|
||||
height: 75px;
|
||||
|
||||
@@ -128,7 +128,7 @@
|
||||
<label for="partner_id" string="Vendor" attrs="{'invisible': [('type','!=','purchase')]}"/>
|
||||
</div>
|
||||
<field name="partner_id" string="Partner" nolabel="1"/>
|
||||
<field name="invoice_id" string="Invoice"/>
|
||||
<field name="invoice_id" string="Invoice" options="{'no_create': True}"/>
|
||||
</group>
|
||||
</group>
|
||||
<notebook colspan="4">
|
||||
|
||||
@@ -47,7 +47,6 @@
|
||||
<form string="Sales Tags">
|
||||
<group>
|
||||
<field name="name"/>
|
||||
<field name="team_id"/>
|
||||
<field name="color"/>
|
||||
</group>
|
||||
</form>
|
||||
@@ -60,7 +59,6 @@
|
||||
<field name="arch" type="xml">
|
||||
<tree string="Sales Tags">
|
||||
<field name="name"/>
|
||||
<field name="team_id"/>
|
||||
<field name="color"/>
|
||||
</tree>
|
||||
</field>
|
||||
@@ -371,7 +369,7 @@
|
||||
help="No salesperson"/>
|
||||
<separator />
|
||||
<filter string="Important Messages" name="message_needaction" domain="[('message_needaction','=',True)]"/>
|
||||
<filter string="Archived" domain="[('active', '=', False)]"/>
|
||||
<filter string="Archived" name="archived" domain="[('active', '=', False)]"/>
|
||||
<separator />
|
||||
<filter string="Followup" name="activities"
|
||||
domain="[('date_action', '<=', datetime.datetime.now().replace(hour=23, minute=59, second=59))]"/>
|
||||
@@ -380,15 +378,15 @@
|
||||
name='not_opt_out' domain="[('opt_out', '=', False)]"
|
||||
help="Leads that did not ask not to be included in mass mailing campaigns"/>
|
||||
<group expand="0" string="Group By">
|
||||
<filter string="Salesperson" domain="[]" context="{'group_by':'user_id'}"/>
|
||||
<filter string="Sales Team" domain="[]" context="{'group_by':'team_id'}"/>
|
||||
<filter string="Stage" domain="[]" context="{'group_by':'stage_id'}"/>
|
||||
<filter string="Salesperson" name="groupby_salesperson" domain="[]" context="{'group_by':'user_id'}"/>
|
||||
<filter string="Sales Team" name="groupby_team" domain="[]" context="{'group_by':'team_id'}"/>
|
||||
<filter string="Stage" name="groupby_stage" domain="[]" context="{'group_by':'stage_id'}"/>
|
||||
<filter name="customer" string="Customer" help="Partner" domain="[]" context="{'group_by':'partner_id'}"/>
|
||||
<filter string="Campaign" domain="[]" context="{'group_by':'campaign_id'}"/>
|
||||
<filter string="Source" domain="[]" context="{'group_by':'source_id'}"/>
|
||||
<filter string="Channel" domain="[]" context="{'group_by':'medium_id'}"/>
|
||||
<filter string="Creation Month" domain="[]" context="{'group_by':'create_date'}"/>
|
||||
<filter string="Expected Closing" domain="[]" context="{'group_by':'date_deadline'}"/>
|
||||
<filter string="Campaign" name="groupby_campaign" domain="[]" context="{'group_by':'campaign_id'}"/>
|
||||
<filter string="Source" name="groupby_source" domain="[]" context="{'group_by':'source_id'}"/>
|
||||
<filter string="Channel" name="groupby_channel" domain="[]" context="{'group_by':'medium_id'}"/>
|
||||
<filter string="Creation Month" name="groupby_createmonth" domain="[]" context="{'group_by':'create_date'}"/>
|
||||
<filter string="Expected Closing" name="groupby_expectclosing" domain="[]" context="{'group_by':'date_deadline'}"/>
|
||||
</group>
|
||||
</search>
|
||||
</field>
|
||||
|
||||
@@ -857,11 +857,9 @@ class google_calendar(osv.AbstractModel):
|
||||
if actSrc == 'GG':
|
||||
try:
|
||||
self.delete_an_event(cr, uid, current_event[0], context=context)
|
||||
except Exception, e:
|
||||
error = json.loads(e.read())
|
||||
error_nr = error.get('error', {}).get('code')
|
||||
except urllib2.HTTPError, e:
|
||||
# if already deleted from gmail or never created
|
||||
if error_nr in (404, 410,):
|
||||
if e.code in (404, 410,):
|
||||
pass
|
||||
else:
|
||||
raise e
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
# Translation of Odoo Server.
|
||||
# This file contains the translation of the following modules:
|
||||
# * im_livechat
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Odoo Server 9.0\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2015-10-09 09:20+0000\n"
|
||||
"PO-Revision-Date: 2015-10-09 09:20+0000\n"
|
||||
"Last-Translator: <>\n"
|
||||
"Language-Team: \n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: \n"
|
||||
"Plural-Forms: \n"
|
||||
|
||||
#. module: im_livechat
|
||||
#: model:ir.model.fields,field_description:im_livechat.field_im_livechat_report_channel_partner_id
|
||||
msgid "Opertor"
|
||||
msgstr "Operator"
|
||||
@@ -199,7 +199,7 @@ class ImLivechatChannel(models.Model):
|
||||
if country_code:
|
||||
country_ids = self.env['res.country'].sudo().search([('code', '=', country_code)])
|
||||
if country_ids:
|
||||
country_id = country_ids[0]
|
||||
country_id = country_ids[0].id
|
||||
# extract url
|
||||
url = request.httprequest.headers.get('Referer') or request.httprequest.base_url
|
||||
# find the match rule for the given country and url
|
||||
|
||||
@@ -82,8 +82,9 @@ class MailMail(osv.Model):
|
||||
body = tools.append_content_to_html(base, body, plaintext=False, container_tag='div')
|
||||
# resolve relative image url to absolute for outlook.com
|
||||
def _sub_relative2absolute(match):
|
||||
return match.group(1) + urlparse.urljoin(domain, match.group(2)) + '"'
|
||||
body = re.sub('(<img(?=\s)[^>]*\ssrc=")(/[^/][^"]+)"', _sub_relative2absolute, body)
|
||||
return match.group(1) + urlparse.urljoin(domain, match.group(2))
|
||||
body = re.sub('(<img(?=\s)[^>]*\ssrc=")(/[^/][^"]+)', _sub_relative2absolute, body)
|
||||
body = re.sub(r'(<[^>]+\bstyle="[^"]+\burl\(\'?)(/[^/\'][^\'")]+)', _sub_relative2absolute, body)
|
||||
|
||||
# generate tracking URL
|
||||
if mail.statistics_ids:
|
||||
|
||||
@@ -393,7 +393,7 @@ class PaymentTransaction(osv.Model):
|
||||
custom_method_name = '%s_compute_fees' % acquirer.provider
|
||||
if hasattr(Acquirer, custom_method_name):
|
||||
fees = getattr(Acquirer, custom_method_name)(
|
||||
cr, uid, acquirer.id, values.get('amount', 0.0), values.get('currency_id'), values.get('country_id'), context=None)
|
||||
cr, uid, acquirer.id, values.get('amount', 0.0), values.get('currency_id'), values.get('partner_country_id'), context=None)
|
||||
values['fees'] = float_round(fees, 2)
|
||||
|
||||
# custom create
|
||||
|
||||
@@ -40,8 +40,8 @@
|
||||
<field name="config_id"/>
|
||||
<field name="partner_id"/>
|
||||
<field name="user_id"/>
|
||||
<field name="partner_id"/>
|
||||
<field name="user_id"/>
|
||||
<field name="product_id"/>
|
||||
<field name="product_categ_id"/>
|
||||
<group expand="1" string="Group By">
|
||||
<filter string="Salesperson" name="User" context="{'group_by':'user_id'}"/>
|
||||
<filter string="Product Category" context="{'group_by':'product_categ_id'}"/>
|
||||
|
||||
@@ -1809,6 +1809,11 @@ var PaymentScreenWidget = ScreenWidget.extend({
|
||||
return;
|
||||
}
|
||||
|
||||
// get rid of payment lines with an amount of 0, because
|
||||
// since accounting v9 we cannot have bank statement lines
|
||||
// with an amount of 0
|
||||
order.clean_empty_paymentlines();
|
||||
|
||||
var plines = order.get_paymentlines();
|
||||
for (var i = 0; i < plines.length; i++) {
|
||||
if (plines[i].get_type() === 'bank' && plines[i].get_amount() < 0) {
|
||||
|
||||
@@ -79,8 +79,7 @@
|
||||
t-field-options='{"widget": "monetary", "display_currency": "statement.currency_id"}'/>
|
||||
</td>
|
||||
<td class="text-right" groups="base.group_multi_currency">
|
||||
<span t-field="statement.currency"
|
||||
t-field-options='{"widget": "monetary", "display_currency": "statement.currency"}'/>
|
||||
<span t-field="statement.currency"/>
|
||||
</td>
|
||||
<td class="text-right"><span t-field="statement.state"/></td>
|
||||
</tr>
|
||||
|
||||
@@ -140,8 +140,8 @@ class wizard_user(osv.osv_memory):
|
||||
wizard_user.user_id.write({'active': True, 'groups_id': [(4, portal.id)]})
|
||||
# prepare for the signup process
|
||||
wizard_user.user_id.partner_id.signup_prepare()
|
||||
self._send_email(cr, uid, wizard_user.id, context)
|
||||
wizard_user.refresh()
|
||||
self._send_email(cr, uid, wizard_user.id, context)
|
||||
else:
|
||||
# remove the user (if it exists) from the portal group
|
||||
if wizard_user.user_id and (portal in wizard_user.user_id.groups_id):
|
||||
|
||||
@@ -66,7 +66,7 @@
|
||||
<field name="product_id"/>
|
||||
<field name="user_id"/>
|
||||
<group expand="0" string="Extended Filters">
|
||||
<field name="categ_id"/>
|
||||
<field name="categ_id" filter_domain="[('categ_id', 'child_of', self)]"/>
|
||||
<field name="company_id" groups="base.group_multi_company"/>
|
||||
</group>
|
||||
<group expand="1" string="Group By">
|
||||
|
||||
@@ -62,4 +62,4 @@ class crm_team(osv.Model):
|
||||
}
|
||||
|
||||
def update_invoiced_target(self, cr, uid, id, value, context=None):
|
||||
return self.write(cr, uid, [id], {'invoiced_target': round(float(value))}, context=context)
|
||||
return self.write(cr, uid, [id], {'invoiced_target': round(float(value or 0))}, context=context)
|
||||
|
||||
@@ -2341,7 +2341,7 @@ class stock_move(osv.osv):
|
||||
context = context or {}
|
||||
quant_obj = self.pool.get("stock.quant")
|
||||
uom_obj = self.pool['product.uom']
|
||||
to_assign_moves = []
|
||||
to_assign_moves = set()
|
||||
main_domain = {}
|
||||
todo_moves = []
|
||||
operations = set()
|
||||
@@ -2349,12 +2349,12 @@ class stock_move(osv.osv):
|
||||
if move.state not in ('confirmed', 'waiting', 'assigned'):
|
||||
continue
|
||||
if move.location_id.usage in ('supplier', 'inventory', 'production'):
|
||||
to_assign_moves.append(move.id)
|
||||
to_assign_moves.add(move.id)
|
||||
#in case the move is returned, we want to try to find quants before forcing the assignment
|
||||
if not move.origin_returned_move_id:
|
||||
continue
|
||||
if move.product_id.type == 'consu':
|
||||
to_assign_moves.append(move.id)
|
||||
to_assign_moves.add(move.id)
|
||||
continue
|
||||
else:
|
||||
todo_moves.append(move)
|
||||
@@ -2419,7 +2419,7 @@ class stock_move(osv.osv):
|
||||
#force assignation of consumable products and incoming from supplier/inventory/production
|
||||
# Do not take force_assign as it would create pack operations
|
||||
if to_assign_moves:
|
||||
self.write(cr, uid, to_assign_moves, {'state': 'assigned'}, context=context)
|
||||
self.write(cr, uid, list(to_assign_moves), {'state': 'assigned'}, context=context)
|
||||
if not no_prepare:
|
||||
self.check_recompute_pack_op(cr, uid, ids, context=context)
|
||||
|
||||
@@ -2783,12 +2783,15 @@ class stock_move(osv.osv):
|
||||
'product_uom_qty': uom_qty,
|
||||
'procure_method': 'make_to_stock',
|
||||
'restrict_lot_id': restrict_lot_id,
|
||||
'restrict_partner_id': restrict_partner_id,
|
||||
'split_from': move.id,
|
||||
'procurement_id': move.procurement_id.id,
|
||||
'move_dest_id': move.move_dest_id.id,
|
||||
'origin_returned_move_id': move.origin_returned_move_id.id,
|
||||
}
|
||||
|
||||
if restrict_partner_id:
|
||||
defaults['restrict_partner_id'] = restrict_partner_id
|
||||
|
||||
if context.get('source_location_id'):
|
||||
defaults['location_id'] = context['source_location_id']
|
||||
new_move = self.copy(cr, uid, move.id, defaults, context=context)
|
||||
|
||||
@@ -44,7 +44,7 @@ class stock_change_product_qty(osv.osv_memory):
|
||||
try:
|
||||
self.pool.get('stock.location').check_access_rule(cr, uid, [location_id], 'read', context=context)
|
||||
except (AccessError):
|
||||
pass
|
||||
location_id = False
|
||||
res['location_id'] = location_id
|
||||
return res
|
||||
|
||||
|
||||
@@ -182,13 +182,13 @@ class stock_landed_cost(osv.osv):
|
||||
if not cost.valuation_adjustment_lines or not self._check_sum(cr, uid, cost, context=context):
|
||||
raise UserError(_('You cannot validate a landed cost which has no valid valuation adjustments lines. Did you click on Compute?'))
|
||||
move_id = self._create_account_move(cr, uid, cost, context=context)
|
||||
quant_dict = {}
|
||||
for line in cost.valuation_adjustment_lines:
|
||||
if not line.move_id:
|
||||
continue
|
||||
per_unit = line.final_cost / line.quantity
|
||||
diff = per_unit - line.former_cost_per_unit
|
||||
quants = [quant for quant in line.move_id.quant_ids]
|
||||
quant_dict = {}
|
||||
for quant in quants:
|
||||
if quant.id not in quant_dict:
|
||||
quant_dict[quant.id] = quant.cost + diff
|
||||
|
||||
@@ -189,7 +189,7 @@
|
||||
<group>
|
||||
<field name="split_method"/>
|
||||
<field name="standard_price"/>
|
||||
<field name="property_account_expense_id"/>
|
||||
<field name="property_account_expense_id" domain="[('internal_type','=','other'),('deprecated', '=', False)]"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="company_id" groups="base.group_multi_company" options="{'no_create': True}"/>
|
||||
|
||||
@@ -108,7 +108,7 @@ class purchase_order(osv.Model):
|
||||
class account_invoice(osv.osv):
|
||||
_inherit = 'account.invoice'
|
||||
|
||||
@api.onchange('partner_id')
|
||||
@api.onchange('partner_id', 'company_id')
|
||||
def _onchange_partner_id(self):
|
||||
result = super(account_invoice, self)._onchange_partner_id()
|
||||
partner = self.partner_id
|
||||
|
||||
@@ -34,7 +34,6 @@ import openerp
|
||||
import openerp.modules.registry
|
||||
from openerp.addons.base.ir.ir_qweb import AssetsBundle, QWebTemplateNotFound
|
||||
from openerp.modules import get_module_resource
|
||||
from openerp.service import model as service_model
|
||||
from openerp.tools import topological_sort
|
||||
from openerp.tools.translate import _
|
||||
from openerp.tools import ustr
|
||||
@@ -964,10 +963,7 @@ class DataSet(http.Controller):
|
||||
if method.startswith('_'):
|
||||
raise AccessError(_("Underscore prefixed methods cannot be remotely called"))
|
||||
|
||||
@service_model.check
|
||||
def checked_call(__dbname, *args, **kwargs):
|
||||
return getattr(request.registry.get(model), method)(request.cr, request.uid, *args, **kwargs)
|
||||
return checked_call(request.db, *args, **kwargs)
|
||||
return getattr(request.registry.get(model), method)(request.cr, request.uid, *args, **kwargs)
|
||||
|
||||
@http.route('/web/dataset/call', type='json', auth="user")
|
||||
def call(self, model, method, args, domain_id=None, context_id=None):
|
||||
|
||||
Vendored
+4854
-3787
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
+12633
-13779
File diff suppressed because it is too large
Load Diff
@@ -812,7 +812,7 @@ var BufferedDataSet = DataSetStatic.extend({
|
||||
_.each(ids, function (id) {
|
||||
self.get_cache(id).to_delete = true;
|
||||
});
|
||||
this.set_ids(_.without(this.ids, _.pluck(_.filter(this.cache, function (c) {return c.to_delete;}), 'id')));
|
||||
this.set_ids(_.difference(this.ids, _.pluck(_.filter(this.cache, function (c) {return c.to_delete;}), 'id')));
|
||||
this.trigger("dataset_changed", ids, callback, error_callback);
|
||||
return $.async_when({result: true}).done(callback);
|
||||
},
|
||||
|
||||
@@ -780,7 +780,7 @@ var FieldBarChart = common.AbstractField.extend({
|
||||
.margin({top: 0, right: 0, bottom: 0, left: 0})
|
||||
.tooltips(false)
|
||||
.showValues(false)
|
||||
.transitionDuration(350)
|
||||
.transition(350)
|
||||
.showXAxis(false)
|
||||
.showYAxis(false);
|
||||
|
||||
|
||||
@@ -158,7 +158,7 @@ return Widget.extend({
|
||||
var chart = nv.models.multiBarChart();
|
||||
chart.options({
|
||||
delay: 250,
|
||||
transitionDuration: 10,
|
||||
transition: 10,
|
||||
showLegend: true,
|
||||
showXAxis: true,
|
||||
showYAxis: true,
|
||||
@@ -212,7 +212,7 @@ return Widget.extend({
|
||||
var chart = nv.models.pieChart();
|
||||
chart.options({
|
||||
delay: 250,
|
||||
transitionDuration: 100,
|
||||
transition: 100,
|
||||
color: d3.scale.category10().range(),
|
||||
});
|
||||
|
||||
|
||||
@@ -72,6 +72,9 @@ var ProgressBar = Widget.extend({
|
||||
|
||||
on_change_input: function(e) {
|
||||
var $input = $(e.target);
|
||||
if(e.type === 'change' && !$input.is(':focus')) {
|
||||
return;
|
||||
}
|
||||
if(isNaN($input.val())) {
|
||||
this.do_warn(_t("Wrong value entered!"), _t("Only Integer Value should be valid."));
|
||||
} else {
|
||||
|
||||
@@ -216,8 +216,13 @@
|
||||
<t t-raw="0"/>
|
||||
</div>
|
||||
<div class="btn-group btn-group-sm oe-pager-buttons" style="display: inline-block">
|
||||
<a class="fa fa-chevron-left btn btn-default oe-pager-button" accesskey="O" type="button" data-pager-action="previous"></a>
|
||||
<a class="fa fa-chevron-right btn btn-default oe-pager-button" accesskey="P" type="button" data-pager-action="next"></a>
|
||||
<!-- accesskeys not wanted in X2Many widgets -->
|
||||
<t t-if="!widget.x2m">
|
||||
<t t-set="att_prev" t-value="{'accesskey': 'O'}" />
|
||||
<t t-set="att_next" t-value="{'accesskey': 'P'}" />
|
||||
</t>
|
||||
<a class="fa fa-chevron-left btn btn-default oe-pager-button" t-att="att_prev" type="button" data-pager-action="previous"></a>
|
||||
<a class="fa fa-chevron-right btn btn-default oe-pager-button" t-att="att_next" type="button" data-pager-action="next"></a>
|
||||
</div>
|
||||
</span>
|
||||
|
||||
@@ -361,7 +366,7 @@
|
||||
<t t-name="ListView.buttons">
|
||||
<t t-if="!widget.no_leaf and widget.options.action_buttons !== false and widget.options.addable and widget.is_action_enabled('create')">
|
||||
<div class="oe_list_buttons">
|
||||
<button type="button" class="btn btn-sm btn-primary o_list_button_add">
|
||||
<button type="button" class="btn btn-sm btn-primary o_list_button_add" accesskey="C">
|
||||
<t t-esc="widget.options.addable"/>
|
||||
</button>
|
||||
</div>
|
||||
@@ -402,10 +407,10 @@
|
||||
</tr>
|
||||
<t t-extend="ListView.buttons">
|
||||
<t t-jquery="button.o_list_button_add" t-operation="after">
|
||||
<button type="button" class="btn btn-primary btn-sm o_list_button_save">
|
||||
<button type="button" class="btn btn-primary btn-sm o_list_button_save" accesskey="S">
|
||||
Save
|
||||
</button>
|
||||
<button type="button" class="btn btn-default btn-sm o_list_button_discard">
|
||||
<button type="button" class="btn btn-default btn-sm o_list_button_discard" accesskey="D">
|
||||
Discard
|
||||
</button>
|
||||
</t>
|
||||
@@ -1425,8 +1430,13 @@
|
||||
<div t-name="Pager">
|
||||
<span class="o_pager_value"></span> / <span class="o_pager_limit"></span>
|
||||
<span class="btn-group btn-group-sm">
|
||||
<button type="button" class="fa fa-chevron-left btn btn-icon o_pager_previous" accesskey="O"/>
|
||||
<button type="button" class="fa fa-chevron-right btn btn-icon o_pager_next" accesskey="P"/>
|
||||
<!-- accesskeys not wanted in X2Many widgets -->
|
||||
<t t-if="!widget.getParent().x2m">
|
||||
<t t-set="att_prev" t-value="{'accesskey': 'O'}" />
|
||||
<t t-set="att_next" t-value="{'accesskey': 'P'}" />
|
||||
</t>
|
||||
<button type="button" class="fa fa-chevron-left btn btn-icon o_pager_previous" t-att="att_prev"/>
|
||||
<button type="button" class="fa fa-chevron-right btn btn-icon o_pager_next" t-att="att_next"/>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -248,8 +248,7 @@ var BuildingBlock = Widget.extend({
|
||||
});
|
||||
|
||||
$styles.addClass("hidden");
|
||||
globalSelector = {
|
||||
closest: function ($from) {
|
||||
globalSelector.closest = function ($from) {
|
||||
var $temp;
|
||||
var $target;
|
||||
var len = selector.length;
|
||||
@@ -260,8 +259,8 @@ var BuildingBlock = Widget.extend({
|
||||
}
|
||||
}
|
||||
return $target;
|
||||
},
|
||||
all: function ($from) {
|
||||
};
|
||||
globalSelector.all = function ($from) {
|
||||
var $target;
|
||||
var len = selector.length;
|
||||
for (var i = 0; i<len; i++) {
|
||||
@@ -269,8 +268,8 @@ var BuildingBlock = Widget.extend({
|
||||
else $target = $target.add(selector[i].all($from));
|
||||
}
|
||||
return $target;
|
||||
},
|
||||
is: function ($from) {
|
||||
};
|
||||
globalSelector.is = function ($from) {
|
||||
var len = selector.length;
|
||||
for (var i = 0; i<len; i++) {
|
||||
if (selector[i].is($from)) {
|
||||
@@ -278,7 +277,6 @@ var BuildingBlock = Widget.extend({
|
||||
}
|
||||
}
|
||||
return false;
|
||||
},
|
||||
};
|
||||
|
||||
var number = 0;
|
||||
@@ -318,11 +316,11 @@ var BuildingBlock = Widget.extend({
|
||||
$("body").toggleClass("editor_has_snippets", !!number);
|
||||
|
||||
// select all default text to edit (if snippet default text)
|
||||
/*self.$snippets.find('.oe_snippet_body, .oe_snippet_body *')
|
||||
self.$snippets.find('.oe_snippet_body, .oe_snippet_body *')
|
||||
.contents()
|
||||
.filter(function() {
|
||||
return this.nodeType === 3 && this.textContent.match(/\S/);
|
||||
}).parent().addClass("o_default_snippet_text");*/
|
||||
}).parent().addClass("o_default_snippet_text");
|
||||
$(document).on("mouseup", ".o_default_snippet_text", function (event) {
|
||||
$(event.target).selectContent();
|
||||
});
|
||||
@@ -1149,8 +1147,9 @@ var Editor = Class.extend({
|
||||
});
|
||||
|
||||
var data = {
|
||||
'Class': BuildingBlock,
|
||||
'globalSelector': globalSelector,
|
||||
Class: BuildingBlock,
|
||||
Editor: Editor,
|
||||
globalSelector: globalSelector,
|
||||
};
|
||||
return data;
|
||||
|
||||
|
||||
@@ -814,17 +814,6 @@ range.WrappedRange.prototype.reRange = function (keep_end, isNotBreakable) {
|
||||
|
||||
return new range.WrappedRange(sc, so, ec, eo);
|
||||
};
|
||||
range.WrappedRange.prototype.deleteContents = function (towrite) {
|
||||
var prevBP = dom.removeBetween(this.sc, this.so, this.ec, this.eo, towrite);
|
||||
|
||||
$(dom.node(prevBP.sc)).trigger("click"); // trigger click to disable and reanable editor and image handler
|
||||
return new range.WrappedRange(
|
||||
prevBP.sc,
|
||||
prevBP.so,
|
||||
prevBP.ec,
|
||||
prevBP.eo
|
||||
);
|
||||
};
|
||||
// isOnImg: judge whether range is an image node or not
|
||||
range.WrappedRange.prototype.isOnImg = function () {
|
||||
var nb = 0;
|
||||
@@ -1242,10 +1231,13 @@ $.summernote.pluginEvents.delete = function (event, editor, layoutInfo) {
|
||||
if (!r.isCollapsed()) {
|
||||
if (dom.isCell(dom.node(r.sc)) || dom.isCell(dom.node(r.ec))) {
|
||||
remove_table_content(r);
|
||||
return range.create(r.ec, 0).select();
|
||||
range.create(r.ec, 0).select();
|
||||
} else {
|
||||
r = r.deleteContents();
|
||||
r.select();
|
||||
}
|
||||
r = r.deleteContents();
|
||||
r.select();
|
||||
event.preventDefault();
|
||||
return false;
|
||||
}
|
||||
|
||||
var target = r.ec;
|
||||
@@ -1378,10 +1370,13 @@ $.summernote.pluginEvents.backspace = function (event, editor, layoutInfo) {
|
||||
if (!r.isCollapsed()) {
|
||||
if (dom.isCell(dom.node(r.sc)) || dom.isCell(dom.node(r.ec))) {
|
||||
remove_table_content(r);
|
||||
return range.create(r.sc, dom.nodeLength(r.sc)).select();
|
||||
range.create(r.sc, dom.nodeLength(r.sc)).select();
|
||||
} else {
|
||||
r = r.deleteContents();
|
||||
r.select();
|
||||
}
|
||||
r = r.deleteContents();
|
||||
r.select();
|
||||
event.preventDefault();
|
||||
return false;
|
||||
}
|
||||
|
||||
var target = r.sc;
|
||||
|
||||
@@ -132,7 +132,7 @@ Tour.register({
|
||||
},
|
||||
{
|
||||
waitFor: '#editable_area > section .row > div:first:has( font:last:containsExact(Bat) )',
|
||||
element: '.note-air-popover button[data-event="undo"]',
|
||||
element: '.note-image-popover button[data-event="undo"]',
|
||||
title: "undo",
|
||||
},
|
||||
{
|
||||
|
||||
@@ -285,6 +285,9 @@ var ImageDialog = Widget.extend({
|
||||
o.id = +o.url.match(/\/web\/content\/([0-9]*)/, '')[1];
|
||||
}
|
||||
this.parent.$(".pager > li").click(function (e) {
|
||||
if(!self.$el.is(':visible')) {
|
||||
return;
|
||||
}
|
||||
e.preventDefault();
|
||||
var $target = $(e.currentTarget);
|
||||
if ($target.hasClass('disabled')) {
|
||||
@@ -303,7 +306,6 @@ var ImageDialog = Widget.extend({
|
||||
var img = _.select(this.images, function (v) { return v.id == attachment.id;});
|
||||
if (img.length) {
|
||||
this.images.splice(this.images.indexOf(img[0]),1);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
this.images = [];
|
||||
@@ -421,11 +423,11 @@ var ImageDialog = Widget.extend({
|
||||
var $button = this.$('button.filepicker');
|
||||
if (!error) {
|
||||
$button.addClass('btn-success');
|
||||
this.set_image(attachment);
|
||||
} else {
|
||||
this.$('form').addClass('has-error')
|
||||
.find('.help-block').text(error);
|
||||
$button.addClass('btn-danger');
|
||||
this.set_image(attachment, error);
|
||||
}
|
||||
|
||||
if (!this.options.select_images) {
|
||||
|
||||
@@ -24,10 +24,10 @@
|
||||
// Generate all margins for one size, given
|
||||
// the name of the margin and intended size
|
||||
.o-margins-define(@name, @size: @name) {
|
||||
.mt@{name} {margin-top: ~"@{size}px";}
|
||||
.mb@{name} {margin-bottom: ~"@{size}px";}
|
||||
.ml@{name} {margin-left: ~"@{size}px";}
|
||||
.mr@{name} {margin-right: ~"@{size}px";}
|
||||
.mt@{name} {margin-top: ~"@{size}px"!important;}
|
||||
.mb@{name} {margin-bottom: ~"@{size}px"!important;}
|
||||
.ml@{name} {margin-left: ~"@{size}px"!important;}
|
||||
.mr@{name} {margin-right: ~"@{size}px"!important;}
|
||||
}
|
||||
|
||||
// Generate all margins
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<t t-name="KanbanView.buttons">
|
||||
<t t-if="display">
|
||||
<div>
|
||||
<button type="button" class="btn btn-primary btn-sm o-kanban-button-new">
|
||||
<button type="button" class="btn btn-primary btn-sm o-kanban-button-new" accesskey="C">
|
||||
<t t-esc="widget.options.create_text || _t('Create')"/>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@@ -12,8 +12,8 @@ import urllib2
|
||||
import werkzeug.wrappers
|
||||
|
||||
import openerp
|
||||
from openerp.addons.web.controllers.main import WebClient
|
||||
from openerp.addons.web_editor.controllers.main import Web_Editor
|
||||
from openerp.addons.base.ir.ir_qweb import AssetsBundle
|
||||
from openerp.addons.web.controllers.main import WebClient, Binary
|
||||
from openerp.addons.web import http
|
||||
from openerp.http import request
|
||||
|
||||
@@ -303,7 +303,7 @@ class Website(openerp.addons.web.controllers.main.Home):
|
||||
return [enable, disable]
|
||||
|
||||
@http.route(['/website/theme_customize'], type='json', auth="public", website=True)
|
||||
def theme_customize(self, enable, disable):
|
||||
def theme_customize(self, enable, disable, get_bundle=False):
|
||||
""" enable or Disable lists of ``xml_id`` of the inherit templates
|
||||
"""
|
||||
cr, uid, context, pool = request.cr, request.uid, request.context, request.registry
|
||||
@@ -317,6 +317,10 @@ class Website(openerp.addons.web.controllers.main.Home):
|
||||
set_active(disable, False)
|
||||
set_active(enable, True)
|
||||
|
||||
if get_bundle:
|
||||
bundle = AssetsBundle('website.assets_frontend', cr=http.request.cr, uid=http.request.uid, context={}, registry=http.request.registry)
|
||||
return bundle.to_html()
|
||||
|
||||
return True
|
||||
|
||||
@http.route(['/website/theme_customize_reload'], type='http', auth="public", website=True)
|
||||
@@ -369,3 +373,29 @@ class Website(openerp.addons.web.controllers.main.Home):
|
||||
if res:
|
||||
return res
|
||||
return request.redirect('/')
|
||||
|
||||
|
||||
#------------------------------------------------------
|
||||
# Retrocompatibility routes
|
||||
#------------------------------------------------------
|
||||
class WebsiteBinary(openerp.http.Controller):
|
||||
@http.route([
|
||||
'/website/image',
|
||||
'/website/image/<xmlid>',
|
||||
'/website/image/<xmlid>/<int:width>x<int:height>',
|
||||
'/website/image/<xmlid>/<field>',
|
||||
'/website/image/<xmlid>/<field>/<int:width>x<int:height>',
|
||||
'/website/image/<model>/<id>/<field>',
|
||||
'/website/image/<model>/<id>/<field>/<int:width>x<int:height>'
|
||||
], type='http', auth="public", website=False, multilang=False)
|
||||
def content_image(self, id=None, max_width=0, max_height=0, **kw):
|
||||
if max_width:
|
||||
kw['width'] = max_width
|
||||
if max_height:
|
||||
kw['height'] = max_height
|
||||
if id:
|
||||
id, _, unique = id.partition('_')
|
||||
kw['id'] = int(id)
|
||||
if unique:
|
||||
kw['unique'] = unique
|
||||
return Binary().content_image(**kw)
|
||||
|
||||
@@ -125,6 +125,8 @@ DEFAULT_CDN_FILTERS = [
|
||||
"^/web/(css|js)/",
|
||||
"^/web/image",
|
||||
"^/web/content",
|
||||
# retrocompatibility
|
||||
"^/website/image/",
|
||||
]
|
||||
|
||||
def unslug(s):
|
||||
|
||||
@@ -589,7 +589,7 @@ ul.oe_menu_editor .disclose {
|
||||
}
|
||||
|
||||
#theme_customize_modal {
|
||||
overflow: visible;
|
||||
overflow: auto;
|
||||
z-index: 1020;
|
||||
background: transparent;
|
||||
display: block;
|
||||
@@ -655,11 +655,3 @@ ul.oe_menu_editor .disclose {
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
/* ---- force the browse to re-compute the stylesheets ---- */
|
||||
body.theme_customize_css_loading {
|
||||
magin-top: 1px;
|
||||
}
|
||||
body.theme_customize_css_loading #wrapwrap {
|
||||
magin-top: -1px;
|
||||
}
|
||||
|
||||
@@ -508,7 +508,7 @@ $infobar_height: 20px
|
||||
#theme_error
|
||||
background: #ffc
|
||||
#theme_customize_modal
|
||||
overflow: visible
|
||||
overflow: auto
|
||||
z-index: 1020
|
||||
background: transparent
|
||||
display: block
|
||||
@@ -565,11 +565,3 @@ $infobar_height: 20px
|
||||
opacity: 0.3
|
||||
position: absolute
|
||||
z-index: 1
|
||||
|
||||
/* ---- force the browse to re-compute the stylesheets ---- */
|
||||
body.theme_customize_css_loading
|
||||
magin-top: 1px
|
||||
#wrapwrap
|
||||
magin-top: -1px
|
||||
|
||||
// }}}
|
||||
|
||||
@@ -187,7 +187,7 @@
|
||||
}
|
||||
|
||||
#theme_customize_modal {
|
||||
overflow: visible;
|
||||
overflow: auto;
|
||||
z-index: 1020;
|
||||
background: transparent;
|
||||
display: block;
|
||||
@@ -253,11 +253,3 @@
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
/* ---- force the browse to re-compute the stylesheets ---- */
|
||||
body.theme_customize_css_loading {
|
||||
magin-top: 1px;
|
||||
}
|
||||
body.theme_customize_css_loading #wrapwrap {
|
||||
magin-top: -1px;
|
||||
}
|
||||
|
||||
@@ -167,7 +167,7 @@ $infobar_height: 20px
|
||||
#theme_error
|
||||
background: #ffc
|
||||
#theme_customize_modal
|
||||
overflow: visible
|
||||
overflow: auto
|
||||
z-index: 1020
|
||||
background: transparent
|
||||
display: block
|
||||
@@ -224,11 +224,3 @@ $infobar_height: 20px
|
||||
opacity: 0.3
|
||||
position: absolute
|
||||
z-index: 1
|
||||
|
||||
/* ---- force the browse to re-compute the stylesheets ---- */
|
||||
body.theme_customize_css_loading
|
||||
magin-top: 1px
|
||||
#wrapwrap
|
||||
magin-top: -1px
|
||||
|
||||
// }}}
|
||||
|
||||
@@ -232,7 +232,7 @@ options.registry.gallery = options.Class.extend({
|
||||
editor.on('saved', this, function (attachments) {
|
||||
for (var i = 0 ; i < attachments.length; i++) {
|
||||
var img = $('<img class="img img-responsive mb8 mt8"/>')
|
||||
.attr("src", attachments[i].url)
|
||||
.attr("src", attachments[i].src)
|
||||
.attr('data-index', index+i)
|
||||
.data('index', index+i)
|
||||
.appendTo($container);
|
||||
|
||||
@@ -3,6 +3,7 @@ odoo.define('website.theme', function (require) {
|
||||
|
||||
var ajax = require('web.ajax');
|
||||
var core = require('web.core');
|
||||
var session = require('web.session');
|
||||
var Widget = require('web.Widget');
|
||||
var base = require('web_editor.base');
|
||||
var website = require('website.website');
|
||||
@@ -121,57 +122,41 @@ var Theme = Widget.extend({
|
||||
});
|
||||
return xml_ids;
|
||||
},
|
||||
compute_stylesheets: function () {
|
||||
var self = this;
|
||||
self.has_error = false;
|
||||
$('link[href*=".assets_"]').attr('data-loading', true);
|
||||
function theme_customize_css_onload() {
|
||||
if ($('link[data-loading]').size()) {
|
||||
$('body').toggleClass('theme_customize_css_loading');
|
||||
setTimeout(theme_customize_css_onload, 50);
|
||||
} else {
|
||||
$('body').removeClass('theme_customize_css_loading');
|
||||
self.$el.removeClass("loading");
|
||||
|
||||
if (window.getComputedStyle($('button[data-toggle="collapse"]:first')[0]).getPropertyValue('position') === 'static' ||
|
||||
window.getComputedStyle($('#theme_customize_modal')[0]).getPropertyValue('display') === 'none') {
|
||||
if (self.has_error) {
|
||||
window.location.hash = "theme=true";
|
||||
window.location.reload();
|
||||
} else {
|
||||
self.has_error = true;
|
||||
$('link[href*=".assets_"][data-error]').removeAttr('data-error').attr('data-loading', true);
|
||||
self.update_stylesheets();
|
||||
setTimeout(theme_customize_css_onload, 50);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
theme_customize_css_onload();
|
||||
},
|
||||
update_stylesheets: function () {
|
||||
$('link[href*=".assets_"]').each(function update () {
|
||||
var $style = $(this);
|
||||
var href = $style.attr("href").replace(/[^\/]+$/, new Date().getTime());
|
||||
var $asset = $('<link rel="stylesheet" href="'+href+'"/>');
|
||||
$asset.attr("onload", "$(this).prev().attr('disable', true).remove(); $(this).removeAttr('onload').removeAttr('onerror');");
|
||||
$asset.attr("onerror", "$(this).prev().removeAttr('data-loading').attr('data-error','loading'); $(this).attr('disable', true).remove();");
|
||||
$style.after($asset);
|
||||
});
|
||||
},
|
||||
update_style: function (enable, disable, reload) {
|
||||
var self = this;
|
||||
if (this.$el.hasClass("loading")) return;
|
||||
this.$el.addClass("loading");
|
||||
if (this.$el.hasClass("loading")) {
|
||||
return;
|
||||
}
|
||||
this.$el.addClass('loading');
|
||||
|
||||
if (!reload && $('link[href*=".assets_"]').size()) {
|
||||
this.compute_stylesheets();
|
||||
if (!reload && !session.debug) {
|
||||
var self = this;
|
||||
return ajax.jsonRpc('/website/theme_customize', 'call', {
|
||||
'enable': enable,
|
||||
'disable': disable
|
||||
}).then(function () {
|
||||
self.update_stylesheets();
|
||||
enable: enable,
|
||||
disable: disable,
|
||||
get_bundle: true,
|
||||
}).then(function (bundleHTML) {
|
||||
var $links = $('link[href*=".assets_frontend"]');
|
||||
var $newLinks = $(bundleHTML).filter('link');
|
||||
|
||||
var linksLoaded = $.Deferred();
|
||||
var nbLoaded = 0;
|
||||
$newLinks.on('load', function (e) {
|
||||
if (++nbLoaded >= $newLinks.length) {
|
||||
linksLoaded.resolve();
|
||||
}
|
||||
});
|
||||
$newLinks.on('error', function (e) {
|
||||
linksLoaded.reject();
|
||||
window.location.hash = "theme=true";
|
||||
window.location.reload();
|
||||
});
|
||||
|
||||
$links.last().after($newLinks);
|
||||
return linksLoaded.then(function () {
|
||||
$links.remove();
|
||||
self.$el.removeClass('loading');
|
||||
});
|
||||
});
|
||||
} else {
|
||||
var href = '/website/theme_customize_reload'+
|
||||
'?href='+encodeURIComponent(window.location.href)+
|
||||
@@ -325,4 +310,6 @@ website.TopBar.include({
|
||||
}
|
||||
});
|
||||
|
||||
return Theme;
|
||||
|
||||
});
|
||||
|
||||
@@ -827,7 +827,7 @@ class Tags(models.Model):
|
||||
posts_count = fields.Integer('Number of Posts', compute='_get_posts_count', store=True)
|
||||
|
||||
_sql_constraints = [
|
||||
('name_uniq', 'unique (name)', "Tag name already exists !"),
|
||||
('name_uniq', 'unique (name, forum_id)', "Tag name already exists !"),
|
||||
]
|
||||
|
||||
@api.multi
|
||||
|
||||
@@ -60,7 +60,7 @@ if(!$('.o_website_links_chart').length) {
|
||||
.x(function(d) { return getDate(d); })
|
||||
.y(function(d) { return getNbClicks(d); })
|
||||
.tooltips(true)
|
||||
.transitionDuration(0)
|
||||
.transition(0)
|
||||
.showYAxis(true)
|
||||
.showXAxis(true);
|
||||
|
||||
|
||||
@@ -37,16 +37,16 @@ class WebsiteMembership(http.Controller):
|
||||
partner_obj = request.registry['res.partner']
|
||||
post_name = post.get('search') or post.get('name', '')
|
||||
current_country = None
|
||||
today = time.strftime(DEFAULT_SERVER_DATE_FORMAT)
|
||||
|
||||
# base domain for groupby / searches
|
||||
base_line_domain = [("partner.website_published", "=", True), ('state', 'in', ['free', 'paid'])]
|
||||
base_line_domain = [
|
||||
("partner.website_published", "=", True), ('state', '=', 'paid'),
|
||||
('date_to', '>=', today), ('date_from', '<=', today)
|
||||
]
|
||||
if membership_id and membership_id != 'free':
|
||||
membership_id = int(membership_id)
|
||||
today = time.strftime(DEFAULT_SERVER_DATE_FORMAT)
|
||||
base_line_domain += [
|
||||
('membership_id', '=', membership_id), ('date_to', '>=', today),
|
||||
('date_from', '<=', today), ('state', '=', 'paid')
|
||||
]
|
||||
base_line_domain.append(('membership_id', '=', membership_id))
|
||||
membership = product_obj.browse(cr, uid, membership_id, context=context)
|
||||
else:
|
||||
membership = None
|
||||
@@ -58,12 +58,14 @@ class WebsiteMembership(http.Controller):
|
||||
if membership_id != 'free':
|
||||
membership_line_ids = membership_line_obj.search(cr, SUPERUSER_ID, base_line_domain, context=context)
|
||||
country_domain = [('member_lines', 'in', membership_line_ids)]
|
||||
if not membership_id:
|
||||
country_domain = ['|', country_domain[0], ('membership_state', '=', 'free')]
|
||||
else:
|
||||
membership_line_ids = []
|
||||
country_domain = [('membership_state', '=', 'free')]
|
||||
if post_name:
|
||||
country_domain += ['|', ('name', 'ilike', post_name),
|
||||
('website_description', 'ilike', post_name)]
|
||||
if post_name:
|
||||
country_domain += ['|', ('name', 'ilike', post_name),
|
||||
('website_description', 'ilike', post_name)]
|
||||
countries = partner_obj.read_group(
|
||||
cr, SUPERUSER_ID, country_domain + [("website_published", "=", True)], ["id", "country_id"],
|
||||
groupby="country_id", orderby="country_id", context=request.context)
|
||||
|
||||
@@ -36,8 +36,8 @@
|
||||
</div>
|
||||
</div>
|
||||
<h1>Payment Methods</h1>
|
||||
<h4>Add a new payment method:</h4>
|
||||
<div class="row">
|
||||
<h4 t-if="acquirers">Add a new payment method:</h4>
|
||||
<div t-if="acquirers" class="row">
|
||||
<div class="col-md-6">
|
||||
<div t-if="error_message" class="alert alert-danger">
|
||||
<t t-foreach="error_message" t-as="err"><t t-esc="err"/><br /></t>
|
||||
|
||||
@@ -77,7 +77,7 @@ class sale_quote(http.Controller):
|
||||
context=render_ctx)
|
||||
return request.website.render('website_quote.so_quotation', values)
|
||||
|
||||
@http.route(['/quote/accept/<int:order_id>'], type='http', auth="public", website=True, methods=['POST'])
|
||||
@http.route(['/quote/accept'], type='json', auth="public", website=True)
|
||||
def accept(self, order_id, token=None, signer=None, sign=None, **post):
|
||||
order_obj = request.registry.get('sale.order')
|
||||
order = order_obj.browse(request.cr, SUPERUSER_ID, order_id)
|
||||
@@ -85,6 +85,8 @@ class sale_quote(http.Controller):
|
||||
return request.website.render('website.404')
|
||||
if order.require_payment:
|
||||
return request.website.render('website.404')
|
||||
if order.state != 'sent':
|
||||
return False
|
||||
attachments=sign and [('signature.png', sign.decode('base64'))] or []
|
||||
order_obj.action_confirm(request.cr, SUPERUSER_ID, [order_id], context=request.context)
|
||||
message = _('Order signed by %s') % (signer,)
|
||||
@@ -97,6 +99,8 @@ class sale_quote(http.Controller):
|
||||
order = order_obj.browse(request.cr, SUPERUSER_ID, order_id)
|
||||
if token != order.access_token:
|
||||
return request.website.render('website.404')
|
||||
if order.state != 'sent':
|
||||
return werkzeug.utils.redirect("/quote/%s/%s?message=4" % (order_id, token))
|
||||
request.registry.get('sale.order').action_cancel(request.cr, SUPERUSER_ID, [order_id])
|
||||
message = post.get('decline_message')
|
||||
if message:
|
||||
|
||||
@@ -62,15 +62,25 @@ if(!$('.o_website_quote').length) {
|
||||
submitForm: function(ev){
|
||||
// extract data
|
||||
var self = this;
|
||||
var $confirm_btn = self.$el.find('button[type="submit"]');
|
||||
|
||||
// Support 2 routes:
|
||||
// - <form id="accept" method="POST" t-attf-action="/quote/accept/#{quotation.id}/?token=#{quotation.access_token}" ...>
|
||||
// - <form id="accept" method="POST" t-att-data-order-id="quotation.id" t-att-data-token="quotation.access_token" ...>
|
||||
// The first route is deprecated but might still be used if the template is not updated
|
||||
var href = self.$el.find('form').attr("action");
|
||||
var action = href.match(/quote\/([a-z]+)/);
|
||||
var order_id = href.match(/quote\/[a-z]+\/([0-9]+)/);
|
||||
var token = href.match(/token=(.*)/);
|
||||
if (token){
|
||||
token = token[1];
|
||||
if (href) {
|
||||
var action = href.match(/quote\/([a-z]+)/)[1];
|
||||
var order_id = parseInt(href.match(/quote\/[a-z]+\/([0-9]+)/)[1]);
|
||||
var token = href.match(/token=(.*)/) && href.match(/token=(.*)/)[1];
|
||||
}
|
||||
else {
|
||||
var action = 'accept';
|
||||
var order_id = self.$el.find('form').data("order-id");
|
||||
var token = self.$el.find('form').data("token");
|
||||
}
|
||||
|
||||
if (action[1]=='accept') {
|
||||
if (action == 'accept') {
|
||||
ev.preventDefault();
|
||||
// process : display errors, or submit
|
||||
var signer_name = self.$("#name").val();
|
||||
@@ -84,14 +94,16 @@ if(!$('.o_website_quote').length) {
|
||||
})
|
||||
return false;
|
||||
}
|
||||
ajax.jsonRpc("/quote/"+action[1], 'call', {
|
||||
'order_id': parseInt(order_id[1]),
|
||||
$confirm_btn.prepend('<i class="fa fa-spinner fa-spin"></i> ');
|
||||
$confirm_btn.attr('disabled', true);
|
||||
ajax.jsonRpc("/quote/"+action, 'call', {
|
||||
'order_id': order_id,
|
||||
'token': token,
|
||||
'signer': signer_name,
|
||||
'sign': signature?JSON.stringify(signature[1]):false,
|
||||
}).then(function (data) {
|
||||
self.$el.modal('hide');
|
||||
window.location.href = '/quote/'+order_id[1]+'/'+token+'?message=3';
|
||||
window.location.href = '/quote/'+order_id.toString()+'/'+token+'?message=3';
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -225,11 +225,19 @@
|
||||
Your transaction is waiting confirmation.
|
||||
<span t-if='tx_post_msg' t-raw="tx_post_msg"/>
|
||||
</div>
|
||||
<div class="alert alert-warning alert-dismissable" t-if="message==4 and quotation.state != 'sent'">
|
||||
<button type="button" class="close" data-dismiss="alert" aria-hidden="true">&times;</button>
|
||||
This order has already been
|
||||
<t t-if="quotation.state=='cancel'">cancelled</t>
|
||||
<t t-if="quotation.state not in ('cancel','draft')">validated</t>
|
||||
! You can <a href="#discussion">contact us</a> for
|
||||
any question.
|
||||
</div>
|
||||
|
||||
<!-- modal relative to the actions Accept/Reject/Cancel -->
|
||||
<div class="modal fade" id="modalaccept" role="dialog" aria-hidden="true">
|
||||
<div class="modal-dialog" t-if="not quotation.require_payment and not need_payment">
|
||||
<form id="accept" method="POST" t-attf-action="/quote/accept/#{quotation.id}/?token=#{quotation.access_token}" class="js_accept_json modal-content js_website_submit_form">
|
||||
<form id="accept" method="POST" t-att-data-order-id="quotation.id" t-att-data-token="quotation.access_token" class="js_accept_json modal-content js_website_submit_form">
|
||||
<input type="hidden" name="csrf_token" t-att-value="request.csrf_token()"/>
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
|
||||
|
||||
@@ -123,10 +123,16 @@ class sale_order(osv.Model):
|
||||
class website(orm.Model):
|
||||
_inherit = 'website'
|
||||
|
||||
def _get_pricelist_id(self, cr, uid, ids, name, args, context=None):
|
||||
res = {}
|
||||
pricelist = self.get_current_pricelist(cr, uid, context=context)
|
||||
for data in self.browse(cr, uid, ids, context=context):
|
||||
res[data.id] = pricelist.id
|
||||
return res
|
||||
|
||||
_columns = {
|
||||
'pricelist_id': fields.related(
|
||||
'user_id', 'partner_id', 'property_product_pricelist',
|
||||
type='many2one', relation='product.pricelist', string='Default Pricelist'),
|
||||
'pricelist_id': fields.function(_get_pricelist_id,\
|
||||
type='many2one', relation="product.pricelist", string='Default Pricelist'),
|
||||
'currency_id': fields.related(
|
||||
'pricelist_id', 'currency_id',
|
||||
type='many2one', relation='res.currency', string='Default Currency'),
|
||||
@@ -180,7 +186,7 @@ class website(orm.Model):
|
||||
"""
|
||||
isocountry = request.session.geoip and request.session.geoip.get('country_code') or False
|
||||
pl_ids = self._get_pl(cr, uid, isocountry, show_visible,
|
||||
request.website.pricelist_id.id,
|
||||
request.website.user_id.partner_id.property_product_pricelist.id,
|
||||
request.session.get('website_sale_current_pl'),
|
||||
request.website.website_pricelist_ids)
|
||||
return self.pool['product.pricelist'].browse(cr, uid, pl_ids, context=context)
|
||||
@@ -229,6 +235,11 @@ class website(orm.Model):
|
||||
sale_order_id = request.session.get('sale_order_id') or (partner.last_website_so_id.id if partner.last_website_so_id and partner.last_website_so_id.state == 'draft' else False)
|
||||
|
||||
sale_order = None
|
||||
# Test validity of the sale_order_id
|
||||
if sale_order_id and sale_order_obj.exists(cr, SUPERUSER_ID, sale_order_id, context=context):
|
||||
sale_order = sale_order_obj.browse(cr, SUPERUSER_ID, sale_order_id, context=context)
|
||||
else:
|
||||
sale_order_id = None
|
||||
pricelist_id = request.session.get('website_sale_current_pl')
|
||||
|
||||
if force_pricelist and self.pool['product.pricelist'].search_count(cr, uid, [('id', '=', force_pricelist)], context=context):
|
||||
@@ -243,26 +254,24 @@ class website(orm.Model):
|
||||
affiliate_id = request.session.get('affiliate_id')
|
||||
salesperson_id = affiliate_id if user_obj.exists(cr, SUPERUSER_ID, affiliate_id, context=context) else request.website.salesperson_id.id
|
||||
for w in self.browse(cr, uid, ids):
|
||||
addr = partner.address_get(['delivery', 'invoice'])
|
||||
values = {
|
||||
'partner_id': partner.id,
|
||||
'pricelist_id': pricelist_id,
|
||||
'payment_term_id': partner.property_payment_term_id.id if partner.property_payment_term_id else False,
|
||||
'team_id': w.salesteam_id.id,
|
||||
'partner_invoice_id': addr['invoice'],
|
||||
'partner_shipping_id': addr['delivery'],
|
||||
'user_id': salesperson_id or w.salesperson_id.id,
|
||||
}
|
||||
sale_order_id = sale_order_obj.create(cr, SUPERUSER_ID, values, context=context)
|
||||
sale_order_obj.onchange_partner_id(cr, SUPERUSER_ID, [sale_order_id], context=context)
|
||||
values = {'user_id': salesperson_id or w.salesperson_id.id}
|
||||
|
||||
sale_order_obj.write(cr, SUPERUSER_ID, [sale_order_id], values, context=context)
|
||||
request.session['sale_order_id'] = sale_order_id
|
||||
sale_order = sale_order_obj.browse(cr, SUPERUSER_ID, sale_order_id, context=context)
|
||||
|
||||
if request.website.partner_id.id != partner.id:
|
||||
self.pool['res.partner'].write(cr, SUPERUSER_ID, partner.id, {'last_website_so_id': sale_order_id})
|
||||
|
||||
if sale_order_id:
|
||||
sale_order = sale_order_obj.browse(cr, SUPERUSER_ID, sale_order_id, context=context)
|
||||
if not sale_order.exists():
|
||||
request.session['sale_order_id'] = None
|
||||
return None
|
||||
|
||||
# check for change of pricelist with a coupon
|
||||
pricelist_id = pricelist_id or partner.property_product_pricelist.id
|
||||
@@ -321,6 +330,10 @@ class website(orm.Model):
|
||||
if (code and code != sale_order.pricelist_id.code) or sale_order.partner_id.id != partner.id or force_pricelist:
|
||||
sale_order = sale_order_obj.browse(cr, SUPERUSER_ID, sale_order.id, context=context)
|
||||
|
||||
else:
|
||||
request.session['sale_order_id'] = None
|
||||
return None
|
||||
|
||||
return sale_order
|
||||
|
||||
def sale_get_transaction(self, cr, uid, ids, context=None):
|
||||
|
||||
@@ -11,6 +11,7 @@ class TestWebsitePriceList(TransactionCase):
|
||||
def setUp(self):
|
||||
super(TestWebsitePriceList, self).setUp()
|
||||
self.website = self.registry('website').browse(self.cr, self.uid, 1)
|
||||
self.website.pricelist_id = self.registry('ir.model.data').xmlid_to_res_id(self.cr, self.uid, 'product.list0')
|
||||
self.patcher = patch('openerp.addons.website_sale.models.sale_order.website.get_pricelist_available', wraps=self._get_pricelist_available)
|
||||
self.mock_get_pricelist_available = self.patcher.start()
|
||||
|
||||
|
||||
@@ -93,16 +93,16 @@
|
||||
<del class="text-danger mr8" style="white-space: nowrap;" t-field="product.lst_price" t-field-options='{
|
||||
"widget": "monetary",
|
||||
"from_currency": "product.company_id.currency_id",
|
||||
"display_currency": "user_id.partner_id.property_product_pricelist.currency_id"
|
||||
"display_currency": "website.currency_id"
|
||||
}'/>
|
||||
</t>
|
||||
<span t-field="product.price" style="white-space: nowrap;" t-field-options='{
|
||||
"widget": "monetary",
|
||||
"display_currency": "user_id.partner_id.property_product_pricelist.currency_id"
|
||||
"display_currency": "website.currency_id"
|
||||
}'>
|
||||
</span>
|
||||
<span itemprop="price" style="display:none;" t-esc="product.price"/>
|
||||
<span itemprop="priceCurrency" style="display:none;" t-esc="user_id.partner_id.property_product_pricelist.currency_id.name"/>
|
||||
<span itemprop="priceCurrency" style="display:none;" t-esc="website.currency_id.name"/>
|
||||
</b>
|
||||
</div>
|
||||
</section>
|
||||
@@ -423,16 +423,16 @@
|
||||
t-field-options='{
|
||||
"widget": "monetary",
|
||||
"from_currency": "product.company_id.currency_id",
|
||||
"display_currency": "user_id.partner_id.property_product_pricelist.currency_id"
|
||||
"display_currency": "website.currency_id"
|
||||
}'/>
|
||||
<b class="oe_price" style="white-space: nowrap;"
|
||||
t-field="product.price"
|
||||
t-field-options='{
|
||||
"widget": "monetary",
|
||||
"display_currency": "user_id.partner_id.property_product_pricelist.currency_id"
|
||||
"display_currency": "website.currency_id"
|
||||
}'/>
|
||||
<span itemprop="price" style="display:none;" t-esc="product.price"/>
|
||||
<span itemprop="priceCurrency" style="display:none;" t-esc="user_id.partner_id.property_product_pricelist.currency_id.name"/>
|
||||
<span itemprop="priceCurrency" style="display:none;" t-esc="website.currency_id.name"/>
|
||||
</h4>
|
||||
<h4 class="css_non_editable_mode_hidden">
|
||||
<span t-field="product.lst_price"
|
||||
@@ -456,7 +456,7 @@
|
||||
<t t-esc="variant_id.price_extra > 0 and '+' or ''"/><span t-field="variant_id.price_extra" style="white-space: nowrap;" t-field-options='{
|
||||
"widget": "monetary",
|
||||
"from_currency": "product.company_id.currency_id",
|
||||
"display_currency": "user_id.partner_id.property_product_pricelist.currency_id"
|
||||
"display_currency": "website.currency_id"
|
||||
}'/>
|
||||
</span>
|
||||
</label>
|
||||
@@ -481,7 +481,7 @@
|
||||
<t t-esc="value_id.price_extra > 0 and '+' or ''"/><span t-field="value_id.price_extra" style="white-space: nowrap;" t-field-options='{
|
||||
"widget": "monetary",
|
||||
"from_currency": "product.company_id.currency_id",
|
||||
"display_currency": "user_id.partner_id.property_product_pricelist.currency_id"
|
||||
"display_currency": "website.currency_id"
|
||||
}'/>
|
||||
</span>
|
||||
</option>
|
||||
@@ -501,7 +501,7 @@
|
||||
<t t-esc="value_id.price_extra > 0 and '+' or ''"/><span t-field="value_id.price_extra" style="white-space: nowrap;" t-field-options='{
|
||||
"widget": "monetary",
|
||||
"from_currency": "product.company_id.currency_id",
|
||||
"display_currency": "user_id.partner_id.property_product_pricelist.currency_id"
|
||||
"display_currency": "website.currency_id"
|
||||
}'/>
|
||||
</span>
|
||||
</label>
|
||||
@@ -776,12 +776,12 @@
|
||||
t-field="line.product_id.lst_price" t-field-options='{
|
||||
"widget": "monetary",
|
||||
"from_currency": "line.company_id.currency_id",
|
||||
"display_currency": "user_id.partner_id.property_product_pricelist.currency_id"
|
||||
"display_currency": "website.currency_id"
|
||||
}'/>
|
||||
<span t-field="line.price_reduce" style="white-space: nowrap;" t-field-options='{
|
||||
"widget": "monetary",
|
||||
"from_currency": "website_sale_order.pricelist_id.currency_id",
|
||||
"display_currency": "user_id.partner_id.property_product_pricelist.currency_id"
|
||||
"display_currency": "website.currency_id"
|
||||
}'/>
|
||||
</td>
|
||||
</tr>
|
||||
@@ -898,12 +898,12 @@
|
||||
t-field="product.lst_price" t-field-options='{
|
||||
"widget": "monetary",
|
||||
"from_currency": "product.company_id.currency_id",
|
||||
"display_currency": "user_id.partner_id.property_product_pricelist.currency_id"
|
||||
"display_currency": "website.currency_id"
|
||||
}'/>
|
||||
</t>
|
||||
<span t-field="product.price" style="white-space: nowrap;" t-field-options='{
|
||||
"widget": "monetary",
|
||||
"display_currency": "user_id.partner_id.property_product_pricelist.currency_id"
|
||||
"display_currency": "website.currency_id"
|
||||
}'/>
|
||||
</td>
|
||||
<td class="text-center">
|
||||
@@ -1153,7 +1153,7 @@
|
||||
<span t-field="line.price_unit" style="white-space: nowrap;" t-field-options='{
|
||||
"widget": "monetary",
|
||||
"from_currency": "website_sale_order.pricelist_id.currency_id",
|
||||
"display_currency": "user_id.partner_id.property_product_pricelist.currency_id"
|
||||
"display_currency": "website.currency_id"
|
||||
}'/>
|
||||
</td>
|
||||
</tr>
|
||||
@@ -1394,7 +1394,7 @@
|
||||
<span t-field="website_sale_order.amount_untaxed" style="white-space: nowrap;" t-field-options='{
|
||||
"widget": "monetary",
|
||||
"from_currency": "website_sale_order.pricelist_id.currency_id",
|
||||
"display_currency": "user_id.partner_id.property_product_pricelist.currency_id"
|
||||
"display_currency": "website.currency_id"
|
||||
}'/>
|
||||
</span>
|
||||
</div>
|
||||
@@ -1404,7 +1404,7 @@
|
||||
<span t-field="website_sale_order.amount_tax" style="white-space: nowrap;" t-field-options='{
|
||||
"widget": "monetary",
|
||||
"from_currency": "website_sale_order.pricelist_id.currency_id",
|
||||
"display_currency": "user_id.partner_id.property_product_pricelist.currency_id"
|
||||
"display_currency": "website.currency_id"
|
||||
}'/>
|
||||
</span>
|
||||
</div>
|
||||
@@ -1414,7 +1414,7 @@
|
||||
<span t-field="website_sale_order.amount_total" style="white-space: nowrap;" t-field-options='{
|
||||
"widget": "monetary",
|
||||
"from_currency": "website_sale_order.pricelist_id.currency_id",
|
||||
"display_currency": "user_id.partner_id.property_product_pricelist.currency_id"
|
||||
"display_currency": "website.currency_id"
|
||||
}'/>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@@ -59,7 +59,7 @@
|
||||
t-field="product.lst_price"
|
||||
t-field-options='{
|
||||
"widget": "monetary",
|
||||
"from_currency": "website.currency_id",
|
||||
"from_currency": "product.company_id.currency_id",
|
||||
"display_currency": "user_id.partner_id.property_product_pricelist.currency_id"
|
||||
}'/>
|
||||
<span class="oe_price" style="white-space: nowrap;"
|
||||
@@ -67,7 +67,7 @@
|
||||
t-field="product.price"
|
||||
t-field-options='{
|
||||
"widget": "monetary",
|
||||
"from_currency": "website.currency_id",
|
||||
"from_currency": "product.company_id.currency_id",
|
||||
"display_currency": "user_id.partner_id.property_product_pricelist.currency_id"
|
||||
}'/>
|
||||
</td>
|
||||
@@ -107,7 +107,7 @@
|
||||
t-field="product.lst_price"
|
||||
t-field-options='{
|
||||
"widget": "monetary",
|
||||
"from_currency": "website.currency_id",
|
||||
"from_currency": "product.company_id.currency_id",
|
||||
"display_currency": "user_id.partner_id.property_product_pricelist.currency_id"
|
||||
}'/><br/>
|
||||
<span class="oe_price" style="white-space: nowrap;"
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
Saudi Arabia, 2015-10-13
|
||||
|
||||
I hereby agree to the terms of the Odoo Individual Contributor License
|
||||
Agreement v1.0.
|
||||
|
||||
I declare that I am authorized and able to make this agreement and sign this
|
||||
declaration.
|
||||
|
||||
Signed,
|
||||
|
||||
Mohammed Barsi barsintod@gmail.com
|
||||
+1
-1
@@ -1973,7 +1973,7 @@ Read-Write Field
|
||||
Read-only fields, which only display content and don't allow the
|
||||
user to modify it can be useful, but most fields in Odoo also allow editing.
|
||||
This makes the field classes more complicated, mostly because fields are
|
||||
supposed to handle both and editable and non-editable mode, those modes are
|
||||
supposed to handle both editable and non-editable mode, those modes are
|
||||
often completely different (for design and usability purpose) and the fields
|
||||
must be able to switch between modes at any moment.
|
||||
|
||||
|
||||
@@ -552,7 +552,7 @@ class view(osv.osv):
|
||||
separator = child.get('separator', ',')
|
||||
if separator == ' ':
|
||||
separator = None # squash spaces
|
||||
to_add = map(str.strip, child.get('add', '').split(separator))
|
||||
to_add = filter(bool, map(str.strip, child.get('add', '').split(separator)))
|
||||
to_remove = map(str.strip, child.get('remove', '').split(separator))
|
||||
values = map(str.strip, node.get(attribute, '').split(separator))
|
||||
value = (separator or ' ').join(filter(lambda s: s not in to_remove, values) + to_add)
|
||||
|
||||
@@ -20,9 +20,6 @@
|
||||
<field name="rml_header1" placeholder="e.g. Global Business Solutions"/>
|
||||
</h3>
|
||||
</div>
|
||||
<group col="4">
|
||||
<field name="parent_id" groups="base.group_multi_company"/>
|
||||
</group>
|
||||
<notebook colspan="4">
|
||||
<page string="General Information">
|
||||
<group>
|
||||
@@ -45,6 +42,7 @@
|
||||
<field name="email"/>
|
||||
<field name="vat"/>
|
||||
<field name="company_registry"/>
|
||||
<field name="parent_id" groups="base.group_multi_company"/>
|
||||
</group>
|
||||
</group>
|
||||
</page>
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
<field name="symbol">R$</field>
|
||||
<field name="rounding">0.01</field>
|
||||
<field name="active" eval="False"/>
|
||||
<field name="position">before</field>
|
||||
</record>
|
||||
|
||||
<record id="CNY" model="res.currency">
|
||||
@@ -229,6 +230,7 @@
|
||||
<field name="symbol">$</field>
|
||||
<field name="rounding">0.01</field>
|
||||
<field name="active" eval="False"/>
|
||||
<field name="position">before</field>
|
||||
</record>
|
||||
|
||||
<record id="MYR" model="res.currency">
|
||||
|
||||
@@ -275,3 +275,27 @@ class TestGlobalDefaults(FiltersCase):
|
||||
dict(name='a', user_id=False, is_default=False, domain='[]', context='{}', sort='[]'),
|
||||
dict(name='b', user_id=False, is_default=True, domain='[]', context=context_value, sort='[]'),
|
||||
])
|
||||
|
||||
from openerp.tests.common import TransactionCase
|
||||
|
||||
class TestReadGroup(TransactionCase):
|
||||
"""Test function read_group with groupby on a many2one field to a model
|
||||
(in test, "user_id" to "res.users") which is ordered by an inherited not stored field (in
|
||||
test, "name" inherited from "res.partners").
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
super(TestReadGroup, self).setUp()
|
||||
self.ir_filters_model = self.env['ir.filters']
|
||||
self.res_partner_model = self.env['res.partner']
|
||||
self.res_users_model = self.env['res.users']
|
||||
|
||||
def test_read_group_1(self):
|
||||
self.assertEqual(self.res_users_model._order, "name, login", "Model res.users must be ordered by name, login")
|
||||
self.assertFalse(self.res_users_model._fields['name'].store, "Field name is not stored in res.users")
|
||||
|
||||
filter_a = self.ir_filters_model.create(dict(name="Filter_A", model_id="ir.filters"))
|
||||
filter_b = self.ir_filters_model.create(dict(name="Filter_B", model_id="ir.filters"))
|
||||
filter_b.write(dict(user_id=False))
|
||||
res = self.ir_filters_model.read_group([], ['name', 'user_id'], ['user_id'])
|
||||
self.assertTrue(any(val['user_id'] == False for val in res), "At least one group must contain val['user_id'] == False.")
|
||||
|
||||
@@ -748,7 +748,7 @@ class test_views(ViewCase):
|
||||
<attribute name="thing">bob tata lolo</attribute>
|
||||
<attribute name="thing" add="bibi and co" remove="tata" separator=" " />
|
||||
<attribute name="otherthing">bob, tata,lolo</attribute>
|
||||
<attribute name="otherthing" add="bibi" remove="tata, bob"/>
|
||||
<attribute name="otherthing" remove="tata, bob"/>
|
||||
</footer>
|
||||
"""
|
||||
})
|
||||
@@ -768,7 +768,7 @@ class test_views(ViewCase):
|
||||
E.p("Replacement data"),
|
||||
E.footer(
|
||||
E.button(name="action_next", type="object", string="New button"),
|
||||
thing="bob lolo bibi and co", otherthing="lolo,bibi"
|
||||
thing="bob lolo bibi and co", otherthing="lolo"
|
||||
),
|
||||
string="Replacement title", version="7.0"))
|
||||
|
||||
|
||||
+4
-4
@@ -2152,7 +2152,7 @@ class BaseModel(object):
|
||||
return parent_alias
|
||||
|
||||
@api.model
|
||||
def _inherits_join_calc(self, alias, field, query):
|
||||
def _inherits_join_calc(self, alias, field, query, implicit=True, outer=False):
|
||||
"""
|
||||
Adds missing table select and join clause(s) to ``query`` for reaching
|
||||
the field coming from an '_inherits' parent table (no duplicates).
|
||||
@@ -2172,7 +2172,7 @@ class BaseModel(object):
|
||||
# JOIN parent_model._table AS parent_alias ON alias.parent_field = parent_alias.id
|
||||
parent_alias, _ = query.add_join(
|
||||
(alias, parent_model._table, parent_field, 'id', parent_field),
|
||||
implicit=True,
|
||||
implicit=implicit, outer=outer,
|
||||
)
|
||||
model, alias = parent_model, parent_alias
|
||||
# handle the case where the field is translated
|
||||
@@ -2225,7 +2225,7 @@ class BaseModel(object):
|
||||
# if val is a many2one, just write the ID
|
||||
if type(val) == tuple:
|
||||
val = val[0]
|
||||
if val is not False:
|
||||
if f._type == 'boolean' or val is not False:
|
||||
cr.execute(update_query, (ss[1](val), key))
|
||||
|
||||
@api.model
|
||||
@@ -4682,7 +4682,7 @@ class BaseModel(object):
|
||||
parent_obj = self.pool[self._inherit_fields[order_field][3]]
|
||||
order_column = parent_obj._columns[order_field]
|
||||
if order_column._classic_read:
|
||||
inner_clauses = [self._inherits_join_calc(alias, order_field, query)]
|
||||
inner_clauses = [self._inherits_join_calc(alias, order_field, query, implicit=False, outer=True)]
|
||||
add_dir = True
|
||||
elif order_column._type == 'many2one':
|
||||
key = (parent_obj._name, order_column._obj, order_field)
|
||||
|
||||
@@ -54,7 +54,12 @@ def check(f):
|
||||
if args and isinstance(args[-1], dict):
|
||||
ctx = args[-1]
|
||||
elif isinstance(kwargs, dict):
|
||||
ctx = kwargs.get('context', {})
|
||||
if 'context' in kwargs:
|
||||
ctx = kwargs['context']
|
||||
elif 'kwargs' in kwargs:
|
||||
# http entry points such as call_kw()
|
||||
ctx = kwargs['kwargs'].get('context')
|
||||
|
||||
|
||||
uid = 1
|
||||
if args and isinstance(args[0], (long, int)):
|
||||
|
||||
Reference in New Issue
Block a user