mirror of
https://github.com/OCA/stock-logistics-warehouse.git
synced 2025-01-21 14:27:28 +02:00
[MIG] stock_available_unreserved: migrate Odoo 11
This commit is contained in:
@@ -23,7 +23,7 @@ Usage
|
||||
|
||||
.. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas
|
||||
:alt: Try me on Runbot
|
||||
:target: https://runbot.odoo-community.org/runbot/153/10.0
|
||||
:target: https://runbot.odoo-community.org/runbot/153/11.0
|
||||
|
||||
|
||||
Bug Tracker
|
||||
@@ -47,6 +47,7 @@ Contributors
|
||||
|
||||
* Jordi Ballester Alomar <jordi.ballester@eficent.com>
|
||||
* Stefan Rijnhart <stefan@opener.amsterdam>
|
||||
* Mykhailo Panarin <m.panarin@mobilunity.com>
|
||||
|
||||
|
||||
Maintainer
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2016 ACSONE SA/NV (<http://acsone.eu>)
|
||||
# Copyright 2016 Eficent Business and IT Consulting Services S.L.
|
||||
# (http://www.eficent.com)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2018 Camptocamp SA
|
||||
# Copyright 2016 ACSONE SA/NV (<http://acsone.eu>)
|
||||
# Copyright 2016 Eficent Business and IT Consulting Services S.L.
|
||||
# (http://www.eficent.com)
|
||||
@@ -6,16 +6,17 @@
|
||||
{
|
||||
"name": "Stock Available Unreserved",
|
||||
"summary": "Quantity of stock available for immediate use",
|
||||
"version": "10.0.1.0.0",
|
||||
"version": "11.0.1.0.0",
|
||||
"author": "Eficent Business and IT Consulting Services S.L,"
|
||||
"Odoo Community Association (OCA)",
|
||||
"website": "https://www.odoo-community.org",
|
||||
"website": "https://github.com/OCA/stock-logistics-warehouse",
|
||||
"category": "Warehouse Management",
|
||||
"depends": ["stock"],
|
||||
"data": ["views/stock_quant_view.xml",
|
||||
"views/product_view.xml"
|
||||
],
|
||||
"depends": [
|
||||
"stock",
|
||||
],
|
||||
"data": [
|
||||
"views/stock_quant_view.xml",
|
||||
"views/product_view.xml",
|
||||
],
|
||||
"license": "AGPL-3",
|
||||
'installable': True,
|
||||
'application': False,
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2016 ACSONE SA/NV (<http://acsone.eu>)
|
||||
# Copyright 2016 Eficent Business and IT Consulting Services S.L.
|
||||
# (http://www.eficent.com)
|
||||
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
|
||||
|
||||
from . import product
|
||||
from . import quant
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2018 Camptocamp SA
|
||||
# Copyright 2016 ACSONE SA/NV (<http://acsone.eu>)
|
||||
# Copyright 2016 Eficent Business and IT Consulting Services S.L.
|
||||
# (http://www.eficent.com)
|
||||
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
|
||||
|
||||
from odoo import api, fields, models, _
|
||||
from odoo.tools.float_utils import float_round
|
||||
from odoo import api, fields, models
|
||||
from odoo.addons import decimal_precision as dp
|
||||
|
||||
UNIT = dp.get_precision('Product Unit of Measure')
|
||||
@@ -15,33 +14,36 @@ class ProductTemplate(models.Model):
|
||||
_inherit = "product.template"
|
||||
|
||||
qty_available_not_res = fields.Float(
|
||||
string='Quantity On Hand Unreserved', digits=UNIT,
|
||||
compute='_compute_product_available_not_res')
|
||||
|
||||
qty_available_stock_text = fields.Char(
|
||||
string='Quantity On Hand Unreserved',
|
||||
digits=UNIT,
|
||||
compute='_compute_product_available_not_res',
|
||||
string='Unreserved stock quantity')
|
||||
)
|
||||
|
||||
@api.multi
|
||||
@api.depends('product_variant_ids.qty_available_not_res')
|
||||
def _compute_product_available_not_res(self):
|
||||
no_new = self.filtered(
|
||||
lambda x: not isinstance(x.id, models.NewId))
|
||||
for tmpl in no_new:
|
||||
tmpl.qty_available_not_res = sum(tmpl.mapped(
|
||||
'product_variant_ids.qty_available_not_res'))
|
||||
tmpl.qty_available_stock_text = "/".join(tmpl.mapped(
|
||||
'product_variant_ids.qty_available_stock_text'))
|
||||
for tmpl in self:
|
||||
if isinstance(tmpl.id, models.NewId):
|
||||
continue
|
||||
tmpl.qty_available_not_res = sum(
|
||||
tmpl.mapped('product_variant_ids.qty_available_not_res')
|
||||
)
|
||||
|
||||
@api.multi
|
||||
def action_open_quants_unreserved(self):
|
||||
products = self.mapped('product_variant_ids').ids
|
||||
products_ids = self.mapped('product_variant_ids').ids
|
||||
quants = self.env['stock.quant'].search([
|
||||
('product_id', 'in', products_ids),
|
||||
])
|
||||
quant_ids = quants.filtered(
|
||||
lambda x: x.product_id.qty_available_not_res > 0
|
||||
).ids
|
||||
result = self.env.ref('stock.product_open_quants').read()[0]
|
||||
result['domain'] = "[('product_id','in',[" + ','.join(
|
||||
map(str, products)) + "]), ('reservation_id', '=', False)]"
|
||||
result[
|
||||
'context'] = "{'search_default_locationgroup': 1, " \
|
||||
"'search_default_internal_loc': 1}"
|
||||
result['domain'] = [('id', 'in', quant_ids)]
|
||||
result['context'] = {
|
||||
'search_default_locationgroup': 1,
|
||||
'search_default_internal_loc': 1,
|
||||
}
|
||||
return result
|
||||
|
||||
|
||||
@@ -49,68 +51,55 @@ class ProductProduct(models.Model):
|
||||
_inherit = 'product.product'
|
||||
|
||||
qty_available_not_res = fields.Float(
|
||||
string='Qty Available Not Reserved', digits=UNIT,
|
||||
compute='_compute_qty_available_not_res')
|
||||
|
||||
qty_available_stock_text = fields.Char(
|
||||
compute='_compute_qty_available_not_res', string='Available per stock')
|
||||
|
||||
@api.multi
|
||||
def _compute_qty_available_not_res(self):
|
||||
res = self._compute_product_available_not_res_dict()
|
||||
for prod in self:
|
||||
qty = res[prod.id]['qty_available_not_res']
|
||||
text = res[prod.id]['qty_available_stock_text']
|
||||
prod.qty_available_not_res = qty
|
||||
prod.qty_available_stock_text = text
|
||||
return res
|
||||
|
||||
@api.model
|
||||
def _prepare_domain_available_not_res(self, products):
|
||||
domain_products = [('product_id', 'in', products.mapped('id'))]
|
||||
domain_quant = []
|
||||
domain_quant_loc = products._get_domain_locations()[0]
|
||||
|
||||
domain_quant += domain_products
|
||||
|
||||
domain_quant.append(('reservation_id', '=', False))
|
||||
|
||||
domain_quant += domain_quant_loc
|
||||
|
||||
return domain_quant
|
||||
string='Qty Available Not Reserved',
|
||||
digits=UNIT,
|
||||
compute='_compute_qty_available_not_reserved',
|
||||
)
|
||||
|
||||
@api.multi
|
||||
def _product_available_not_res_hook(self, quants):
|
||||
"""Hook used to introduce possible variations"""
|
||||
return False
|
||||
|
||||
@api.multi
|
||||
def _prepare_domain_available_not_reserved(self):
|
||||
domain_quant = [
|
||||
('product_id', 'in', self.ids),
|
||||
('contains_unreserved', '=', True),
|
||||
]
|
||||
domain_quant_locations = self._get_domain_locations()[0]
|
||||
domain_quant.extend(domain_quant_locations)
|
||||
return domain_quant
|
||||
|
||||
@api.multi
|
||||
def _compute_product_available_not_res_dict(self):
|
||||
|
||||
res = {}
|
||||
|
||||
domain_quant = self._prepare_domain_available_not_res(self)
|
||||
|
||||
quants = self.env['stock.quant'].with_context(lang=False).read_group(
|
||||
domain_quant = self._prepare_domain_available_not_reserved()
|
||||
quants = self.env['stock.quant'].with_context(lang=False).search(
|
||||
domain_quant,
|
||||
['product_id', 'location_id', 'qty'],
|
||||
['product_id', 'location_id'],
|
||||
lazy=False)
|
||||
values_prod = {}
|
||||
for quant in quants:
|
||||
# create a dictionary with the total value per products
|
||||
values_prod.setdefault(quant['product_id'][0], 0)
|
||||
values_prod[quant['product_id'][0]] += quant['qty']
|
||||
for product in self.with_context(prefetch_fields=False, lang=''):
|
||||
res[product.id] = {}
|
||||
# get total qty for the product
|
||||
qty = float_round(values_prod.get(product.id, 0.0),
|
||||
precision_rounding=product.uom_id.rounding)
|
||||
qty_available_not_res = qty
|
||||
res[product.id].update({'qty_available_not_res':
|
||||
qty_available_not_res})
|
||||
text = str(qty_available_not_res) + _(" On Hand")
|
||||
res[product.id].update({'qty_available_stock_text': text})
|
||||
)
|
||||
# TODO: this should probably be refactored performance-wise
|
||||
for prod in self:
|
||||
vals = {}
|
||||
prod_quant = quants.filtered(lambda x: x.product_id == prod)
|
||||
quantity = sum(prod_quant.mapped(
|
||||
lambda x: x._get_available_quantity(
|
||||
x.product_id,
|
||||
x.location_id
|
||||
)
|
||||
))
|
||||
vals['qty_available_not_res'] = quantity
|
||||
res[prod.id] = vals
|
||||
self._product_available_not_res_hook(quants)
|
||||
|
||||
return res
|
||||
|
||||
@api.multi
|
||||
def _compute_qty_available_not_reserved(self):
|
||||
res = self._compute_product_available_not_res_dict()
|
||||
for prod in self:
|
||||
qty = res[prod.id]['qty_available_not_res']
|
||||
prod.qty_available_not_res = qty
|
||||
return res
|
||||
|
||||
23
stock_available_unreserved/models/quant.py
Normal file
23
stock_available_unreserved/models/quant.py
Normal file
@@ -0,0 +1,23 @@
|
||||
# Copyright 2018 Camptocamp SA
|
||||
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
|
||||
|
||||
from odoo import api, fields, models
|
||||
|
||||
|
||||
class StockQuant(models.Model):
|
||||
_inherit = "stock.quant"
|
||||
|
||||
contains_unreserved = fields.Boolean(
|
||||
string="Contains unreserved products",
|
||||
compute="_compute_contains_unreserved",
|
||||
store=True,
|
||||
)
|
||||
|
||||
@api.depends('product_id', 'location_id', 'quantity', 'reserved_quantity')
|
||||
def _compute_contains_unreserved(self):
|
||||
for record in self:
|
||||
available = record._get_available_quantity(
|
||||
record.product_id,
|
||||
record.location_id,
|
||||
)
|
||||
record.contains_unreserved = True if available > 0 else False
|
||||
@@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2016 ACSONE SA/NV (<http://acsone.eu>)
|
||||
# Copyright 2016 Eficent Business and IT Consulting Services S.L.
|
||||
# (http://www.eficent.com)
|
||||
|
||||
@@ -1,138 +1,142 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2018 Camptocamp SA
|
||||
# Copyright 2016 ACSONE SA/NV (<http://acsone.eu>)
|
||||
# Copyright 2016 Eficent Business and IT Consulting Services S.L.
|
||||
# (http://www.eficent.com)
|
||||
# Copyright 2016 Therp BV <http://therp.nl>
|
||||
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
|
||||
|
||||
from odoo.tests.common import TransactionCase
|
||||
from odoo.tests.common import SavepointCase
|
||||
|
||||
|
||||
class TestStockLogisticsWarehouse(TransactionCase):
|
||||
|
||||
def test01_stock_levels(self):
|
||||
"""checking that qty_available_not_res actually reflects \
|
||||
the variations in stock, both on product and template"""
|
||||
pickingObj = self.env['stock.picking']
|
||||
productObj = self.env['product.product']
|
||||
templateObj = self.env['product.template']
|
||||
supplier_location = self.env.ref('stock.stock_location_suppliers')
|
||||
stock_location = self.env.ref('stock.stock_location_stock')
|
||||
customer_location = self.env.ref('stock.stock_location_customers')
|
||||
uom_unit = self.env.ref('product.product_uom_unit')
|
||||
class TestStockLogisticsWarehouse(SavepointCase):
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super().setUpClass()
|
||||
cls.pickingObj = cls.env['stock.picking']
|
||||
cls.productObj = cls.env['product.product']
|
||||
cls.templateObj = cls.env['product.template']
|
||||
cls.supplier_location = cls.env.ref('stock.stock_location_suppliers')
|
||||
cls.stock_location = cls.env.ref('stock.stock_location_stock')
|
||||
cls.customer_location = cls.env.ref('stock.stock_location_customers')
|
||||
cls.uom_unit = cls.env.ref('product.product_uom_unit')
|
||||
|
||||
# Create product template
|
||||
templateAB = templateObj.create(
|
||||
{'name': 'templAB',
|
||||
'uom_id': uom_unit.id,
|
||||
})
|
||||
cls.templateAB = cls.templateObj.create({
|
||||
'name': 'templAB',
|
||||
'uom_id': cls.uom_unit.id,
|
||||
})
|
||||
|
||||
# Create product A and B
|
||||
productA = productObj.create(
|
||||
{'name': 'product A',
|
||||
'standard_price': 1,
|
||||
'type': 'product',
|
||||
'uom_id': uom_unit.id,
|
||||
'default_code': 'A',
|
||||
'product_tmpl_id': templateAB.id,
|
||||
})
|
||||
cls.productA = cls.productObj.create({
|
||||
'name': 'product A',
|
||||
'standard_price': 1,
|
||||
'type': 'product',
|
||||
'uom_id': cls.uom_unit.id,
|
||||
'default_code': 'A',
|
||||
'product_tmpl_id': cls.templateAB.id,
|
||||
})
|
||||
|
||||
productB = productObj.create(
|
||||
{'name': 'product B',
|
||||
'standard_price': 1,
|
||||
'type': 'product',
|
||||
'uom_id': uom_unit.id,
|
||||
'default_code': 'B',
|
||||
'product_tmpl_id': templateAB.id,
|
||||
})
|
||||
cls.productB = cls.productObj.create({
|
||||
'name': 'product B',
|
||||
'standard_price': 1,
|
||||
'type': 'product',
|
||||
'uom_id': cls.uom_unit.id,
|
||||
'default_code': 'B',
|
||||
'product_tmpl_id': cls.templateAB.id,
|
||||
})
|
||||
|
||||
# Create a picking move from INCOMING to STOCK
|
||||
pickingInA = pickingObj.create({
|
||||
'picking_type_id': self.ref('stock.picking_type_in'),
|
||||
'location_id': supplier_location.id,
|
||||
'location_dest_id': stock_location.id,
|
||||
cls.pickingInA = cls.pickingObj.create({
|
||||
'picking_type_id': cls.env.ref('stock.picking_type_in').id,
|
||||
'location_id': cls.supplier_location.id,
|
||||
'location_dest_id': cls.stock_location.id,
|
||||
'move_lines': [
|
||||
(0, 0, {
|
||||
'name': 'Test move',
|
||||
'product_id': productA.id,
|
||||
'product_uom': productA.uom_id.id,
|
||||
'product_id': cls.productA.id,
|
||||
'product_uom': cls.productA.uom_id.id,
|
||||
'product_uom_qty': 2,
|
||||
'location_id': supplier_location.id,
|
||||
'location_dest_id': stock_location.id,
|
||||
})]
|
||||
'quantity_done': 2,
|
||||
'location_id': cls.supplier_location.id,
|
||||
'location_dest_id': cls.stock_location.id,
|
||||
})
|
||||
]
|
||||
})
|
||||
|
||||
pickingInB = pickingObj.create({
|
||||
'picking_type_id': self.ref('stock.picking_type_in'),
|
||||
'location_id': supplier_location.id,
|
||||
'location_dest_id': stock_location.id,
|
||||
cls.pickingInB = cls.pickingObj.create({
|
||||
'picking_type_id': cls.env.ref('stock.picking_type_in').id,
|
||||
'location_id': cls.supplier_location.id,
|
||||
'location_dest_id': cls.stock_location.id,
|
||||
'move_lines': [
|
||||
(0, 0, {
|
||||
'name': 'Test move',
|
||||
'product_id': productB.id,
|
||||
'product_uom': productB.uom_id.id,
|
||||
'product_id': cls.productB.id,
|
||||
'product_uom': cls.productB.uom_id.id,
|
||||
'product_uom_qty': 3,
|
||||
'location_id': supplier_location.id,
|
||||
'location_dest_id': stock_location.id,
|
||||
})]
|
||||
'quantity_done': 3,
|
||||
'location_id': cls.supplier_location.id,
|
||||
'location_dest_id': cls.stock_location.id,
|
||||
})
|
||||
]
|
||||
})
|
||||
|
||||
def compare_qty_available_not_res(product, value):
|
||||
# Refresh, because the function field is not recalculated between
|
||||
# transactions
|
||||
product.refresh()
|
||||
self.assertEqual(product.qty_available_not_res, value)
|
||||
|
||||
compare_qty_available_not_res(productA, 0)
|
||||
compare_qty_available_not_res(templateAB, 0)
|
||||
|
||||
pickingInA.action_confirm()
|
||||
compare_qty_available_not_res(productA, 0)
|
||||
compare_qty_available_not_res(templateAB, 0)
|
||||
|
||||
pickingInA.action_assign()
|
||||
compare_qty_available_not_res(productA, 0)
|
||||
compare_qty_available_not_res(templateAB, 0)
|
||||
|
||||
pickingInA.action_done()
|
||||
compare_qty_available_not_res(productA, 2)
|
||||
compare_qty_available_not_res(templateAB, 2)
|
||||
|
||||
# will directly trigger action_done on productB
|
||||
pickingInB.action_done()
|
||||
compare_qty_available_not_res(productA, 2)
|
||||
compare_qty_available_not_res(productB, 3)
|
||||
compare_qty_available_not_res(templateAB, 5)
|
||||
|
||||
# Create a picking from STOCK to CUSTOMER
|
||||
pickingOutA = pickingObj.create({
|
||||
'picking_type_id': self.ref('stock.picking_type_out'),
|
||||
'location_id': stock_location.id,
|
||||
'location_dest_id': customer_location.id,
|
||||
cls.pickingOutA = cls.pickingObj.create({
|
||||
'picking_type_id': cls.env.ref('stock.picking_type_out').id,
|
||||
'location_id': cls.stock_location.id,
|
||||
'location_dest_id': cls.customer_location.id,
|
||||
'move_lines': [
|
||||
(0, 0, {
|
||||
'name': 'Test move',
|
||||
'product_id': productB.id,
|
||||
'product_uom': productB.uom_id.id,
|
||||
'product_id': cls.productB.id,
|
||||
'product_uom': cls.productB.uom_id.id,
|
||||
'product_uom_qty': 2,
|
||||
'location_id': stock_location.id,
|
||||
'location_dest_id': customer_location.id,
|
||||
})]
|
||||
'location_id': cls.stock_location.id,
|
||||
'location_dest_id': cls.customer_location.id,
|
||||
})
|
||||
]
|
||||
})
|
||||
|
||||
compare_qty_available_not_res(productB, 3)
|
||||
compare_qty_available_not_res(templateAB, 5)
|
||||
def compare_qty_available_not_res(self, product, value):
|
||||
product.invalidate_cache()
|
||||
self.assertEqual(product.qty_available_not_res, value)
|
||||
|
||||
pickingOutA.action_confirm()
|
||||
compare_qty_available_not_res(productB, 3)
|
||||
compare_qty_available_not_res(templateAB, 5)
|
||||
def test_stock_levels(self):
|
||||
"""checking that qty_available_not_res actually reflects \
|
||||
the variations in stock, both on product and template"""
|
||||
|
||||
pickingOutA.action_assign()
|
||||
compare_qty_available_not_res(productB, 1)
|
||||
compare_qty_available_not_res(templateAB, 3)
|
||||
self.compare_qty_available_not_res(self.productA, 0)
|
||||
self.compare_qty_available_not_res(self.templateAB, 0)
|
||||
|
||||
pickingOutA.action_done()
|
||||
compare_qty_available_not_res(productB, 1)
|
||||
compare_qty_available_not_res(templateAB, 3)
|
||||
self.pickingInA.action_confirm()
|
||||
self.compare_qty_available_not_res(self.productA, 0)
|
||||
self.compare_qty_available_not_res(self.templateAB, 0)
|
||||
|
||||
templateAB.action_open_quants_unreserved()
|
||||
self.pickingInA.action_assign()
|
||||
self.compare_qty_available_not_res(self.productA, 0)
|
||||
self.compare_qty_available_not_res(self.templateAB, 0)
|
||||
|
||||
self.pickingInA.button_validate()
|
||||
self.compare_qty_available_not_res(self.productA, 2)
|
||||
self.compare_qty_available_not_res(self.templateAB, 2)
|
||||
|
||||
# will directly trigger action_done on self.productB
|
||||
self.pickingInB.action_done()
|
||||
self.compare_qty_available_not_res(self.productA, 2)
|
||||
self.compare_qty_available_not_res(self.productB, 3)
|
||||
self.compare_qty_available_not_res(self.templateAB, 5)
|
||||
|
||||
self.compare_qty_available_not_res(self.productB, 3)
|
||||
self.compare_qty_available_not_res(self.templateAB, 5)
|
||||
|
||||
self.pickingOutA.action_confirm()
|
||||
self.compare_qty_available_not_res(self.productB, 3)
|
||||
self.compare_qty_available_not_res(self.templateAB, 5)
|
||||
|
||||
self.pickingOutA.action_assign()
|
||||
self.compare_qty_available_not_res(self.productB, 1)
|
||||
self.compare_qty_available_not_res(self.templateAB, 3)
|
||||
|
||||
self.pickingOutA.action_done()
|
||||
self.compare_qty_available_not_res(self.productB, 1)
|
||||
self.compare_qty_available_not_res(self.templateAB, 3)
|
||||
|
||||
self.templateAB.action_open_quants_unreserved()
|
||||
|
||||
@@ -63,9 +63,9 @@
|
||||
<field name="arch" type="xml">
|
||||
<div name="button_box" position="inside">
|
||||
<button class="oe_stat_button"
|
||||
name="%(product_open_quants_unreserved)d"
|
||||
icon="fa-building-o"
|
||||
type="action" attrs="{'invisible':[('type', '!=', 'product')]}">
|
||||
name="%(product_open_quants_unreserved)d"
|
||||
icon="fa-building-o"
|
||||
type="action" attrs="{'invisible':[('type', '!=', 'product')]}">
|
||||
<field name="qty_available_not_res" widget="statinfo"
|
||||
string="Unreserved"/>
|
||||
</button>
|
||||
|
||||
@@ -1,29 +1,29 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
|
||||
<record id="quant_search_view" model="ir.ui.view">
|
||||
<field name="name">stock.quant.search</field>
|
||||
<field name="model">stock.quant</field>
|
||||
<field name="inherit_id" ref="stock.quant_search_view"/>
|
||||
<field eval="10" name="priority"/>
|
||||
<field name="arch" type="xml">
|
||||
<field name="owner_id" position="after">
|
||||
<field name="reservation_id"/>
|
||||
</field>
|
||||
<filter name="internal_loc" position="after">
|
||||
<filter name='internal_unreserved'
|
||||
string="Internal Unreserved"
|
||||
domain="[('reservation_id','=', False), ('location_id.usage','=', 'internal')]"/>
|
||||
</filter>
|
||||
<record id="quant_search_view" model="ir.ui.view">
|
||||
<field name="name">stock.quant.search</field>
|
||||
<field name="model">stock.quant</field>
|
||||
<field name="inherit_id" ref="stock.quant_search_view"/>
|
||||
<field eval="10" name="priority"/>
|
||||
<field name="arch" type="xml">
|
||||
<field name="owner_id" position="after">
|
||||
<field name="contains_unreserved"/>
|
||||
</field>
|
||||
</record>
|
||||
<filter name="internal_loc" position="after">
|
||||
<filter name='internal_unreserved'
|
||||
string="Internal Unreserved"
|
||||
domain="[('contains_unreserved','=', True), ('location_id.usage','=', 'internal')]"/>
|
||||
</filter>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.actions.act_window"
|
||||
id="product_open_quants_unreserved">
|
||||
<field name="name">Stock On Hand (Unreserved)</field>
|
||||
<field name="context">{'search_default_internal_loc': 1, 'search_default_locationgroup':1}</field>
|
||||
<field name="domain">[('product_id', '=', active_id), ('reservation_id', '=', False)]</field>
|
||||
<field name="res_model">stock.quant</field>
|
||||
</record>
|
||||
<record model="ir.actions.act_window"
|
||||
id="product_open_quants_unreserved">
|
||||
<field name="name">Stock On Hand (Unreserved)</field>
|
||||
<field name="context">{'search_default_internal_loc': 1, 'search_default_locationgroup':1}</field>
|
||||
<field name="domain">[('product_id', '=', active_id), ('contains_unreserved', '=', True)]</field>
|
||||
<field name="res_model">stock.quant</field>
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
|
||||
Reference in New Issue
Block a user