[IMP] stock: improvements in replenishment wizard

With this commit:
========================
- Added a new field `forecasted_qty` in replenishment to display the
  forecasted qty based on a selected warehouse in the wizard
- Changed the `route_ids`(m2m) field to `route_id`(m2o) so that
   we can apply a specific route for the
  replenishment instead of the product's default routes
- Reduced the width of all the fields
- Remove the time from the scheduled date and make the unit field not editable.
- Can see vendor information with info icon field when buy is selected
- Hide the Dropship route from the Route

TaskId : 2579425

closes odoo/odoo#96948

Related: odoo/upgrade#3796
Signed-off-by: William Henrotin (whe) <whe@odoo.com>
This commit is contained in:
kir-odoo
2023-05-24 15:45:23 +02:00
parent 602e1c77b3
commit c3b7a87462
26 changed files with 340 additions and 84 deletions
+1 -1
View File
@@ -20,7 +20,7 @@ class StockWarehouseOrderpoint(models.Model):
self.ensure_one()
domain = [('orderpoint_id', 'in', self.ids)]
if self.env.context.get('written_after'):
domain = AND([domain, [('write_date', '>', self.env.context.get('written_after'))]])
domain = AND([domain, [('write_date', '>=', self.env.context.get('written_after'))]])
production = self.env['mrp.production'].search(domain, limit=1)
if production:
action = self.env.ref('mrp.action_mrp_production_form')
@@ -4,3 +4,4 @@
from . import stock_picking_return
from . import mrp_consumption_warning
from . import change_production_qty
from . import product_replenish
@@ -0,0 +1,13 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import models
from odoo.osv import expression
class ProductReplenish(models.TransientModel):
_inherit = 'product.replenish'
def _get_allowed_route_domain(self):
domains = super()._get_allowed_route_domain()
return expression.AND([domains, [('id', '!=', self.env.ref('mrp_subcontracting.route_resupply_subcontractor_mto', raise_if_not_found=False).id)]])
@@ -2,3 +2,4 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from . import models
from . import wizard
@@ -0,0 +1,4 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from . import product_replenish
@@ -0,0 +1,13 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import models
from odoo.osv import expression
class ProductReplenish(models.TransientModel):
_inherit = 'product.replenish'
def _get_allowed_route_domain(self):
domains = super()._get_allowed_route_domain()
return expression.AND([domains, [('id', '!=', self.env.ref('mrp_subcontracting_dropshipping.route_subcontracting_dropshipping', raise_if_not_found=False).id)]])
+2 -1
View File
@@ -23,7 +23,8 @@
'report/purchase_report_views.xml',
'report/purchase_report_templates.xml',
'report/report_stock_rule.xml',
'wizard/stock_replenishment_info.xml'
'wizard/stock_replenishment_info.xml',
'wizard/product_replenish_views.xml',
],
'demo': [
'data/purchase_stock_demo.xml',
+11
View File
@@ -130,3 +130,14 @@ class SupplierInfo(models.Model):
orderpoint.supplier_id = self
if orderpoint.qty_to_order < self.min_qty:
orderpoint.qty_to_order = self.min_qty
if self._context.get('replenish_id'):
replenish = self.env['product.replenish'].browse(self._context.get('replenish_id'))
replenish.supplier_id = self
return {
'type': 'ir.actions.act_window',
'name': 'Replenish',
'res_model': 'product.replenish',
'res_id': replenish.id,
'target': 'new',
'view_mode': 'form',
}
+1 -1
View File
@@ -152,7 +152,7 @@ class Orderpoint(models.Model):
self.ensure_one()
domain = [('orderpoint_id', 'in', self.ids)]
if self.env.context.get('written_after'):
domain = AND([domain, [('write_date', '>', self.env.context.get('written_after'))]])
domain = AND([domain, [('write_date', '>=', self.env.context.get('written_after'))]])
order = self.env['purchase.order.line'].search(domain, limit=1).order_id
if order:
action = self.env.ref('purchase.action_rfq_form')
@@ -43,11 +43,14 @@ class TestReplenishWizard(TestStockCommon):
'quantity': self.product_uom_qty,
'warehouse_id': self.wh.id,
})
replenish_wizard.launch_replenishment()
last_po_id = self.env['purchase.order'].search([
('origin', 'ilike', '%Manual Replenishment%'),
('partner_id', '=', self.vendor.id)
])[-1]
genrated_picking = replenish_wizard.launch_replenishment()
links = genrated_picking.get("params", {}).get("links")
url = links and links[0].get("url", "") or ""
purchase_order_id, model_name = self.url_extract_rec_id_and_model(url)
last_po_id = False
if purchase_order_id and model_name:
last_po_id = self.env[model_name[0]].browse(int(purchase_order_id[0]))
self.assertTrue(last_po_id, 'Purchase Order not found')
order_line = last_po_id.order_line.search([('product_id', '=', self.product1.id)])
self.assertTrue(order_line, 'The product is not in the Purchase Order')
@@ -93,10 +96,14 @@ class TestReplenishWizard(TestStockCommon):
'quantity': 10,
'warehouse_id': self.wh.id,
})
replenish_wizard.launch_replenishment()
last_po_id = self.env['purchase.order'].search([
('origin', 'ilike', '%Manual Replenishment%'),
])[-1]
genrated_picking = replenish_wizard.launch_replenishment()
links = genrated_picking.get("params", {}).get("links")
url = links and links[0].get("url", "") or ""
purchase_order_id, model_name = self.url_extract_rec_id_and_model(url)
last_po_id = False
if purchase_order_id and model_name:
last_po_id = self.env[model_name[0]].browse(int(purchase_order_id[0]))
self.assertEqual(last_po_id.partner_id, vendor1)
self.assertEqual(last_po_id.order_line.price_unit, 100)
@@ -148,10 +155,14 @@ class TestReplenishWizard(TestStockCommon):
'quantity': 10,
'warehouse_id': self.wh.id,
})
replenish_wizard.launch_replenishment()
last_po_id = self.env['purchase.order'].search([
('origin', 'ilike', '%Manual Replenishment%'),
])[-1]
genrated_picking = replenish_wizard.launch_replenishment()
links = genrated_picking.get("params", {}).get("links")
url = links and links[0].get("url", "") or ""
purchase_order_id, model_name = self.url_extract_rec_id_and_model(url)
last_po_id = False
if purchase_order_id and model_name:
last_po_id = self.env[model_name[0]].browse(int(purchase_order_id[0]))
self.assertEqual(last_po_id.partner_id, vendor1)
self.assertEqual(last_po_id.order_line.price_unit, 100)
@@ -193,10 +204,15 @@ class TestReplenishWizard(TestStockCommon):
'quantity': 10,
'warehouse_id': self.wh.id,
})
replenish_wizard.launch_replenishment()
last_po_id = self.env['purchase.order'].search([
('origin', 'ilike', '%Manual Replenishment%'),
])[-1]
genrated_picking = replenish_wizard.launch_replenishment()
links = genrated_picking.get("params", {}).get("links")
url = links and links[0].get("url", "") or ""
purchase_order_id, model_name = self.url_extract_rec_id_and_model(url)
last_po_id = False
if purchase_order_id and model_name:
last_po_id = self.env[model_name[0]].browse(int(purchase_order_id[0]))
self.assertEqual(last_po_id.partner_id, vendor2)
def test_chose_supplier_4(self):
@@ -241,10 +257,14 @@ class TestReplenishWizard(TestStockCommon):
'quantity': 10,
'warehouse_id': self.wh.id,
})
replenish_wizard.launch_replenishment()
last_po_id = self.env['purchase.order'].search([
('origin', 'ilike', '%Manual Replenishment%'),
])[-1]
genrated_picking = replenish_wizard.launch_replenishment()
links = genrated_picking.get("params", {}).get("links")
url = links and links[0].get("url", "") or ""
purchase_order_id, model_name = self.url_extract_rec_id_and_model(url)
last_po_id = False
if purchase_order_id and model_name:
last_po_id = self.env[model_name[0]].browse(int(purchase_order_id[0]))
self.assertEqual(last_po_id.partner_id, vendor1)
self.assertEqual(last_po_id.order_line.price_unit, 60)
@@ -11,12 +11,14 @@ from odoo.tests import Form
from odoo.tests.common import TransactionCase, tagged
from odoo.addons.account.tests.common import AccountTestInvoicingCommon
from odoo.tools import DEFAULT_SERVER_DATETIME_FORMAT
from odoo.addons.stock.tests.common import TestStockCommon
class TestStockValuation(TransactionCase):
@classmethod
def setUpClass(cls):
super().setUpClass()
cls.url_extract_rec_id_and_model = TestStockCommon.url_extract_rec_id_and_model
cls.supplier_location = cls.env.ref('stock.stock_location_suppliers')
cls.stock_location = cls.env.ref('stock.stock_location_stock')
cls.partner_id = cls.env['res.partner'].create({
@@ -107,12 +109,14 @@ class TestStockValuation(TransactionCase):
'quantity': replenishment_uom_qty,
'warehouse_id': self.env['stock.warehouse'].search([('company_id', '=', self.env.user.id)], limit=1).id,
})
replenish_wizard.launch_replenishment()
genrated_picking = replenish_wizard.launch_replenishment()
links = genrated_picking.get("params", {}).get("links")
url = links and links[0].get("url", "") or ""
purchase_order_id, model_name = self.url_extract_rec_id_and_model(url)
last_po_id = False
if purchase_order_id and model_name:
last_po_id = self.env[model_name[0]].browse(int(purchase_order_id[0]))
last_po_id = self.env['purchase.order'].search([
('origin', 'ilike', '%Manual Replenishment%'),
('partner_id', '=', vendor.id)
])[-1]
order_line = last_po_id.order_line.search([('product_id', '=', self.product1.id)])
self.assertEqual(order_line.product_qty,
ap._compute_quantity(replenishment_uom_qty, kgm, rounding_method='HALF-UP'),
+1
View File
@@ -2,3 +2,4 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from . import stock_replenishment_info
from . import product_replenish
@@ -0,0 +1,71 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import _, api, fields, models
from odoo.osv.expression import AND
class ProductReplenish(models.TransientModel):
_inherit = 'product.replenish'
supplier_id = fields.Many2one("product.supplierinfo", string="Vendor")
show_vendor = fields.Boolean(compute="_compute_show_vendor")
@api.model
def default_get(self, fields):
res = super().default_get(fields)
if res.get('product_id'):
product_id = self.env['product.product'].browse(res['product_id'])
product_tmpl_id = product_id.product_tmpl_id
if 'warehouse_id' not in res:
company = product_tmpl_id.company_id or self.env.company
res['warehouse_id'] = self.env['stock.warehouse'].search([('company_id', '=', company.id)], limit=1).id
orderpoint = self.env['stock.warehouse.orderpoint'].search([('product_id', 'in', [product_tmpl_id.product_variant_id.id, product_id.id]), ("warehouse_id", "=", res['warehouse_id'])], limit=1)
if orderpoint:
res['supplier_id'] = orderpoint.supplier_id.id
elif product_tmpl_id.seller_ids:
res['supplier_id'] = product_tmpl_id.seller_ids[0].id
if not product_tmpl_id.seller_ids:
company = product_tmpl_id.company_id or self.env.company
domain = ['|', ('company_id', '=', False), ('company_id', '=', company.id)] + self._get_allowed_route_domain()
domain = AND([domain, [('id', '!=', self.env.ref('purchase_stock.route_warehouse0_buy', raise_if_not_found=False).id)]])
res['route_id'] = self.env['stock.route'].search(domain, limit=1).id
return res
@api.depends('route_id')
def _compute_show_vendor(self):
for rec in self:
rec.show_vendor = rec.route_id == self.env.ref('purchase_stock.route_warehouse0_buy', raise_if_not_found=False)
@api.onchange('route_id')
def _onchange_route_id(self):
for rec in self:
if rec.route_id == self.env.ref('purchase_stock.route_warehouse0_buy', raise_if_not_found=False) and not rec.product_id.product_tmpl_id.seller_ids:
return {
'warning': {
'title': _("Vendor Not Found in Product %s", rec.product_id.name),
'message': _("Go on the product form and add the list of vendors"),
},
}
def _prepare_orderpoint_values(self):
res = super()._prepare_orderpoint_values()
if self.supplier_id:
res['supplier_id'] = self.supplier_id.id
return res
def action_stock_replenishment_info(self):
self.ensure_one()
orderpoint = self.env["stock.warehouse.orderpoint"].search([("product_id", "=", self.product_id.id), ("warehouse_id", "=", self.warehouse_id.id)], limit=1)
if not orderpoint:
orderpoint = self.env["stock.warehouse.orderpoint"].create({
"product_id": self.product_id.id,
"warehouse_id": self.warehouse_id.id,
})
action = orderpoint.action_stock_replenishment_info()
action["context"] = {
'default_orderpoint_id': orderpoint.id,
'replenish_id': self.id,
}
return action
@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="view_product_replenish_form_inherit_stock" model="ir.ui.view">
<field name="name">product.replenish.form.inherit.stock</field>
<field name="model">product.replenish</field>
<field name="inherit_id" ref="stock.view_product_replenish"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='route_id']" position="after">
<label for="supplier_id" attrs="{'invisible': [('show_vendor', '=', False)]}"/>
<div class="o_row">
<field name="show_vendor" invisible="1"/>
<field name="supplier_id" attrs="{'invisible': [('show_vendor', '=', False)], 'required': [('show_vendor', '=', True)]}" domain="[('product_tmpl_id', '=', product_tmpl_id)]" options="{'no_open': 1, 'no_create': 1}"/>
<button name="action_stock_replenishment_info"
type="object"
title="Show Vendor"
icon="fa-info-circle"
attrs="{'invisible': [('show_vendor', '=', False)]}"/>
</div>
</xpath>
</field>
</record>
</odoo>
@@ -17,3 +17,21 @@ class StockReplenishmentInfo(models.TransientModel):
def _compute_supplierinfo_ids(self):
for replenishment_info in self:
replenishment_info.supplierinfo_ids = replenishment_info.product_id.seller_ids
class StockReplenishmentOption(models.TransientModel):
_inherit = 'stock.replenishment.option'
def select_route(self):
if self._context.get('replenish_id'):
replenish = self.env['product.replenish'].browse(self._context.get('replenish_id'))
replenish.route_id = self.route_id.id
return {
'type': 'ir.actions.act_window',
'name': 'Replenish',
'res_model': 'product.replenish',
'res_id': replenish.id,
'target': 'new',
'view_mode': 'form',
}
return super().select_route()
@@ -12,7 +12,6 @@
<field name="show_set_supplier_button" invisible="1"/>
<field name="last_purchase_date"/>
<button name="action_set_supplier" type="object" string="Set as Supplier" class="btn btn-link"
context="{'orderpoint_id': parent.orderpoint_id, 'stock_replenishment_info_id': parent.id}"
attrs="{'invisible': [('show_set_supplier_button', '=', False)]}"/>
</field>
<xpath expr="//tree" position="attributes">
@@ -38,7 +37,7 @@
<xpath expr="//page" position="before">
<page string="Vendors">
<field name="supplierinfo_id" invisible="1"/>
<field name="supplierinfo_ids" readonly="1" context="{'tree_view_ref': 'purchase_stock.product_supplierinfo_replenishment_tree_view'}"/>
<field name="supplierinfo_ids" readonly="1" context="{'tree_view_ref': 'purchase_stock.product_supplierinfo_replenishment_tree_view', 'stock_replenishment_info_id': id, 'orderpoint_id': orderpoint_id}"/>
</page>
</xpath>
</field>
+1 -1
View File
@@ -180,7 +180,7 @@ class StockMove(models.Model):
display_clear_serial = fields.Boolean(compute='_compute_display_clear_serial')
next_serial = fields.Char('First SN')
next_serial_count = fields.Integer('Number of SN')
orderpoint_id = fields.Many2one('stock.warehouse.orderpoint', 'Original Reordering Rule', check_company=True, index=True)
orderpoint_id = fields.Many2one('stock.warehouse.orderpoint', 'Original Reordering Rule', index=True)
forecast_availability = fields.Float('Forecast Availability', compute='_compute_forecast_information', digits='Product Unit of Measure', compute_sudo=True)
forecast_expected_date = fields.Datetime('Forecasted Expected date', compute='_compute_forecast_information', compute_sudo=True)
lot_ids = fields.Many2many('stock.lot', compute='_compute_lot_ids', inverse='_set_lot_ids', string='Serial Numbers', readonly=False)
+8 -2
View File
@@ -224,7 +224,7 @@ class StockWarehouseOrderpoint(models.Model):
return action
def action_replenish(self):
now = datetime.now()
now = self.env.cr.now()
try:
self._procure_orderpoint_confirm(company_id=self.env.company)
except UserError as e:
@@ -444,14 +444,20 @@ class StockWarehouseOrderpoint(models.Model):
self.ensure_one()
domain = [('orderpoint_id', 'in', self.ids)]
if self.env.context.get('written_after'):
domain = expression.AND([domain, [('write_date', '>', self.env.context.get('written_after'))]])
domain = expression.AND([domain, [('write_date', '>=', self.env.context.get('written_after'))]])
move = self.env['stock.move'].search(domain, limit=1)
if move.picking_id:
action = self.env.ref('stock.stock_picking_action_picking_type')
return {
'type': 'ir.actions.client',
'tag': 'display_notification',
'params': {
'title': _('The inter-warehouse transfers have been generated'),
'message': '%s',
'links': [{
'label': move.picking_id.name,
'url': f'#action={action.id}&id={move.picking_id.id}&model=stock.picking&view_type=form'
}],
'sticky': False,
}
}
+6
View File
@@ -1,5 +1,6 @@
# -*- coding: utf-8 -*-
import re
from odoo.tests import common
@@ -95,3 +96,8 @@ class TestStockCommon(common.TransactionCase):
cls.UnitA = cls.ProductObj.create({'name': 'Unit-A', 'type': 'product'})
cls.kgB = cls.ProductObj.create({'name': 'kg-B', 'type': 'product', 'uom_id': cls.uom_kg.id, 'uom_po_id': cls.uom_kg.id})
cls.gB = cls.ProductObj.create({'name': 'g-B', 'type': 'product', 'uom_id': cls.uom_gm.id, 'uom_po_id': cls.uom_gm.id})
def url_extract_rec_id_and_model(self, url):
rec_id = re.findall(r'[?&]id=([^&]+).*', url)
model_name = re.findall(r'[?&]model=([^&]+).*', url)
return rec_id, model_name
+8 -2
View File
@@ -2722,8 +2722,14 @@ class TestRoutes(TestStockCommon):
'warehouse_id': self.wh.id,
})
replenish_wizard.launch_replenishment()
last_picking_id = self.env['stock.picking'].search([('origin', '=', 'Manual Replenishment')])[-1]
genrated_picking = replenish_wizard.launch_replenishment()
links = genrated_picking.get("params", {}).get("links")
url = links and links[0].get("url", "") or ""
picking_id, model_name = self.url_extract_rec_id_and_model(url)
last_picking_id = False
if picking_id and model_name:
last_picking_id = self.env[model_name[0]].browse(int(picking_id[0]))
self.assertTrue(last_picking_id, 'Picking not found')
move_line = last_picking_id.move_ids.search([('product_id', '=', self.product1.id)])
self.assertTrue(move_line,'The product is not in the picking')
+57 -26
View File
@@ -5,6 +5,7 @@ import datetime
from odoo import _, api, fields, models
from odoo.exceptions import UserError
from odoo.osv import expression
from odoo.tools.misc import clean_context
@@ -16,22 +17,25 @@ class ProductReplenish(models.TransientModel):
product_tmpl_id = fields.Many2one('product.template', string='Product Template', required=True)
product_has_variants = fields.Boolean('Has variants', default=False, required=True)
product_uom_category_id = fields.Many2one('uom.category', related='product_id.uom_id.category_id', readonly=True, required=True)
product_uom_id = fields.Many2one('uom.uom', string='Unit of measure', required=True)
product_uom_id = fields.Many2one('uom.uom', string='Unity of measure', required=True)
forecast_uom_id = fields.Many2one(related='product_id.uom_id')
quantity = fields.Float('Quantity', default=1, required=True)
date_planned = fields.Datetime('Scheduled Date', required=True, help="Date at which the replenishment should take place.")
warehouse_id = fields.Many2one(
'stock.warehouse', string='Warehouse', required=True,
domain="[('company_id', '=', company_id)]")
route_ids = fields.Many2many(
'stock.route', string='Preferred Routes',
help="Apply specific route(s) for the replenishment instead of product's default routes.",
route_id = fields.Many2one(
'stock.route', string='Preferred Route',
help="Apply specific route for the replenishment instead of product's default routes.",
domain="['|', ('company_id', '=', False), ('company_id', '=', company_id)]")
company_id = fields.Many2one('res.company')
forecasted_quantity = fields.Float(string="Forecasted Quantity", compute="_compute_forecasted_quantity")
allowed_route_ids = fields.Many2many("stock.route", compute="_compute_allowed_route_ids")
@api.onchange('product_id')
@api.onchange('product_id', 'warehouse_id')
def _onchange_product_id(self):
if not self.env.context.get('default_quantity'):
self.quantity = abs(self.product_id.virtual_available) if self.product_id.virtual_available < 0 else 1
self.quantity = abs(self.forecasted_quantity) if self.forecasted_quantity < 0 else 1
@api.model
def default_get(self, fields):
@@ -60,38 +64,65 @@ class ProductReplenish(models.TransientModel):
res['warehouse_id'] = warehouse.id
if 'date_planned' in fields:
res['date_planned'] = datetime.datetime.now()
if 'route_id' in fields and 'route_id' not in res:
route_id = False
domain = expression.AND([self._get_allowed_route_domain(), ['|', ('company_id', '=', False), ('company_id', '=', company.id)]])
if product_tmpl_id.route_ids:
product_route_domain = expression.AND([domain, [('product_ids', '=', product_tmpl_id.id)]])
route_id = self.env['stock.route'].search(product_route_domain, limit=1).id
if not route_id:
route_id = self.env['stock.route'].search(domain, limit=1).id
if route_id:
res['route_id'] = route_id
return res
def launch_replenishment(self):
uom_reference = self.product_id.uom_id
self.quantity = self.product_uom_id._compute_quantity(self.quantity, uom_reference, rounding_method='HALF-UP')
try:
self.env['procurement.group'].with_context(clean_context(self.env.context)).run([
self.env['procurement.group'].Procurement(
self.product_id,
self.quantity,
uom_reference,
self.warehouse_id.lot_stock_id, # Location
_("Manual Replenishment"), # Name
_("Manual Replenishment"), # Origin
self.warehouse_id.company_id,
self._prepare_run_values() # Values
)
])
return {
orderpoint = self.env['stock.warehouse.orderpoint'].search([('product_id', '=', self.product_id.id)])
if orderpoint:
orderpoint.write(self._prepare_orderpoint_values())
else:
orderpoint = self.env['stock.warehouse.orderpoint'].create(self._prepare_orderpoint_values())
notification = orderpoint.action_replenish()
act_window_close = {
'type': 'ir.actions.act_window_close',
'infos': {'done': True},
}
if notification:
notification['params']['next'] = act_window_close
return notification
return act_window_close
except UserError as error:
raise UserError(error)
def _prepare_run_values(self):
replenishment = self.env['procurement.group'].create({})
def _prepare_orderpoint_values(self):
values = {
'warehouse_id': self.warehouse_id,
'route_ids': self.route_ids,
'date_planned': self.date_planned,
'group_id': replenishment,
'location_id': self.warehouse_id.lot_stock_id.id,
'product_id': self.product_id.id,
'qty_to_order': self.quantity,
}
if self.route_id:
values['route_id'] = self.route_id.id
return values
@api.depends('warehouse_id', 'product_id')
def _compute_forecasted_quantity(self):
for rec in self:
rec.forecasted_quantity = rec.product_id.with_context(warehouse=rec.warehouse_id.id).virtual_available
# OVERWRITE in 'Drop Shipping', 'Dropship and Subcontracting Management' and 'Dropship and Subcontracting Management' to hide it
def _get_allowed_route_domain(self):
stock_location_inter_wh_id = self.env.ref('stock.stock_location_inter_wh').id
return [
('product_selectable', '=', True),
('rule_ids.location_src_id', '!=', stock_location_inter_wh_id),
('rule_ids.location_dest_id', '!=', stock_location_inter_wh_id)
]
@api.depends('product_id', 'product_tmpl_id')
def _compute_allowed_route_ids(self):
domain = self._get_allowed_route_domain()
route_ids = self.env['stock.route'].search(domain)
self.allowed_route_ids = route_ids
+30 -20
View File
@@ -11,26 +11,36 @@
a manufacturing order or a transfer.
</p>
<group>
<field name="product_tmpl_id" invisible="1"/>
<field name="product_has_variants" invisible="1"/>
<field name="product_id"
domain="[('product_tmpl_id', '=', product_tmpl_id)]"
attrs="{'readonly': [('product_has_variants', '=', False)]}"
options="{'no_create_edit':1}"/>
<field name="product_uom_category_id" invisible="1"/>
<label for="quantity"/>
<div class="o_row">
<field name="quantity" />
<field name="product_uom_id"
domain="[('category_id', '=', product_uom_category_id)]"
groups="uom.group_uom"/>
</div>
<field name="date_planned"/>
<field name="warehouse_id"
groups="stock.group_stock_multi_warehouses"/>
<field name="route_ids"
widget="many2many_tags"/>
<field name="company_id" invisible="1"/>
<group>
<field name="product_tmpl_id" invisible="1"/>
<field name="product_has_variants" invisible="1"/>
<field name="product_uom_category_id" invisible="1"/>
<field name="allowed_route_ids" invisible="1"/>
<field name="company_id" invisible="1"/>
<field name="product_id"
domain="[('product_tmpl_id', '=', product_tmpl_id)]"
attrs="{'readonly': [('product_has_variants', '=', False)]}"
options="{'no_open': 1, 'no_create': 1}"/>
<label for="quantity"/>
<div class="o_row">
<field name="quantity" />
<field name="product_uom_id" groups="!uom.group_uom" invisible="1"/>
<field name="product_uom_id"
domain="[('category_id', '=', product_uom_category_id)]"
groups="uom.group_uom"
options="{'no_open': 1, 'no_create': 1}"/>
</div>
<label for="forecasted_quantity"/>
<div class="o_row">
<field name="forecasted_quantity" readonly="1"/>
<field name="forecast_uom_id" groups="!uom.group_uom" invisible="1"/>
<field name="forecast_uom_id" domain="[('category_id', '=', product_uom_category_id)]" groups="uom.group_uom" readonly="1"/>
</div>
<field name="date_planned" widget="date"/>
<field name="warehouse_id"
groups="stock.group_stock_multi_warehouses"/>
<field name="route_id" domain="[('id', 'in', allowed_route_ids)]"/>
</group>
</group>
<footer>
<button name="launch_replenishment"
@@ -22,7 +22,7 @@
</page>
</notebook>
<footer>
<button string="Close" class="btn-default" special="cancel" data-hotkey="z"/>
<button string="Close" class="btn-secondary" special="cancel" data-hotkey="z"/>
</footer>
</form>
</field>
+2 -1
View File
@@ -1,4 +1,5 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from . import models
from . import models
from . import wizard
@@ -0,0 +1,4 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from . import product_replenish
@@ -0,0 +1,13 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import models
from odoo.osv import expression
class ProductReplenish(models.TransientModel):
_inherit = 'product.replenish'
def _get_allowed_route_domain(self):
domains = super()._get_allowed_route_domain()
return expression.AND([domains, [('id', '!=', self.env.ref('stock_dropshipping.route_drop_shipping', raise_if_not_found=False).id)]])