Комплектация Odoo Manufacturing — показать партии, количество которых больше нуля 0

#odoo #odoo-10 #odoo-11 #odoo-12

#odoo #odoo-10 #odoo-11 #odoo-12

Вопрос:

Я добавил поле количества партии в Производственные операции комплектации. Он идеально отражает количество партии в зависимости от местоположения. Теперь проблема в том, что я не хочу показывать партии, в которых нет товара, т.е. 0 количество. Как мне этого добиться?

Вот скриншот, на котором я хочу внести изменения.введите описание изображения здесь

Вот код модели (stock_move_line.py ):

 class StockMoveLine(models.Model):
    _name = "stock.move.line"
    _description = "Product Moves (Stock Move Line)"
    _rec_name = "product_id"
    _order = "result_package_id desc, id"
    lot_id = fields.Many2one('stock.production.lot', 'Lot/Serial Number123')
    x_lot_qty = fields.Float(compute="fetch_lot_wrt_location", 
                       name="Lot Quantity", 
                       store=True,
                       readonly=True, 
                       digits=(12,3))
    
    @api.onchange('lot_id','location_id')
    def fetch_lot_wrt_location(self):
        self.x_lot_qty = 0.0
        for row in self:
            quants = self.env['stock.quant'].search([('lot_id.id','=',row.lot_id.id)])
            for quant in quants:
                if row.location_id.display_name == quant.location_id.display_name:
                    row.x_lot_qty = quant.quantity
    
 

Вот эти виды (stock_move_views.xml ):

 <field name="lot_id" 
            attrs="{'readonly': ['amp;amp;', ('package_level_id', '!=', False), ('parent.picking_type_entire_packs', '=', True)]}" 
            invisible="not context.get('show_lots_m2o')" 
            domain="[('product_id', '=', parent.product_id)]" 
            groups="stock.group_production_lot" 
            context="{'default_product_id': parent.product_id, 'active_picking_id': picking_id}"/>
<field name="x_lot_qty" 
            attrs="{'readonly': ['amp;amp;', ('package_level_id', '!=', False), ('parent.picking_type_entire_packs', '=', True)]}" 
            invisible="not context.get('show_lots_m2o')" 
            domain="[('product_id', '=', parent.product_id)]" 
            groups="stock.group_production_lot" 
            context="{'default_product_id': parent.product_id, 'active_picking_id': picking_id}"/>
 

Я применил фильтр по атрибуту домена, но он не работает. Как я могу ограничить количество партий количеством 0?

ОБНОВЛЕНИЕ: я попробовал следующее решение: я создал новое поле динамического выбора и попытался заполнить лоты, которые не являются пустыми в зависимости от местоположения, ниже приведен код:

 @api.onchange('location_id')
def _fetch_non_empty_lots(self):
    lots = []
    for row in self:
        quants = self.env['stock.quant'].search([('location_id.id','=',row.location_id.id), ('product_id.id', '=', row.product_id.id), ('quantity', '>', 0)])
        for quant in quants:
            lots.append(quant)
            
    return lots

x_lot_id = fields.Selection(selection="_fetch_non_empty_lots", name="Non Empty Lots")
 

Но теперь, когда я выбираю новое местоположение, я получаю следующую ошибку:

 Odoo Server Error
Traceback (most recent call last):
  File "C:virtual_odoo12Scriptsodoohttp.py", line 656, in _handle_exception
    return super(JsonRequest, self)._handle_exception(exception)
  File "C:virtual_odoo12Scriptsodoohttp.py", line 314, in _handle_exception
    raise pycompat.reraise(type(exception), exception, sys.exc_info()[2])
  File "C:virtual_odoo12Scriptsodootoolspycompat.py", line 87, in reraise
    raise value
  File "C:virtual_odoo12Scriptsodoohttp.py", line 698, in dispatch
    result = self._call_function(**self.params)
  File "C:virtual_odoo12Scriptsodoohttp.py", line 346, in _call_function
    return checked_call(self.db, *args, **kwargs)
  File "C:virtual_odoo12Scriptsodooservicemodel.py", line 97, in wrapper
    return f(dbname, *args, **kwargs)
  File "C:virtual_odoo12Scriptsodoohttp.py", line 339, in checked_call
    result = self.endpoint(*a, **kw)
  File "C:virtual_odoo12Scriptsodoohttp.py", line 941, in __call__
    return self.method(*args, **kw)
  File "C:virtual_odoo12Scriptsodoohttp.py", line 519, in response_wrap
    response = f(*args, **kw)
  File "c:virtual_odoo12scriptsaddonswebcontrollersmain.py", line 962, in call_kw
    return self._call_kw(model, method, args, kwargs)
  File "c:virtual_odoo12scriptsaddonswebcontrollersmain.py", line 954, in _call_kw
    return call_kw(request.env[model], method, args, kwargs)
  File "C:virtual_odoo12Scriptsodooapi.py", line 759, in call_kw
    return _call_kw_multi(method, model, args, kwargs)
  File "C:virtual_odoo12Scriptsodooapi.py", line 746, in _call_kw_multi
    result = method(recs, *args, **kwargs)
  File "C:virtual_odoo12Scriptsodoomodels.py", line 5524, in onchange
    record._onchange_eval(name, field_onchange[name], result)
  File "C:virtual_odoo12Scriptsodoomodels.py", line 5369, in _onchange_eval
    process(method_res)
  File "C:virtual_odoo12Scriptsodoomodels.py", line 5355, in process
    if res.get('value'):
