diff options
| author | Christophe Simonis <chs@odoo.com> | 2015-03-02 18:19:34 +0100 |
|---|---|---|
| committer | Christophe Simonis <chs@odoo.com> | 2015-03-02 18:19:34 +0100 |
| commit | fefb29897272bf0fe6a6e8aca98b1e2c88ae6af5 (patch) | |
| tree | 9b8f3c81f12e3562f9b2ea8f114aae717b70a4a4 /addons | |
| parent | 1ae5323442bdbbf88d3387ef60b77c86d87e0c10 (diff) | |
| parent | dc41a9b645edf54121fefafe83b687a80b9de66b (diff) | |
[MERGE] forward port of branch 8.0 up to dc41a9b
Diffstat (limited to 'addons')
| -rw-r--r-- | addons/account_payment/account_payment.py | 4 | ||||
| -rw-r--r-- | addons/auth_ldap/users_ldap.py | 4 | ||||
| -rw-r--r-- | addons/hr_timesheet_invoice/hr_timesheet_invoice.py | 3 | ||||
| -rw-r--r-- | addons/procurement_jit/procurement_jit.py | 6 | ||||
| -rw-r--r-- | addons/product/product_demo.xml | 1 | ||||
| -rw-r--r-- | addons/purchase/purchase.py | 17 | ||||
| -rw-r--r-- | addons/purchase_requisition/purchase_requisition.py | 1 | ||||
| -rw-r--r-- | addons/sale/sale.py | 6 | ||||
| -rw-r--r-- | addons/sale/sale_data.xml | 2 | ||||
| -rw-r--r-- | addons/stock/product.py | 130 | ||||
| -rw-r--r-- | addons/stock/stock.py | 101 | ||||
| -rw-r--r-- | addons/stock/stock_view.xml | 22 | ||||
| -rw-r--r-- | addons/warning/warning.py | 5 | ||||
| -rw-r--r-- | addons/warning/warning_view.xml | 12 | ||||
| -rw-r--r-- | addons/website_event/data/event_demo.xml | 6 | ||||
| -rw-r--r-- | addons/website_sale/controllers/main.py | 3 |
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 < 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) |
