summaryrefslogtreecommitdiff
path: root/addons
diff options
context:
space:
mode:
authorChristophe Simonis <chs@odoo.com>2015-03-02 18:19:34 +0100
committerChristophe Simonis <chs@odoo.com>2015-03-02 18:19:34 +0100
commitfefb29897272bf0fe6a6e8aca98b1e2c88ae6af5 (patch)
tree9b8f3c81f12e3562f9b2ea8f114aae717b70a4a4 /addons
parent1ae5323442bdbbf88d3387ef60b77c86d87e0c10 (diff)
parentdc41a9b645edf54121fefafe83b687a80b9de66b (diff)
[MERGE] forward port of branch 8.0 up to dc41a9b
Diffstat (limited to 'addons')
-rw-r--r--addons/account_payment/account_payment.py4
-rw-r--r--addons/auth_ldap/users_ldap.py4
-rw-r--r--addons/hr_timesheet_invoice/hr_timesheet_invoice.py3
-rw-r--r--addons/procurement_jit/procurement_jit.py6
-rw-r--r--addons/product/product_demo.xml1
-rw-r--r--addons/purchase/purchase.py17
-rw-r--r--addons/purchase_requisition/purchase_requisition.py1
-rw-r--r--addons/sale/sale.py6
-rw-r--r--addons/sale/sale_data.xml2
-rw-r--r--addons/stock/product.py130
-rw-r--r--addons/stock/stock.py101
-rw-r--r--addons/stock/stock_view.xml22
-rw-r--r--addons/warning/warning.py5
-rw-r--r--addons/warning/warning_view.xml12
-rw-r--r--addons/website_event/data/event_demo.xml6
-rw-r--r--addons/website_sale/controllers/main.py3
16 files changed, 193 insertions, 130 deletions
diff --git a/addons/account_payment/account_payment.py b/addons/account_payment/account_payment.py
index 0b617eeacae..44345abe8e1 100644
--- a/addons/account_payment/account_payment.py
+++ b/addons/account_payment/account_payment.py
@@ -397,8 +397,8 @@ class payment_line(osv.osv):
break
return {'value': data}
- def fields_get(self, cr, uid, fields=None, context=None):
- res = super(payment_line, self).fields_get(cr, uid, fields, context)
+ def fields_get(self, cr, uid, fields=None, context=None, write_access=True, attributes=None):
+ res = super(payment_line, self).fields_get(cr, uid, fields, context, write_access, attributes)
if 'communication2' in res:
res['communication2'].setdefault('states', {})
res['communication2']['states']['structured'] = [('readonly', True)]
diff --git a/addons/auth_ldap/users_ldap.py b/addons/auth_ldap/users_ldap.py
index 5963ee2a66d..937e672ef89 100644
--- a/addons/auth_ldap/users_ldap.py
+++ b/addons/auth_ldap/users_ldap.py
@@ -105,7 +105,7 @@ class CompanyLDAP(osv.osv):
if results and len(results) == 1:
dn = results[0][0]
conn = self.connect(conf)
- conn.simple_bind_s(dn, password)
+ conn.simple_bind_s(dn, password.encode('utf-8'))
conn.unbind()
entry = results[0]
except ldap.INVALID_CREDENTIALS:
@@ -141,7 +141,7 @@ class CompanyLDAP(osv.osv):
try:
conn = self.connect(conf)
conn.simple_bind_s(conf['ldap_binddn'] or '',
- conf['ldap_password'] or '')
+ conf['ldap_password'].encode('utf-8') or '')
results = conn.search_st(conf['ldap_base'], ldap.SCOPE_SUBTREE,
filter, retrieve_attributes, timeout=60)
conn.unbind()
diff --git a/addons/hr_timesheet_invoice/hr_timesheet_invoice.py b/addons/hr_timesheet_invoice/hr_timesheet_invoice.py
index 0276e49582d..f34e03b69a7 100644
--- a/addons/hr_timesheet_invoice/hr_timesheet_invoice.py
+++ b/addons/hr_timesheet_invoice/hr_timesheet_invoice.py
@@ -281,7 +281,8 @@ class account_analytic_line(osv.osv):
details.append("%s" % (line['unit_amount'], ))
if data.get('name', False):
details.append(line['name'])
- note.append(u' - '.join(map(lambda x: unicode(x) or '',details)))
+ if details:
+ note.append(u' - '.join(map(lambda x: unicode(x) or '',details)))
if note:
curr_line['name'] += "\n" + ("\n".join(map(lambda x: unicode(x) or '',note)))
invoice_line_obj.create(cr, uid, curr_line, context=context)
diff --git a/addons/procurement_jit/procurement_jit.py b/addons/procurement_jit/procurement_jit.py
index 9fa3ed45f83..c7b36ebed98 100644
--- a/addons/procurement_jit/procurement_jit.py
+++ b/addons/procurement_jit/procurement_jit.py
@@ -26,7 +26,9 @@ class procurement_order(osv.osv):
_inherit = "procurement.order"
def create(self, cr, uid, vals, context=None):
+ context = context or {}
procurement_id = super(procurement_order, self).create(cr, uid, vals, context=context)
- self.run(cr, uid, [procurement_id], context=context)
- self.check(cr, uid, [procurement_id], context=context)
+ if not context.get('procurement_autorun_defer'):
+ self.run(cr, uid, [procurement_id], context=context)
+ self.check(cr, uid, [procurement_id], context=context)
return procurement_id
diff --git a/addons/product/product_demo.xml b/addons/product/product_demo.xml
index c239098bcf7..144685d45cf 100644
--- a/addons/product/product_demo.xml
+++ b/addons/product/product_demo.xml
@@ -186,7 +186,6 @@ FaceTime HD Camera, 1.2 MP Photos</field>
<field name="type">consu</field>
<field name="uom_id" ref="product_uom_unit"/>
<field name="uom_po_id" ref="product_uom_unit"/>
- <field name="description">Custom computer assembled on order based on customer's requirement.</field>
<field name="description_sale">Bose's smallest portable Bluetooth speaker</field>
<field name="default_code">B3423</field>
</record>
diff --git a/addons/purchase/purchase.py b/addons/purchase/purchase.py
index 6122c46edb9..98a3fa1ae33 100644
--- a/addons/purchase/purchase.py
+++ b/addons/purchase/purchase.py
@@ -197,6 +197,13 @@ class purchase_order(osv.osv):
('done', 'Done'),
('cancel', 'Cancelled')
]
+
+ READONLY_STATES = {
+ 'confirmed': [('readonly', True)],
+ 'approved': [('readonly', True)],
+ 'done': [('readonly', True)]
+ }
+
_track = {
'state': {
'purchase.mt_rfq_confirmed': lambda self, cr, uid, obj, ctx=None: obj.state == 'confirmed',
@@ -225,16 +232,16 @@ class purchase_order(osv.osv):
copy=False),
'date_approve':fields.date('Date Approved', readonly=1, select=True, copy=False,
help="Date on which purchase order has been approved"),
- 'partner_id':fields.many2one('res.partner', 'Supplier', required=True, states={'confirmed':[('readonly',True)], 'approved':[('readonly',True)],'done':[('readonly',True)]},
+ 'partner_id':fields.many2one('res.partner', 'Supplier', required=True, states=READONLY_STATES,
change_default=True, track_visibility='always'),
'dest_address_id':fields.many2one('res.partner', 'Customer Address (Direct Delivery)',
- states={'confirmed':[('readonly',True)], 'approved':[('readonly',True)],'done':[('readonly',True)]},
+ states=READONLY_STATES,
help="Put an address if you want to deliver directly from the supplier to the customer. " \
"Otherwise, keep empty to deliver to your own company."
),
- 'location_id': fields.many2one('stock.location', 'Destination', required=True, domain=[('usage','<>','view')], states={'confirmed':[('readonly',True)], 'approved':[('readonly',True)],'done':[('readonly',True)]} ),
- 'pricelist_id':fields.many2one('product.pricelist', 'Pricelist', required=True, states={'confirmed':[('readonly',True)], 'approved':[('readonly',True)],'done':[('readonly',True)]}, help="The pricelist sets the currency used for this purchase order. It also computes the supplier price for the selected products/quantities."),
- 'currency_id': fields.many2one('res.currency','Currency', readonly=True, required=True,states={'draft': [('readonly', False)],'sent': [('readonly', False)]}),
+ 'location_id': fields.many2one('stock.location', 'Destination', required=True, domain=[('usage','<>','view')], states=READONLY_STATES),
+ 'pricelist_id':fields.many2one('product.pricelist', 'Pricelist', required=True, states=READONLY_STATES, help="The pricelist sets the currency used for this purchase order. It also computes the supplier price for the selected products/quantities."),
+ 'currency_id': fields.many2one('res.currency','Currency', required=True, states=READONLY_STATES),
'state': fields.selection(STATE_SELECTION, 'Status', readonly=True,
help="The status of the purchase order or the quotation request. "
"A request for quotation is a purchase order in a 'Draft' status. "
diff --git a/addons/purchase_requisition/purchase_requisition.py b/addons/purchase_requisition/purchase_requisition.py
index 277b3945798..5a8630c9a90 100644
--- a/addons/purchase_requisition/purchase_requisition.py
+++ b/addons/purchase_requisition/purchase_requisition.py
@@ -173,6 +173,7 @@ class purchase_requisition(osv.osv):
'order_id': purchase_id,
'product_id': product.id,
'account_analytic_id': requisition_line.account_analytic_id.id,
+ 'taxes_id': [(6, 0, vals.get('taxes_ids', []))],
})
return vals
diff --git a/addons/sale/sale.py b/addons/sale/sale.py
index cce41a92050..78d887469ee 100644
--- a/addons/sale/sale.py
+++ b/addons/sale/sale.py
@@ -690,7 +690,7 @@ class sale_order(osv.osv):
:return: True
"""
- context = dict(context)
+ context = context or {}
context['lang'] = self.pool['res.users'].browse(cr, uid, uid).lang
procurement_obj = self.pool.get('procurement.order')
sale_line_obj = self.pool.get('sale.order.line')
@@ -714,7 +714,9 @@ class sale_order(osv.osv):
if (line.state == 'done') or not line.product_id:
continue
vals = self._prepare_order_line_procurement(cr, uid, order, line, group_id=order.procurement_group_id.id, context=context)
- proc_id = procurement_obj.create(cr, uid, vals, context=context)
+ ctx = context.copy()
+ ctx['procurement_autorun_defer'] = True
+ proc_id = procurement_obj.create(cr, uid, vals, context=ctx)
proc_ids.append(proc_id)
#Confirm procurement order such that rules will be applied on it
#note that the workflow normally ensure proc_ids isn't an empty list
diff --git a/addons/sale/sale_data.xml b/addons/sale/sale_data.xml
index 74f3f060bd7..f5126a25727 100644
--- a/addons/sale/sale_data.xml
+++ b/addons/sale/sale_data.xml
@@ -40,7 +40,7 @@
<record id="mt_order_confirmed" model="mail.message.subtype">
<field name="name">Sales Order Confirmed</field>
<field name="res_model">sale.order</field>
- <field name="default" eval="True"/>
+ <field name="default" eval="False"/>
<field name="description">Quotation confirmed</field>
</record>
diff --git a/addons/stock/product.py b/addons/stock/product.py
index 17dbdc17cc2..729f9051846 100644
--- a/addons/stock/product.py
+++ b/addons/stock/product.py
@@ -94,11 +94,29 @@ class product_product(osv.osv):
operator = context.get('compute_child', True) and 'child_of' or 'in'
domain = context.get('force_company', False) and ['&', ('company_id', '=', context['force_company'])] or []
- return (
- domain + [('location_id', operator, location_ids)],
- domain + ['&', ('location_dest_id', operator, location_ids), '!', ('location_id', operator, location_ids)],
- domain + ['&', ('location_id', operator, location_ids), '!', ('location_dest_id', operator, location_ids)]
- )
+ locations = location_obj.browse(cr, uid, location_ids, context=context)
+ if operator == "child_of" and locations and locations[0].parent_left != 0:
+ loc_domain = []
+ dest_loc_domain = []
+ for loc in locations:
+ if loc_domain:
+ loc_domain = ['|'] + loc_domain + ['&', ('location_id.parent_left', '>=', loc.parent_left), ('location_id.parent_left', '<', loc.parent_right)]
+ dest_loc_domain = ['|'] + dest_loc_domain + ['&', ('location_dest_id.parent_left', '>=', loc.parent_left), ('location_dest_id.parent_left', '<', loc.parent_right)]
+ else:
+ loc_domain += ['&', ('location_id.parent_left', '>=', loc.parent_left), ('location_id.parent_left', '<', loc.parent_right)]
+ dest_loc_domain += ['&', ('location_dest_id.parent_left', '>=', loc.parent_left), ('location_dest_id.parent_left', '<', loc.parent_right)]
+
+ return (
+ domain + loc_domain,
+ domain + ['&'] + dest_loc_domain + ['!'] + loc_domain,
+ domain + ['&'] + loc_domain + ['!'] + dest_loc_domain
+ )
+ else:
+ return (
+ domain + [('location_id', operator, location_ids)],
+ domain + ['&', ('location_dest_id', operator, location_ids), '!', ('location_id', operator, location_ids)],
+ domain + ['&', ('location_id', operator, location_ids), '!', ('location_dest_id', operator, location_ids)]
+ )
def _get_domain_dates(self, cr, uid, ids, context):
from_date = context.get('from_date', False)
@@ -115,7 +133,8 @@ class product_product(osv.osv):
field_names = field_names or []
domain_products = [('product_id', 'in', ids)]
- domain_quant, domain_move_in, domain_move_out = self._get_domain_locations(cr, uid, ids, context=context)
+ domain_quant, domain_move_in, domain_move_out = [], [], []
+ domain_quant_loc, domain_move_in_loc, domain_move_out_loc = self._get_domain_locations(cr, uid, ids, context=context)
domain_move_in += self._get_domain_dates(cr, uid, ids, context=context) + [('state', 'not in', ('done', 'cancel', 'draft'))] + domain_products
domain_move_out += self._get_domain_dates(cr, uid, ids, context=context) + [('state', 'not in', ('done', 'cancel', 'draft'))] + domain_products
domain_quant += domain_products
@@ -129,9 +148,12 @@ class product_product(osv.osv):
moves_in = []
moves_out = []
else:
+ domain_move_in += domain_move_in_loc
+ domain_move_out += domain_move_out_loc
moves_in = self.pool.get('stock.move').read_group(cr, uid, domain_move_in, ['product_id', 'product_qty'], ['product_id'], context=context)
moves_out = self.pool.get('stock.move').read_group(cr, uid, domain_move_out, ['product_id', 'product_qty'], ['product_id'], context=context)
+ domain_quant += domain_quant_loc
quants = self.pool.get('stock.quant').read_group(cr, uid, domain_quant, ['product_id', 'qty'], ['product_id'], context=context)
quants = dict(map(lambda x: (x['product_id'][0], x['qty']), quants))
@@ -163,17 +185,33 @@ class product_product(osv.osv):
if operator == '=':
operator = '=='
- product_ids = self.search(cr, uid, [], context=context)
ids = []
- if product_ids:
- #TODO: use a query instead of this browse record which is probably making the too much requests, but don't forget
- #the context that can be set with a location, an owner...
- for element in self.browse(cr, uid, product_ids, context=context):
- if eval(str(element[field]) + operator + str(value)):
- ids.append(element.id)
- res.append(('id', 'in', ids))
+ if name == 'qty_available':
+ res.append(('id', 'in', self._search_qty_available(cr, uid, operator, value, context)))
+ else:
+ product_ids = self.search(cr, uid, [], context=context)
+ if product_ids:
+ #TODO: Still optimization possible when searching virtual quantities
+ for element in self.browse(cr, uid, product_ids, context=context):
+ if eval(str(element[field]) + operator + str(value)):
+ ids.append(element.id)
+ res.append(('id', 'in', ids))
return res
+ def _search_qty_available(self, cr, uid, operator, value, context):
+ domain_quant = []
+ if context.get('lot_id'):
+ domain_quant.append(('lot_id', '=', context['lot_id']))
+ if context.get('owner_id'):
+ domain_quant.append(('owner_id', '=', context['owner_id']))
+ if context.get('package_id'):
+ domain_quant.append(('package_id', '=', context['package_id']))
+ domain_quant += self._get_domain_locations(cr, uid, [], context=context)[0]
+ quants = self.pool.get('stock.quant').read_group(cr, uid, domain_quant, ['product_id', 'qty'], ['product_id'], context=context)
+ quants = dict(map(lambda x: (x['product_id'][0], x['qty']), quants))
+ quants = dict((k, v) for k, v in quants.iteritems() if eval(str(v) + operator + str(value)))
+ return(list(quants))
+
def _product_available_text(self, cr, uid, ids, field_names=None, arg=False, context=None):
res = {}
for product in self.browse(cr, uid, ids, context=context):
@@ -294,41 +332,35 @@ class product_template(osv.osv):
_inherit = 'product.template'
def _product_available(self, cr, uid, ids, name, arg, context=None):
- res = dict.fromkeys(ids, 0)
- for product in self.browse(cr, uid, ids, context=context):
- res[product.id] = {
- # "reception_count": sum([p.reception_count for p in product.product_variant_ids]),
- # "delivery_count": sum([p.delivery_count for p in product.product_variant_ids]),
- "qty_available": sum([p.qty_available for p in product.product_variant_ids]),
- "virtual_available": sum([p.virtual_available for p in product.product_variant_ids]),
- "incoming_qty": sum([p.incoming_qty for p in product.product_variant_ids]),
- "outgoing_qty": sum([p.outgoing_qty for p in product.product_variant_ids]),
+ prod_available = {}
+ product_ids = self.browse(cr, uid, ids, context=context)
+ var_ids = []
+ for product in product_ids:
+ var_ids += [p.id for p in product.product_variant_ids]
+ variant_available= self.pool['product.product']._product_available(cr, uid, var_ids, context=context)
+
+ for product in product_ids:
+ qty_available = 0
+ virtual_available = 0
+ incoming_qty = 0
+ outgoing_qty = 0
+ for p in product.product_variant_ids:
+ qty_available += variant_available[p.id]["qty_available"]
+ virtual_available += variant_available[p.id]["virtual_available"]
+ incoming_qty += variant_available[p.id]["incoming_qty"]
+ outgoing_qty += variant_available[p.id]["outgoing_qty"]
+ prod_available[product.id] = {
+ "qty_available": qty_available,
+ "virtual_available": virtual_available,
+ "incoming_qty": incoming_qty,
+ "outgoing_qty": outgoing_qty,
}
- return res
+ return prod_available
def _search_product_quantity(self, cr, uid, obj, name, domain, context):
prod = self.pool.get("product.product")
- res = []
- for field, operator, value in domain:
- #to prevent sql injections
- assert field in ('qty_available', 'virtual_available', 'incoming_qty', 'outgoing_qty'), 'Invalid domain left operand'
- assert operator in ('<', '>', '=', '!=', '<=', '>='), 'Invalid domain operator'
- assert isinstance(value, (float, int)), 'Invalid domain right operand'
-
- if operator == '=':
- operator = '=='
-
- product_ids = prod.search(cr, uid, [], context=context)
- ids = []
- if product_ids:
- #TODO: use a query instead of this browse record which is probably making the too much requests, but don't forget
- #the context that can be set with a location, an owner...
- for element in prod.browse(cr, uid, product_ids, context=context):
- if eval(str(element[field]) + operator + str(value)):
- ids.append(element.id)
- res.append(('product_variant_ids', 'in', ids))
- return res
-
+ product_variant_ids = prod.search(cr, uid, domain, context=context)
+ return [('product_variant_ids', 'in', product_variant_ids)]
def _product_available_text(self, cr, uid, ids, field_names=None, arg=False, context=None):
res = {}
@@ -372,13 +404,13 @@ class product_template(osv.osv):
# fnct_search=_search_product_quantity, type='float', string='Quantity On Hand'),
# 'delivery_count': fields.function(_product_available, multi='qty_available',
# fnct_search=_search_product_quantity, type='float', string='Quantity On Hand'),
- 'qty_available': fields.function(_product_available, multi='qty_available',
+ 'qty_available': fields.function(_product_available, multi='qty_available', digits_compute=dp.get_precision('Product Unit of Measure'),
fnct_search=_search_product_quantity, type='float', string='Quantity On Hand'),
- 'virtual_available': fields.function(_product_available, multi='qty_available',
+ 'virtual_available': fields.function(_product_available, multi='qty_available', digits_compute=dp.get_precision('Product Unit of Measure'),
fnct_search=_search_product_quantity, type='float', string='Quantity Available'),
- 'incoming_qty': fields.function(_product_available, multi='qty_available',
+ 'incoming_qty': fields.function(_product_available, multi='qty_available', digits_compute=dp.get_precision('Product Unit of Measure'),
fnct_search=_search_product_quantity, type='float', string='Incoming'),
- 'outgoing_qty': fields.function(_product_available, multi='qty_available',
+ 'outgoing_qty': fields.function(_product_available, multi='qty_available', digits_compute=dp.get_precision('Product Unit of Measure'),
fnct_search=_search_product_quantity, type='float', string='Outgoing'),
'route_ids': fields.many2many('stock.location.route', 'stock_route_product', 'product_id', 'route_id', 'Routes', domain="[('product_selectable', '=', True)]",
diff --git a/addons/stock/stock.py b/addons/stock/stock.py
index fa004c232d0..d2a5265ef13 100644
--- a/addons/stock/stock.py
+++ b/addons/stock/stock.py
@@ -296,7 +296,7 @@ class stock_quant(osv.osv):
_columns = {
'name': fields.function(_get_quant_name, type='char', string='Identifier'),
'product_id': fields.many2one('product.product', 'Product', required=True, ondelete="restrict", readonly=True, select=True),
- 'location_id': fields.many2one('stock.location', 'Location', required=True, ondelete="restrict", readonly=True, select=True),
+ 'location_id': fields.many2one('stock.location', 'Location', required=True, ondelete="restrict", readonly=True, select=True, auto_join=True),
'qty': fields.float('Quantity', required=True, help="Quantity of products in this quant, in the default unit of measure of the product", readonly=True, select=True),
'product_uom_id': fields.related('product_id', 'uom_id', type='many2one', relation="product.uom", string='Unit of Measure', readonly=True),
'package_id': fields.many2one('stock.quant.package', string='Package', help="The package containing this quant", readonly=True, select=True),
@@ -1770,8 +1770,10 @@ class stock_move(osv.osv):
'product_packaging': fields.many2one('product.packaging', 'Prefered Packaging', help="It specifies attributes of packaging like type, quantity of packaging,etc."),
- 'location_id': fields.many2one('stock.location', 'Source Location', required=True, select=True, states={'done': [('readonly', True)]}, help="Sets a location if you produce at a fixed location. This can be a partner location if you subcontract the manufacturing operations."),
- 'location_dest_id': fields.many2one('stock.location', 'Destination Location', required=True, states={'done': [('readonly', True)]}, select=True, help="Location where the system will stock the finished products."),
+ 'location_id': fields.many2one('stock.location', 'Source Location', required=True, select=True, auto_join=True,
+ states={'done': [('readonly', True)]}, help="Sets a location if you produce at a fixed location. This can be a partner location if you subcontract the manufacturing operations."),
+ 'location_dest_id': fields.many2one('stock.location', 'Destination Location', required=True, states={'done': [('readonly', True)]}, select=True,
+ auto_join=True, help="Location where the system will stock the finished products."),
'partner_id': fields.many2one('res.partner', 'Destination Address ', states={'done': [('readonly', True)]}, help="Optional address where goods are to be delivered, specifically used for allotment"),
@@ -2587,7 +2589,7 @@ class stock_inventory(osv.osv):
:rtype: list of tuple
"""
#default available choices
- res_filter = [('none', _('All products')), ('product', _('One product only'))]
+ res_filter = [('none', _('All products')), ('partial', _('Manual Selection of Products')), ('product', _('One product only'))]
settings_obj = self.pool.get('stock.config.settings')
config_ids = settings_obj.search(cr, uid, [], limit=1, order='id DESC', context=context)
#If we don't have updated config until now, all fields are by default false and so should be not dipslayed
@@ -2630,7 +2632,10 @@ class stock_inventory(osv.osv):
'partner_id': fields.many2one('res.partner', 'Inventoried Owner', readonly=True, states={'draft': [('readonly', False)]}, help="Specify Owner to focus your inventory on a particular Owner."),
'lot_id': fields.many2one('stock.production.lot', 'Inventoried Lot/Serial Number', readonly=True, states={'draft': [('readonly', False)]}, help="Specify Lot/Serial Number to focus your inventory on a particular Lot/Serial Number.", copy=False),
'move_ids_exist': fields.function(_get_move_ids_exist, type='boolean', string=' Stock Move Exists?', help='technical field for attrs in view'),
- 'filter': fields.selection(_get_available_filters, 'Selection Filter', required=True),
+ 'filter': fields.selection(_get_available_filters, 'Inventory of', required=True,
+ help="If you do an entire inventory, you can choose 'All Products' and it will prefill the inventory with the current stock. If you only do some products "\
+ "(e.g. Cycle Counting) you can choose 'Manual Selection of Products' and the system won't propose anything. You can also let the "\
+ "system propose for a single product / lot /... "),
'total_qty': fields.function(_get_total_qty, type="float"),
}
@@ -2742,25 +2747,11 @@ class stock_inventory(osv.osv):
for inventory in self.browse(cr, uid, ids, context=context):
# If there are inventory lines already (e.g. from import), respect those and set their theoretical qty
line_ids = [line.id for line in inventory.line_ids]
- if not line_ids:
+ if not line_ids and inventory.filter != 'partial':
#compute the inventory lines and create them
vals = self._get_inventory_lines(cr, uid, inventory, context=context)
for product_line in vals:
inventory_line_obj.create(cr, uid, product_line, context=context)
- else:
- # On import calculate theoretical quantity
- quant_obj = self.pool.get("stock.quant")
- for line in inventory.line_ids:
- dom = [('company_id', '=', line.company_id.id), ('location_id', 'child_of', line.location_id.id), ('lot_id', '=', line.prod_lot_id.id),
- ('product_id','=', line.product_id.id), ('owner_id', '=', line.partner_id.id)]
- if line.package_id:
- dom += [('package_id', '=', line.package_id.id)]
- quants = quant_obj.search(cr, uid, dom, context=context)
- tot_qty = 0
- for quant in quant_obj.browse(cr, uid, quants, context=context):
- tot_qty += quant.qty
- inventory_line_obj.write(cr, uid, [line.id], {'theoretical_qty': tot_qty}, context=context)
-
return self.write(cr, uid, ids, {'state': 'confirm', 'date': time.strftime(DEFAULT_SERVER_DATETIME_FORMAT)})
def _get_inventory_lines(self, cr, uid, inventory, context=None):
@@ -2816,6 +2807,19 @@ class stock_inventory_line(osv.osv):
def _get_prodlot_change(self, cr, uid, ids, context=None):
return self.pool.get('stock.inventory.line').search(cr, uid, [('prod_lot_id', 'in', ids)], context=context)
+ def _get_theoretical_qty(self, cr, uid, ids, name, args, context=None):
+ res = {}
+ quant_obj = self.pool["stock.quant"]
+ uom_obj = self.pool["product.uom"]
+ for line in self.browse(cr, uid, ids, context=context):
+ quant_ids = self._get_quants(cr, uid, line, context=context)
+ quants = quant_obj.browse(cr, uid, quant_ids, context=context)
+ tot_qty = sum([x.qty for x in quants])
+ if line.product_uom_id and line.product_id.uom_id.id != line.product_uom_id.id:
+ tot_qty = uom_obj._compute_qty_obj(cr, uid, line.product_id.uom_id, tot_qty, line.product_uom_id, context=context)
+ res[line.id] = tot_qty
+ return res
+
_columns = {
'inventory_id': fields.many2one('stock.inventory', 'Inventory', ondelete='cascade', select=True),
'location_id': fields.many2one('stock.location', 'Location', required=True, select=True),
@@ -2826,7 +2830,8 @@ class stock_inventory_line(osv.osv):
'company_id': fields.related('inventory_id', 'company_id', type='many2one', relation='res.company', string='Company', store=True, select=True, readonly=True),
'prod_lot_id': fields.many2one('stock.production.lot', 'Serial Number', domain="[('product_id','=',product_id)]"),
'state': fields.related('inventory_id', 'state', type='char', string='Status', readonly=True),
- 'theoretical_qty': fields.float('Theoretical Quantity', digits_compute=dp.get_precision('Product Unit of Measure'), readonly=True),
+ 'theoretical_qty': fields.function(_get_theoretical_qty, type='float', digits_compute=dp.get_precision('Product Unit of Measure'),
+ store=True, readonly=True, string="Theoretical Quantity"),
'partner_id': fields.many2one('res.partner', 'Owner'),
'product_name': fields.related('product_id', 'name', type='char', string='Product Name', store={
'product.product': (_get_product_name_change, ['name', 'default_code'], 20),
@@ -2843,7 +2848,7 @@ class stock_inventory_line(osv.osv):
}
_defaults = {
- 'product_qty': 1,
+ 'product_qty': 0,
}
def create(self, cr, uid, values, context=None):
@@ -2854,6 +2859,37 @@ class stock_inventory_line(osv.osv):
values['product_uom_id'] = product_obj.browse(cr, uid, values.get('product_id'), context=context).uom_id.id
return super(stock_inventory_line, self).create(cr, uid, values, context=context)
+ def _get_quants(self, cr, uid, line, context=None):
+ quant_obj = self.pool["stock.quant"]
+ dom = [('company_id', '=', line.company_id.id), ('location_id', 'child_of', line.location_id.id), ('lot_id', '=', line.prod_lot_id.id),
+ ('product_id','=', line.product_id.id), ('owner_id', '=', line.partner_id.id), ('package_id', '=', line.package_id.id)]
+ quants = quant_obj.search(cr, uid, dom, context=context)
+ return quants
+
+ def onchange_createline(self, cr, uid, ids, location_id=False, product_id=False, uom_id=False, package_id=False, prod_lot_id=False, partner_id=False, company_id=False, context=None):
+ quant_obj = self.pool["stock.quant"]
+ uom_obj = self.pool["product.uom"]
+ res = {'value': {}}
+ # If no UoM already put the default UoM of the product
+ if product_id and not uom_id:
+ product = self.pool.get('product.product').browse(cr, uid, product_id, context=context)
+ res['value']['product_uom_id'] = product.uom_id.id
+ res['domain'] = {'product_uom_id': [('category_id','=',product.uom_id.category_id.id)]}
+ uom_id = product.uom_id.id
+ # Calculate theoretical quantity by searching the quants as in quants_get
+ if product_id and location_id:
+ product = self.pool.get('product.product').browse(cr, uid, product_id, context=context)
+ if not company_id:
+ company_id = self.pool.get('res.users').browse(cr, uid, uid, context=context).company_id.id
+ dom = [('company_id', '=', company_id), ('location_id', 'child_of', location_id), ('lot_id', '=', prod_lot_id),
+ ('product_id','=', product_id), ('owner_id', '=', partner_id), ('package_id', '=', package_id)]
+ quants = quant_obj.search(cr, uid, dom, context=context)
+ th_qty = sum([x.qty for x in quant_obj.browse(cr, uid, quants, context=context)])
+ if product_id and uom_id and product.uom_id.id != uom_id:
+ th_qty = uom_obj._compute_qty(cr, uid, product.uom_id.id, th_qty, uom_id)
+ res['value']['theoretical_qty'] = th_qty
+ return res
+
def _resolve_inventory_line(self, cr, uid, inventory_line, context=None):
stock_move_obj = self.pool.get('stock.move')
diff = inventory_line.theoretical_qty - inventory_line.product_qty
@@ -2884,26 +2920,11 @@ class stock_inventory_line(osv.osv):
vals['product_uom_qty'] = diff
return stock_move_obj.create(cr, uid, vals, context=context)
+ # Should be left out in next version
def restrict_change(self, cr, uid, ids, theoretical_qty, context=None):
- if ids and theoretical_qty:
- #if the user try to modify a line prepared by openerp, reject the change and display an error message explaining how he should do
- old_value = self.browse(cr, uid, ids[0], context=context)
- return {
- 'value': {
- 'product_id': old_value.product_id.id,
- 'product_uom_id': old_value.product_uom_id.id,
- 'location_id': old_value.location_id.id,
- 'prod_lot_id': old_value.prod_lot_id.id,
- 'package_id': old_value.package_id.id,
- 'partner_id': old_value.partner_id.id,
- },
- 'warning': {
- 'title': _('Error'),
- 'message': _('You can only change the checked quantity of an existing inventory line. If you want modify a data, please set the checked quantity to 0 and create a new inventory line.')
- }
- }
return {}
+ # Should be left out in next version
def on_change_product_id(self, cr, uid, ids, product, uom, theoretical_qty, context=None):
""" Changes UoM
@param location_id: Location id
@@ -2911,8 +2932,6 @@ class stock_inventory_line(osv.osv):
@param uom: UoM product
@return: Dictionary of changed values
"""
- if ids and theoretical_qty:
- return self.restrict_change(cr, uid, ids, theoretical_qty, context=context)
if not product:
return {'value': {'product_uom_id': False}}
obj_product = self.pool.get('product.product').browse(cr, uid, product, context=context)
diff --git a/addons/stock/stock_view.xml b/addons/stock/stock_view.xml
index 6c1de13baa9..43f5463dc8c 100644
--- a/addons/stock/stock_view.xml
+++ b/addons/stock/stock_view.xml
@@ -58,7 +58,7 @@
</field>
</record>
- <record id="view_inventory_filter" model="ir.ui.view">
+ <record id="view_inventory_filter" model="ir.ui.view">
<field name="name">stock.inventory.filter</field>
<field name="model">stock.inventory</field>
<field name="arch" type="xml">
@@ -112,10 +112,10 @@
<group>
<field name="date"/>
<field name="company_id" groups="base.group_multi_company" options="{'no_create': True}"/>
- <field name="product_id" domain="[('type','=','product')]" attrs="{'invisible': [('filter', 'not in', ('product', 'product_owner'))]}"/>
- <field name="lot_id" attrs="{'invisible': [('filter', '!=', 'lot')]}" groups="stock.group_production_lot" />
- <field name="partner_id" attrs="{'invisible': [('filter', 'not in', ('owner', 'product_owner'))]}" groups="stock.group_tracking_owner"/>
- <field name="package_id" attrs="{'invisible': [('filter', '!=', 'pack')]}" groups="stock.group_tracking_lot"/>
+ <field name="product_id" domain="[('type','=','product')]" attrs="{'invisible': [('filter', 'not in', ('product', 'product_owner'))], 'required': [('filter', 'in', ('product', 'product_owner'))]}"/>
+ <field name="lot_id" attrs="{'invisible': [('filter', '!=', 'lot')], 'required': [('filter', '=', 'lot')]}" groups="stock.group_production_lot" />
+ <field name="partner_id" attrs="{'invisible': [('filter', 'not in', ('owner', 'product_owner'))], 'required': [('filter', 'in', ('owner', 'product_owner'))]}" groups="stock.group_tracking_owner"/>
+ <field name="package_id" attrs="{'invisible': [('filter', '!=', 'pack')], 'required': [('filter', '=', 'pack')]}" groups="stock.group_tracking_lot"/>
</group>
</group>
<notebook attrs="{'invisible':[('state','=','draft')]}">
@@ -123,12 +123,12 @@
<button name="reset_real_qty" states="confirm" string="⇒ Set quantities to 0" type="object" class="oe_link oe_right" groups="stock.group_stock_user"/>
<field name="line_ids" string="Inventory Details" context="{'default_location_id': location_id, 'default_product_id': product_id, 'default_prod_lot_id': lot_id, 'default_package_id': package_id, 'default_partner_id': partner_id}">
<tree string="Inventory Details" editable="bottom" colors="blue: product_qty != theoretical_qty; red: theoretical_qty &lt; 0">
- <field context="{'location':location_id, 'uom':product_uom_id, 'to_date':parent.date}" name="product_id" on_change="on_change_product_id(product_id,product_uom_id,theoretical_qty,context)" domain="[('type','=','product')]"/>
- <field name="product_uom_id" groups="product.group_uom" on_change="restrict_change(theoretical_qty)"/>
- <field domain="[('usage','=','internal')]" name="location_id" groups="stock.group_locations" on_change="restrict_change(theoretical_qty)"/>
- <field name="prod_lot_id" on_change="restrict_change(theoretical_qty)" domain="[('product_id', '=', product_id)]" context="{'default_product_id': product_id}" groups="stock.group_production_lot"/>
- <field name="package_id" on_change="restrict_change(theoretical_qty)" groups="stock.group_tracking_lot"/>
- <field name="partner_id" on_change="restrict_change(theoretical_qty)" groups="stock.group_tracking_owner"/>
+ <field name="product_id" domain="[('type','=','product')]" on_change="onchange_createline(location_id, product_id, product_uom_id, package_id, prod_lot_id, partner_id)"/>
+ <field name="product_uom_id" groups="product.group_uom" string="UoM" on_change="onchange_createline(location_id, product_id, product_uom_id, package_id, prod_lot_id, partner_id)"/>
+ <field name="location_id" domain="[('id', 'child_of', parent.location_id)]" groups="stock.group_locations" on_change="onchange_createline(location_id, product_id, product_uom_id, package_id, prod_lot_id, partner_id)"/>
+ <field name="prod_lot_id" on_change="onchange_createline(location_id, product_id, product_uom_id, package_id, prod_lot_id, partner_id)" domain="[('product_id', '=', product_id)]" context="{'default_product_id': product_id}" groups="stock.group_production_lot"/>
+ <field name="package_id" domain="['|', ('location_id','=', False), ('location_id', '=', location_id)]" on_change="onchange_createline(location_id, product_id, product_uom_id, package_id, prod_lot_id, partner_id)" groups="stock.group_tracking_lot"/>
+ <field name="partner_id" on_change="onchange_createline(location_id, product_id, product_uom_id, package_id, prod_lot_id, partner_id)" groups="stock.group_tracking_owner"/>
<field name="theoretical_qty" readonly="1"/>
<field name="product_qty" string="Real Quantity"/>
<field name="state" invisible="True"/>
diff --git a/addons/warning/warning.py b/addons/warning/warning.py
index f9b19839b20..e888144a2a2 100644
--- a/addons/warning/warning.py
+++ b/addons/warning/warning.py
@@ -172,10 +172,7 @@ class stock_picking(osv.osv):
if partner.picking_warn == 'block':
return {'value': {'partner_id': False}, 'warning': warning}
- result = {}
- if result.get('warning',False):
- warning['title'] = title and title +' & '+ result['warning']['title'] or result['warning']['title']
- warning['message'] = message and message + ' ' + result['warning']['message'] or result['warning']['message']
+ result = {'value': {}}
if warning:
result['warning'] = warning
diff --git a/addons/warning/warning_view.xml b/addons/warning/warning_view.xml
index 735758cb9ab..06314a37582 100644
--- a/addons/warning/warning_view.xml
+++ b/addons/warning/warning_view.xml
@@ -59,15 +59,17 @@
</notebook>
</field>
</record>
- <record id="stock_picking_form_view_inherit" model="ir.ui.view">
- <field name="name">stock.picking.form.inherit</field>
+
+ <record id="view_picking_form" model="ir.ui.view">
+ <field name="name">stock.picking.form.warning</field>
<field name="model">stock.picking</field>
<field name="inherit_id" ref="stock.view_picking_form"/>
<field name="arch" type="xml">
- <xpath expr="//sheet/group/group/field[@name='partner_id']" position="replace">
- <field name="partner_id" on_change="onchange_partner_id(partner_id)" />
- </xpath>
+ <field name="partner_id" position="attributes">
+ <attribute name="on_change">onchange_partner_id(partner_id)</attribute>
+ </field>
</field>
</record>
+
</data>
</openerp>
diff --git a/addons/website_event/data/event_demo.xml b/addons/website_event/data/event_demo.xml
index c36230fa1b5..f63e01e9f49 100644
--- a/addons/website_event/data/event_demo.xml
+++ b/addons/website_event/data/event_demo.xml
@@ -61,7 +61,7 @@
<li class="list-group-item">Loops</li>
<li class="list-group-item">Arrays</li>
</ul><div class="panel-footer text-center">
-<p class="text-muted"><i>You will be able to develop simple dynamic compenents in HTML pages.</i></p>
+<p class="text-muted"><i>You will be able to develop simple dynamic components in HTML pages.</i></p>
</div>
</div>
</div>
@@ -227,7 +227,7 @@
<li class="list-group-item">Loops</li>
<li class="list-group-item">Arrays</li>
</ul><div class="panel-footer text-center">
-<p class="text-muted"><i>You will be able to develop simple dynamic compenents in HTML pages.</i></p>
+<p class="text-muted"><i>You will be able to develop simple dynamic components in HTML pages.</i></p>
</div>
</div>
</div>
@@ -405,7 +405,7 @@
<li class="list-group-item">Loops</li>
<li class="list-group-item">Arrays</li>
</ul><div class="panel-footer text-center">
-<p class="text-muted"><i>You will be able to develop simple dynamic compenents in HTML pages.</i></p>
+<p class="text-muted"><i>You will be able to develop simple dynamic components in HTML pages.</i></p>
</div>
</div>
</div>
diff --git a/addons/website_sale/controllers/main.py b/addons/website_sale/controllers/main.py
index 476a6d30c47..e32ca940dae 100644
--- a/addons/website_sale/controllers/main.py
+++ b/addons/website_sale/controllers/main.py
@@ -153,7 +153,6 @@ class website_sale(http.Controller):
('description_sale', 'ilike', srch), ('product_variant_ids.default_code', 'ilike', srch)]
if category:
domain += [('public_categ_ids', 'child_of', int(category))]
-
attrib_list = request.httprequest.args.getlist('attrib')
attrib_values = [map(int,v.split("-")) for v in attrib_list if v]
attrib_set = set([v[1] for v in attrib_values])
@@ -188,6 +187,8 @@ class website_sale(http.Controller):
if category:
category = pool['product.public.category'].browse(cr, uid, int(category), context=context)
url = "/shop/category/%s" % slug(category)
+ if attrib_list:
+ post['attrib'] = attrib_list
style_obj = pool['product.style']
style_ids = style_obj.search(cr, uid, [], context=context)