[MERGE] [IMP] product: product and product template management

product
- added a is_only_child field, telling whether the product is the only child of its template
- form view: fields coming from the template are now readonly when there are several products (variants) for the template; added a link to the template on the product form view
- renamed Products menu into All Products
- some improvements in search view, notably groupby

product.template
- improved form view to look more similar to the product one
- added Product Template menus when using variants

website_sale: do not fetch templates without products

bzr revid: tde@openerp.com-20140122183759-s9lfvfeo8f6ytm34
This commit is contained in:
Thibault Delavallée
2014-01-22 19:37:59 +01:00
14 changed files with 423 additions and 176 deletions
+18 -10
View File
@@ -11,12 +11,16 @@
<page string="Accounting" groups="account.group_account_invoice">
<group name="properties">
<group>
<field name="property_account_income" domain="[('type','=','other')]" groups="account.group_account_user"/>
<field name="taxes_id" colspan="2" attrs="{'readonly':[('sale_ok','=',0)]}" widget="many2many_tags"/>
<field name="property_account_income" domain="[('type','=','other')]" groups="account.group_account_user"
attrs="{'readonly': [('is_only_child', '=', False)]}"/>
<field name="taxes_id" colspan="2" widget="many2many_tags"
attrs="{'readonly':[ '|', ('sale_ok','=',0), ('is_only_child', '=', False)]}"/>
</group>
<group>
<field name="property_account_expense" domain="[('type','=','other')]" groups="account.group_account_user"/>
<field name="supplier_taxes_id" colspan="2" widget="many2many_tags"/>
<field name="property_account_expense" domain="[('type','=','other')]" groups="account.group_account_user"
attrs="{'readonly': [('is_only_child', '=', False)]}"/>
<field name="supplier_taxes_id" colspan="2" widget="many2many_tags"
attrs="{'readonly': [('is_only_child', '=', False)]}"/>
</group>
</group>
</page>
@@ -31,12 +35,16 @@
<field name="arch" type="xml">
<notebook position="inside">
<page string="Accounting">
<separator string="Sales Properties" colspan="2"/>
<separator string="Purchase Properties" colspan="2"/>
<field name="property_account_income" domain="[('type','&lt;&gt;','view'),('type','&lt;&gt;','consolidation')]"/>
<field name="property_account_expense" domain="[('type','&lt;&gt;','view'),('type','&lt;&gt;','consolidation')]"/>
<field name="taxes_id"/>
<field name="supplier_taxes_id"/>
<group name="properties">
<group>
<field name="property_account_income" domain="[('type','&lt;&gt;','view'),('type','&lt;&gt;','consolidation')]"/>
<field name="taxes_id" colspan="2" widget="many2many_tags"/>
</group>
<group>
<field name="property_account_expense" domain="[('type','&lt;&gt;','view'),('type','&lt;&gt;','consolidation')]"/>
<field name="supplier_taxes_id" colspan="2" widget="many2many_tags"/>
</group>
</group>
</page>
</notebook>
</field>
+3 -2
View File
@@ -8,10 +8,11 @@
<field name="arch" type="xml">
<div name="options" position="inside">
<field name="event_ok" on_change="onchange_event_ok(type, event_ok, context)"/>
<label for="event_ok"/>
<label for="event_ok"
attrs="{'readonly': [('is_only_child', '=', False)]}"/>
</div>
<div name='ean' position="after">
<field name="event_type_id" attrs="{'readonly': [('event_ok', '=', False)]}"/>
<field name="event_type_id" attrs="{'readonly': ['|', ('event_ok', '=', False), ('is_only_child', '=', False)]}"/>
</div>
</field>
</record>
+39 -3
View File
@@ -291,15 +291,49 @@
<field name="res_model">stock.warehouse.orderpoint</field>
</record>
<record id="product_template_search_view_procurment" model="ir.ui.view">
<field name="name">product.template.search.procurement</field>
<field name="model">product.template</field>
<field name="inherit_id" ref="product.product_template_search_view"/>
<field name="arch" type="xml">
<filter name="consumable" position="before">
<filter string="Products" icon="terp-accessories-archiver" domain="[('type','=','product')]" help="Stockable products"/>
</filter>
</field>
</record>
<record model="ir.ui.view" id="product_template_form_view_procurement">
<field name="name">product.template.procurement</field>
<field name="model">product.template</field>
<field name="inherit_id" ref="product.product_template_form_view"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='type']" position="after">
<xpath expr="//field[@name='cost_method']" position="before">
<field name="procure_method"/>
<field name="supply_method"/>
</xpath>
<xpath expr="//group[@name='general']" position="after" >
<group name="procurement_help" class="oe_grey" col="1" groups="base.group_user">
<p attrs="{'invisible': ['|', '|', ('type', '!=', 'service'), ('procure_method', '!=', 'make_to_stock')]}">
When you sell this service, nothing special will be triggered
to deliver the customer, as you set the procurement method as
'Make to Stock'.
</p>
<p attrs="{'invisible': ['|', '|', ('type', '!=', 'product'), ('procure_method', '!=', 'make_to_stock')]}">
When you sell this product, OpenERP will <b>use the available
inventory</b> for the delivery order.
<br/><br/>
If there are not enough quantities available, the delivery order
will wait for new products. To fulfill the inventory, you should
create others rules like orderpoints.
</p>
<p attrs="{'invisible': ['|', '|', ('type', '!=', 'consu'), ('procure_method', '!=', 'make_to_stock')]}">
When you sell this product, a delivery order will be created.
OpenERP will consider that the <b>required quantities are always
available</b> as it's a consumable (as a result of this, the quantity
on hand may become negative).
</p>
</group>
</xpath>
</field>
</record>
@@ -326,8 +360,10 @@
<button string="Orderpoints" name="%(product_open_orderpoint)d" type="action" attrs="{'invisible':[('type', '=', 'service')]}"/>
</xpath>
<xpath expr="//field[@name='cost_method']" position="before">
<field name="procure_method" groups="base.group_user"/>
<field name="supply_method" groups="base.group_user"/>
<field name="procure_method" groups="base.group_user"
attrs="{'readonly': [('is_only_child', '=', False)]}"/>
<field name="supply_method" groups="base.group_user"
attrs="{'readonly': [('is_only_child', '=', False)]}"/>
</xpath>
<xpath expr="//group[@name='general']" position="after" >
<group name="procurement_help" class="oe_grey" col="1" groups="base.group_user">
+57 -27
View File
@@ -23,10 +23,10 @@ import math
import re
from _common import rounding
from openerp import SUPERUSER_ID
from openerp import SUPERUSER_ID
from openerp import tools
from openerp.osv import osv, fields
from openerp.osv import osv, orm, fields
from openerp.tools.translate import _
import openerp.addons.decimal_precision as dp
@@ -391,7 +391,7 @@ class product_template(osv.osv):
'categ_id': fields.many2one('product.category','Category', required=True, change_default=True, domain="[('type','=','normal')]" ,help="Select category for the current product"),
'public_categ_id': fields.many2one('product.public.category','Public Category', help="Those categories are used to group similar products for public sales (eg.: point of sale, e-commerce)."),
'list_price': fields.float('Sale Price', digits_compute=dp.get_precision('Product Price'), help="Base price to compute the customer price. Sometimes called the catalog price."),
'standard_price': fields.float('Cost', digits_compute=dp.get_precision('Product Price'), help="Cost price of the product used for standard stock valuation in accounting and used as a base price on purchase orders.", groups="base.group_user"),
'standard_price': fields.float('Cost Price', digits_compute=dp.get_precision('Product Price'), help="Cost price of the product template used for standard stock valuation in accounting and used as a base price on purchase orders.", groups="base.group_user"),
'volume': fields.float('Volume', help="The volume in m3."),
'weight': fields.float('Gross Weight', digits_compute=dp.get_precision('Stock Weight'), help="The gross weight in Kg."),
'weight_net': fields.float('Net Weight', digits_compute=dp.get_precision('Stock Weight'), help="The net weight in Kg."),
@@ -468,6 +468,13 @@ class product_template(osv.osv):
raise osv.except_osv(_('Unit of Measure categories Mismatch!'), _("New Unit of Measure '%s' must belong to same Unit of Measure category '%s' as of old Unit of Measure '%s'. If you need to change the unit of measure, you may deactivate this product from the 'Procurements' tab and create a new one.") % (new_uom.name, old_uom.category_id.name, old_uom.name,))
return super(product_template, self).write(cr, uid, ids, vals, context=context)
def copy(self, cr, uid, id, default=None, context=None):
if default is None:
default = {}
template = self.browse(cr, uid, id, context=context)
default['name'] = _("%s (copy)") % (template['name'])
return super(product_template, self).copy(cr, uid, id, default=default, context=context)
_defaults = {
'company_id': lambda s,cr,uid,c: s.pool.get('res.company')._company_default_get(cr, uid, 'product.template', context=c),
'list_price': 1,
@@ -510,6 +517,13 @@ class product_template(osv.osv):
class product_product(osv.osv):
_name = "product.product"
_description = "Product"
_table = "product_product"
_inherits = {'product.template': 'product_tmpl_id'}
_inherit = ['mail.thread']
_order = 'default_code,name_template'
def view_header_get(self, cr, uid, view_id, view_type, context=None):
if context is None:
context = {}
@@ -583,6 +597,7 @@ class product_product(osv.osv):
list_price = (field_value - product.price_extra) / (product.price_margin or 1.0)
return self.write(cr, uid, [product_id], {'list_price': list_price}, context=context)
def _get_partner_code_name(self, cr, uid, ids, product, partner_id, context=None):
for supinfo in product.seller_ids:
if supinfo.name.id == partner_id:
@@ -614,6 +629,12 @@ class product_product(osv.osv):
(data['name'] or '') + (data['variants'] and (' - '+data['variants']) or '')
return res
def _is_only_child(self, cr, uid, ids, name, arg, context=None):
res = dict.fromkeys(ids, True)
for product in self.browse(cr, uid, ids, context=context):
if product.product_tmpl_id and len(product.product_tmpl_id.product_variant_ids) > 1:
res[product.id] = False
return res
def _get_main_product_supplier(self, cr, uid, product, context=None):
"""Determines the main (best) product supplier for ``product``,
@@ -641,7 +662,6 @@ class product_product(osv.osv):
}
return result
def _get_name_template_ids(self, cr, uid, ids, context=None):
result = set()
template_ids = self.pool.get('product.product').search(cr, uid, [('product_tmpl_id', 'in', ids)])
@@ -649,19 +669,6 @@ class product_product(osv.osv):
result.add(el)
return list(result)
_defaults = {
'active': lambda *a: 1,
'price_extra': lambda *a: 0.0,
'price_margin': lambda *a: 1.0,
'color': 0,
}
_name = "product.product"
_description = "Product"
_table = "product_product"
_inherits = {'product.template': 'product_tmpl_id'}
_inherit = ['mail.thread']
_order = 'default_code,name_template'
_columns = {
'qty_available': fields.function(_product_qty_available, type='float', string='Quantity On Hand'),
'virtual_available': fields.function(_product_virtual_available, type='float', string='Quantity Available'),
@@ -673,24 +680,34 @@ class product_product(osv.osv):
'partner_ref' : fields.function(_product_partner_ref, type='char', string='Customer ref'),
'default_code' : fields.char('Internal Reference', size=64, select=True),
'active': fields.boolean('Active', help="If unchecked, it will allow you to hide the product without removing it."),
'variants': fields.char('Variants', size=64),
'variants': fields.char('Variants', size=64, translate=True),
'product_tmpl_id': fields.many2one('product.template', 'Product Template', required=True, ondelete="cascade", select=True),
'is_only_child': fields.function(
_is_only_child, type='boolean', string='Sole child of the parent template'),
'ean13': fields.char('EAN13 Barcode', size=13, help="International Article Number used for product identification."),
'packaging' : fields.one2many('product.packaging', 'product_id', 'Logistical Units', help="Gives the different ways to package the same product. This has no impact on the picking order and is mainly used if you use the EDI module."),
'price_extra': fields.float('Variant Price Extra', digits_compute=dp.get_precision('Product Price')),
'price_margin': fields.float('Variant Price Margin', digits_compute=dp.get_precision('Product Price')),
'price_extra': fields.float('Variant Price Extra', digits_compute=dp.get_precision('Product Price'), help="Price Extra: Extra price for the variant on sale price. eg. 200 price extra, 1000 + 200 = 1200."),
'price_margin': fields.float('Variant Price Margin', digits_compute=dp.get_precision('Product Price'), help="Price Margin: Margin in percentage amount on sale price for the variant. eg. 10 price margin, 1000 * 1.1 = 1100."),
'pricelist_id': fields.dummy(string='Pricelist', relation='product.pricelist', type='many2one'),
'name_template': fields.related('product_tmpl_id', 'name', string="Template Name", type='char', size=128, store={
'product.template': (_get_name_template_ids, ['name'], 10),
'product.product': (lambda self, cr, uid, ids, c=None: ids, [], 10),
}, select=True),
}, select=True),
'color': fields.integer('Color Index'),
'seller_info_id': fields.function(_calc_seller, type='many2one', relation="product.supplierinfo", string="Supplier Info", multi="seller_info"),
'seller_delay': fields.function(_calc_seller, type='integer', string='Supplier Lead Time', multi="seller_info", help="This is the average delay in days between the purchase order confirmation and the reception of goods for this product and for the default supplier. It is used by the scheduler to order requests based on reordering delays."),
'seller_qty': fields.function(_calc_seller, type='float', string='Supplier Quantity', multi="seller_info", help="This is minimum quantity to purchase from Main Supplier."),
'seller_id': fields.function(_calc_seller, type='many2one', relation="res.partner", string='Main Supplier', help="Main Supplier who has highest priority in Supplier List.", multi="seller_info"),
}
_defaults = {
'active': lambda *a: 1,
'price_extra': lambda *a: 0.0,
'price_margin': lambda *a: 1.0,
'color': 0,
'is_only_child': True,
}
def unlink(self, cr, uid, ids, context=None):
unlink_ids = []
unlink_product_tmpl_ids = []
@@ -721,7 +738,6 @@ class product_product(osv.osv):
res = check_ean(product['ean13'])
return res
_constraints = [(_check_ean_key, 'You provided an invalid "EAN13 Barcode" reference. You may use the "Internal Reference" field instead.', ['ean13'])]
def on_order(self, cr, uid, ids, orderline, quantity):
@@ -785,7 +801,7 @@ class product_product(osv.osv):
# OR operator (and given the fact that the 'name' lookup results come from the ir.translation table
# Performing a quick memory merge of ids in Python will give much better performance
ids = set()
ids.update(self.search(cr, user, args + [('default_code',operator,name)], limit=limit, context=context))
ids.update(self.search(cr, user, args + ['|',('default_code',operator,name),('variants',operator,name)], limit=limit, context=context))
if not limit or len(ids) < limit:
# we may underrun the limit because of dupes in the results, that's fine
ids.update(self.search(cr, user, args + [('name',operator,name)], limit=(limit and (limit-len(ids)) or False) , context=context))
@@ -847,9 +863,12 @@ class product_product(osv.osv):
# will do the other languages).
context_wo_lang = context.copy()
context_wo_lang.pop('lang', None)
product = self.read(cr, uid, id, ['name'], context=context_wo_lang)
product = self.read(cr, uid, id, ['name', 'list_price', 'standard_price', 'categ_id', 'variants', 'product_tmpl_id'], context=context_wo_lang)
default = default.copy()
default.update(name=_("%s (copy)") % (product['name']))
if product['variants']:
default.update(variants=_("%s (copy)") % (product['variants']), product_tmpl_id=product['product_tmpl_id'][0])
else:
default.update(name=_("%s (copy)") % (product['name']), list_price=product['list_price'], standard_price=product['standard_price'], categ_id=product['categ_id'][0], product_tmpl_id=None)
if context.get('variant',False):
fields = ['product_tmpl_id', 'active', 'variants', 'default_code',
@@ -867,10 +886,21 @@ class product_product(osv.osv):
context=context)
def search(self, cr, uid, args, offset=0, limit=None, order=None, context=None, count=False):
if context and context.get('search_default_categ_id'):
if context is None:
context = {}
if context.get('search_default_categ_id'):
args.append((('categ_id', 'child_of', context['search_default_categ_id'])))
return super(product_product, self).search(cr, uid, args, offset=offset, limit=limit, order=order, context=context, count=count)
def open_product_template(self, cr, uid, ids, context=None):
""" Utility method used to add an "Open Template" button in product views """
product = self.browse(cr, uid, ids[0], context=context)
return {'type': 'ir.actions.act_window',
'res_model': 'product.template',
'view_mode': 'form',
'res_id': product.product_tmpl_id.id,
'target': 'new'}
class product_packaging(osv.osv):
_name = "product.packaging"
+206 -96
View File
@@ -8,7 +8,7 @@
<field name="model">product.product</field>
<field name="arch" type="xml">
<search string="Product">
<field name="name" string="Product" filter_domain="['|',('name','ilike',self),('default_code','ilike',self)]"/>
<field name="name" string="Product" filter_domain="['|','|',('name','ilike',self),('default_code','ilike',self),('variants','ilike',self)]"/>
<filter string="Services" icon="terp-accessories-archiver" domain="[('type','=','service')]"/>
<filter string="Consumable" name="consumable" icon="terp-accessories-archiver" domain="[('type','=','consu')]" help="Consumable products"/>
<separator/>
@@ -23,6 +23,7 @@
<filter string='Default Unit of Measure' icon="terp-mrp" domain="[]" context="{'group_by' : 'uom_id'}"/>
<filter string='Type' icon="terp-stock_symbol-selection" domain="[]" context="{'group_by' : 'type'}"/>
<filter string='Company' icon="terp-go-home" domain="[]" context="{'group_by' : 'company_id'}" groups="base.group_multi_company"/>
<filter string='Template' name="template_id" domain="[]" context="{'group_by' : 'product_tmpl_id'}" groups="product.group_product_variant"/>
</group>
</search>
</field>
@@ -36,6 +37,7 @@
<tree colors="red:virtual_available&lt;0;blue:virtual_available&gt;=0 and state in ('draft', 'end', 'obsolete');black:virtual_available&gt;=0 and state not in ('draft', 'end', 'obsolete')" string="Products">
<field name="default_code"/>
<field name="name"/>
<field name="variants" groups="product.group_product_variant"/>
<field name="categ_id" invisible="1"/>
<field name="type" invisible="1"/>
<field name="uom_id" string="Unit of Measure" groups="product.group_uom"/>
@@ -46,6 +48,7 @@
<field name="standard_price" invisible="1"/>
<field name="state"/>
<field name="company_id" groups="base.group_multi_company" invisible="1"/>
<field name="product_tmpl_id" invisible="1"/>
</tree>
</field>
</record>
@@ -57,18 +60,35 @@
<field name="arch" type="xml">
<form string="Product" version="7.0">
<sheet>
<field name="image_medium" widget="image" class="oe_avatar oe_left"/>
<field name="is_only_child" invisible="1"/>
<field name="image_medium" widget="image" class="oe_avatar oe_left"
attrs="{'readonly': [('is_only_child', '=', False)]}"/>
<div class="oe_title">
<div class="oe_edit_only">
<label for="name" string="Product Name"/>
<label for="name" name='label_name' string="Product Name"
attrs="{'invisible': [('is_only_child', '=', False)]}"/>
<label for="name" name='label_name' string="Product Template"
attrs="{'invisible': [('is_only_child', '=', True)]}"/>
</div>
<h1>
<field name="name"/>
<field name="name" class="oe_inline"
attrs="{'required': [('is_only_child', '=', True)],
'invisible': [('is_only_child', '=', False)]}"/>
<field name="product_tmpl_id" class="oe_inline" readonly="1" required="0"
attrs="{'invisible': [('is_only_child', '=', True)]}"/>
<span attrs="{'invisible':[('variants','=',False)]}" groups="product.group_product_variant"> - </span>
<field name="variants" placeholder="Variant Name"
groups="product.group_product_variant" class="oe_inline"/>
</h1>
<span attrs="{'invisible': [('is_only_child', '=', True)]}">
<p>Generic product settings are managed on
<button name="open_product_template" type="object" string="the template" class="oe_link"/>
</p>
</span>
<label for="categ_id" class="oe_edit_only"/>
<h2><field name="categ_id"/></h2>
<h2><field name="categ_id" attrs="{'readonly': [('is_only_child', '=', False)]}"/></h2>
<label for="public_categ_id" class="oe_edit_only"/>
<h3><field name="public_categ_id"/></h3>
<h3><field name="public_categ_id" attrs="{'readonly': [('is_only_child', '=', False)]}"/></h3>
<div name="options" groups="base.group_user">
<field name="sale_ok"/>
<label for="sale_ok"/>
@@ -80,9 +100,14 @@
<page string="Information">
<group>
<group>
<field name="type"/>
<field name="uom_id" on_change="onchange_uom(uom_id,uom_po_id)" groups="product.group_uom"/>
<field name="list_price"/>
<field name="type"
attrs="{'invisible': [('is_only_child', '=', False)]}"/>
<field name="uom_id" on_change="onchange_uom(uom_id,uom_po_id)" groups="product.group_uom"
attrs="{'invisible': [('is_only_child', '=', False)]}"/>
<field name="lst_price" string="Public Sale Price"
attrs="{'readonly': [('is_only_child', '=', False)]}"/>
<field name="price_extra"
attrs="{'invisible': [('is_only_child', '=', True)]}"/>
</group>
<group>
<field name="default_code"/>
@@ -90,19 +115,24 @@
<div name="ean">
<field name="ean13" placeholder="e.g. 5901234123457"/>
</div>
<field name="company_id" groups="base.group_multi_company" widget="selection"/>
<field name="company_id" groups="base.group_multi_company" widget="selection"
attrs="{'invisible': [('is_only_child', '=', False)]}"/>
</group>
</group>
<field name="description" placeholder="describe the product characteristics..."/>
<field name="description" placeholder="describe the product characteristics..."
attrs="{'readonly': [('is_only_child', '=', False)]}"/>
</page>
<page string="Procurements" groups="base.group_user">
<group name="procurement">
<group name="general">
<field name="cost_method" groups="product.group_costing_method"/>
<field name="standard_price" attrs="{'readonly':[('cost_method','=','average')]}"/>
<field name="cost_method" groups="product.group_costing_method"
attrs="{'readonly': [('is_only_child', '=', False)]}"/>
<field name="standard_price"
attrs="{'readonly': ['|', ('cost_method','=','average'), ('is_only_child', '=', False)]}"/>
</group>
<group name="procurement_uom" groups="product.group_uom" string="Purchase">
<field name="uom_po_id"/>
<field name="uom_po_id"
attrs="{'readonly': [('is_only_child', '=', False)]}"/>
</group>
</group>
<separator string="Description for Suppliers"/>
@@ -111,14 +141,19 @@
<page string="Inventory" groups="base.group_user">
<group name="inventory">
<group name="status" string="Status">
<field name="state"/>
<field name="state"
attrs="{'readonly': [('is_only_child', '=', False)]}"/>
<field name="product_manager"
context="{'default_groups_ref': ['base.group_user', 'base.group_partner_manager', 'base.group_sale_manager']}"/>
attrs="{'readonly': [('is_only_child', '=', False)]}"
context="{'default_groups_ref': ['base.group_user', 'base.group_partner_manager', 'base.group_sale_manager']}"/>
</group>
<group name="Weights" groups="product.group_stock_packaging" string="Weights">
<field digits="(14, 3)" name="volume" attrs="{'readonly':[('type','=','service')]}"/>
<field name="weight" attrs="{'readonly':[('type','=','service')]}"/>
<field name="weight_net" attrs="{'readonly':[('type','=','service')]}"/>
<field digits="(14, 3)" name="volume"
attrs="{'readonly': ['|', ('type','=','service'), ('is_only_child', '=', False)]}"/>
<field name="weight"
attrs="{'readonly': ['|', ('type','=','service'), ('is_only_child', '=', False)]}"/>
<field name="weight_net"
attrs="{'readonly': ['|', ('type','=','service'), ('is_only_child', '=', False)]}"/>
</group>
</group>
</page>
@@ -126,17 +161,21 @@
<group name="sale">
<group string="Sale Conditions">
<label for="warranty"/>
<div>
<div attrs="{'readonly': [('is_only_child', '=', False)]}">
<field name="warranty" class="oe_inline"/> months
</div>
</group>
<group groups="product.group_uos" string="Unit of Measure">
<field name="uos_id"/>
<field name="uos_coeff"/>
<field name="mes_type"/>
<field name="uos_id"
attrs="{'readonly': [('is_only_child', '=', False)]}"/>
<field name="uos_coeff"
attrs="{'readonly': [('is_only_child', '=', False)]}"/>
<field name="mes_type"
attrs="{'readonly': [('is_only_child', '=', False)]}"/>
</group>
</group>
<field name="packaging" groups="product.group_stock_packaging">
<field name="packaging" groups="product.group_stock_packaging"
attrs="{'readonly': [('is_only_child', '=', False)]}">
<form string="Packaging" version="7.0">
<group col="4">
<field name="ean"/>
@@ -159,7 +198,8 @@
</form>
</field>
<separator string="Description for Quotations"/>
<field name="description_sale" placeholder="note to be displayed on quotations..."/>
<field name="description_sale" placeholder="note to be displayed on quotations..."
attrs="{'readonly': [('is_only_child', '=', False)]}"/>
</page>
</notebook>
</sheet>
@@ -189,7 +229,7 @@
<div class="oe_kanban_details">
<h4>
<a type="open">
<t t-if="record.code.raw_value">[<field name="code"/>]</t> <field name="name"/>
<t t-if="record.code.raw_value">[<field name="code"/>]</t> <field name="name"/> <t t-if="record.variants.raw_value">(<field name="variants"/>)</t>
</a>
</h4>
<div name="tags"/>
@@ -222,7 +262,7 @@
</field>
</record>
<record id="product_normal_action_sell" model="ir.actions.act_window">
<field name="name">Products</field>
<field name="name">All Products</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">product.product</field>
<field name="view_mode">kanban,tree,form</field>
@@ -364,7 +404,7 @@
action="product_category_action"
id="product.menu_products_category"
parent="base.menu_product"
sequence="0" groups="base.group_no_one"/>
sequence="2" groups="base.group_no_one"/>
<record id="product_category_action_form" model="ir.actions.act_window">
<field name="name">Product Categories</field>
<field name="type">ir.actions.act_window</field>
@@ -578,7 +618,7 @@
</field>
</record>
<menuitem
action="product_ul_form_action" groups="product.group_stock_packaging" id="menu_product_ul_form_action" parent="prod_config_main" sequence="3"/>
action="product_ul_form_action" groups="product.group_stock_packaging" id="menu_product_ul_form_action" parent="prod_config_main" sequence="5"/>
<record id="product_packaging_tree_view" model="ir.ui.view">
<field name="name">product.packaging.tree.view</field>
@@ -668,6 +708,19 @@
</record>
<!-- Variants -->
<record id="product_variant_search_form_view" model="ir.ui.view">
<field name="name">product.variant.search.form</field>
<field name="model">product.product</field>
<field name="arch" type="xml">
<search string="Product Variant">
<field name="product_tmpl_id"/>
<field name="name" string="Product" filter_domain="['|','|',('name','ilike',self),('default_code','ilike',self),('variants','ilike',self)]"/>
<group expand='0' string='Group by...'>
<filter string='Template' name="template_id" domain="[]" context="{'group_by' : 'product_tmpl_id'}"/>
</group>
</search>
</field>
</record>
<record id="product_variant_form_view" model="ir.ui.view">
<field name="name">product.variant.form</field>
<field name="model">product.product</field>
@@ -704,12 +757,12 @@
<record id="product_variant_action" model="ir.actions.act_window">
<field name="name">Product Variants</field>
<field name="type">ir.actions.act_window</field>
<!--<field name="domain">[('variants','&lt;&gt;', False)]</field>-->
<field name="domain">[('variants','!=', '')]</field>
<field name="res_model">product.product</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form,kanban</field>
<field name="view_id" ref="product_variant_tree_view"/>
<field name="search_view_id" ref="product_search_form_view"/>
<field name="search_view_id" ref="product_variant_search_form_view"/>
<field name="help" type="html">
<p class="oe_view_nocontent_create">
Click to define a new variant of product.
@@ -728,9 +781,30 @@
<field name="view_id" ref="product_variant_form_view"/>
<field name="act_window_id" ref="product_variant_action"/>
</record>
<menuitem action="product.product_variant_action" id="product.menu_variant_product" parent="base.menu_product" sequence="100" groups="product.group_product_variant"/>
<menuitem action="product.product_variant_action" id="product.menu_variant_product" parent="prod_config_main" sequence="4" groups="product.group_product_variant"/>
<!-- templates -->
<record id="product_template_search_view" model="ir.ui.view">
<field name="name">product.template.search</field>
<field name="model">product.template</field>
<field name="arch" type="xml">
<search string="Product Template">
<field name="name" string="Product"/>
<filter string="Services" icon="terp-accessories-archiver" domain="[('type','=','service')]"/>
<filter string="Consumable" name="consumable" icon="terp-accessories-archiver" domain="[('type','=','consu')]" help="Consumable products"/>
<separator/>
<filter string="Can be Sold" name="filter_to_sell" icon="terp-accessories-archiver-minus" domain="[('sale_ok','=',1)]"/>
<field name="categ_id"/>
<group expand='0' string='Group by...'>
<filter string='Category' icon="terp-stock_symbol-selection" domain="[]" context="{'group_by' : 'categ_id'}"/>
<filter string='Default Unit of Measure' icon="terp-mrp" domain="[]" context="{'group_by' : 'uom_id'}"/>
<filter string='Type' icon="terp-stock_symbol-selection" domain="[]" context="{'group_by' : 'type'}"/>
</group>
</search>
</field>
</record>
<record id="product_template_tree_view" model="ir.ui.view">
<field name="name">product.template.product.tree</field>
<field name="model">product.template</field>
@@ -740,6 +814,7 @@
<field name="categ_id"/>
<field name="type"/>
<field name="state"/>
<field name="uom_id" invisible="1"/>
</tree>
</field>
</record>
@@ -762,81 +837,85 @@
<h2><field name="categ_id"/></h2>
<label for="public_categ_id" class="oe_edit_only"/>
<h3><field name="public_categ_id"/></h3>
<div name="options" groups="base.group_user">
<field name="sale_ok"/>
<label for="sale_ok"/>
</div>
</div>
<notebook>
<page string="Information">
<group>
<group string="Product Type">
<field name="sale_ok"/>
</group>
<group string="Procurement">
<group colspan="4">
<group>
<field name="type"/>
</group>
<group string="Base Prices">
<field name="uom_id" on_change="onchange_uom(uom_id,uom_po_id)" groups="product.group_uom"/>
<field name="list_price"/>
<field name="standard_price" attrs="{'readonly':[('cost_method','=','average')]}"/>
<field name="cost_method"/>
</group>
<group string="Weights">
<group>
<field name="company_id" groups="base.group_multi_company" widget="selection"/>
</group>
</group>
<group colspan="4" string="Product Variants" groups="product.group_product_variant">
<field colspan="4" name="product_variant_ids" nolabel="1" >
<tree string="Product Variants" editable="bottom">
<field name="variants" required="1"/>
<field name="price_margin" string="Variant Price Margin"/>
<field name="price_extra"/>
<field name="lst_price" string="Sale Price" readonly="1"/>
</tree>
</field>
</group>
<field name="description" placeholder="describe the product characteristics..."/>
</page>
<page string="Procurements" groups="base.group_user">
<group name="procurement">
<group name="general">
<field name="cost_method" groups="product.group_costing_method"/>
<field name="standard_price" attrs="{'readonly':[('cost_method','=','average')]}"/>
</group>
<group name="delay" string="Delays">
<label for="produce_delay"/>
<div>
<field name="produce_delay" class="oe_inline" style="vertical-align:baseline"/> days
</div>
</group>
<group name="procurement_uom" groups="product.group_uom" string="Purchase">
<field name="uom_po_id"/>
</group>
</group>
<separator string="Suppliers"/>
<field name="seller_ids"/>
<separator string="Description for Suppliers"/>
<field name="description_purchase" placeholder="This note will be displayed on requests for quotation..."/>
</page>
<page string="Inventory">
<group name="inventory">
<group name="status" string="Status">
<field name="state"/>
<field name="product_manager"/>
</group>
<group name ="weight" string="Weights">
<field digits="(14, 3)" name="volume" attrs="{'readonly':[('type','=','service')]}"/>
<field digits="(14, 3)" name="weight" attrs="{'readonly':[('type','=','service')]}"/>
<field digits="(14, 3)" name="weight_net" attrs="{'readonly':[('type','=','service')]}"/>
</group>
<group name="status" string="Status">
<field name="categ_id"/>
<field name="state"/>
<field name="product_manager" context="{'default_groups_ref': ['base.group_user', 'base.group_partner_manager', 'base.group_sale_manager']}"/>
</group>
<group name="uom" string="Unit of Measure">
<field name="uom_id" on_change="onchange_uom(uom_id,uom_po_id)" groups="product.group_uom"/>
<field name="uom_po_id"/>
</group>
</page>
<page string="Sales" attrs="{'invisible':[('sale_ok','=',False)]}">
<group name="sale">
<group name="sale_condition" string="Sale Conditions">
<label for="warranty"/>
<div>
<field name="warranty" class="oe_inline" style="vertical-align:baseline"/> months
</div>
</group>
<group name="uos" groups="product.group_uom" string="Second Unit of Measure">
<group groups="product.group_uos" string="Unit of Measure">
<field name="uos_id"/>
<field name="uos_coeff"/>
<field name="mes_type"/>
</group>
<group colspan="4" string="Product Variants" groups="product.group_product_variant">
<field colspan="4" name="product_variant_ids" nolabel="1">
<tree string="Product Variants" editable="bottom">
<field name="active"/>
<field name="variants" required="1"/>
<field name="default_code"/>
<field name="price_margin"/>
<field name="price_extra"/>
</tree>
</field>
</group>
</group>
</page>
<page string="Procurement &amp; Locations">
<group>
<group name="delay" string="Delays">
<label for="produce_delay"/>
<div>
<field name="produce_delay" class="oe_inline"/> days
</div>
<field name="warranty"/>
</group>
</group>
</page>
<page string="Suppliers">
<field name="seller_ids"/>
</page>
<page string="Descriptions">
<separator string="Internal Description"/>
<field name="description"/>
<separator string="Sale Description"/>
<field name="description_sale"/>
<separator string="Purchase Description"/>
<field name="description_purchase"/>
<separator string="Description for Quotations"/>
<field name="description_sale" placeholder="note to be displayed on quotations..."/>
</page>
</notebook>
</sheet>
@@ -848,17 +927,48 @@
</field>
</record>
<record id="product_template_action_tree" model="ir.actions.act_window">
<record model="ir.ui.view" id="product_template_kanban_view">
<field name="name">Product Template Kanban</field>
<field name="model">product.template</field>
<field name="arch" type="xml">
<kanban>
<field name="image_small"/>
<field name="list_price"/>
<templates>
<t t-name="kanban-box">
<div class="oe_kanban_vignette oe_semantic_html_override">
<a type="open"><img t-att-src="kanban_image('product.template', 'image_small', record.id.value)" class="oe_kanban_image"/></a>
<div class="oe_kanban_details">
<h4>
<a type="open">
<field name="name"/>
</a>
</h4>
<div name="tags"/>
<ul>
<li>Price: <field name="list_price"></field></li>
</ul>
</div>
</div>
</t>
</templates>
</kanban>
</field>
</record>
<record id="product_template_action" model="ir.actions.act_window">
<field name="name">Product Templates</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">product.template</field>
<field name="view_mode">kanban,tree,form</field>
<field name="view_type">form</field>
<field name="view_id" ref="product_template_tree_view"/>
<field name="view_id" ref="product_template_kanban_view"/>
</record>
<menuitem id="product_template_menu"
parent="base.menu_product" sequence="25"
action="product_template_action_tree"/>
<menuitem action="product_template_action"
id="menu_product_template_action"
parent="base.menu_product" sequence="0"
groups="product.group_product_variant"/>
</data>
</openerp>
+9 -1
View File
@@ -5,10 +5,18 @@
DO NOT CHANGE IT IN TRUNK -->
<record id="group_product_variant" model="res.groups">
<field name="name">Product Variant (not supported)</field>
<field name="name">Manage Product Variants</field>
<field name="category_id" ref="base.module_category_hidden"/>
</record>
<record id="group_product_mono" model="res.groups">
<field name="name">Do Not Manage Product Variants</field>
<field name="category_id" ref="base.module_category_hidden"/>
</record>
<record id="base.group_user" model="res.groups">
<field name="implied_ids" eval="[(4, ref('product.group_product_mono'))]"/>
</record>
<record id="group_sale_pricelist" model="res.groups">
<field name="name">Sales Pricelists</field>
<field name="category_id" ref="base.module_category_hidden"/>
+16 -3
View File
@@ -552,7 +552,8 @@
<field name="arch" type="xml">
<div name="options" position="inside">
<field name="purchase_ok"/>
<label for="purchase_ok"/>
<label for="purchase_ok"
attrs="{'readonly': [('is_only_child', '=', False)]}"/>
</div>
<group name="procurement" position="after">
<separator string="Suppliers"/>
@@ -597,14 +598,26 @@
</field>
</record>
<record id="product_template_search_view_purchase" model="ir.ui.view">
<field name="name">product.template.search.purchase</field>
<field name="model">product.template</field>
<field name="inherit_id" ref="product.product_template_search_view"/>
<field name="arch" type="xml">
<filter name="filter_to_sell" position="after">
<filter name="filter_to_purchase" string="Can be Purchased" icon="terp-accessories-archiver+" domain="[('purchase_ok', '=', 1)]"/>
</filter>
</field>
</record>
<record id="view_template_purchase_ok_form" model="ir.ui.view">
<field name="name">product.template.purchase.ok.form.inherit</field>
<field name="model">product.template</field>
<field name="inherit_id" ref="product.product_template_form_view"/>
<field name="arch" type="xml">
<field name="sale_ok" position="after">
<div name="options" position="inside">
<field name="purchase_ok"/>
</field>
<label for="purchase_ok"/>
</div>
</field>
</record>
<record model="ir.actions.act_window" id="action_purchase_line_product_tree">
@@ -31,7 +31,8 @@
<field name="inherit_id" ref="product.product_normal_form_view"/>
<field name="arch" type="xml">
<field name="product_manager" position="after">
<field name="intrastat_id"/>
<field name="intrastat_id"
attrs="{'readonly': [('is_only_child', '=', False)]}"/>
</field>
</field>
</record>
+29 -1
View File
@@ -26,9 +26,37 @@ from openerp.tools.translate import _
_logger = logging.getLogger(__name__)
class sale_configuration(osv.osv_memory):
class sale_configuration(osv.TransientModel):
_inherit = 'sale.config.settings'
def set_group_product_variant(self, cr, uid, ids, context=None):
""" This method is automatically called by res_config as it begins
with set. It is used to implement the 'one group or another'
behavior. We have to perform some group manipulation by hand
because in res_config.execute(), set_* methods are called
after group_*; therefore writing on an hidden res_config file
could not work.
If group_product_variant is checked: remove group_product_mono
from group_user, remove the users. Otherwise, just add
group_product_mono in group_user.
The inverse logic about group_product_variant is managed by the
normal behavior of 'group_product_variant' field.
"""
def ref(xml_id):
mod, xml = xml_id.split('.', 1)
return self.pool['ir.model.data'].get_object(cr, uid, mod, xml, context)
for obj in self.browse(cr, uid, ids, context=context):
config_group = ref('product.group_product_mono')
base_group = ref('base.group_user')
if obj.group_product_variant:
base_group.write({'implied_ids': [(3, config_group.id)]})
config_group.write({'users': [(3, u.id) for u in base_group.users]})
else:
base_group.write({'implied_ids': [(4, config_group.id)]})
return True
_columns = {
'group_invoice_so_lines': fields.boolean('Generate invoices based on the sales order lines',
implied_group='sale.group_invoice_so_lines',
+32 -27
View File
@@ -37,29 +37,30 @@
<field name="model">product.template</field>
<field name="inherit_id" ref="product.product_template_form_view"/>
<field name="arch" type="xml">
<group name="delay" position="inside">
<field name="sale_delay" attrs="{'readonly':[('sale_ok','=',False)]}"/>
</group>
<group name="delay" position="after">
<group name="store" string="Storage Location">
<xpath expr="//group[@name='sale_condition']" position="inside">
<label for="sale_delay"/>
<div>
<field name="sale_delay" attrs="{'readonly':[('sale_ok','=',False)]}" class="oe_inline" style="vertical-align:baseline"/> days
</div>
</xpath>
<xpath expr="//group[@name='status']" position="after">
<group name="store" string="Storage Location">
<field name="loc_rack"/>
<field name="loc_row"/>
<field name="loc_case"/>
</group>
</group>
<page position="after" string="Information">
<page string="Properties">
<group string="Counter-Part Locations Properties" groups="stock.group_locations">
<field name="property_stock_procurement" domain="[('usage','=','procurement')]"/>
<field name="property_stock_production" domain="[('usage','=','production')]"/>
<field name="property_stock_inventory" domain="[('usage','=','inventory')]"/>
</group>
<group string="Accounting Entries">
<field name="property_stock_account_input" domain="[('type','&lt;&gt;','view'),('type','&lt;&gt;','consolidation')]"/>
<field name="property_stock_account_output" domain="[('type','&lt;&gt;','view'),('type','&lt;&gt;','consolidation')]"/>
</group>
</page>
</page>
<field name="loc_case"/>
</group>
</xpath>
<xpath expr="//group[@name='weight']" position="after">
<group string="Accounting Entries">
<field name="property_stock_account_input" domain="[('type','&lt;&gt;','view'),('type','&lt;&gt;','consolidation')]"/>
<field name="property_stock_account_output" domain="[('type','&lt;&gt;','view'),('type','&lt;&gt;','consolidation')]"/>
</group>
<group string="Counter-Part Locations Properties" groups="stock.group_locations">
<field name="property_stock_procurement" domain="[('usage','=','procurement')]"/>
<field name="property_stock_production" domain="[('usage','=','production')]"/>
<field name="property_stock_inventory" domain="[('usage','=','inventory')]"/>
</group>
</xpath>
<field name="product_manager" position="attributes" version="7.0">
<attribute name="context">{'default_groups_ref': ['base.group_user', 'base.group_sale_manager', 'stock.group_stock_manager']}</attribute>
</field>
@@ -121,9 +122,9 @@
</group>
<group name="Weights" position="after">
<group name="store" groups="stock.group_locations" string="Counter-Part Locations Properties">
<field name="property_stock_procurement" attrs="{'readonly':[('type','=','service')]}" domain="[('usage','=','procurement')]"/>
<field name="property_stock_production" attrs="{'readonly':[('type','=','service')]}" domain="[('usage','=','production')]"/>
<field name="property_stock_inventory" attrs="{'readonly':[('type','=','service')]}" domain="[('usage','=','inventory')]"/>
<field name="property_stock_procurement" attrs="{'readonly':['|', '|', ('type','=','service'), ('is_only_child', '=', False), ('is_only_child', '=', False)]}" domain="[('usage','=','procurement')]"/>
<field name="property_stock_production" attrs="{'readonly':['|', '|', ('type','=','service'), ('is_only_child', '=', False), ('is_only_child', '=', False)]}" domain="[('usage','=','production')]"/>
<field name="property_stock_inventory" attrs="{'readonly':['|', '|', ('type','=','service'), ('is_only_child', '=', False), ('is_only_child', '=', False)]}" domain="[('usage','=','inventory')]"/>
</group>
</group>
<field name="product_manager" position="attributes" version="7.0">
@@ -138,7 +139,7 @@
<field name="inherit_id" ref="product.product_normal_form_view"/>
<field name="arch" type="xml">
<field name="standard_price" position="replace" version="7.0">
<label string="Cost Price" for="standard_price" align="1.0" groups="base.group_user"/>
<label for="standard_price" align="1.0" groups="base.group_user"/>
<div groups="base.group_user">
<field name="standard_price" attrs="{'readonly':[('cost_method','=','average')]}" nolabel="1"/>
<button name="%(action_view_change_standard_price)d" string="- update"
@@ -162,9 +163,13 @@
<field name="valuation" attrs="{'readonly':[('type', '=', 'service')]}"/>
</group>
<group colspan="2" col="2">
<field name="property_stock_account_input" attrs="{'invisible':[('valuation', '!=', 'real_time')]}"
<field name="property_stock_account_input"
attrs="{'invisible':[('valuation', '!=', 'real_time')],
'readonly': [('is_only_child', '=', False)]}"
domain="[('type','&lt;&gt;','view'),('type','&lt;&gt;','consolidation')]"/>
<field name="property_stock_account_output" attrs="{'invisible':[('valuation', '!=', 'real_time')]}"
<field name="property_stock_account_output"
attrs="{'invisible':[('valuation', '!=', 'real_time')],
'readonly': [('is_only_child', '=', False)]}"
domain="[('type','&lt;&gt;','view'),('type','&lt;&gt;','consolidation')]"/>
</group>
</group>
+7 -1
View File
@@ -23,9 +23,15 @@
<menuitem
action="product.product_category_action_form" id="menu_product_category_config_stock"
parent="stock.menu_product_in_config_stock" sequence="0"/>
<menuitem
action="product.product_variant_action" id="menu_product_variant_config_stock"
parent="stock.menu_product_in_config_stock" groups="product.group_product_variant" sequence="2"/>
<menuitem
action="product.product_template_action" id="menu_product_template_config_stock"
parent="stock.menu_product_in_config_stock" groups="product.group_product_variant" sequence="1"/>
<menuitem
action="product.product_ul_form_action" groups="product.group_stock_packaging"
id="menu_product_packaging_stock_action" parent="stock.menu_product_in_config_stock" sequence="1"/>
id="menu_product_packaging_stock_action" parent="stock.menu_product_in_config_stock" sequence="3"/>
<menuitem
id="menu_stock_unit_measure_stock" name="Units of Measure"
parent="stock.menu_product_in_config_stock" sequence="35" groups="product.group_uom"/>
+2 -2
View File
@@ -138,8 +138,8 @@ class product_product(osv.Model):
def _website_url(self, cr, uid, ids, field_name, arg, context=None):
res = {}
base_url = self.pool.get('ir.config_parameter').get_param(cr, uid, 'web.base.url')
for id in ids:
res[id] = "%s/shop/product/%s/" % (base_url, id)
for product in self.browse(cr, uid, ids, context=context):
res[product.id] = "%s/shop/product/%s/" % (base_url, product.product_tmpl_id.id)
return res
_columns = {
+1 -1
View File
@@ -216,4 +216,4 @@ class Website(orm.Model):
return super(Website, self).preprocess_request(cr, uid, ids, request, context=None)
def ecommerce_get_product_domain(self):
return [("sale_ok", "=", True)]
return [("sale_ok", "=", True),("product_variant_ids","!=",False)]
@@ -53,11 +53,12 @@
<field name="inherit_id" ref="product.product_template_form_view"/>
<field name="arch" type="xml">
<!-- add state field in header -->
<xpath expr="//sheet/div" position="before">
<field name="website_url" invisible="1"/>
<field name="website_published" class="pull-right" widget="website_button"/>
</xpath>
<xpath expr="//page[@string='Information']" position="inside">
<xpath expr="//field[@name='description']" position="before">
<group colspan="4" string="Website Options">
<field name="suggested_product_ids" widget="many2many_tags"/>
<field name="website_style_ids" widget="many2many_tags"/>