AttributeError: 'list' object has no attribute 'get'
 

ОБНОВЛЕНИЕ 2:
Вместо того чтобы изменять существующий выпадающий список, я создал динамический выпадающий список. При выборе местоположения выпадающий список заполняется номерами партий, количество которых больше 0. Однако я столкнулся с основной проблемой / ошибкой:

 AttributeError: 'list' object has no attribute 'get'
 

Вот поле выбора модели и код для его заполнения:

 @api.onchange('location_id')
def _fetch_non_empty_lots(self):
        quants = self.env['stock.quant'].search([('location_id.id','=',self.location_id.id), 
                        ('product_id.id', '=', self.product_id.id), 
                        ('quantity', '>', 0)])
        return {str(quant.lot_id.id) :str.title(quant.lot_id.display_name) for quant in quants} 

## declare field    

x_lot_id = fields.Selection(selection="_fetch_non_empty_lots", name="Non Empty Lots")
 

Когда список пуст, ошибки нет. Когда есть непустая партия, тогда я получаю ошибку. Мне нужна некоторая помощь.

Комментарии:

1. Вы пытались определить свое поле (сохраненное, чтобы сделать его доступным для поиска) в модели партии?

Ответ №1:

Odoo определяет поле количества продукта в модели партии для производства на складе, вы можете использовать это поле в домене для фильтрации партий, и вам нужно установить store True значение, чтобы сделать его доступным для поиска. Если вам нужно определить новое поле, используйте ту же логику и определите его в модели партии для использования в lot_id домене поля.

Вы получили эту ошибку, потому что Odoo ожидает словарь, а вместо него получил список.

Метод может возвращать словарь для изменения доменов полей и выдавать предупреждающее сообщение, как в старом API.

В ваших двух последних примерах вы устанавливаете атрибут выбора в строку, но selection атрибут указывает возможные значения для этого поля. Он задается либо как список пар ( value , string ), либо как метод модели, либо как имя метода. Вы можете найти пример в модуле website_customer.

Редактировать: (возвращает домен из onchange метода)

Метод может возвращать словарь для изменения доменов полей и выдавать предупреждающее сообщение, как в старом API::

 return {
    'domain': {'other_id': [('partner_id', '=', partner_id)]},
    'warning': {'title': "Warning", 'message': "What is this?"},
}
 

Комментарии:

1. Не могли бы вы пояснить, как я использую код, потому что я следовал той же логике, что и link, т.е. [(quant.lot_id.display_name, str.title (quant.lot_id.display_name)) для quant в quants], но получаю ту же ошибку «AttributeError: объект ‘list’ не имеет атрибута ‘get'»

2. Вы получили эту ошибку, потому что вы вернули список, а Odoo ожидает словарь. Вам необходимо удалить onchange декоратор из метода, который вычисляет значение выборки.

3. Когда я удалил декоратор onchange, каждый раз, когда метод выбора возвращает пустое значение

4. однако я изменил оператор возврата на {str(quant.lot_id.id ) :str.title(quant.lot_id.display_name) для quant в quants}, но все равно получаю пустые данные взамен. Я отлаживал с помощью pdb, функция вызывается 4 раза, когда я меняю местоположение. При 3-м вызове функции я вижу данные в self (stock.move.line), там моя функция работает, но при вызове функции 4-го раза она не возвращает никаких лотов, поскольку self ничего не содержит

