Odoo Help


This community is for beginners and experts willing to share their Odoo knowledge. It's not a forum to discuss ideas, but a knowledge base of questions and their answers.


nan_product_pack (product_pack) module messing up Sale Order line sequence

on 4/6/15, 6:10 PM 716 views

I need some help to determine exactly what part of the below code is the responsible for messing up the sequence in Sale Order Lines as discussed in this thread: https://www.odoo.com/forum/help-1/question/lines-in-sale-orders-dont-respect-order-in-which-they-are-entered-69320

"The behavior I've noticed is the following, if you enter each product line continuously by clicking "Add an item" and add all items one after another without abandoning the "Create: Order Lines" form by clicking "Save & New", once you're done entering them all this way and click "(update)" or "Save" the Sale Order, the items respect the order in which they where entered. BUT, if for example you add an item by clicking "Add an item" then click "Save & Close" on the ""Create: Order Lines", once you start adding items again, any new items you add will appear after the first line entered in the Sale Order one you save or update it, instead of being places after the LAST line in the sequence they have been added. This behaviour is very annoying specially when creating a quotation with many items, it can get all messed up and you have to drag and drop each item to place them in the intended order. "

Any help will be very appreciated! and if anyone else is using this module and noticed the same bug, please report it here as well. Thank you

# -*- encoding: latin-1 -*-
import math
from osv import fields, osv
from openerp.tools.translate import _
class sale_order_line(osv.osv):
    _inherit = 'sale.order.line'
    _columns = {
        'pack_depth': fields.integer('Depth', required=True, help='Depth of the product if it is part of a pack.'),
        'pack_parent_line_id': fields.many2one('sale.order.line', 'Pack', help='The pack that contains this product.'),
        'pack_child_line_ids': fields.one2many('sale.order.line', 'pack_parent_line_id', 'Lines in pack', help=''),
    _defaults = {
        'pack_depth': lambda *a: 0,
class sale_order(osv.osv):
    _inherit = 'sale.order'
    def copy(self, cr, uid, id, default=None, context=None):
        # Raise an error if lines are product pack because we can not copy pack
        # in a right way
        for line in self.browse(cr, uid, id, context=context). order_line:
            if line.pack_parent_line_id:
                raise osv.except_osv(
                    _('Error!'), _('You can not copy a sale order wich hast product packs!'))
        return super(sale_order, self).copy(
            cr, uid, id, default, context=context)
    def create(self, cr, uid, vals, context=None):
        result = super(sale_order, self).create(cr, uid, vals, context)
        self.expand_packs(cr, uid, [result], context)
        return result
    def write(self, cr, uid, ids, vals, context=None):
        result = super(sale_order, self).write(cr, uid, ids, vals, context)
        if 'order_line' in vals:
            self.expand_packs(cr, uid, ids, context)
        return result
    def expand_packs(self, cr, uid, ids, context={}, depth=1):
        def get_real_price(res_dict, product_id, qty, uom, pricelist):
            item_obj = self.pool.get('product.pricelist.item')
            price_type_obj = self.pool.get('product.price.type')
            product_obj = self.pool.get('product.product')
            template_obj = self.pool.get('product.template')
            field_name = 'list_price'
            if res_dict.get('item_id', False) and res_dict['item_id'].get(pricelist, False):
                item = res_dict['item_id'].get(pricelist, False)
                item_base = item_obj.read(cr, uid, [item], ['base'])[0]['base']
                if item_base > 0:
                    field_name = price_type_obj.browse(
                        cr, uid, item_base).field
            product = product_obj.browse(cr, uid, product_id, context)
            product_tmpl_id = product.product_tmpl_id.id
            product_read = template_obj.read(
                cr, uid, product_tmpl_id, [field_name], context)
            factor = 1.0
            if uom and uom != product.uom_id.id:
                product_uom_obj = self.pool.get('product.uom')
                uom_data = product_uom_obj.browse(cr, uid,  product.uom_id.id)
                factor = uom_data.factor
            return product_read[field_name] * factor
        if depth == 10:
        updated_orders = []
        for order in self.browse(cr, uid, ids, context):
            fiscal_position = order.fiscal_position and self.pool.get(
                'account.fiscal.position').browse(cr, uid, order.fiscal_position, context) or False
            # The reorder variable is used to ensure lines of the same pack go right after their
            # parent.
            # What the algorithm does is check if the previous item had children. As children items
            # must go right after the parent if the line we're evaluating doesn't have a parent it
            # means it's a new item (and probably has the default 10 sequence number - unless the
            # appropiate c2c_sale_sequence module is installed). In this case we mark the item for
            # reordering and evaluate the next one. Note that as the item is not evaluated and it might
            # have to be expanded it's put on the queue for another iteration (it's simple and works well).
            # Once the next item has been evaluated the sequence of the item marked for reordering is updated
            # with the next value.
            sequence = -1
            reorder = []
            last_had_children = False
            lines_to_unlink = []
            for line in order.order_line:
                if last_had_children and not line.pack_parent_line_id:
                    if line.product_id.pack_line_ids and not order.id in updated_orders:
                sequence += 1
                if sequence > line.sequence:
                    self.pool.get('sale.order.line').write(cr, uid, [line.id], {
                        'sequence': sequence,
                    }, context)
                    sequence = line.sequence
                if line.state != 'draft':
                if not line.product_id:
                # If pack was already expanded (in another create/write operation or in
                # a previous iteration) don't do it again.
                if line.pack_child_line_ids:
                    # Cambiamos esto para que se borren las lienas viejas y se
                    # creen nuevas
                    unlink_line_ids = [x.id for x in line.pack_child_line_ids]
                    # last_had_children = True
                    # continue
                last_had_children = False
                pack_price = 0.0
                # pack_discount = 0.0
                for subline in line.product_id.pack_line_ids:
                    sequence += 1
                    subproduct = subline.product_id
                    quantity = subline.quantity * line.product_uom_qty
                    if line.product_id.pack_fixed_price:
                        price = 0.0
                        discount = 0.0
                        pricelist = order.pricelist_id.id
                        list_price = self.pool.get('product.pricelist').price_get(cr, uid, [pricelist],
                                                                                  subproduct.id, quantity, order.partner_id.id, {
                            'uom': subproduct.uom_id.id,
                            'date': order.date_order,
                        price = list_price[pricelist]
                        # Added functionality for compatibility with
                        # product_visible_discount module
                        pricelist_obj = self.pool.get('product.pricelist')
                        pricelists = pricelist_obj.read(
                            cr, uid, [pricelist], ['visible_discount'])
                        new_list_price = get_real_price(
                            list_price, subproduct.id, quantity, subproduct.uom_id.id, pricelist)
                        discount = line.discount
                        if pricelists[0].has_key("visible_discount"):
                            if(len(pricelists) > 0 and pricelists[0]['visible_discount'] and list_price[pricelist] != 0):
                                discount += (new_list_price -
                                             price) / new_list_price * 100
                                price = new_list_price
                    # Obtain product name in partner's language
                    ctx = {'lang': order.partner_id.lang}
                    # subproduct_name = self.pool.get('product.product').browse(cr, uid, subproduct.id, ctx).name
                    tax_ids = self.pool.get('account.fiscal.position').map_tax(
                        cr, uid, fiscal_position, subproduct.taxes_id)
                    tax_id = [(6, 0, tax_ids)]
                    if subproduct.uos_id:
                        uos_id = subproduct.uos_id.id
                        uos_qty = quantity * subproduct.uos_coeff
                        uos_id = False
                        uos_qty = quantity
                    # TODO no lo termine de implementar a esto porque lo complica, tal vez scanterra lo quiera
                    # If totalice price we sum the prices and set prices to 0
                    if line.product_id.totalice_price and not line.product_id.pack_fixed_price:
                        pack_price += (price * uos_qty)
                        # pack_discount += price
                        price = 0.0
                        discount = 0.0
                        tax_id = False
                    subproduct_name = self.pool.get('product.product').name_get(
                        cr, uid, [subproduct.id], context=ctx)[0][1]
                    if subproduct.description_sale:
                        subproduct_name += '\n' + subproduct.description_sale
                    vals = {
                        'order_id': order.id,
                        'name': '%s%s' % ('> ' * (line.pack_depth + 1), subproduct_name),
                        # 'name': '%s%s' % ('> '* (line.pack_depth+1), subproduct_name),
                        'sequence': sequence,
                        'delay': subproduct.sale_delay or 0.0,
                        'product_id': subproduct.id,
                        'procurement_id': line.procurement_id and line.procurement_id.id or False,
                        'price_unit': price,
                        'tax_id': tax_id,
                        'type': subproduct.procure_method,
                        'property_ids': [(6, 0, [])],
                        'address_allotment_id': False,
                        'product_uom_qty': quantity,
                        'product_uom': subproduct.uom_id.id,
                        'product_uos_qty': uos_qty,
                        'product_uos': uos_id,
                        'product_packaging': False,
                        'move_ids': [(6, 0, [])],
                        'discount': discount,
                        'number_packages': False,
                        'notes': False,
                        'th_weight': False,
                        'state': 'draft',
                        'pack_parent_line_id': line.id,
                        'pack_depth': line.pack_depth + 1,
                    # It's a control for the case that the nan_external_prices
                    # was installed with the product pack
                    if 'prices_used' in line:
                        vals['prices_used'] = line.prices_used
                        cr, uid, vals, context)
                    if not order.id in updated_orders:
                if line.product_id.totalice_price and not line.product_id.pack_fixed_price:
                    if quantity and int(quantity) != 0:
                        pack_price = pack_price / quantity
                    self.pool.get('sale.order.line').write(cr, uid, [line.id],
                                                           {'price_unit': pack_price}, context=context)
                for id in reorder:
                    sequence += 1
                        cr, uid, [id], {'sequence': sequence}, context)
        # Borramos las lienas que se actualizan
            cr, uid, lines_to_unlink, context=context)
        # Sacamos esto porque es medio horrible
        # if updated_orders:
        # Try to expand again all those orders that had a pack in this iteration.
        # This way we support packs inside other packs.
        #     self.expand_packs(cr, uid, ids, context, depth+1)

Your Answer

Please try to give a substantial answer. If you wanted to comment on the question or answer, just use the commenting tool. Please remember that you can always revise your answers - no need to answer the same question twice. Also, please don't forget to vote - it really helps to select the best questions and answers!

About This Community

This community is for professionals and enthusiasts of our products and services. Read Guidelines

Question tools

1 follower(s)


Asked: 4/6/15, 6:10 PM
Seen: 716 times
Last updated: 5/10/15, 6:15 PM