diff --git a/addons/account/tests/test_product_id_change.py b/addons/account/tests/test_product_id_change.py new file mode 100644 index 00000000000..1bd5154f4ba --- /dev/null +++ b/addons/account/tests/test_product_id_change.py @@ -0,0 +1,50 @@ +from openerp.tests.common import TransactionCase + +class TestProductIdChange(TransactionCase): + """Test that when an included tax is mapped by a fiscal position, the included tax must be + subtracted to the price of the product. + """ + + def setUp(self): + super(TestProductIdChange, self).setUp() + self.fiscal_position_model = self.registry('account.fiscal.position') + self.fiscal_position_tax_model = self.registry('account.fiscal.position.tax') + self.tax_model = self.registry('account.tax') + self.pricelist_model = self.registry('product.pricelist') + self.res_partner_model = self.registry('res.partner') + self.product_tmpl_model = self.registry('product.template') + self.product_model = self.registry('product.product') + self.invoice_line_model = self.registry('account.invoice.line') + + def test_product_id_change(self): + cr, uid = self.cr, self.uid + partner_id = self.res_partner_model.create(cr, uid, dict(name="George")) + tax_include_id = self.tax_model.create(cr, uid, dict(name="Include tax", + type='percent', + amount='0.21', + price_include=True)) + tax_exclude_id = self.tax_model.create(cr, uid, dict(name="Exclude tax", + type='percent', + amount='0.00')) + product_tmpl_id = self.product_tmpl_model.create(cr, uid, dict(name="Voiture", + list_price='121', + standard_price='121', + taxes_id=[(6, 0, [tax_include_id])], + supplier_taxes_id=[(6, 0, [tax_include_id])])) + product_id = self.product_model.create(cr, uid, dict(product_tmpl_id=product_tmpl_id)) + product = self.product_model.browse(cr, uid, [product_id]) + fp_id = self.fiscal_position_model.create(cr, uid, dict(name="fiscal position", + sequence=1)) + fp_tax_id = self.fiscal_position_tax_model.create(cr, uid, dict(position_id=fp_id, + tax_src_id=tax_include_id, + tax_dest_id=tax_exclude_id)) + + res = self.invoice_line_model.product_id_change(cr, uid, [], product.id, product.uom_id.id, + qty=1, type='out_invoice', partner_id=partner_id, + fposition_id=fp_id) + self.assertEquals(100, res['value']['price_unit'], "The included tax must be subtracted to the price") + + res = self.invoice_line_model.product_id_change(cr, uid, [], product.id, product.uom_id.id, + qty=1, type='in_invoice', partner_id=partner_id, + fposition_id=fp_id) + self.assertEquals(100, res['value']['price_unit'], "The included tax must be subtracted to the price") diff --git a/addons/auth_signup/controllers/main.py b/addons/auth_signup/controllers/main.py index 9035a650809..b508514617f 100644 --- a/addons/auth_signup/controllers/main.py +++ b/addons/auth_signup/controllers/main.py @@ -36,7 +36,11 @@ class AuthSignupHome(openerp.addons.web.controllers.main.Home): self.do_signup(qcontext) return super(AuthSignupHome, self).web_login(*args, **kw) except (SignupError, AssertionError), e: - qcontext['error'] = _(e.message) + if request.env["res.users"].sudo().search([("login", "=", qcontext.get("login"))]): + qcontext["error"] = _("Another user is already registered using this email address.") + else: + _logger.error(e.message) + qcontext['error'] = _("Could not create a new account.") return request.render('auth_signup.signup', qcontext) diff --git a/addons/calendar/calendar.py b/addons/calendar/calendar.py index 596bdc5677f..c46bd5a16e9 100644 --- a/addons/calendar/calendar.py +++ b/addons/calendar/calendar.py @@ -1469,7 +1469,7 @@ class calendar_event(osv.Model): for arg in args: new_arg = arg - if arg[0] in ('start_date', 'start_datetime', 'start',) and arg[1] == ">=": + if arg[0] in ('stop_date', 'stop_datetime', 'stop',) and arg[1] == ">=": if context.get('virtual_id', True): new_args += ['|', '&', ('recurrency', '=', 1), ('final_date', arg[1], arg[2])] elif arg[0] == "id": diff --git a/addons/crm/crm_lead_view.xml b/addons/crm/crm_lead_view.xml index 7fef14bfcb1..371724682fb 100644 --- a/addons/crm/crm_lead_view.xml +++ b/addons/crm/crm_lead_view.xml @@ -131,6 +131,7 @@ diff --git a/addons/crm/wizard/crm_lead_to_opportunity.py b/addons/crm/wizard/crm_lead_to_opportunity.py index e621910c71d..b08d8445b3d 100644 --- a/addons/crm/wizard/crm_lead_to_opportunity.py +++ b/addons/crm/wizard/crm_lead_to_opportunity.py @@ -121,6 +121,7 @@ class crm_lead2opportunity_partner(osv.osv_memory): context = {} lead_obj = self.pool['crm.lead'] + partner_obj = self.pool['res.partner'] w = self.browse(cr, uid, ids, context=context)[0] opp_ids = [o.id for o in w.opportunity_ids] @@ -144,6 +145,9 @@ class crm_lead2opportunity_partner(osv.osv_memory): lead_ids = context.get('active_ids', []) vals.update({'lead_ids': lead_ids, 'user_ids': [w.user_id.id]}) self._convert_opportunity(cr, uid, ids, vals, context=context) + for lead in lead_obj.browse(cr, uid, lead_ids, context=context): + if lead.partner_id and lead.partner_id.user_id != lead.user_id: + partner_obj.write(cr, uid, [lead.partner_id.id], {'user_id': lead.user_id.id}, context=context) return self.pool.get('crm.lead').redirect_opportunity_view(cr, uid, lead_ids[0], context=context) diff --git a/addons/delivery/models/delivery_carrier.py b/addons/delivery/models/delivery_carrier.py index 4aa8987b5dc..dbf93a92853 100644 --- a/addons/delivery/models/delivery_carrier.py +++ b/addons/delivery/models/delivery_carrier.py @@ -3,7 +3,7 @@ import logging from openerp import api, fields, models, _ -from openerp.exceptions import UserError +from openerp.exceptions import UserError, ValidationError _logger = logging.getLogger(__name__) @@ -52,6 +52,25 @@ class DeliveryCarrier(models.Model): fixed_price = fields.Float(compute='_compute_fixed_price', inverse='_set_product_fixed_price', store=True, string='Fixed Price',help="Keep empty if the pricing depends on the advanced pricing per destination") shipping_enabled = fields.Boolean(string="Shipping enabled", default=True, help="Uncheck this box to disable package shipping while validating Delivery Orders") + @api.multi + def name_get(self): + display_delivery = self.env.context.get('display_delivery', False) + order_id = self.env.context.get('order_id', False) + if display_delivery and order_id: + order = self.env['sale.order'].browse(order_id) + currency = order.pricelist_id.currency_id.name or '' + res = [] + for carrier_id in self.ids: + try: + r = self.read([carrier_id], ['name', 'price'])[0] + res.append((r['id'], r['name'] + ' (' + (str(r['price'])) + ' ' + currency + ')')) + except ValidationError: + r = self.read([carrier_id], ['name'])[0] + res.append((r['id'], r['name'])) + else: + res = super(DeliveryCarrier, self).name_get() + return res + @api.depends('product_id.list_price', 'product_id.product_tmpl_id.list_price') def _compute_fixed_price(self): for carrier in self: @@ -234,6 +253,8 @@ class DeliveryCarrier(models.Model): quantity += qty total = (order.amount_total or 0.0) - total_delivery + total = order.currency_id.with_context(date=order.date_order).compute(total, order.company_id.currency_id) + return self.get_price_from_picking(total, weight, volume, quantity) def get_price_from_picking(self, total, weight, volume, quantity): diff --git a/addons/delivery/views/delivery_view.xml b/addons/delivery/views/delivery_view.xml index a001ffd21a4..ab38c276ae9 100644 --- a/addons/delivery/views/delivery_view.xml +++ b/addons/delivery/views/delivery_view.xml @@ -171,7 +171,8 @@
-
- + @@ -702,9 +702,6 @@
Served by
- -
Guests:
-