5. Я получаю акции.двигайся. строка с соответствующими данными в строке с 3-м вызовом функции. Как мне получить данные, отличные от self object than ?

Ответ №2:

Я думаю, что так и должно быть?

 lot_id = fields.Many2one('stock.production.lot', 'Lot/Serial Number123' 
  domain="[('product_id', '=', parent.product_id),('product_qty', '>', 0)]" )
 

Комментарии:

1. Это не работает, поле x_lot_qty .. Он вычисляет количество продукта в номере партии, также учитывая местоположение … Я сделал что-то вроде этого lot_id = поля. Many2one(‘stock.production. лот’, ‘Лот / серийный номер1234’, домен=»[(‘product_id’, ‘=’, parent.product_id),(‘x_lot_qty’, ‘>’, 0)]»)

2. x_lot_qty находится на stock.move.line , но вы ищете из stock.production.lot

3. да, потому что lot_id недоступен в stock_move_line .. и моя цель — показать только те партии, количество товара в которых больше 0 по отношению к местоположению

Ответ №3:

Насколько я понимаю, вы хотите показать только те lots товары, у которых более 0 товаров, в выпадающем списке ваших лотов

Вы можете попробовать это в своем XML

 <field name="lot_id" 
            attrs="{'readonly': ['amp;amp;', ('package_level_id', '!=', False), ('parent.picking_type_entire_packs', '=', True)]}" 
            invisible="not context.get('show_lots_m2o')" 
            domain="[('product_qty', '>', 0)]"     //View this
            groups="stock.group_production_lot" 
            context="{'default_product_id': parent.product_id, 'active_picking_id': picking_id}"/>
 

Комментарии:

1. Это не работает … каждый раз должен быть один фильтр (‘product_id’, ‘=’, parent.product_id) .. в противном случае он будет показывать все партии продуктов, тогда product_qty не работает, поскольку он содержит общее количество продуктов не по номерам партий. поэтому я создал пользовательское вычисляемое поле с именем x_lot_qty, чтобы оно видело, какое местоположение находитсявыбрано в столбце «от», и какой продукт выбран, затем отображается количество. Поэтому нам также нужно учитывать местоположение

2. Я попробовал domain=»[(‘product_id’, ‘=’, parent.product_id),(‘xlot_qty’, ‘>’, 0)]» но получаю эту ошибку «Ошибка значения: недопустимое поле ‘xlot_qty’ в листе «<osv.ExtendedLeaf: (‘xlot_qty’, ‘>’, 0) на stock_production_lot (ctx: )>»»

3. Ты пытаешься ('xlot_qty', '>', 0) , но я говорю ('product_qty', '>', 0) , попробуй это, мой дорогой друг 🙂

4. Привет @AdamStrauss, это не работает, потому что поле ‘product_qty’ в stock.production. лот — это вычисляемое поле и поле, не относящееся к хранилищу.

5. О, понял, тогда каково решение, какие-либо рекомендации 🙂

Ответ №4:

добавьте это в свой запас.двигайтесь.линейная модель:

 @api.onchange('parent.product_id')
def _onchange_product_id(self):
    quants = self.env['stock.quant'].search([('product_id', '=', self.parent.product_id.id), ('quantity', '>', 0)])
    return {
        'domain': {
            'lot_id': [
                ('id', 'in', quants.lot_id.ids)
            ],
        },
    }
 

Комментарии:

1. Он не работает.. Я изменил параметр декоратора на @api.onchange(‘lot_id’, ‘location_id’) … и добавлен фильтр местоположения на складе. quant .. но не повезло

2. Нет, не меняйте параметры декоратора onchange на ‘lot_id’ и ‘location_id’. Можете ли вы убедиться, что вызывается onchange?

3. Да, когда я меняю декоратор на location_id, он вызывается. Пожалуйста, смотрите скриншот, продукт уже выбран. Однако при изменении местоположения следует фильтровать только партии, количество которых превышает 0

4. location_id — это поле ‘From’?, если да, то измените на @api.onchange(‘location_id’), если функция вызывается, тогда вам нужно проверить результат ‘quants.lot_id.ids’, если ‘quants.lot_id.ids’ неверно, тогда проблемаэто поисковый запрос.

5. Я добавил дополнительный фильтр в ваш запрос… (‘location_id.display_name’,’=’, self.location_id.display_name) … это дает правильный результат.. но не заполняет выпадающий список