diff --git a/addons/crm/static/description/crm_sc_05.png b/addons/crm/static/description/crm_sc_05.png new file mode 100644 index 00000000000..b71ce805250 Binary files /dev/null and b/addons/crm/static/description/crm_sc_05.png differ diff --git a/addons/product/product.py b/addons/product/product.py index 3d4d2350d1a..f0a207a8064 100644 --- a/addons/product/product.py +++ b/addons/product/product.py @@ -364,7 +364,7 @@ class product_attribute_value(osv.osv): _columns = { 'sequence': fields.integer('Sequence', help="Determine the display order"), 'name': fields.char('Value', translate=True, required=True), - 'attribute_id': fields.many2one('product.attribute', 'Attribute', required=True), + 'attribute_id': fields.many2one('product.attribute', 'Attribute', required=True, ondelete='cascade'), 'product_ids': fields.many2many('product.product', id1='att_id', id2='prod_id', string='Variants', readonly=True), 'price_extra': fields.function(_get_price_extra, type='float', string='Attribute Price Extra', fnct_inv=_set_price_extra, @@ -378,20 +378,26 @@ class product_attribute_value(osv.osv): _defaults = { 'price_extra': 0.0, } + def unlink(self, cr, uid, ids, context=None): + ctx = dict(context or {}, active_test=False) + product_ids = self.pool['product.product'].search(cr, uid, [('attribute_value_ids', 'in', ids)], context=ctx) + if product_ids: + raise osv.except_osv(_('Integrity Error!'), _('The operation cannot be completed:\nYou trying to delete an attribute value with a reference on a product variant.')) + return super(product_attribute_value, self).unlink(cr, uid, ids, context=context) class product_attribute_price(osv.osv): _name = "product.attribute.price" _columns = { - 'product_tmpl_id': fields.many2one('product.template', 'Product Template', required=True), - 'value_id': fields.many2one('product.attribute.value', 'Product Attribute Value', required=True), + 'product_tmpl_id': fields.many2one('product.template', 'Product Template', required=True, ondelete='cascade'), + 'value_id': fields.many2one('product.attribute.value', 'Product Attribute Value', required=True, ondelete='cascade'), 'price_extra': fields.float('Price Extra', digits_compute=dp.get_precision('Product Price')), } class product_attribute_line(osv.osv): _name = "product.attribute.line" _columns = { - 'product_tmpl_id': fields.many2one('product.template', 'Product Template', required=True), - 'attribute_id': fields.many2one('product.attribute', 'Attribute', required=True), + 'product_tmpl_id': fields.many2one('product.template', 'Product Template', required=True, ondelete='cascade'), + 'attribute_id': fields.many2one('product.attribute', 'Attribute', required=True, ondelete='restrict'), 'value_ids': fields.many2many('product.attribute.value', id1='line_id', id2='val_id', string='Product Attribute Value'), } @@ -898,7 +904,7 @@ class product_product(osv.osv): 'product.template': (_get_name_template_ids, ['name'], 10), 'product.product': (lambda self, cr, uid, ids, c=None: ids, [], 10), }, select=True), - 'attribute_value_ids': fields.many2many('product.attribute.value', id1='prod_id', id2='att_id', string='Attributes', readonly=True), + 'attribute_value_ids': fields.many2many('product.attribute.value', id1='prod_id', id2='att_id', string='Attributes', readonly=True, ondelete='restrict'), # image: all image fields are base64 encoded and PIL-supported 'image_variant': fields.binary("Variant Image", diff --git a/addons/product/product_view.xml b/addons/product/product_view.xml index 963bdd0eea0..2e72d1aebf0 100644 --- a/addons/product/product_view.xml +++ b/addons/product/product_view.xml @@ -193,6 +193,7 @@ variants.template.tree product.attribute.value + @@ -214,7 +215,7 @@ - + product.product.search @@ -385,6 +386,54 @@ + + product.attribute.tree + product.attribute + + + + + + + + + Attributes + ir.actions.act_window + product.attribute + tree + form + + + + + + product.attribute.value.tree + product.attribute.value + + + + + + + + + + + Attribute Values + ir.actions.act_window + product.attribute.value + tree + form + + + + + + Product Variants ir.actions.act_window diff --git a/addons/stock/wizard/make_procurement_product.py b/addons/stock/wizard/make_procurement_product.py index 5c6f4b618ee..dbc15ad81a7 100644 --- a/addons/stock/wizard/make_procurement_product.py +++ b/addons/stock/wizard/make_procurement_product.py @@ -20,7 +20,8 @@ ############################################################################## -from openerp.osv import fields, osv +from openerp.osv import fields, osv, orm +from openerp.tools.translate import _ class make_procurement(osv.osv_memory): _name = 'make.procurement' @@ -108,6 +109,13 @@ class make_procurement(osv.osv_memory): context = {} record_id = context.get('active_id') + if context.get('active_model') == 'product.template': + product_ids = self.pool.get('product.product').search(cr, uid, [('product_tmpl_id', '=', context.get('active_id'))], context=context) + if len(product_ids) == 1: + record_id = product_ids[0] + else: + raise orm.except_orm(_('Warning'), _('Please use the Product Variant vue to request a procurement.')) + res = super(make_procurement, self).default_get(cr, uid, fields, context=context) if record_id and 'product_id' in fields: diff --git a/addons/stock/wizard/stock_change_product_qty.py b/addons/stock/wizard/stock_change_product_qty.py index 646a047d63c..62290e17572 100644 --- a/addons/stock/wizard/stock_change_product_qty.py +++ b/addons/stock/wizard/stock_change_product_qty.py @@ -50,6 +50,13 @@ class stock_change_product_qty(osv.osv_memory): res = super(stock_change_product_qty, self).default_get(cr, uid, fields, context=context) + if context.get('active_model') == 'product.template': + product_ids = self.pool.get('product.product').search(cr, uid, [('product_tmpl_id', '=', context.get('active_id'))], context=context) + if len(product_ids) == 1: + res['product_id'] = product_ids[0] + else: + raise orm.except_orm(_('Warning'), _('Please use the Product Variant vue to update the product quantity.')) + if 'location_id' in fields: location_id = res.get('location_id', False) if not location_id: @@ -77,12 +84,8 @@ class stock_change_product_qty(osv.osv_memory): if context is None: context = {} - rec_id = context and context.get('active_id', False) - assert rec_id, _('Active ID is not set in Context') - inventory_obj = self.pool.get('stock.inventory') inventory_line_obj = self.pool.get('stock.inventory.line') - prod_obj_pool = self.pool.get('product.product') for data in self.browse(cr, uid, ids, context=context): if data.new_quantity < 0: @@ -90,15 +93,18 @@ class stock_change_product_qty(osv.osv_memory): ctx = context.copy() ctx['location'] = data.location_id.id ctx['lot_id'] = data.lot_id.id - res_original = prod_obj_pool.browse(cr, uid, rec_id, context=ctx) - inventory_id = inventory_obj.create(cr, uid, {'name': _('INV: %s') % tools.ustr(res_original.name), 'product_id': rec_id, 'location_id': data.location_id.id, 'lot_id': data.lot_id.id}, context=context) - th_qty = res_original.qty_available + inventory_id = inventory_obj.create(cr, uid, { + 'name': _('INV: %s') % tools.ustr(data.product_id.name), + 'product_id': data.product_id.id, + 'location_id': data.location_id.id, + 'lot_id': data.lot_id.id}, context=context) + th_qty = data.product_id.qty_available line_data = { 'inventory_id': inventory_id, 'product_qty': data.new_quantity, 'location_id': data.location_id.id, - 'product_id': rec_id, - 'product_uom_id': res_original.uom_id.id, + 'product_id': data.product_id.id, + 'product_uom_id': data.product_id.uom_id.id, 'th_qty': th_qty, 'prod_lot_id': data.lot_id.id } diff --git a/addons/web_diagram/static/src/js/diagram.js b/addons/web_diagram/static/src/js/diagram.js index 34dc5e084c7..77876c9dc52 100644 --- a/addons/web_diagram/static/src/js/diagram.js +++ b/addons/web_diagram/static/src/js/diagram.js @@ -24,15 +24,6 @@ instance.web.DiagramView = instance.web.View.extend({ this.ids = this.dataset.ids; this.on('pager_action_executed', self, self.pager_action_trigger); }, - start: function () { - return this._super().then(function () { - return $.when( - openerp.webclient.session.load_js(['/web/js/web_diagram.assets_raphael']) - ); - }).fail(function () { - throw new Error("Could not load raphael.js"); - }); - }, view_loading: function(r) { return this.load_diagram(r); diff --git a/addons/web_kanban_gauge/static/lib/justgage/justgage.js b/addons/web_kanban_gauge/static/lib/justgage/justgage.js index 3e23c12a29e..3e3437eeb5f 100644 --- a/addons/web_kanban_gauge/static/lib/justgage/justgage.js +++ b/addons/web_kanban_gauge/static/lib/justgage/justgage.js @@ -295,7 +295,7 @@ } if (obj.config.relativeGaugeSize === true) { - obj.canvas.setViewBox(0, 0, 200, 150, true); + obj.canvas.setViewBox(0, 0, 200, 150, false); } // canvas dimensions @@ -306,7 +306,7 @@ canvasW = obj.config.width; canvasH = obj.config.height; } else if (obj.config.parentNode !== null) { - obj.canvas.setViewBox(0, 0, 200, 150, true); + obj.canvas.setViewBox(0, 0, 200, 150, false); canvasW = 200; canvasH = 150; } else { diff --git a/addons/website_sale/views/views.xml b/addons/website_sale/views/views.xml index fcc42502035..9971097a061 100644 --- a/addons/website_sale/views/views.xml +++ b/addons/website_sale/views/views.xml @@ -45,6 +45,16 @@ + + variants.template.tree + product.attribute.value + + + + + + +