[MERGE] forward port of branch 8.0 up to 327e471
This commit is contained in:
@@ -1272,7 +1272,7 @@ class account_move_line(osv.osv):
|
||||
period = period_obj.browse(cr, uid, period_id, context=context)
|
||||
for (state,) in result:
|
||||
if state == 'done':
|
||||
raise UserError(_('You can not add/modify entries in a closed period %s of journal %s.') % (period.name,journal.name))
|
||||
raise UserError(_('You can not add/modify entries in a closed period %s of journal %s.') % (period.name, journal.name))
|
||||
if not result:
|
||||
jour_period_obj.create(cr, uid, {
|
||||
'name': (journal.code or journal.name)+':'+(period.name or ''),
|
||||
|
||||
@@ -158,12 +158,19 @@ class ir_import(orm.TransientModel):
|
||||
all the fields to traverse
|
||||
:rtype: list(Field)
|
||||
"""
|
||||
string_match = None
|
||||
for field in fields:
|
||||
# FIXME: should match all translations & original
|
||||
# TODO: use string distance (levenshtein? hamming?)
|
||||
if header.lower() == field['name'].lower() \
|
||||
or header.lower() == field['string'].lower():
|
||||
if header.lower() == field['name'].lower():
|
||||
return [field]
|
||||
if header.lower() == field['string'].lower():
|
||||
# matching string are not reliable way because
|
||||
# strings have no unique constraint
|
||||
string_match = field
|
||||
if string_match:
|
||||
# this behavior is only applied if there is no matching field['name']
|
||||
return [string_match]
|
||||
|
||||
if '/' not in header:
|
||||
return []
|
||||
|
||||
@@ -480,13 +480,13 @@ class calendar_alarm_manager(osv.AbstractModel):
|
||||
|
||||
def get_next_notif(self, cr, uid, context=None):
|
||||
ajax_check_every_seconds = 300
|
||||
partner = self.pool['res.users'].read(cr, SUPERUSER_ID, uid, ['calendar_last_notif_ack'], context=context)
|
||||
partner = self.pool['res.users'].read(cr, SUPERUSER_ID, uid, ['partner_id', 'calendar_last_notif_ack'], context=context)
|
||||
all_notif = []
|
||||
|
||||
if not partner:
|
||||
return []
|
||||
|
||||
all_events = self.get_next_potential_limit_alarm(cr, uid, ajax_check_every_seconds, partner_id=partner['id'], mail=False, context=context)
|
||||
all_events = self.get_next_potential_limit_alarm(cr, uid, ajax_check_every_seconds, partner_id=partner['partner_id'][0], mail=False, context=context)
|
||||
|
||||
for event in all_events: # .values()
|
||||
max_delta = all_events[event]['max_duration']
|
||||
@@ -586,7 +586,7 @@ class calendar_alarm(osv.Model):
|
||||
def _update_cron(self, cr, uid, context=None):
|
||||
try:
|
||||
cron = self.pool['ir.model.data'].get_object(
|
||||
cr, uid, 'calendar', 'ir_cron_scheduler_alarm', context=context)
|
||||
cr, SUPERUSER_ID, 'calendar', 'ir_cron_scheduler_alarm', context=context)
|
||||
except ValueError:
|
||||
return False
|
||||
return cron.toggle(model=self._name, domain=[('type', '=', 'email')])
|
||||
|
||||
@@ -976,7 +976,9 @@ class calendar_event(osv.Model):
|
||||
_inherit = "calendar.event"
|
||||
|
||||
def get_fields_need_update_google(self, cr, uid, context=None):
|
||||
return ['name', 'description', 'allday', 'date', 'date_end', 'stop', 'attendee_ids', 'alarm_ids', 'location', 'class', 'active']
|
||||
return ['name', 'description', 'allday', 'start', 'date_end', 'stop',
|
||||
'attendee_ids', 'alarm_ids', 'location', 'class', 'active',
|
||||
'start_date', 'start_datetime', 'stop_date', 'stop_datetime']
|
||||
|
||||
def write(self, cr, uid, ids, vals, context=None):
|
||||
if context is None:
|
||||
|
||||
@@ -319,7 +319,7 @@ class mail_compose_message(osv.TransientModel):
|
||||
# static wizard (mail.message) values
|
||||
mail_values = {
|
||||
'subject': wizard.subject,
|
||||
'body': wizard.body,
|
||||
'body': wizard.body or '',
|
||||
'parent_id': wizard.parent_id and wizard.parent_id.id,
|
||||
'partner_ids': [partner.id for partner in wizard.partner_ids],
|
||||
'attachment_ids': [attach.id for attach in wizard.attachment_ids],
|
||||
|
||||
@@ -214,6 +214,8 @@ class mrp_bom(osv.osv):
|
||||
@param properties: List of related properties.
|
||||
@return: False or BoM id.
|
||||
"""
|
||||
if not context:
|
||||
context = {}
|
||||
if properties is None:
|
||||
properties = []
|
||||
if product_id:
|
||||
@@ -231,6 +233,8 @@ class mrp_bom(osv.osv):
|
||||
else:
|
||||
# neither product nor template, makes no sense to search
|
||||
return False
|
||||
if context.get('company_id'):
|
||||
domain = domain + [('company_id', '=', context['company_id'])]
|
||||
domain = domain + [ '|', ('date_start', '=', False), ('date_start', '<=', time.strftime(DEFAULT_SERVER_DATE_FORMAT)),
|
||||
'|', ('date_stop', '=', False), ('date_stop', '>=', time.strftime(DEFAULT_SERVER_DATE_FORMAT))]
|
||||
# order to prioritize bom with product_id over the one without
|
||||
|
||||
@@ -87,7 +87,7 @@ class procurement_order(osv.osv):
|
||||
else:
|
||||
properties = [x.id for x in procurement.property_ids]
|
||||
bom_id = bom_obj._bom_find(cr, uid, product_id=procurement.product_id.id,
|
||||
properties=properties, context=context)
|
||||
properties=properties, context=dict(context, company_id=procurement.company_id.id))
|
||||
bom = bom_obj.browse(cr, uid, bom_id, context=context)
|
||||
routing_id = bom.routing_id.id
|
||||
return {
|
||||
|
||||
@@ -44,6 +44,23 @@ class StockMove(osv.osv):
|
||||
if move.raw_material_production_id and move.location_dest_id.usage == 'production' and move.raw_material_production_id.product_id.track_production and not move.consumed_for:
|
||||
raise UserError(_("Because the product %s requires it, you must assign a serial number to your raw material %s to proceed further in your production. Please use the 'Produce' button to do so.") % (move.raw_material_production_id.product_id.name, move.product_id.name))
|
||||
|
||||
# TODO master: remove me, no longer used
|
||||
def _check_phantom_bom(self, cr, uid, move, context=None):
|
||||
"""check if product associated to move has a phantom bom
|
||||
return list of ids of mrp.bom for that product """
|
||||
user_company = self.pool.get('res.users').browse(cr, uid, uid, context=context).company_id.id
|
||||
#doing the search as SUPERUSER because a user with the permission to write on a stock move should be able to explode it
|
||||
#without giving him the right to read the boms.
|
||||
domain = [
|
||||
'|', ('product_id', '=', move.product_id.id),
|
||||
'&', ('product_id', '=', False), ('product_tmpl_id.product_variant_ids', '=', move.product_id.id),
|
||||
('type', '=', 'phantom'),
|
||||
'|', ('date_start', '=', False), ('date_start', '<=', time.strftime(DEFAULT_SERVER_DATETIME_FORMAT)),
|
||||
'|', ('date_stop', '=', False), ('date_stop', '>=', time.strftime(DEFAULT_SERVER_DATETIME_FORMAT)),
|
||||
('company_id', '=', user_company)]
|
||||
return self.pool.get('mrp.bom').search(cr, SUPERUSER_ID, domain, context=context)
|
||||
|
||||
|
||||
def _action_explode(self, cr, uid, move, context=None):
|
||||
""" Explodes pickings.
|
||||
@param move: Stock moves
|
||||
|
||||
@@ -337,7 +337,7 @@ class mrp_production(osv.osv):
|
||||
for po in self.browse(cr, uid, ids, context=context):
|
||||
direction[po.id] = cmp(po.date_start, vals.get('date_start', False))
|
||||
result = super(mrp_production, self).write(cr, uid, ids, vals, context=context)
|
||||
if (vals.get('workcenter_lines', False) or vals.get('date_start', False)) and update:
|
||||
if (vals.get('workcenter_lines', False) or vals.get('date_start', False) or vals.get('date_planned', False)) and update:
|
||||
self._compute_planned_workcenter(cr, uid, ids, context=context, mini=mini)
|
||||
for d in direction:
|
||||
if direction[d] == 1:
|
||||
|
||||
@@ -172,7 +172,7 @@ class TxAdyen(osv.Model):
|
||||
})
|
||||
return True
|
||||
else:
|
||||
error = _('Paypal: feedback error')
|
||||
error = _('Adyen: feedback error')
|
||||
_logger.info(error)
|
||||
tx.write({
|
||||
'state': 'error',
|
||||
|
||||
@@ -1362,9 +1362,9 @@ class pos_order_line(osv.osv):
|
||||
'name': fields.char('Line No', required=True, copy=False),
|
||||
'notice': fields.char('Discount Notice'),
|
||||
'product_id': fields.many2one('product.product', 'Product', domain=[('sale_ok', '=', True)], required=True, change_default=True),
|
||||
'price_unit': fields.float(string='Unit Price', digits_compute=dp.get_precision('Account')),
|
||||
'price_unit': fields.float(string='Unit Price', digits_compute=dp.get_precision('Product Price')),
|
||||
'qty': fields.float('Quantity', digits_compute=dp.get_precision('Product UoS')),
|
||||
'price_subtotal': fields.function(_amount_line_all, multi='pos_order_line_amount', digits_compute=dp.get_precision('Account'), string='Subtotal w/o Tax', store=True),
|
||||
'price_subtotal': fields.function(_amount_line_all, multi='pos_order_line_amount', digits_compute=dp.get_precision('Product Price'), string='Subtotal w/o Tax', store=True),
|
||||
'price_subtotal_incl': fields.function(_amount_line_all, multi='pos_order_line_amount', digits_compute=dp.get_precision('Account'), string='Subtotal', store=True),
|
||||
'discount': fields.float('Discount (%)', digits_compute=dp.get_precision('Account')),
|
||||
'order_id': fields.many2one('pos.order', 'Order Ref', ondelete='cascade'),
|
||||
|
||||
@@ -1646,34 +1646,34 @@ openerp.point_of_sale.load_models = function load_models(instance, module){ //mo
|
||||
},
|
||||
/* ---- Payment Status --- */
|
||||
get_subtotal : function(){
|
||||
return this.orderlines.reduce((function(sum, orderLine){
|
||||
return round_pr(this.orderlines.reduce((function(sum, orderLine){
|
||||
return sum + orderLine.get_display_price();
|
||||
}), 0);
|
||||
}), 0), this.pos.currency.rounding);
|
||||
},
|
||||
get_total_with_tax: function() {
|
||||
return this.orderlines.reduce((function(sum, orderLine) {
|
||||
return round_pr(this.orderlines.reduce((function(sum, orderLine) {
|
||||
return sum + orderLine.get_price_with_tax();
|
||||
}), 0);
|
||||
}), 0), this.pos.currency.rounding);
|
||||
},
|
||||
get_total_without_tax: function() {
|
||||
return this.orderlines.reduce((function(sum, orderLine) {
|
||||
return round_pr(this.orderlines.reduce((function(sum, orderLine) {
|
||||
return sum + orderLine.get_price_without_tax();
|
||||
}), 0);
|
||||
}), 0), this.pos.currency.rounding);
|
||||
},
|
||||
get_total_discount: function() {
|
||||
return this.orderlines.reduce((function(sum, orderLine) {
|
||||
return round_pr(this.orderlines.reduce((function(sum, orderLine) {
|
||||
return sum + (orderLine.get_unit_price() * (orderLine.get_discount()/100) * orderLine.get_quantity());
|
||||
}), 0);
|
||||
}), 0), this.pos.currency.rounding);
|
||||
},
|
||||
get_total_tax: function() {
|
||||
return this.orderlines.reduce((function(sum, orderLine) {
|
||||
return round_pr(this.orderlines.reduce((function(sum, orderLine) {
|
||||
return sum + orderLine.get_tax();
|
||||
}), 0);
|
||||
}), 0), this.pos.currency.rounding);
|
||||
},
|
||||
get_total_paid: function() {
|
||||
return this.paymentlines.reduce((function(sum, paymentLine) {
|
||||
return round_pr(this.paymentlines.reduce((function(sum, paymentLine) {
|
||||
return sum + paymentLine.get_amount();
|
||||
}), 0);
|
||||
}), 0), this.pos.currency.rounding);
|
||||
},
|
||||
get_tax_details: function(){
|
||||
var details = {};
|
||||
|
||||
@@ -1453,6 +1453,7 @@ openerp.point_of_sale.load_screens = function load_screens(instance, module){ //
|
||||
|
||||
this.inputbuffer = "";
|
||||
this.firstinput = true;
|
||||
this.decimal_point = instance.web._t.database.parameters.decimal_point;
|
||||
|
||||
// This is a keydown handler that prevents backspace from
|
||||
// doing a back navigation
|
||||
@@ -1515,7 +1516,14 @@ openerp.point_of_sale.load_screens = function load_screens(instance, module){ //
|
||||
this.inputbuffer = newbuf;
|
||||
var order = this.pos.get_order();
|
||||
if (order.selected_paymentline) {
|
||||
order.selected_paymentline.set_amount(parseFloat(this.inputbuffer));
|
||||
var amount;
|
||||
try{
|
||||
amount = instance.web.parse_value(this.inputbuffer, {type: "float"});
|
||||
}
|
||||
catch(e){
|
||||
amount = 0;
|
||||
}
|
||||
order.selected_paymentline.set_amount(amount);
|
||||
this.order_changes();
|
||||
this.render_paymentlines();
|
||||
this.$('.paymentline.selected .edit').text(this.inputbuffer);
|
||||
|
||||
@@ -48,6 +48,7 @@ openerp.point_of_sale.load_basewidget = function load_basewidget(instance, modul
|
||||
|
||||
if (typeof amount === 'number') {
|
||||
amount = round_di(amount,decimals).toFixed(decimals);
|
||||
amount = openerp.instances[this.session.name].web.format_value(parseFloat(amount), { type : 'float' });
|
||||
}
|
||||
|
||||
return amount;
|
||||
|
||||
@@ -1174,7 +1174,10 @@
|
||||
<div class='paymentline-name'>
|
||||
<t t-esc="line.name"/>
|
||||
</div>
|
||||
<input class='paymentline-input' type="number" step="0.01" t-att-value="line.get_amount_str()" />
|
||||
<input class='paymentline-input'
|
||||
t-att-type="widget.decimal_point === '.' ? 'number' : 'text'"
|
||||
t-attf-pattern="[0-9]+([\\#{widget.decimal_point || '.' }][0-9]+)?"
|
||||
step="0.01" t-att-value="line.get_amount_str()" />
|
||||
<span class='paymentline-delete'>
|
||||
<img src="/point_of_sale/static/src/img/search_reset.gif" />
|
||||
</span>
|
||||
|
||||
@@ -43,7 +43,7 @@ openerp.pos_restaurant.load_splitbill = function(instance, module){
|
||||
}
|
||||
}else{
|
||||
if( split.quantity < line.get_quantity()){
|
||||
split.quantity += line.get_unit().rounding;
|
||||
split.quantity += line.get_unit().is_unit ? 1 : line.get_unit().rounding;
|
||||
if(split.quantity > line.get_quantity()){
|
||||
split.quantity = line.get_quantity();
|
||||
}
|
||||
|
||||
@@ -1666,7 +1666,7 @@ class account_invoice(osv.Model):
|
||||
else:
|
||||
user_id = uid
|
||||
po_ids = purchase_order_obj.search(cr, user_id, [('invoice_ids', 'in', ids)], context=context)
|
||||
for order in purchase_order_obj.browse(cr, uid, po_ids, context=context):
|
||||
for order in purchase_order_obj.browse(cr, user_id, po_ids, context=context):
|
||||
purchase_order_obj.message_post(cr, user_id, order.id, body=_("Invoice received"), context=context)
|
||||
invoiced = []
|
||||
shipped = True
|
||||
@@ -1679,8 +1679,8 @@ class account_invoice(osv.Model):
|
||||
if all(line.invoice_id.state not in ['draft', 'cancel'] for line in po_line.invoice_lines):
|
||||
invoiced.append(po_line.id)
|
||||
if invoiced and shipped:
|
||||
self.pool['purchase.order.line'].write(cr, uid, invoiced, {'invoiced': True})
|
||||
workflow.trg_write(uid, 'purchase.order', order.id, cr)
|
||||
self.pool['purchase.order.line'].write(cr, user_id, invoiced, {'invoiced': True})
|
||||
workflow.trg_write(user_id, 'purchase.order', order.id, cr)
|
||||
return res
|
||||
|
||||
def confirm_paid(self, cr, uid, ids, context=None):
|
||||
|
||||
@@ -39,7 +39,6 @@ import lxml.html
|
||||
import os
|
||||
import subprocess
|
||||
from contextlib import closing
|
||||
from datetime import datetime
|
||||
from distutils.version import LooseVersion
|
||||
from functools import partial
|
||||
from pyPdf import PdfFileWriter, PdfFileReader
|
||||
@@ -141,7 +140,6 @@ class Report(osv.Model):
|
||||
context = dict(context, translatable=context.get('lang') != request.website.default_lang_code)
|
||||
values.update(
|
||||
time=time,
|
||||
datetime=datetime,
|
||||
context_timestamp=lambda t: fields.datetime.context_timestamp(cr, uid, t, context),
|
||||
translate_doc=translate_doc,
|
||||
editable=True,
|
||||
|
||||
@@ -124,7 +124,7 @@
|
||||
<div class="header">
|
||||
<div class="row">
|
||||
<div class="col-xs-3">
|
||||
<span t-esc="context_timestamp(datetime.now()).strftime('%Y-%m-%d %H:%M')"/>
|
||||
<span t-esc="context_timestamp(datetime.datetime.now()).strftime('%Y-%m-%d %H:%M')"/>
|
||||
</div>
|
||||
<div class="col-xs-2 col-xs-offset-2 text-center">
|
||||
<span t-esc="company.name"/>
|
||||
|
||||
@@ -363,6 +363,14 @@ class sale_order_line(osv.osv):
|
||||
res.update({'warning': warning})
|
||||
return res
|
||||
|
||||
def button_cancel(self, cr, uid, ids, context=None):
|
||||
lines = self.browse(cr, uid, ids, context=context)
|
||||
for procurement in lines.mapped('procurement_ids'):
|
||||
for move in procurement.move_ids:
|
||||
if move.state == 'done' and not move.scrapped:
|
||||
raise osv.except_osv(_('Invalid Action!'), _('You cannot cancel a sale order line which is linked to a stock move already done.'))
|
||||
return super(sale_order_line, self).button_cancel(cr, uid, ids, context=context)
|
||||
|
||||
class stock_move(osv.osv):
|
||||
_inherit = 'stock.move'
|
||||
|
||||
@@ -389,7 +397,7 @@ class stock_move(osv.osv):
|
||||
if move.procurement_id and move.procurement_id.sale_line_id and move.procurement_id.sale_line_id.order_id.order_policy == 'picking':
|
||||
sale_order = move.procurement_id.sale_line_id.order_id
|
||||
return sale_order.partner_invoice_id, sale_order.user_id.id, sale_order.pricelist_id.currency_id.id
|
||||
elif move.picking_id.sale_id and context.get('inv_type') == 'out_invoice':
|
||||
elif move.picking_id.sale_id and context.get('inv_type') in ('out_invoice', 'out_refund'):
|
||||
# In case of extra move, it is better to use the same data as the original moves
|
||||
sale_order = move.picking_id.sale_id
|
||||
return sale_order.partner_invoice_id, sale_order.user_id.id, sale_order.pricelist_id.currency_id.id
|
||||
@@ -397,7 +405,7 @@ class stock_move(osv.osv):
|
||||
|
||||
def _get_invoice_line_vals(self, cr, uid, move, partner, inv_type, context=None):
|
||||
res = super(stock_move, self)._get_invoice_line_vals(cr, uid, move, partner, inv_type, context=context)
|
||||
if move.procurement_id and move.procurement_id.sale_line_id:
|
||||
if inv_type in ('out_invoice', 'out_refund') and move.procurement_id and move.procurement_id.sale_line_id:
|
||||
sale_line = move.procurement_id.sale_line_id
|
||||
res['invoice_line_tax_id'] = [(6, 0, [x.id for x in sale_line.tax_id])]
|
||||
res['account_analytic_id'] = sale_line.order_id.project_id and sale_line.order_id.project_id.id or False
|
||||
|
||||
@@ -794,7 +794,7 @@ class stock_picking(osv.osv):
|
||||
'name': fields.char('Reference', select=True, states={'done': [('readonly', True)], 'cancel': [('readonly', True)]}, copy=False),
|
||||
'origin': fields.char('Source Document', states={'done': [('readonly', True)], 'cancel': [('readonly', True)]}, help="Reference of the document", select=True),
|
||||
'backorder_id': fields.many2one('stock.picking', '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, copy=False),
|
||||
'note': fields.text('Notes', states={'done': [('readonly', True)], 'cancel': [('readonly', True)]}),
|
||||
'note': fields.text('Notes'),
|
||||
'move_type': fields.selection([('direct', 'Partial'), ('one', 'All at once')], 'Delivery Method', required=True, states={'done': [('readonly', True)], 'cancel': [('readonly', True)]}, help="It specifies goods to be deliver partially or all at once"),
|
||||
'state': fields.function(_state_get, type="selection", copy=False,
|
||||
store={
|
||||
|
||||
@@ -91,7 +91,7 @@ class stock_transfer_details(models.TransientModel):
|
||||
'owner_id': prod.owner_id.id,
|
||||
}
|
||||
if prod.packop_id:
|
||||
prod.packop_id.write(pack_datas)
|
||||
prod.packop_id.with_context(no_recompute=True).write(pack_datas)
|
||||
processed_ids.append(prod.packop_id.id)
|
||||
else:
|
||||
pack_datas['picking_id'] = self.picking_id.id
|
||||
@@ -99,8 +99,7 @@ class stock_transfer_details(models.TransientModel):
|
||||
processed_ids.append(packop_id.id)
|
||||
# Delete the others
|
||||
packops = self.env['stock.pack.operation'].search(['&', ('picking_id', '=', self.picking_id.id), '!', ('id', 'in', processed_ids)])
|
||||
for packop in packops:
|
||||
packop.unlink()
|
||||
packops.unlink()
|
||||
|
||||
# Execute the transfer of the picking
|
||||
self.picking_id.do_transfer()
|
||||
|
||||
@@ -1183,6 +1183,7 @@ class Binary(http.Controller):
|
||||
}
|
||||
except Exception:
|
||||
args = {'error': "Something horrible happened"}
|
||||
_logger.exception("Fail to upload attachment %s" % ufile.filename)
|
||||
return out % (simplejson.dumps(callback), simplejson.dumps(args))
|
||||
|
||||
@http.route([
|
||||
|
||||
@@ -1023,7 +1023,7 @@ instance.web.Sidebar = instance.web.Widget.extend({
|
||||
init: function(parent) {
|
||||
var self = this;
|
||||
this._super(parent);
|
||||
var view = this.getParent();
|
||||
this.view = this.getParent();
|
||||
this.sections = [
|
||||
{ 'name' : 'print', 'label' : _t('Print'), },
|
||||
{ 'name' : 'other', 'label' : _t('More'), }
|
||||
|
||||
@@ -625,12 +625,12 @@
|
||||
</t>
|
||||
<a class="oe_file_attachment" t-att-title="item.title or ''" t-att-data-section="section.name" t-att-data-index="item_index" t-att-href="item.url">
|
||||
<t t-raw="item.label"/>
|
||||
<span t-if="section.name == 'files' and !item.callback" class="oe_sidebar_delete_item" t-att-data-id="item.id" title="Delete this attachment">
|
||||
<span t-if="widget.view.is_action_enabled('edit') && section.name == 'files' and !item.callback" class="oe_sidebar_delete_item" t-att-data-id="item.id" title="Delete this attachment">
|
||||
<i class="fa fa-trash-o"/>
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
<li t-if="section.name == 'files'" class="oe_sidebar_add_attachment">
|
||||
<li t-if="widget.view.is_action_enabled('edit') && section.name == 'files'" class="oe_sidebar_add_attachment">
|
||||
<t t-call="HiddenInputFile">
|
||||
<t t-set="fileupload_id" t-value="widget.fileupload_id"/>
|
||||
<t t-set="fileupload_action" t-translation="off">/web/binary/upload_attachment</t>
|
||||
|
||||
@@ -60,9 +60,11 @@
|
||||
ev.preventDefault();
|
||||
var link = $('.js_language_selector a[data-default-lang]')[0];
|
||||
if (link) {
|
||||
link.pathname = $(link).data('lang') + link.pathname;
|
||||
link.search += (link.search ? '&' : '?') + 'enable_editor=1';
|
||||
window.location = link.attributes.href.value;
|
||||
var url = link.pathname + link.search + window.location.hash;
|
||||
link.pathname = '/website/lang/default';
|
||||
link.search = '?' + $.param({r: url});
|
||||
window.location = link.href;
|
||||
}
|
||||
},
|
||||
translate: function () {
|
||||
|
||||
@@ -92,8 +92,8 @@
|
||||
|
||||
<t t-set="languages" t-value="website.get_languages() if website else None"/>
|
||||
<t t-if="request and request.website_multilang and website">
|
||||
<t t-foreach="website.get_alternate_languages(request.httprequest)" t-as="lang">
|
||||
<link rel="alternate" t-att-hreflang="lang['hreflang']" t-att-href="lang['href']"/>
|
||||
<t t-foreach="website.get_alternate_languages(request.httprequest)" t-as="lg">
|
||||
<link rel="alternate" t-att-hreflang="lg['hreflang']" t-att-href="lg['href']"/>
|
||||
</t>
|
||||
</t>
|
||||
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
United Kingdom, 2015-05-18
|
||||
|
||||
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,
|
||||
|
||||
Colin Newell colin.newell@gmail.com https://github.com/colinnewell
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
Belgium, 2015-04-14
|
||||
|
||||
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,
|
||||
|
||||
Samuel Lefever sam@niboo.be https://github.com/samuellefever
|
||||
@@ -137,9 +137,10 @@ class ir_http(osv.AbstractModel):
|
||||
|
||||
# This is done first as the attachment path may
|
||||
# not match any HTTP controller
|
||||
attach = self._serve_attachment()
|
||||
if attach:
|
||||
return attach
|
||||
if isinstance(exception, werkzeug.exceptions.HTTPException) and exception.code == 404:
|
||||
attach = self._serve_attachment()
|
||||
if attach:
|
||||
return attach
|
||||
|
||||
# Don't handle exception but use werkeug debugger if server in --dev mode
|
||||
if openerp.tools.config['dev_mode']:
|
||||
|
||||
@@ -261,6 +261,7 @@ class ir_property(osv.osv):
|
||||
def search_multi(self, name, model, operator, value):
|
||||
""" Return a domain for the records that match the given condition. """
|
||||
default_matches = False
|
||||
include_zero = False
|
||||
|
||||
field = self.env[model]._fields[name]
|
||||
if field.type == 'many2one':
|
||||
@@ -282,6 +283,27 @@ class ir_property(osv.osv):
|
||||
target_names = target.name_search(value, operator=operator, limit=None)
|
||||
target_ids = map(itemgetter(0), target_names)
|
||||
operator, value = 'in', map(makeref, target_ids)
|
||||
elif field.type in ('integer', 'float'):
|
||||
# No record is created in ir.property if the field's type is float or integer with a value
|
||||
# equal to 0. Then to match with the records that are linked to a property field equal to 0,
|
||||
# the negation of the operator must be taken to compute the goods and the domain returned
|
||||
# to match the searched records is just the opposite.
|
||||
if value == 0 and operator == '=':
|
||||
operator = '!='
|
||||
include_zero = True
|
||||
elif value <= 0 and operator == '>=':
|
||||
operator = '<'
|
||||
include_zero = True
|
||||
elif value <= 0 and operator == '>':
|
||||
operator = '<='
|
||||
include_zero = True
|
||||
elif value >= 0 and operator == '<=':
|
||||
operator = '>'
|
||||
include_zero = True
|
||||
elif value >= 0 and operator == '<':
|
||||
operator = '>='
|
||||
include_zero = True
|
||||
|
||||
|
||||
# retrieve the properties that match the condition
|
||||
domain = self._get_domain(name, model)
|
||||
@@ -298,7 +320,9 @@ class ir_property(osv.osv):
|
||||
else:
|
||||
default_matches = True
|
||||
|
||||
if default_matches:
|
||||
if include_zero:
|
||||
return [('id', 'not in', good_ids)]
|
||||
elif default_matches:
|
||||
# exclude all records with a property that does not match
|
||||
all_ids = []
|
||||
props = self.search(domain + [('res_id', '!=', False)])
|
||||
|
||||
+2
-1
@@ -1054,7 +1054,8 @@ class Float(Field):
|
||||
@property
|
||||
def digits(self):
|
||||
if callable(self._digits):
|
||||
return self._digits(fields.get_cursor())
|
||||
with fields._get_cursor() as cr:
|
||||
return self._digits(cr)
|
||||
else:
|
||||
return self._digits
|
||||
|
||||
|
||||
@@ -203,7 +203,11 @@ class WebRequest(object):
|
||||
def env(self):
|
||||
"""
|
||||
The :class:`~openerp.api.Environment` bound to current request.
|
||||
Raises a :class:`RuntimeError` if the current requests is not bound
|
||||
to a database.
|
||||
"""
|
||||
if not self.db:
|
||||
return RuntimeError('request not bound to a database')
|
||||
return openerp.api.Environment(self.cr, self.uid, self.context)
|
||||
|
||||
@lazy_property
|
||||
@@ -238,6 +242,8 @@ class WebRequest(object):
|
||||
"""
|
||||
# can not be a lazy_property because manual rollback in _call_function
|
||||
# if already set (?)
|
||||
if not self.db:
|
||||
return RuntimeError('request not bound to a database')
|
||||
if not self._cr:
|
||||
self._cr = self.registry.cursor()
|
||||
return self._cr
|
||||
@@ -924,6 +930,7 @@ class OpenERPSession(werkzeug.contrib.sessions.Session):
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.inited = False
|
||||
self.modified = False
|
||||
self.rotate = False
|
||||
super(OpenERPSession, self).__init__(*args, **kwargs)
|
||||
self.inited = True
|
||||
self._default_values()
|
||||
@@ -959,6 +966,7 @@ class OpenERPSession(werkzeug.contrib.sessions.Session):
|
||||
uid = dispatch_rpc('common', 'authenticate', [db, login, password, env])
|
||||
else:
|
||||
security.check(db, uid, password)
|
||||
self.rotate = True
|
||||
self.db = db
|
||||
self.uid = uid
|
||||
self.login = login
|
||||
@@ -984,6 +992,7 @@ class OpenERPSession(werkzeug.contrib.sessions.Session):
|
||||
if not (keep_db and k == 'db'):
|
||||
del self[k]
|
||||
self._default_values()
|
||||
self.rotate = True
|
||||
|
||||
def _default_values(self):
|
||||
self.setdefault("db", None)
|
||||
@@ -1391,6 +1400,10 @@ class Root(object):
|
||||
}
|
||||
|
||||
if httprequest.session.should_save:
|
||||
if httprequest.session.rotate:
|
||||
self.session_store.delete(httprequest.session)
|
||||
httprequest.session.sid = self.session_store.generate_key()
|
||||
httprequest.session.modified = True
|
||||
self.session_store.save(httprequest.session)
|
||||
# We must not set the cookie if the session id was specified using a http header or a GET parameter.
|
||||
# There are two reasons to this:
|
||||
|
||||
@@ -401,7 +401,7 @@ def is_leaf(element, internal=False):
|
||||
and len(element) == 3 \
|
||||
and element[1] in INTERNAL_OPS \
|
||||
and ((isinstance(element[0], basestring) and element[0])
|
||||
or element in (TRUE_LEAF, FALSE_LEAF))
|
||||
or tuple(element) in (TRUE_LEAF, FALSE_LEAF))
|
||||
|
||||
|
||||
# --------------------------------------------------
|
||||
|
||||
+18
-6
@@ -43,6 +43,7 @@ import pytz
|
||||
import re
|
||||
import xmlrpclib
|
||||
from operator import itemgetter
|
||||
from contextlib import contextmanager
|
||||
from psycopg2 import Binary
|
||||
|
||||
import openerp
|
||||
@@ -52,13 +53,22 @@ from openerp.tools import float_repr, float_round, frozendict, html_sanitize
|
||||
import simplejson
|
||||
from openerp import SUPERUSER_ID, registry
|
||||
|
||||
def get_cursor():
|
||||
# retrieve a valid cursor from any environment
|
||||
@contextmanager
|
||||
def _get_cursor():
|
||||
# yield a valid cursor from any environment or create a new one if none found
|
||||
from openerp.api import Environment
|
||||
from openerp.http import request
|
||||
try:
|
||||
request.env # force request's env to be computed
|
||||
except RuntimeError:
|
||||
pass # ignore if not in a request
|
||||
for env in Environment.envs:
|
||||
if not env.cr.closed:
|
||||
return env.cr
|
||||
raise RuntimeError("No valid cursor found")
|
||||
yield env.cr
|
||||
break
|
||||
else:
|
||||
with registry().cursor() as cr:
|
||||
yield cr
|
||||
|
||||
EMPTY_DICT = frozendict()
|
||||
|
||||
@@ -396,7 +406,8 @@ class float(_column):
|
||||
@property
|
||||
def digits(self):
|
||||
if self._digits_compute:
|
||||
return self._digits_compute(get_cursor())
|
||||
with _get_cursor() as cr:
|
||||
return self._digits_compute(cr)
|
||||
else:
|
||||
return self._digits
|
||||
|
||||
@@ -1316,7 +1327,8 @@ class function(_column):
|
||||
@property
|
||||
def digits(self):
|
||||
if self._digits_compute:
|
||||
return self._digits_compute(get_cursor())
|
||||
with _get_cursor() as cr:
|
||||
return self._digits_compute(cr)
|
||||
else:
|
||||
return self._digits
|
||||
|
||||
|
||||
Reference in New Issue
Block a user