mirror of
https://gitlab.com/hibou-io/hibou-odoo/suite.git
synced 2025-01-20 12:37:31 +02:00
[MIG] sale_planner: to Odoo 13.0
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
'name': 'Sale Order Planner',
|
'name': 'Sale Order Planner',
|
||||||
'summary': 'Plans order dates and warehouses.',
|
'summary': 'Plans order dates and warehouses.',
|
||||||
'version': '12.0.1.0.0',
|
'version': '13.0.1.0.0',
|
||||||
'author': "Hibou Corp.",
|
'author': "Hibou Corp.",
|
||||||
'category': 'Sale',
|
'category': 'Sale',
|
||||||
'license': 'AGPL-3',
|
'license': 'AGPL-3',
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ except ImportError:
|
|||||||
class Partner(models.Model):
|
class Partner(models.Model):
|
||||||
_inherit = 'res.partner'
|
_inherit = 'res.partner'
|
||||||
|
|
||||||
@api.multi
|
|
||||||
def geo_localize(self):
|
def geo_localize(self):
|
||||||
# We need country names in English below
|
# We need country names in English below
|
||||||
for partner in self.with_context(lang='en_US'):
|
for partner in self.with_context(lang='en_US'):
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ class ProductTemplate(models.Model):
|
|||||||
string="Order Planner Policy",
|
string="Order Planner Policy",
|
||||||
help="The Order Planner Policy to use when making a sale order planner.")
|
help="The Order Planner Policy to use when making a sale order planner.")
|
||||||
|
|
||||||
@api.multi
|
|
||||||
def get_planning_policy(self):
|
def get_planning_policy(self):
|
||||||
self.ensure_one()
|
self.ensure_one()
|
||||||
return self.property_planning_policy_id or self.categ_id.property_planning_policy_categ_id
|
return self.property_planning_policy_id or self.categ_id.property_planning_policy_categ_id
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ from odoo.addons.resource.models.resource import make_aware
|
|||||||
class ResourceCalendar(models.Model):
|
class ResourceCalendar(models.Model):
|
||||||
_inherit = 'resource.calendar'
|
_inherit = 'resource.calendar'
|
||||||
|
|
||||||
@api.multi
|
|
||||||
def plan_days_end(self, days, day_dt, compute_leaves=False, domain=None):
|
def plan_days_end(self, days, day_dt, compute_leaves=False, domain=None):
|
||||||
"""
|
"""
|
||||||
Override to `plan_days` that allows you to get the nearest 'end' including today.
|
Override to `plan_days` that allows you to get the nearest 'end' including today.
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ from odoo import api, fields, models
|
|||||||
class SaleOrder(models.Model):
|
class SaleOrder(models.Model):
|
||||||
_inherit = 'sale.order'
|
_inherit = 'sale.order'
|
||||||
|
|
||||||
@api.multi
|
|
||||||
def action_planorder(self):
|
def action_planorder(self):
|
||||||
plan_obj = self.env['sale.order.make.plan']
|
plan_obj = self.env['sale.order.make.plan']
|
||||||
for order in self:
|
for order in self:
|
||||||
|
|||||||
@@ -128,36 +128,36 @@ class TestPlanner(common.TransactionCase):
|
|||||||
'standard_price': 1.0,
|
'standard_price': 1.0,
|
||||||
})
|
})
|
||||||
self.product_both = self.product_both.product_variant_id
|
self.product_both = self.product_both.product_variant_id
|
||||||
self.env['stock.change.product.qty'].create({
|
self.env['stock.quant'].create({
|
||||||
'location_id': self.warehouse_1.lot_stock_id.id,
|
'location_id': self.warehouse_1.lot_stock_id.id,
|
||||||
'product_id': self.product_1.id,
|
'product_id': self.product_1.id,
|
||||||
'new_quantity': 100,
|
'quantity': 100,
|
||||||
}).change_product_qty()
|
})
|
||||||
self.env['stock.change.product.qty'].create({
|
self.env['stock.quant'].create({
|
||||||
'location_id': self.warehouse_1.lot_stock_id.id,
|
'location_id': self.warehouse_1.lot_stock_id.id,
|
||||||
'product_id': self.product_12.id,
|
'product_id': self.product_12.id,
|
||||||
'new_quantity': 100,
|
'quantity': 100,
|
||||||
}).change_product_qty()
|
})
|
||||||
self.env['stock.change.product.qty'].create({
|
self.env['stock.quant'].create({
|
||||||
'location_id': self.warehouse_1.lot_stock_id.id,
|
'location_id': self.warehouse_1.lot_stock_id.id,
|
||||||
'product_id': self.product_both.id,
|
'product_id': self.product_both.id,
|
||||||
'new_quantity': 100,
|
'quantity': 100,
|
||||||
}).change_product_qty()
|
})
|
||||||
self.env['stock.change.product.qty'].create({
|
self.env['stock.quant'].create({
|
||||||
'location_id': self.warehouse_2.lot_stock_id.id,
|
'location_id': self.warehouse_2.lot_stock_id.id,
|
||||||
'product_id': self.product_2.id,
|
'product_id': self.product_2.id,
|
||||||
'new_quantity': 100,
|
'quantity': 100,
|
||||||
}).change_product_qty()
|
})
|
||||||
self.env['stock.change.product.qty'].create({
|
self.env['stock.quant'].create({
|
||||||
'location_id': self.warehouse_2.lot_stock_id.id,
|
'location_id': self.warehouse_2.lot_stock_id.id,
|
||||||
'product_id': self.product_22.id,
|
'product_id': self.product_22.id,
|
||||||
'new_quantity': 100,
|
'quantity': 100,
|
||||||
}).change_product_qty()
|
})
|
||||||
self.env['stock.change.product.qty'].create({
|
self.env['stock.quant'].create({
|
||||||
'location_id': self.warehouse_2.lot_stock_id.id,
|
'location_id': self.warehouse_2.lot_stock_id.id,
|
||||||
'product_id': self.product_both.id,
|
'product_id': self.product_both.id,
|
||||||
'new_quantity': 100,
|
'quantity': 100,
|
||||||
}).change_product_qty()
|
})
|
||||||
|
|
||||||
self.policy_closest = self.env['sale.order.planning.policy'].create({
|
self.policy_closest = self.env['sale.order.planning.policy'].create({
|
||||||
'always_closest_warehouse': True,
|
'always_closest_warehouse': True,
|
||||||
@@ -210,8 +210,11 @@ class TestPlanner(common.TransactionCase):
|
|||||||
'product_id': self.product_1.id,
|
'product_id': self.product_1.id,
|
||||||
'name': 'demo',
|
'name': 'demo',
|
||||||
})
|
})
|
||||||
self.assertEqual(self.product_1.with_context(warehouse=self.warehouse_1.id).qty_available, 100)
|
|
||||||
self.assertEqual(self.product_1.with_context(warehouse=self.warehouse_2.id).qty_available, 0)
|
self.assertEqual(self.product_1.with_context(warehouse=self.warehouse_2.id).qty_available, 0)
|
||||||
|
self.product_1.invalidate_cache(fnames=['qty_available'], ids=self.product_1.ids)
|
||||||
|
self.assertEqual(self.product_1.with_context(warehouse=self.warehouse_1.id).qty_available, 100)
|
||||||
|
self.product_1.invalidate_cache(fnames=['qty_available'], ids=self.product_1.ids)
|
||||||
|
|
||||||
both_wh_ids = self.both_wh_ids()
|
both_wh_ids = self.both_wh_ids()
|
||||||
planner = self.env['sale.order.make.plan'].with_context(warehouse_domain=[('id', 'in', both_wh_ids)], skip_plan_shipping=True).create({'order_id': self.so.id})
|
planner = self.env['sale.order.make.plan'].with_context(warehouse_domain=[('id', 'in', both_wh_ids)], skip_plan_shipping=True).create({'order_id': self.so.id})
|
||||||
self.assertTrue(planner.planning_option_ids, 'Must have one or more plans.')
|
self.assertTrue(planner.planning_option_ids, 'Must have one or more plans.')
|
||||||
@@ -229,7 +232,9 @@ class TestPlanner(common.TransactionCase):
|
|||||||
'name': 'demo',
|
'name': 'demo',
|
||||||
})
|
})
|
||||||
self.assertEqual(self.product_2.with_context(warehouse=self.warehouse_1.id).qty_available, 0)
|
self.assertEqual(self.product_2.with_context(warehouse=self.warehouse_1.id).qty_available, 0)
|
||||||
|
self.product_2.invalidate_cache(fnames=['qty_available'], ids=self.product_2.ids)
|
||||||
self.assertEqual(self.product_2.with_context(warehouse=self.warehouse_2.id).qty_available, 100)
|
self.assertEqual(self.product_2.with_context(warehouse=self.warehouse_2.id).qty_available, 100)
|
||||||
|
self.product_2.invalidate_cache(fnames=['qty_available'], ids=self.product_2.ids)
|
||||||
both_wh_ids = self.both_wh_ids()
|
both_wh_ids = self.both_wh_ids()
|
||||||
planner = self.env['sale.order.make.plan'].with_context(warehouse_domain=[('id', 'in', both_wh_ids)], skip_plan_shipping=True).create({'order_id': self.so.id})
|
planner = self.env['sale.order.make.plan'].with_context(warehouse_domain=[('id', 'in', both_wh_ids)], skip_plan_shipping=True).create({'order_id': self.so.id})
|
||||||
self.assertTrue(planner.planning_option_ids, 'Must have one or more plans.')
|
self.assertTrue(planner.planning_option_ids, 'Must have one or more plans.')
|
||||||
@@ -396,7 +401,7 @@ class TestPlanner(common.TransactionCase):
|
|||||||
planner = self.env['sale.order.make.plan'].with_context(warehouse_domain=[('id', 'in', both_wh_ids)],
|
planner = self.env['sale.order.make.plan'].with_context(warehouse_domain=[('id', 'in', both_wh_ids)],
|
||||||
skip_plan_shipping=True).create({'order_id': self.so.id})
|
skip_plan_shipping=True).create({'order_id': self.so.id})
|
||||||
self.assertTrue(planner.planning_option_ids, 'Must have one or more plans.')
|
self.assertTrue(planner.planning_option_ids, 'Must have one or more plans.')
|
||||||
self.assertEqual(planner.planning_option_ids.warehouse_id, self.warehouse_1, 'If this fails, it will probably pass next time.')
|
self.assertEqual(planner.planning_option_ids.warehouse_id, self.warehouse_2)
|
||||||
self.assertTrue(planner.planning_option_ids.sub_options)
|
self.assertTrue(planner.planning_option_ids.sub_options)
|
||||||
|
|
||||||
sub_options = json_decode(planner.planning_option_ids.sub_options)
|
sub_options = json_decode(planner.planning_option_ids.sub_options)
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ except ImportError:
|
|||||||
SearchEngine = None
|
SearchEngine = None
|
||||||
|
|
||||||
from odoo import api, fields, models, tools
|
from odoo import api, fields, models, tools
|
||||||
from odoo.addons.base_geolocalize.models.res_partner import geo_find, geo_query_address
|
|
||||||
|
|
||||||
|
|
||||||
class FakeCollection():
|
class FakeCollection():
|
||||||
@@ -28,8 +27,11 @@ class FakeCollection():
|
|||||||
def filtered(self, f):
|
def filtered(self, f):
|
||||||
return filter(f, self.vals)
|
return filter(f, self.vals)
|
||||||
|
|
||||||
|
def sudo(self, *args, **kwargs):
|
||||||
|
return self
|
||||||
|
|
||||||
class FakePartner():
|
|
||||||
|
class FakePartner(FakeCollection):
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
"""
|
"""
|
||||||
'delivery.carrier'.verify_carrier(contact) ->
|
'delivery.carrier'.verify_carrier(contact) ->
|
||||||
@@ -71,11 +73,9 @@ class FakePartner():
|
|||||||
return self._date_localization
|
return self._date_localization
|
||||||
|
|
||||||
# The slow way.
|
# The slow way.
|
||||||
result = geo_find(geo_query_address(
|
geo_obj = self.env['base.geocoder']
|
||||||
city=self.city,
|
search = geo_obj.geo_query_address(city=self.city, state=self.state_id.name, country=self.country_id.name)
|
||||||
state=self.state_id.name,
|
result = geo_obj.geo_find(search, force_country=self.country_id.id)
|
||||||
country=self.country_id.name,
|
|
||||||
))
|
|
||||||
if result:
|
if result:
|
||||||
self.partner_latitude = result[0]
|
self.partner_latitude = result[0]
|
||||||
self.partner_longitude = result[1]
|
self.partner_longitude = result[1]
|
||||||
@@ -92,7 +92,7 @@ class FakePartner():
|
|||||||
return getattr(self, item)
|
return getattr(self, item)
|
||||||
|
|
||||||
|
|
||||||
class FakeOrderLine():
|
class FakeOrderLine(FakeCollection):
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
"""
|
"""
|
||||||
'delivery.carrier'.get_price_available(order) ->
|
'delivery.carrier'.get_price_available(order) ->
|
||||||
@@ -130,7 +130,7 @@ class FakeOrderLine():
|
|||||||
return getattr(self, item)
|
return getattr(self, item)
|
||||||
|
|
||||||
|
|
||||||
class FakeSaleOrder():
|
class FakeSaleOrder(FakeCollection):
|
||||||
"""
|
"""
|
||||||
partner_id :: used in shipping
|
partner_id :: used in shipping
|
||||||
partner_shipping_id :: is used in several places
|
partner_shipping_id :: is used in several places
|
||||||
@@ -209,7 +209,6 @@ class SaleOrderMakePlan(models.TransientModel):
|
|||||||
def plan_order(self, vals):
|
def plan_order(self, vals):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@api.multi
|
|
||||||
def select_option(self, option):
|
def select_option(self, option):
|
||||||
for plan in self:
|
for plan in self:
|
||||||
self.plan_order_option(plan.order_id, option)
|
self.plan_order_option(plan.order_id, option)
|
||||||
@@ -304,6 +303,8 @@ class SaleOrderMakePlan(models.TransientModel):
|
|||||||
domain = tools.safe_eval(domain)
|
domain = tools.safe_eval(domain)
|
||||||
else:
|
else:
|
||||||
domain = []
|
domain = []
|
||||||
|
if 'allowed_company_ids' in self.env.context:
|
||||||
|
domain.append(('company_id', 'in', self.env.context['allowed_company_ids']))
|
||||||
|
|
||||||
if self.env.context.get('warehouse_domain'):
|
if self.env.context.get('warehouse_domain'):
|
||||||
domain.extend(self.env.context.get('warehouse_domain'))
|
domain.extend(self.env.context.get('warehouse_domain'))
|
||||||
@@ -606,7 +607,7 @@ class SaleOrderMakePlan(models.TransientModel):
|
|||||||
return base_option
|
return base_option
|
||||||
|
|
||||||
def _is_in_stock(self, p_stock, buy_qty):
|
def _is_in_stock(self, p_stock, buy_qty):
|
||||||
return p_stock['real_qty_available'] >= buy_qty
|
return p_stock['type'] == 'consu' or p_stock['qty_available'] >= buy_qty
|
||||||
|
|
||||||
def _find_closest_warehouse_by_partner(self, warehouses, partner):
|
def _find_closest_warehouse_by_partner(self, warehouses, partner):
|
||||||
if not partner.date_localization:
|
if not partner.date_localization:
|
||||||
@@ -637,6 +638,8 @@ class SaleOrderMakePlan(models.TransientModel):
|
|||||||
def _fetch_product_stock(self, warehouses, products):
|
def _fetch_product_stock(self, warehouses, products):
|
||||||
output = {}
|
output = {}
|
||||||
for wh in warehouses:
|
for wh in warehouses:
|
||||||
|
products.invalidate_cache(fnames=['qty_available', 'virtual_available', 'incoming_qty', 'outgoing_qty'],
|
||||||
|
ids=products.ids)
|
||||||
products = products.with_context({'location': wh.lot_stock_id.id})
|
products = products.with_context({'location': wh.lot_stock_id.id})
|
||||||
output[wh.id] = {
|
output[wh.id] = {
|
||||||
p.id: {
|
p.id: {
|
||||||
@@ -644,22 +647,22 @@ class SaleOrderMakePlan(models.TransientModel):
|
|||||||
'virtual_available': p.virtual_available,
|
'virtual_available': p.virtual_available,
|
||||||
'incoming_qty': p.incoming_qty,
|
'incoming_qty': p.incoming_qty,
|
||||||
'outgoing_qty': p.outgoing_qty,
|
'outgoing_qty': p.outgoing_qty,
|
||||||
'real_qty_available': p.qty_available - p.outgoing_qty,
|
'sku': p.default_code or str(p.id),
|
||||||
'sku': p.default_code
|
'type': p.type,
|
||||||
} for p in products}
|
} for p in products}
|
||||||
return output
|
return output
|
||||||
|
|
||||||
def generate_shipping_options(self, base_option, order_fake):
|
def generate_shipping_options(self, base_option, order_fake):
|
||||||
# generate a carrier_id, amount, requested_date (promise date)
|
# generate a carrier_id, amount, requested_date (promise date)
|
||||||
# if base_option['carrier_id'] then that is the only carrier we want to collect rates for.
|
# if base_option['carrier_id'] then that is the only carrier we want to collect rates for.
|
||||||
product_lines = list(filter(lambda line: line.product_id.type == 'product', order_fake.order_line))
|
product_lines = list(filter(lambda line: line.product_id.type in ('product', 'consu'), order_fake.order_line))
|
||||||
domain = []
|
domain = []
|
||||||
for line in product_lines:
|
for line in product_lines:
|
||||||
policy = line.product_id.product_tmpl_id.get_planning_policy()
|
policy = line.product_id.product_tmpl_id.get_planning_policy()
|
||||||
if policy and policy.carrier_filter_id:
|
if policy and policy.carrier_filter_id:
|
||||||
domain.extend(tools.safe_eval(policy.carrier_filter_id.domain))
|
domain.extend(tools.safe_eval(policy.carrier_filter_id.domain))
|
||||||
carriers = self.get_shipping_carriers(base_option.get('carrier_id'), domain=domain)
|
carriers = self.get_shipping_carriers(base_option.get('carrier_id'), domain=domain)
|
||||||
_logger.info('generate_shipping_options:: base_optoin: ' + str(base_option) + ' order_fake: ' + str(order_fake) + ' carriers: ' + str(carriers))
|
_logger.info('generate_shipping_options:: base_option: ' + str(base_option) + ' order_fake: ' + str(order_fake) + ' carriers: ' + str(carriers))
|
||||||
|
|
||||||
if not carriers:
|
if not carriers:
|
||||||
return base_option
|
return base_option
|
||||||
@@ -800,7 +803,6 @@ class SaleOrderPlanningOption(models.TransientModel):
|
|||||||
values['sub_options'] = dumps(values['sub_options'], default=datetime_converter)
|
values['sub_options'] = dumps(values['sub_options'], default=datetime_converter)
|
||||||
return super(SaleOrderPlanningOption, self).create(values)
|
return super(SaleOrderPlanningOption, self).create(values)
|
||||||
|
|
||||||
@api.multi
|
|
||||||
def _compute_sub_options_text(self):
|
def _compute_sub_options_text(self):
|
||||||
for option in self:
|
for option in self:
|
||||||
sub_options = option.sub_options
|
sub_options = option.sub_options
|
||||||
@@ -839,7 +841,6 @@ class SaleOrderPlanningOption(models.TransientModel):
|
|||||||
sub_options = fields.Text('Sub Options JSON')
|
sub_options = fields.Text('Sub Options JSON')
|
||||||
sub_options_text = fields.Text('Sub Options', compute=_compute_sub_options_text)
|
sub_options_text = fields.Text('Sub Options', compute=_compute_sub_options_text)
|
||||||
|
|
||||||
@api.multi
|
|
||||||
def select_plan(self):
|
def select_plan(self):
|
||||||
for option in self:
|
for option in self:
|
||||||
option.plan_id.select_option(option)
|
option.plan_id.select_option(option)
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
<field name="type">form</field>
|
<field name="type">form</field>
|
||||||
<field name="arch" type="xml">
|
<field name="arch" type="xml">
|
||||||
<form>
|
<form>
|
||||||
<field name="planning_option_ids">
|
<field name="planning_option_ids" readonly="1" force_save="1">
|
||||||
<tree>
|
<tree>
|
||||||
<field name="warehouse_id" />
|
<field name="warehouse_id" />
|
||||||
<field name="date_planned" />
|
<field name="date_planned" />
|
||||||
@@ -32,7 +32,6 @@
|
|||||||
<record id="action_plan_sale_order" model="ir.actions.act_window">
|
<record id="action_plan_sale_order" model="ir.actions.act_window">
|
||||||
<field name="name">Plan Sale Order</field>
|
<field name="name">Plan Sale Order</field>
|
||||||
<field name="res_model">sale.order.make.plan</field>
|
<field name="res_model">sale.order.make.plan</field>
|
||||||
<field name="view_type">form</field>
|
|
||||||
<field name="view_mode">form</field>
|
<field name="view_mode">form</field>
|
||||||
<field name="view_id" ref="view_plan_sale_order" />
|
<field name="view_id" ref="view_plan_sale_order" />
|
||||||
<field name="target">new</field>
|
<field name="target">new</field>
|
||||||
|
|||||||
Reference in New Issue
Block a user