X-Git-Url: http://git.inspyration.org/?a=blobdiff_plain;f=addons%2Fsale_stock%2Fsale_stock.py;h=63f6068f3c224a8140e1ad2f756a66a51d86d6f3;hb=cde4ad1159105b3b26e9c4a7a9d0f0f072b4e0eb;hp=9f45bf0be59597d0c21b019ba2f9ed0990bdb23e;hpb=d5a9dfd30b68bb21057b581c5866822fe749477c;p=odoo%2Fodoo.git diff --git a/addons/sale_stock/sale_stock.py b/addons/sale_stock/sale_stock.py index 9f45bf0..63f6068 100644 --- a/addons/sale_stock/sale_stock.py +++ b/addons/sale_stock/sale_stock.py @@ -21,7 +21,6 @@ ############################################################################## from datetime import datetime, timedelta from openerp.tools import DEFAULT_SERVER_DATE_FORMAT, DEFAULT_SERVER_DATETIME_FORMAT, DATETIME_FORMATS_MAP, float_compare -from dateutil.relativedelta import relativedelta from openerp.osv import fields, osv from openerp.tools.safe_eval import safe_eval as eval from openerp.tools.translate import _ @@ -44,7 +43,7 @@ class sale_order(osv.osv): company_id = self.pool.get('res.users')._get_company(cr, uid, context=context) warehouse_ids = self.pool.get('stock.warehouse').search(cr, uid, [('company_id', '=', company_id)], context=context) if not warehouse_ids: - raise osv.except_osv(_('Error!'), _('There is no warehouse defined for selected company.')) + return False return warehouse_ids[0] def _get_shipped(self, cr, uid, ids, name, args, context=None): @@ -63,32 +62,31 @@ class sale_order(osv.osv): if move.procurement_id and move.procurement_id.sale_line_id: res.add(move.procurement_id.sale_line_id.order_id.id) return list(res) - + def _get_orders_procurements(self, cr, uid, ids, context=None): res = set() for proc in self.pool.get('procurement.order').browse(cr, uid, ids, context=context): - if proc.sale_line_id: + if proc.state =='done' and proc.sale_line_id: res.add(proc.sale_line_id.order_id.id) return list(res) - + def _get_picking_ids(self, cr, uid, ids, name, args, context=None): res = {} for sale in self.browse(cr, uid, ids, context=context): if not sale.procurement_group_id: res[sale.id] = [] continue - picking_ids = set() - for procurement in sale.procurement_group_id.procurement_ids: - for move in procurement.move_ids: - if move.picking_id: - picking_ids.add(move.picking_id.id) - res[sale.id] = list(picking_ids) + res[sale.id] = self.pool.get('stock.picking').search(cr, uid, [('group_id', '=', sale.procurement_group_id.id)], context=context) return res - def _prepare_order_line_procurement(self, cr, uid, order, line, group_id = False, context=None): + def _prepare_order_line_procurement(self, cr, uid, order, line, group_id=False, context=None): vals = super(sale_order, self)._prepare_order_line_procurement(cr, uid, order, line, group_id=group_id, context=context) location_id = order.partner_shipping_id.property_stock_customer.id vals['location_id'] = location_id + routes = line.route_id and [(4, line.route_id.id)] or [] + vals['route_ids'] = routes + vals['warehouse_id'] = order.warehouse_id and order.warehouse_id.id or False + vals['partner_dest_id'] = order.partner_shipping_id.id return vals _columns = { @@ -103,7 +101,6 @@ class sale_order(osv.osv): ], 'Create Invoice', required=True, readonly=True, states={'draft': [('readonly', False)], 'sent': [('readonly', False)]}, help="""On demand: A draft invoice can be created from the sales order when needed. \nOn delivery order: A draft invoice can be created from the delivery order when the products have been delivered. \nBefore delivery: A draft invoice is created from the sales order and must be paid before the products can be delivered."""), 'shipped': fields.function(_get_shipped, string='Delivered', type='boolean', store={ - 'stock.move': (_get_orders, ['state'], 10), 'procurement.order': (_get_orders_procurements, ['state'], 10) }), 'warehouse_id': fields.many2one('stock.warehouse', 'Warehouse', required=True), @@ -122,15 +119,13 @@ class sale_order(osv.osv): val['company_id'] = warehouse.company_id.id return {'value': val} - # FP Note: to change, take the picking related to the moves related to the - # procurements related to SO lines - def action_view_delivery(self, cr, uid, ids, context=None): ''' This function returns an action that display existing delivery orders of given sales order ids. It can either be a in a list or in a form view, if there is only one delivery order to show. ''' + mod_obj = self.pool.get('ir.model.data') act_obj = self.pool.get('ir.actions.act_window') @@ -142,6 +137,7 @@ class sale_order(osv.osv): pick_ids = [] for so in self.browse(cr, uid, ids, context=context): pick_ids += [picking.id for picking in so.picking_ids] + #choose the view_mode accordingly if len(pick_ids) > 1: result['domain'] = "[('id','in',[" + ','.join(map(str, pick_ids)) + "])]" @@ -151,10 +147,6 @@ class sale_order(osv.osv): result['res_id'] = pick_ids and pick_ids[0] or False return result - - # TODO: FP Note: I guess it's better to do: - # if order_policy<>picking: super() - # else: call invoice_on_picking_method() def action_invoice_create(self, cr, uid, ids, grouped=False, states=['confirmed', 'done', 'exception'], date_invoice = False, context=None): move_obj = self.pool.get("stock.move") res = super(sale_order,self).action_invoice_create(cr, uid, ids, grouped=grouped, states=states, date_invoice = date_invoice, context=context) @@ -176,12 +168,6 @@ class sale_order(osv.osv): raise osv.except_osv( _('Cannot cancel sales order!'), _('You must first cancel all delivery order(s) attached to this sales order.')) - # FP Note: not sure we need this - #if pick.state == 'cancel': - # for mov in pick.move_lines: - # proc_ids = proc_obj.search(cr, uid, [('move_id', '=', mov.id)]) - # if proc_ids: - # proc_obj.signal_button_check(cr, uid, proc_ids) stock_obj.signal_button_cancel(cr, uid, [p.id for p in sale.picking_ids]) return super(sale_order, self).action_cancel(cr, uid, ids, context=context) @@ -251,6 +237,7 @@ class sale_order_line(osv.osv): _columns = { 'product_packaging': fields.many2one('product.packaging', 'Packaging'), 'number_packages': fields.function(_number_packages, type='integer', string='Number Packages'), + 'route_id': fields.many2one('stock.location.route', 'Route', domain=[('sale_selectable', '=', True)]), } _defaults = { @@ -287,14 +274,11 @@ class sale_order_line(osv.osv): res = self.product_id_change(cr, uid, ids, pricelist=pricelist, product=product, qty=qty, uom=uom, partner_id=partner_id, packaging=packaging, flag=False, context=context) - warning_msgs = res.get('warning') and res['warning']['message'] + warning_msgs = res.get('warning') and res['warning'].get('message', '') or '' products = product_obj.browse(cr, uid, product, context=context) - if not products.packaging: + if not products.packaging_ids: packaging = result['product_packaging'] = False - elif not packaging and products.packaging and not flag: - packaging = products.packaging[0].id - result['product_packaging'] = packaging if packaging: default_uom = products.uom_id and products.uom_id.id @@ -321,12 +305,11 @@ class sale_order_line(osv.osv): return {'value': result, 'warning': warning} - def product_id_change(self, cr, uid, ids, pricelist, product, qty=0, + def product_id_change_with_wh(self, cr, uid, ids, pricelist, product, qty=0, uom=False, qty_uos=0, uos=False, name='', partner_id=False, - lang=False, update_tax=True, date_order=False, packaging=False, fiscal_position=False, flag=False, context=None): + lang=False, update_tax=True, date_order=False, packaging=False, fiscal_position=False, flag=False, warehouse_id=False, context=None): context = context or {} product_uom_obj = self.pool.get('product.uom') - partner_obj = self.pool.get('res.partner') product_obj = self.pool.get('product.product') warning = {} res = super(sale_order_line, self).product_id_change(cr, uid, ids, pricelist, product, qty=qty, @@ -341,27 +324,47 @@ class sale_order_line(osv.osv): product_obj = product_obj.browse(cr, uid, product, context=context) res['value']['delay'] = (product_obj.sale_delay or 0.0) - #check if product is available, and if not: raise an error - uom2 = False - if uom: - uom2 = product_uom_obj.browse(cr, uid, uom) - if product_obj.uom_id.category_id.id != uom2.category_id.id: - uom = False - if not uom2: - uom2 = product_obj.uom_id - # Calling product_packaging_change function after updating UoM res_packing = self.product_packaging_change(cr, uid, ids, pricelist, product, qty, uom, partner_id, packaging, context=context) res['value'].update(res_packing.get('value', {})) warning_msgs = res_packing.get('warning') and res_packing['warning']['message'] or '' - compare_qty = float_compare(product_obj.virtual_available * uom2.factor, qty * product_obj.uom_id.factor, precision_rounding=product_obj.uom_id.rounding) - if (product_obj.type=='product') and int(compare_qty) == -1: - #and (product_obj.procure_method=='make_to_stock'): --> need to find alternative for procure_method - warn_msg = _('You plan to sell %.2f %s but you only have %.2f %s available !\nThe real stock is %.2f %s. (without reservations)') % \ - (qty, uom2 and uom2.name or product_obj.uom_id.name, - max(0,product_obj.virtual_available), product_obj.uom_id.name, - max(0,product_obj.qty_available), product_obj.uom_id.name) - warning_msgs += _("Not enough stock ! : ") + warn_msg + "\n\n" + + #determine if the product is MTO or not (for a further check) + isMto = False + if warehouse_id: + warehouse = self.pool.get('stock.warehouse').browse(cr, uid, warehouse_id, context=context) + for product_route in product_obj.route_ids: + if warehouse.mto_pull_id and warehouse.mto_pull_id.route_id and warehouse.mto_pull_id.route_id.id == product_route.id: + isMto = True + break + else: + try: + mto_route_id = self.pool.get('ir.model.data').get_object(cr, uid, 'stock', 'route_warehouse0_mto').id + except: + # if route MTO not found in ir_model_data, we treat the product as in MTS + mto_route_id = False + if mto_route_id: + for product_route in product_obj.route_ids: + if product_route.id == mto_route_id: + isMto = True + break + + #check if product is available, and if not: raise a warning, but do this only for products that aren't processed in MTO + if not isMto: + uom2 = False + if uom: + uom2 = product_uom_obj.browse(cr, uid, uom, context=context) + if product_obj.uom_id.category_id.id != uom2.category_id.id: + uom = False + if not uom2: + uom2 = product_obj.uom_id + compare_qty = float_compare(product_obj.virtual_available, qty, precision_rounding=uom2.rounding) + if (product_obj.type=='product') and int(compare_qty) == -1: + warn_msg = _('You plan to sell %.2f %s but you only have %.2f %s available !\nThe real stock is %.2f %s. (without reservations)') % \ + (qty, uom2.name, + max(0,product_obj.virtual_available), uom2.name, + max(0,product_obj.qty_available), uom2.name) + warning_msgs += _("Not enough stock ! : ") + warn_msg + "\n\n" #update of warning messages if warning_msgs: @@ -375,8 +378,17 @@ class sale_order_line(osv.osv): class stock_move(osv.osv): _inherit = 'stock.move' + def action_cancel(self, cr, uid, ids, context=None): + sale_ids = [] + for move in self.browse(cr, uid, ids, context=context): + if move.procurement_id and move.procurement_id.sale_line_id: + sale_ids.append(move.procurement_id.sale_line_id.order_id.id) + if sale_ids: + self.pool.get('sale.order').signal_ship_except(cr, uid, sale_ids) + return super(stock_move, self).action_cancel(cr, uid, ids, context=context) + def _create_invoice_line_from_vals(self, cr, uid, move, invoice_line_vals, context=None): - invoice_line_id = self.pool.get('account.invoice.line').create(cr, uid, invoice_line_vals, context=context) + invoice_line_id = super(stock_move, self)._create_invoice_line_from_vals(cr, uid, move, invoice_line_vals, context=context) if move.procurement_id and move.procurement_id.sale_line_id: sale_line = move.procurement_id.sale_line_id self.pool.get('sale.order.line').write(cr, uid, [sale_line.id], { @@ -402,3 +414,43 @@ class stock_move(osv.osv): res['price_unit'] = sale_line.price_unit res['discount'] = sale_line.discount return res + + +class stock_location_route(osv.osv): + _inherit = "stock.location.route" + _columns = { + 'sale_selectable': fields.boolean("Selectable on Sales Order Line") + } + + +class stock_picking(osv.osv): + _inherit = "stock.picking" + + def _get_sale_id(self, cr, uid, ids, name, args, context=None): + sale_obj = self.pool.get("sale.order") + res = {} + for picking in self.browse(cr, uid, ids, context=context): + res[picking.id] = False + if picking.group_id: + sale_ids = sale_obj.search(cr, uid, [('procurement_group_id', '=', picking.group_id.id)], context=context) + if sale_ids: + res[picking.id] = sale_ids[0] + return res + + _columns = { + 'sale_id': fields.function(_get_sale_id, type="many2one", relation="sale.order", string="Sale Order"), + } + + def _create_invoice_from_picking(self, cr, uid, picking, vals, context=None): + sale_obj = self.pool.get('sale.order') + sale_line_obj = self.pool.get('sale.order.line') + invoice_line_obj = self.pool.get('account.invoice.line') + invoice_id = super(stock_picking, self)._create_invoice_from_picking(cr, uid, picking, vals, context=context) + if picking.group_id: + sale_ids = sale_obj.search(cr, uid, [('procurement_group_id', '=', picking.group_id.id)], context=context) + if sale_ids: + sale_line_ids = sale_line_obj.search(cr, uid, [('order_id', 'in', sale_ids), ('product_id.type', '=', 'service'), ('invoiced', '=', False)], context=context) + if sale_line_ids: + created_lines = sale_line_obj.invoice_line_create(cr, uid, sale_line_ids, context=context) + invoice_line_obj.write(cr, uid, created_lines, {'invoice_id': invoice_id}, context=context) + return invoice_id