@@ -1006,7 +1003,7 @@
- + @@ -1298,7 +1295,7 @@ Subtotal: - + diff --git a/addons/point_of_sale/tools/posbox/configuration/odoo.conf b/addons/point_of_sale/tools/posbox/configuration/odoo.conf index 1bc0a0d3ba5..bbe21f44c3e 100644 --- a/addons/point_of_sale/tools/posbox/configuration/odoo.conf +++ b/addons/point_of_sale/tools/posbox/configuration/odoo.conf @@ -3,4 +3,3 @@ data_dir = /var/run/odoo log_level = error logfile = /var/log/odoo/odoo-server.log pidfile = /var/run/odoo/odoo.pid -server_wide_modules = web,hw_proxy,hw_posbox_homepage,hw_posbox_upgrade,hw_scale,hw_scanner,hw_escpos \ No newline at end of file diff --git a/addons/point_of_sale/tools/posbox/overwrite_before_init/etc/init.d/odoo b/addons/point_of_sale/tools/posbox/overwrite_before_init/etc/init.d/odoo index f9bc0400abc..30be4bd26d3 100755 --- a/addons/point_of_sale/tools/posbox/overwrite_before_init/etc/init.d/odoo +++ b/addons/point_of_sale/tools/posbox/overwrite_before_init/etc/init.d/odoo @@ -26,7 +26,7 @@ test -x $DAEMON || exit 0 set -e function _start() { - start-stop-daemon --start --quiet --pidfile $PIDFILE --chuid $USER:$USER --background --make-pidfile --exec $DAEMON -- --config $CONFIG --logfile $LOGFILE --load=web,hw_proxy,hw_posbox_homepage,hw_posbox_upgrade,hw_scale,hw_scanner,hw_escpos + start-stop-daemon --start --quiet --pidfile $PIDFILE --chuid $USER:$USER --background --make-pidfile --exec $DAEMON -- --config $CONFIG --logfile $LOGFILE --load=web,hw_proxy,hw_posbox_homepage,hw_posbox_upgrade,hw_scale,hw_scanner,hw_escpos,hw_blackbox_be } function _stop() { diff --git a/addons/pos_restaurant/static/src/xml/floors.xml b/addons/pos_restaurant/static/src/xml/floors.xml index 10ee218a33e..279fc40bc32 100644 --- a/addons/pos_restaurant/static/src/xml/floors.xml +++ b/addons/pos_restaurant/static/src/xml/floors.xml @@ -6,6 +6,9 @@ at table + +
Guests:
+
@@ -14,6 +17,9 @@ at table + +
Guests:
+
diff --git a/addons/product/product.py b/addons/product/product.py index ec2738e011a..e85de77b249 100644 --- a/addons/product/product.py +++ b/addons/product/product.py @@ -567,7 +567,7 @@ class product_template(osv.osv): if ptype != 'standard_price': res[product.id] = product[ptype] or 0.0 else: - company_id = product.env.user.company_id.id + company_id = context.get('force_company') or product.env.user.company_id.id product = product.with_context(force_company=company_id) res[product.id] = res[product.id] = product.sudo()[ptype] if ptype == 'list_price': diff --git a/addons/purchase/purchase.py b/addons/purchase/purchase.py index 63fad3f2921..9d5ff181c26 100644 --- a/addons/purchase/purchase.py +++ b/addons/purchase/purchase.py @@ -3,7 +3,7 @@ from datetime import datetime from dateutil.relativedelta import relativedelta -from openerp import api, fields, models, _ +from openerp import api, fields, models, _, SUPERUSER_ID from openerp.tools import DEFAULT_SERVER_DATETIME_FORMAT from openerp.tools.translate import _ from openerp.tools.float_utils import float_is_zero, float_compare @@ -581,7 +581,11 @@ class PurchaseOrderLine(models.Model): self.name += '\n' + product_lang.description_purchase fpos = self.order_id.fiscal_position_id - self.taxes_id = fpos.map_tax(self.product_id.supplier_taxes_id) + if self.env.uid == SUPERUSER_ID: + company_id = self.env.user.company_id.id + self.taxes_id = self.product_id.supplier_taxes_id.filtered(lambda r: r.company_id.id == company_id) + else: + self.taxes_id = fpos.map_tax(self.product_id.supplier_taxes_id) self._suggest_quantity() self._onchange_quantity() diff --git a/addons/sale/sale.py b/addons/sale/sale.py index bfaea2e26d0..0c86a60c3d3 100644 --- a/addons/sale/sale.py +++ b/addons/sale/sale.py @@ -741,7 +741,8 @@ class SaleOrderLine(models.Model): quantity=self.product_uom_qty, date_order=self.order_id.date_order, pricelist=self.order_id.pricelist_id.id, - uom=self.product_uom.id + uom=self.product_uom.id, + fiscal_position=self.env.context.get('fiscal_position') ) self.price_unit = self.env['account.tax']._fix_tax_included_price(product.price, product.taxes_id, self.tax_id) @@ -751,6 +752,15 @@ class SaleOrderLine(models.Model): raise UserError(_('You can not remove a sale order line.\nDiscard changes and try setting the quantity to 0.')) return super(SaleOrderLine, self).unlink() + def onchange_product_uom(self, cursor, user, ids, pricelist, product, qty=0, + uom=False, qty_uos=0, uos=False, name='', partner_id=False, + lang=False, update_tax=True, date_order=False, fiscal_position=False, context=None): + ctx = dict(context or {}, fiscal_position=fiscal_position) + return self.product_uom_change(cursor, user, ids, pricelist, product, + qty=qty, uom=uom, qty_uos=qty_uos, uos=uos, name=name, + partner_id=partner_id, lang=lang, update_tax=update_tax, + date_order=date_order, context=ctx) + @api.multi def _get_delivered_qty(self): ''' diff --git a/addons/sale_stock/sale_stock.py b/addons/sale_stock/sale_stock.py index d3b4f9ffdb9..0387dbd6783 100644 --- a/addons/sale_stock/sale_stock.py +++ b/addons/sale_stock/sale_stock.py @@ -4,6 +4,7 @@ from datetime import datetime, timedelta from openerp import api, fields, models, _ from openerp.tools import DEFAULT_SERVER_DATETIME_FORMAT, float_compare +from openerp.exceptions import UserError class SaleOrder(models.Model): @@ -203,7 +204,7 @@ class SaleOrderLine(models.Model): mto_route_id = False try: mto_route_id = self.env['stock.warehouse']._get_mto_route() - except models.except_orm: + except UserError: # if route MTO not found in ir_model_data, we treat the product as in MTS pass if mto_route_id and mto_route_id in product_routes.ids: diff --git a/addons/stock/stock.py b/addons/stock/stock.py index 40e395fddf2..380b90c4609 100644 --- a/addons/stock/stock.py +++ b/addons/stock/stock.py @@ -1556,7 +1556,6 @@ class stock_picking(models.Model): for pack in pick.pack_operation_ids: if pack.product_id and pack.product_id.tracking != 'none': raise UserError(_('Some products require lots, so you need to specify those first!')) - view = data_obj.xmlid_to_res_id(cr, uid, 'stock.view_immediate_transfer') wiz_id = self.pool['stock.immediate.transfer'].create(cr, uid, {'pick_id': pick.id}, context=context) return { @@ -4206,7 +4205,7 @@ class stock_package(osv.osv): quant_obj = self.pool.get('stock.quant') for package in self.browse(cr, uid, ids, context=context): quant_ids = [quant.id for quant in package.quant_ids] - quant_obj.write(cr, uid, quant_ids, {'package_id': package.parent_id.id or False}, context=context) + quant_obj.write(cr, SUPERUSER_ID, quant_ids, {'package_id': package.parent_id.id or False}, context=context) children_package_ids = [child_package.id for child_package in package.children_ids] self.write(cr, uid, children_package_ids, {'parent_id': package.parent_id.id or False}, context=context) #delete current package since it contains nothing anymore diff --git a/addons/stock_account/stock_account.py b/addons/stock_account/stock_account.py index 08861ab5d2c..16b4d79c27b 100644 --- a/addons/stock_account/stock_account.py +++ b/addons/stock_account/stock_account.py @@ -368,7 +368,7 @@ class stock_move(osv.osv): def _store_average_cost_price(self, cr, uid, move, context=None): ''' move is a browe record ''' product_obj = self.pool.get('product.product') - if any([q.qty <= 0 for q in move.quant_ids]): + if any([q.qty <= 0 for q in move.quant_ids]) or move.product_qty == 0: #if there is a negative quant, the standard price shouldn't be updated return #Note: here we can't store a quant.cost directly as we may have moved out 2 units (1 unit to 5€ and 1 unit to 7€) and in case of a product return of 1 unit, we can't know which of the 2 costs has to be used (5€ or 7€?). So at that time, thanks to the average valuation price we are storing we will valuate it at 6€ diff --git a/addons/web/controllers/main.py b/addons/web/controllers/main.py index 1c3723fa3b4..eb45ef32680 100644 --- a/addons/web/controllers/main.py +++ b/addons/web/controllers/main.py @@ -1478,7 +1478,7 @@ class Reports(http.Controller): if 'name' not in action: reports = request.session.model('ir.actions.report.xml') res_id = reports.search([('report_name', '=', action['report_name']),], - 0, False, False, context) + context=context) if len(res_id) > 0: file_name = reports.read(res_id[0], ['name'], context)['name'] else: diff --git a/addons/web_calendar/static/src/js/web_calendar.js b/addons/web_calendar/static/src/js/web_calendar.js index f522e9e3164..08f685e9677 100644 --- a/addons/web_calendar/static/src/js/web_calendar.js +++ b/addons/web_calendar/static/src/js/web_calendar.js @@ -843,7 +843,7 @@ var CalendarView = View.extend({ var index = this.dataset.get_id_index(id); if (index !== null) { event_id = this.dataset.ids[index]; - this.dataset.write(event_id, data, {}).done(function() { + this.dataset.write(event_id, data, {}).always(function() { if (is_virtual_id(event_id)) { // this is a virtual ID and so this will create a new event // with an unknown id for us. diff --git a/addons/website/static/src/css/website.css b/addons/website/static/src/css/website.css index 2e4764c469e..c6f9b2d9add 100644 --- a/addons/website/static/src/css/website.css +++ b/addons/website/static/src/css/website.css @@ -675,6 +675,7 @@ table.well tr td { } .oe_website_spinner input { text-align: center; + min-width: 50px; } div.carousel .container > .carousel-caption { diff --git a/addons/website/static/src/css/website.sass b/addons/website/static/src/css/website.sass index 206eedf4480..8b03d1d86cd 100644 --- a/addons/website/static/src/css/website.sass +++ b/addons/website/static/src/css/website.sass @@ -545,6 +545,7 @@ table.well tr width: 121px input text-align: center + min-width: 50px div.carousel .container diff --git a/addons/website_sale/models/sale_order.py b/addons/website_sale/models/sale_order.py index b847cd5a9dd..e3fb18813f1 100644 --- a/addons/website_sale/models/sale_order.py +++ b/addons/website_sale/models/sale_order.py @@ -133,6 +133,32 @@ class sale_order_line(osv.Model): 'discounted_price': fields.function(_fnct_get_discounted_price, string='Discounted price', type='float', digits_compute=dp.get_precision('Product Price')), } +class sale_order_line(osv.Model): + _inherit = "sale.order.line" + + def _fnct_get_discounted_price(self, cr, uid, ids, field_name, args, context=None): + res = dict.fromkeys(ids, False) + for line in self.browse(cr, uid, ids, context=context): + res[line.id] = (line.price_unit * (1.0 - (line.discount or 0.0) / 100.0)) + return res + + _columns = { + 'discounted_price': fields.function(_fnct_get_discounted_price, string='Discounted price', type='float', digits_compute=dp.get_precision('Product Price')), + } + +class sale_order_line(osv.Model): + _inherit = "sale.order.line" + + def _fnct_get_discounted_price(self, cr, uid, ids, field_name, args, context=None): + res = dict.fromkeys(ids, False) + for line in self.browse(cr, uid, ids, context=context): + res[line.id] = (line.price_unit * (1.0 - (line.discount or 0.0) / 100.0)) + return res + + _columns = { + 'discounted_price': fields.function(_fnct_get_discounted_price, string='Discounted price', type='float', digits_compute=dp.get_precision('Product Price')), + } + class website(orm.Model): _inherit = 'website' diff --git a/addons/website_sale/static/src/js/website_sale.js b/addons/website_sale/static/src/js/website_sale.js index 18b47dfe285..0149232558d 100644 --- a/addons/website_sale/static/src/js/website_sale.js +++ b/addons/website_sale/static/src/js/website_sale.js @@ -101,7 +101,7 @@ $('.oe_website_sale').each(function () { $(oe_website_sale).on("change", 'input[name="add_qty"]', function (event) { var product_ids = []; - var product_dom = $(".js_add_cart_variants[data-attribute_value_ids]").last(); + var product_dom = $(".js_product .js_add_cart_variants[data-attribute_value_ids]").last(); if (!product_dom.length) { return; } diff --git a/addons/website_sale/views/templates.xml b/addons/website_sale/views/templates.xml index b00e2c893e3..6784fb9eeca 100644 --- a/addons/website_sale/views/templates.xml +++ b/addons/website_sale/views/templates.xml @@ -1478,10 +1478,13 @@ diff --git a/addons/website_sale_delivery/__openerp__.py b/addons/website_sale_delivery/__openerp__.py index c05689e506f..b03a495ccaf 100644 --- a/addons/website_sale_delivery/__openerp__.py +++ b/addons/website_sale_delivery/__openerp__.py @@ -12,7 +12,6 @@ Delivery Costs 'data': [ 'views/website_sale_delivery.xml', 'views/website_sale_delivery_view.xml', - 'security/ir.model.access.csv', 'demo/website_sale_delivery_data.xml' ], 'demo': [ diff --git a/addons/website_sale_delivery/models/sale_order.py b/addons/website_sale_delivery/models/sale_order.py index 7d94006d578..f5d49849783 100644 --- a/addons/website_sale_delivery/models/sale_order.py +++ b/addons/website_sale_delivery/models/sale_order.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +import logging from openerp.osv import orm, fields from openerp import SUPERUSER_ID @@ -7,6 +8,9 @@ from openerp.exceptions import ValidationError from openerp.tools.translate import _ +_logger = logging.getLogger(__name__) + + class delivery_carrier(orm.Model): _name = 'delivery.carrier' _inherit = ['delivery.carrier', 'website.published.mixin'] @@ -99,25 +103,32 @@ class SaleOrder(orm.Model): if carrier_id: order.delivery_set() else: - order._delivery_unset() + order._delivery_unset() return bool(carrier_id) def _get_delivery_methods(self, cr, uid, order, context=None): carrier_obj = self.pool.get('delivery.carrier') - delivery_ids = carrier_obj.search(cr, uid, [('website_published','=',True)], context=context) + carrier_ids = carrier_obj.search(cr, SUPERUSER_ID, [('website_published', '=', True)], context=context) + available_carrier_ids = [] # Following loop is done to avoid displaying delivery methods who are not available for this order # This can surely be done in a more efficient way, but at the moment, it mimics the way it's # done in delivery_set method of sale.py, from delivery module - for delivery_id in carrier_obj.browse(cr, SUPERUSER_ID, delivery_ids, context=dict(context, order_id=order.id)): + + new_context = dict(context, order_id=order.id) + for carrier in carrier_ids: + try: - if not delivery_id.available: - delivery_ids.remove(delivery_id.id) - except ValidationError: - # RIM: hack to remove in master, because available field should not depend on a SOAP call to external shipping provider - # The validation error is used in backend to display errors in fedex config, but should fail silently in frontend - delivery_ids.remove(delivery_id.id) - return delivery_ids + _logger.debug("Checking availability of carrier #%s" % carrier) + available = carrier_obj.read(cr, SUPERUSER_ID, [carrier], fields=['available'], context=new_context)[0]['available'] + if available: + available_carrier_ids = available_carrier_ids + [carrier] + except ValidationError as e: + # RIM: hack to remove in master, because available field should not depend on a SOAP call to external shipping provider + # The validation error is used in backend to display errors in fedex config, but should fail silently in frontend + _logger.debug("Carrier #%s removed from e-commerce carrier list. %s" % (carrier, e)) + + return available_carrier_ids def _get_errors(self, cr, uid, order, context=None): errors = super(SaleOrder, self)._get_errors(cr, uid, order, context=context) diff --git a/addons/website_sale_delivery/security/ir.model.access.csv b/addons/website_sale_delivery/security/ir.model.access.csv deleted file mode 100644 index 69e804cfd28..00000000000 --- a/addons/website_sale_delivery/security/ir.model.access.csv +++ /dev/null @@ -1,2 +0,0 @@ -id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink -access_delivery_carrier_public,delivery.carrier.public,delivery.model_delivery_carrier,,1,0,0,0 diff --git a/addons/website_slides/static/src/less/website_slides.less b/addons/website_slides/static/src/less/website_slides.less index fc4cb43e9f7..12469b90f46 100644 --- a/addons/website_slides/static/src/less/website_slides.less +++ b/addons/website_slides/static/src/less/website_slides.less @@ -12,6 +12,11 @@ @MAX-Z-INDEX : 2147483647; } .define-color-if-not-defined(); +.top_covered_image { + object-fit: cover; + object-position: top; +} + .oe_slides_panel_footer { background-color: @navbar-default-bg; } @@ -45,6 +50,7 @@ section.oe_slides_promote_box { } img.oe_slides_channel_thumbnail { + .top_covered_image(); height: 250px; width: 100% } @@ -54,6 +60,7 @@ img.oe_slides_opacity { } img.oe_slides_promote_image { + .top_covered_image(); max-height: 300px; width: 100%; overflow: hidden; @@ -65,6 +72,7 @@ img.oe_slides_promote_image { } .oe_slides_apart_small { + .top_covered_image(); height: 60px; width: 90px; } @@ -97,6 +105,7 @@ textarea.o_slides_comment { } .oe_slides_grid_thumbnail { + .top_covered_image(); height: 200px; width: 100%; overflow: hidden; diff --git a/doc/cla/corporate/abstract.md b/doc/cla/corporate/abstract.md new file mode 100644 index 00000000000..7c1f520bfd8 --- /dev/null +++ b/doc/cla/corporate/abstract.md @@ -0,0 +1,18 @@ +Italy, 2015-11-05 + +Abstract Srl agrees to the terms of the Odoo Corporate Contributor License +Agreement v1.0. + +I declare that I am authorized and able to make this agreement and sign this +declaration. + +Signed, + +Davide Corio davide.corio@abstract.it https://github.com/dcorio + + +List of contributors: + +Davide Corio davide.corio@abstract.it https://github.com/dcorio +Simone Orsi simone.orsi@abstract.it https://github.com/simahawk +Giacomo Spettoli giacomo.spettoli@abstract.it https://github.com/giacomos diff --git a/doc/cla/corporate/brain-tec.md b/doc/cla/corporate/brain-tec.md new file mode 100644 index 00000000000..f598f64eb1b --- /dev/null +++ b/doc/cla/corporate/brain-tec.md @@ -0,0 +1,40 @@ +Switzerland, 2015-11-09 + +brain-tec AG agrees to the terms of the Odoo Corporate Contributor License +Agreement v1.0. + +I declare that I am authorized and able to make this agreement and sign this +declaration. + +Signed, + +Pascal Zenklusen pascal.zenklusen@braintec-group.com https://github.com/BT-pzenklusen + +List of contributors: + +Álvaro Estébanez López alvaro.estebanez@braintec-group.com https://github.com/BT-aestebanez +Andreas Stauder andreas.stauder@braintec-group.com https://github.com/BT-astauder +Carlos Millán de Silva carlos.millan@braintec-group.com https://github.com/BT-cmillan +Cesar Andres Sanchez cesar-andres.sanchez@braintec-group.com https://github.com/BT-csanchez +Carlos Serra Toro carlos.serra@braintec-group.com https://github.com/BT-cserra +Dominik Schleich dominik.schleich@braintec-group.com https://github.com/BT-dschleich +Frédéric Aebi frederic.aebi@braintec-group.com https://github.com/BT-faebi +Fréderic Garbely frederic.garbely@braintec-group.com https://github.com/BT-fgarbely +Florian Wonneberger florian.wonneberger@braintec-group.com https://github.com/BT-fwonneberger +Jorge Asensio jorge.asensio@braintec-group.com https://github.com/BT-jasensio +Jose J. Duran-Martin jose-javier.duran@braintec-group.com https://github.com/BT-jduran +Federico Javier Mesa Hidalgo javier.mesa@braintec-group.com https://github.com/BT-jmesa +Joël Michaud joel.michaud@braintec-group.com https://github.com/BT-jmichaud +Jose Montero jose.montero@braintec-group.com https://github.com/BT-jmontero +José Luis Vallejo Diaz jose.vallejo@braintec-group.com https://github.com/BT-jvallejo +Kumar Aberer kumar.aberer@braintec-group.com https://github.com/BT-kaberer +Matt Fasola matt.fasola@braintec-group.com https://github.com/BT-mfasola +Miguel Tallon Benitez miguel.tallon@braintec-group.com https://github.com/BT-mtallon +Nicolas Frei nicolas.frei@braintec-group.com https://github.com/BT-nfrei +Nathanael Leutenegger nathanael.leutenegger@braintec-group.com https://github.com/BT-nleutenegger +Olivier Jossen olivier.jossen@braintec-group.com https://github.com/BT-ojossen +Philipp Fux philipp.fux@braintec-group.com https://github.com/BT-pfux +Raúl Martín raul.martin@braintec-group.com https://github.com/BT-rmartin +Silvan Wyden silvan.wyden@braintec-group.com https://github.com/BT-swyden +Timka Piric Muratovic timka.piric@braintec-group.com https://github.com/BT-tpiric +Pascal Zenklusen pascal.zenklusen@braintec-group.com https://github.com/BT-pzenklusen diff --git a/doc/cla/corporate/elico-corporation.md b/doc/cla/corporate/elico-corporation.md new file mode 100644 index 00000000000..aa5e28564bc --- /dev/null +++ b/doc/cla/corporate/elico-corporation.md @@ -0,0 +1,30 @@ +China, 2015-12-10 + +Elico Corporation agrees to the terms of the Odoo Corporate Contributor License +Agreement v1.0. + +I declare that I am authorized and able to make this agreement and sign this +declaration. + +Signed, + +Eric Caudal eric.caudal@elico-corp.com https://github.com/elicoidal + +List of contributors: +Eric Caudal eric.caudal@elico-corp.com https://github.com/elicoidal +Sébastien Maillard sebastien.maillard@elico-corp.com https://github.com/seb-elico +Siyuan Gu siyuancn@hotmail.com https://github.com/siyuancn +Wang Liping lynn.config@gmail.com https://github.com/lynndotconfig +Luke Zheng cialuo@126.com https://github.com/cialuo +Gary Wei gary.wei@elico-corp.com https://github.com/Garywei1314 +Yaxi Zheng zheng.yaxi@elico-corp.com https://github.com/Elico-Yaxi +Faust Huang faust_huang@hotmail.com https://github.com/fausthuang +Yu Lin yu_lin@me.com https://github.com/uynil +Xie Xiaopeng hellomyjob@163.com https://github.com/xie8899 +Connie Xiao connie.xiao@elico-corp.com https://github.com/ConnieXiao +Alex Duan alex.duan@elico-corp.com https://github.com/duanyp1991 +Liu Lixia liulixia19901988@163.com https://github.com/liulixia1990 +Liliane Li liliane.li@elico-corp.com http://github.com/lilianeandbrody +Rona Lin ssauapw@qq.com https://github.com/Rona111 +Bob Luo luo.yumeng0831@gmail.com https://github.com/m6519578 +Waller Zhang waller.zhang@elico-corp.com https://github.com/wallerzhang diff --git a/doc/cla/corporate/kmee.md b/doc/cla/corporate/kmee.md new file mode 100644 index 00000000000..e29245abf56 --- /dev/null +++ b/doc/cla/corporate/kmee.md @@ -0,0 +1,21 @@ +KMEE, Brazil, 2015-12-04 + +KMEE INFORMATICA LTDA agrees to the terms of the Odoo Corporate +Contributor License Agreement v1.0. + +I declare that I am authorized and able to make this agreement and sign this +declaration. + +Signed, + +Luis Felipe Miléo mileo@kmee.com.br https://github.com/kmee + +List of contributors: + +Luis Felipe Miléo mileo@kmee.com.br https://github.com/mileo +Ananias Pereira Batista Filho ananias@kmee.com.br https://github.com/ananiasfilho +Michell Stuttigart michell.stuttigart@kmee.com.br https://github.com/mstuttgart +Fernando Marcato Rodrigues fernando.marcato@kmee.com.br https://github.com/fernandomr +Daniel Sadamo daniel.sadamo@kmee.com.br https://github.com/sadamo +Bianca Tella bianca.tella@kmee.com.br https://github.com/biancatella +Luiz Felipe do Divino luiz.divino@kmee.com.br https://github.com/lfdivino \ No newline at end of file diff --git a/doc/cla/corporate/sodexis.md b/doc/cla/corporate/sodexis.md index 2a642474497..3b7544acd9c 100644 --- a/doc/cla/corporate/sodexis.md +++ b/doc/cla/corporate/sodexis.md @@ -17,3 +17,4 @@ Suganthi Karunanithi ksuganthi@sodexis.com https://github.com/suganthikarunanith Xavier Dass xavier@sodexis.com https://github.com/xavier-dass Atchuthan atchuthan@sodexis.com https://github.com/atchuthan Dhinesh dhinesh@sodexis.com https://github.com/dvdhinesh +SodexisTeam dev@sodexis.com https://github.com/SodexisTeam diff --git a/doc/cla/individual/The-Compiler.md b/doc/cla/individual/The-Compiler.md new file mode 100644 index 00000000000..45d0819ba67 --- /dev/null +++ b/doc/cla/individual/The-Compiler.md @@ -0,0 +1,12 @@ +Switzerland, 2015-12-11 + +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, + +Florian Bruhin git@the-compiler.org https://github.com/The-Compiler +Florian Bruhin me@the-compiler.org https://github.com/The-Compiler diff --git a/doc/cla/individual/jcarlosmontoya.md b/doc/cla/individual/jcarlosmontoya.md new file mode 100644 index 00000000000..e10276c9f3c --- /dev/null +++ b/doc/cla/individual/jcarlosmontoya.md @@ -0,0 +1,11 @@ +Spain, 2015-12-15 + +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, + +Juan Carlos Montoya juancarlos.montoya.chamba@gmail.com https://github.com/jcarlosmontoya diff --git a/openerp/addons/base/ir/ir_model.py b/openerp/addons/base/ir/ir_model.py index 000ae87e4a7..671c6a4c1f7 100644 --- a/openerp/addons/base/ir/ir_model.py +++ b/openerp/addons/base/ir/ir_model.py @@ -1098,10 +1098,31 @@ class ir_model_data(osv.osv): },context=context) else: if mode=='init' or (mode=='update' and xml_id): + inherit_xml_ids = [] + for table, field_name in model_obj._inherits.items(): + xml_ids = self.pool['ir.model.data'].search(cr, uid, [ + ('module', '=', module), + ('name', '=', xml_id + '_' + table.replace('.', '_')), + ], context=context) + # XML ID found in the database, try to recover an existing record + if xml_ids: + found_xml_id = self.pool['ir.model.data'].browse(cr, uid, xml_ids[0], context=context) + record = self.pool[found_xml_id.model].browse(cr, uid, [found_xml_id.res_id], context=context)[0] + # The record exists, store the id and don't recreate the XML ID + if record.exists(): + inherit_xml_ids.append(found_xml_id.model) + values[field_name] = found_xml_id.res_id + # Orphan XML ID, delete it + else: + found_xml_id.unlink() + res_id = model_obj.create(cr, uid, values, context=context) if xml_id: if model_obj._inherits: for table in model_obj._inherits: + if table in inherit_xml_ids: + continue + inherit_id = model_obj.browse(cr, uid, res_id,context=context)[model_obj._inherits[table]] self.create(cr, SUPERUSER_ID, { diff --git a/openerp/addons/base/ir/ir_translation.py b/openerp/addons/base/ir/ir_translation.py index 3e5eb350d4a..33935841d75 100644 --- a/openerp/addons/base/ir/ir_translation.py +++ b/openerp/addons/base/ir/ir_translation.py @@ -673,12 +673,13 @@ class ir_translation(osv.osv): return ir_translation_import_cursor(cr, uid, self, context=context) def load_module_terms(self, cr, modules, langs, context=None): - context = dict(context or {}) # local copy + context_template = dict(context or {}) # local copy for module_name in modules: modpath = openerp.modules.get_module_path(module_name) if not modpath: continue for lang in langs: + context = dict(context_template) lang_code = tools.get_iso_codes(lang) base_lang_code = None if '_' in lang_code: diff --git a/openerp/addons/base/ir/ir_ui_view.py b/openerp/addons/base/ir/ir_ui_view.py index 2e8f71f234f..4e73c00f852 100644 --- a/openerp/addons/base/ir/ir_ui_view.py +++ b/openerp/addons/base/ir/ir_ui_view.py @@ -619,10 +619,15 @@ class view(osv.osv): requested (similar to ``id``) """ if context is None: context = {} + context = context.copy() # if view_id is not a root view, climb back to the top. base = v = self.browse(cr, uid, view_id, context=context) + check_view_ids = context.setdefault('check_view_ids', []) while v.mode != 'primary': + # Add inherited views to the list of loading forced views + # Otherwise, inherited views could not find elements created in their direct parents if that parent is defined in the same module + check_view_ids.append(v.id) v = v.inherit_id root_id = v.id diff --git a/openerp/http.py b/openerp/http.py index 2d68fd216ff..e1a70fa72c6 100644 --- a/openerp/http.py +++ b/openerp/http.py @@ -433,8 +433,8 @@ def route(route=None, **kw): * ``user``: The user must be authenticated and the current request will perform using the rights of the user. - * ``admin``: The user may not be authenticated and the current request - will perform using the admin user. + * ``public``: The user may or may not be authenticated. If she isn't, + the current request will perform using the shared Public user. * ``none``: The method is always active, even if there is no database. Mainly used by the framework and authentication modules. There request code will not have any facilities to access diff --git a/openerp/models.py b/openerp/models.py index a90dedcbb9b..93640f8c3dc 100644 --- a/openerp/models.py +++ b/openerp/models.py @@ -1375,6 +1375,7 @@ class BaseModel(object): from openerp.http import request Users = self.pool['res.users'] for group_ext_id in groups.split(','): + group_ext_id = group_ext_id.strip() if group_ext_id == 'base.group_no_one': # check: the group_no_one is effective in debug mode only if Users.has_group(cr, uid, group_ext_id) and request and request.debug: diff --git a/openerp/tools/mail.py b/openerp/tools/mail.py index cd18ebd8fa1..2eb6a6cf55a 100644 --- a/openerp/tools/mail.py +++ b/openerp/tools/mail.py @@ -11,7 +11,7 @@ import re import socket import threading import time -from email.utils import getaddresses +from email.utils import getaddresses, formataddr import openerp from openerp.loglevels import ustr @@ -632,10 +632,10 @@ def append_content_to_html(html, content, plaintext=True, preserve=False, contai #---------------------------------------------------------- # matches any email in a body of text -email_re = re.compile(r"""([a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,6})""", re.VERBOSE) +email_re = re.compile(r"""([a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,63})""", re.VERBOSE) # matches a string containing only one email -single_email_re = re.compile(r"""^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,6}$""", re.VERBOSE) +single_email_re = re.compile(r"""^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,63}$""", re.VERBOSE) res_re = re.compile(r"\[([0-9]+)\]", re.UNICODE) command_re = re.compile("^Set-([a-z]+) *: *(.+)$", re.I + re.UNICODE) @@ -709,3 +709,15 @@ def email_split(text): # is strictly required in RFC2822's `addr-spec`. if addr[1] if '@' in addr[1]] + +def email_split_and_format(text): + """ Return a list of email addresses found in ``text``, formatted using + formataddr. """ + if not text: + return [] + return [formataddr((addr[0], addr[1])) for addr in getaddresses([text]) + # getaddresses() returns '' when email parsing fails, and + # sometimes returns emails without at least '@'. The '@' + # is strictly required in RFC2822's `addr-spec`. + if addr[1] + if '@' in addr[1]]