MIG sale_planner to 12.0

This commit is contained in:
Jared Kipe
2019-03-16 10:43:43 -07:00
parent 41c1cc0130
commit 2fdfc7f916
6 changed files with 85 additions and 37 deletions

View File

@@ -1,7 +1,7 @@
{
'name': 'Sale Order Planner',
'summary': 'Plans order dates and warehouses.',
'version': '11.0.1.0.0',
'version': '12.0.1.0.0',
'author': "Hibou Corp.",
'category': 'Sale',
'license': 'AGPL-3',
@@ -23,7 +23,6 @@ on the specific method's characteristics. (e.g. Do they deliver on Saturday?)
""",
'depends': [
'sale_order_dates',
'sale_sourced_by_line',
'base_geolocalize',
'delivery',
@@ -32,6 +31,7 @@ on the specific method's characteristics. (e.g. Do they deliver on Saturday?)
],
'demo': [],
'data': [
'security/ir.model.access.csv',
'wizard/order_planner_views.xml',
'views/sale.xml',
'views/stock.xml',

View File

@@ -1,6 +1,7 @@
from . import delivery
from . import partner
from . import planning
from . import product
from . import resource
from . import sale
from . import stock
from . import delivery
from . import product
from . import planning
from . import partner

View File

@@ -0,0 +1,47 @@
from functools import partial
from datetime import timedelta
from odoo import api, models
from odoo.addons.resource.models.resource import make_aware
class ResourceCalendar(models.Model):
_inherit = 'resource.calendar'
@api.multi
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.
"""
day_dt, revert = make_aware(day_dt)
# which method to use for retrieving intervals
if compute_leaves:
get_intervals = partial(self._work_intervals, domain=domain)
else:
get_intervals = self._attendance_intervals
if days >= 0:
found = set()
delta = timedelta(days=14)
for n in range(100):
dt = day_dt + delta * n
for start, stop, meta in get_intervals(dt, dt + delta):
found.add(start.date())
if len(found) >= days:
return revert(stop)
return False
elif days < 0:
days = abs(days)
found = set()
delta = timedelta(days=14)
for n in range(100):
dt = day_dt - delta * n
for start, stop, meta in reversed(get_intervals(dt - delta, dt)):
found.add(start.date())
if len(found) == days:
return revert(start)
return False
else:
return revert(day_dt)

View File

@@ -0,0 +1,2 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_sale_order_planning_policy,access_sale_order_planning_policy,model_sale_order_planning_policy,base.group_user,1,1,1,1
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_sale_order_planning_policy access_sale_order_planning_policy model_sale_order_planning_policy base.group_user 1 1 1 1

View File

@@ -48,26 +48,38 @@ class TestPlanner(common.TransactionCase):
'partner_latitude': 40.51525,
'partner_longitude': -107.54645,
})
hour_from = (self.today.hour - 1) % 24
hour_to = (self.today.hour + 1) % 24
if hour_to < hour_from:
hour_to, hour_from = hour_from, hour_to
self.warehouse_calendar_1 = self.env['resource.calendar'].create({
'name': 'Washington Warehouse Hours',
'tz': 'UTC',
'attendance_ids': [
(0, 0, {'name': 'today',
'dayofweek': str(self.today.weekday()),
'hour_from': (self.today.hour - 1) % 24,
'hour_to': (self.today.hour + 1) % 24}),
'hour_from': hour_from,
'hour_to': hour_to,
'day_period': 'morning'}),
(0, 0, {'name': 'tomorrow',
'dayofweek': str(self.tomorrow.weekday()),
'hour_from': (self.tomorrow.hour - 1) % 24,
'hour_to': (self.tomorrow.hour + 1) % 24}),
'hour_from': hour_from,
'hour_to': hour_to,
'day_period': 'morning'}),
]
})
self.warehouse_calendar_2 = self.env['resource.calendar'].create({
'name': 'Colorado Warehouse Hours',
'tz': 'UTC',
'attendance_ids': [
(0, 0, {'name': 'tomorrow',
'dayofweek': str(self.tomorrow.weekday()),
'hour_from': (self.tomorrow.hour - 1) % 24,
'hour_to': (self.tomorrow.hour + 1) % 24}),
'hour_from': hour_from,
'hour_to': hour_to,
'day_period': 'morning'}),
]
})
self.warehouse_1 = self.env['stock.warehouse'].create({

View File

@@ -1,7 +1,7 @@
from math import sin, cos, sqrt, atan2, radians
from json import dumps, loads
from copy import deepcopy
from datetime import datetime
from datetime import datetime, timedelta
from collections import defaultdict
from logging import getLogger
@@ -299,7 +299,6 @@ class SaleOrderMakePlan(models.TransientModel):
return Carrier.search(domain)
def _generate_base_option(self, order_fake, policy_group):
policy = False
flag_force_closest = False
warehouse_domain = False
if 'policy' in policy_group:
@@ -382,11 +381,11 @@ class SaleOrderMakePlan(models.TransientModel):
# nobody has stock!
primary_wh = self._find_closest_warehouse_by_partner(warehouses, order_fake.partner_shipping_id)
#order_fake.warehouse_id = primary_wh
return {'warehouse_id': primary_wh.id}
def generate_base_option(self, order_fake):
_logger.error('generate_base_option:')
__start_date = datetime.now() - timedelta(days=-30)
product_lines = list(filter(lambda line: line.product_id.type == 'product', order_fake.order_line))
if not product_lines:
return {}
@@ -410,7 +409,6 @@ class SaleOrderMakePlan(models.TransientModel):
policy_groups[0]['products'].append(p)
policy_groups[0]['buy_qty'][p.id] = buy_qty[p.id]
for _, policy_group in policy_groups.items():
product_set = self.env['product.product'].browse()
for p in policy_group['products']:
@@ -418,8 +416,7 @@ class SaleOrderMakePlan(models.TransientModel):
policy_group['products'] = product_set
policy_group['base_option'] = self._generate_base_option(order_fake, policy_group)
option_policy_groups = defaultdict(lambda: {'products': self.env['product.product'].browse(), 'policies': self.env['sale.order.planning.policy'].browse(), 'date_planned': '1900', 'sub_options': [],})
option_policy_groups = defaultdict(lambda: {'products': self.env['product.product'].browse(), 'policies': self.env['sale.order.planning.policy'].browse(), 'date_planned': __start_date, 'sub_options': [],})
for policy_id, policy_group in policy_groups.items():
base_option = policy_group['base_option']
_logger.error(' base_option: ' + str(base_option))
@@ -444,7 +441,7 @@ class SaleOrderMakePlan(models.TransientModel):
if not option_group['sub_options']:
del option_group['sub_options']
else:
sub_options = defaultdict(lambda: {'date_planned': '1900', 'product_ids': [], 'product_skus': []})
sub_options = defaultdict(lambda: {'date_planned': __start_date, 'product_ids': [], 'product_skus': []})
remaining_products = option_group['products']
for options in option_group['sub_options']:
for wh_id, option in options.items():
@@ -466,7 +463,7 @@ class SaleOrderMakePlan(models.TransientModel):
# At this point we should have all of the policy options collapsed.
# Collapse warehouse options.
base_option = {'date_planned': '1900', 'products': self.env['product.product'].browse()}
base_option = {'date_planned': __start_date, 'products': self.env['product.product'].browse()}
for wh_id, intermediate_option in option_policy_groups.items():
_logger.error(' base_option: ' + str(base_option))
_logger.error(' intermediate_option: ' + str(intermediate_option))
@@ -596,7 +593,7 @@ class SaleOrderMakePlan(models.TransientModel):
def _next_warehouse_shipping_date(self, warehouse):
if warehouse.shipping_calendar_id:
return fields.Datetime.to_string(warehouse.shipping_calendar_id.plan_days(0.01, fields.Datetime.from_string(fields.Datetime.now()), compute_leaves=True))
return warehouse.shipping_calendar_id.plan_days_end(0, fields.Datetime.now(), compute_leaves=True)
return False
@api.model
@@ -683,15 +680,11 @@ class SaleOrderMakePlan(models.TransientModel):
max_requested_date = None
for option in sub_options.values():
requested_date = option.get('requested_date')
if requested_date and isinstance(requested_date, str):
requested_date = fields.Datetime.from_string(requested_date)
if requested_date and not max_requested_date:
max_requested_date = requested_date
elif requested_date:
if requested_date > max_requested_date:
max_requested_date = requested_date
if max_requested_date:
return fields.Datetime.to_string(max_requested_date)
return max_requested_date
def _get_max_transit_days(self, sub_options):
@@ -748,7 +741,7 @@ class SaleOrderMakePlan(models.TransientModel):
option = deepcopy(base_option)
option['carrier_id'] = carrier.id
option['shipping_price'] = final_price
option['requested_date'] = fields.Datetime.to_string(date_delivered) if (date_delivered and isinstance(date_delivered, datetime)) else date_delivered
option['requested_date'] = date_delivered
option['transit_days'] = transit_days
return option
except Exception as e:
@@ -759,13 +752,15 @@ class SaleOrderMakePlan(models.TransientModel):
return None
class SaleOrderPlanningOption(models.TransientModel):
_name = 'sale.order.planning.option'
_description = 'Order Planning Option'
def create(self, values):
if 'sub_options' in values and not isinstance(values['sub_options'], str):
for wh_id, option in values['sub_options'].items():
if option.get('date_planned'):
option['date_planned'] = str(option['date_planned'])
values['sub_options'] = dumps(values['sub_options'])
return super(SaleOrderPlanningOption, self).create(values)
@@ -782,13 +777,9 @@ class SaleOrderPlanningOption(models.TransientModel):
line = ''
for wh_id, wh_option in sub_options.items():
product_skus = wh_option.get('product_skus', [])
date_planned = wh_option.get('date_planned')
product_skus = ', '.join(product_skus)
requested_date = wh_option.get('requested_date', '')
if requested_date:
requested_date = self._context_datetime(requested_date)
date_planned = wh_option.get('date_planned', '')
if date_planned:
date_planned = self._context_datetime(date_planned)
shipping_price = float(wh_option.get('shipping_price', 0.0))
transit_days = int(wh_option.get('transit_days', 0))
@@ -817,8 +808,3 @@ class SaleOrderPlanningOption(models.TransientModel):
for option in self:
option.plan_id.select_option(option)
return
def _context_datetime(self, date):
date = fields.Datetime.from_string(date)
date = fields.Datetime.context_timestamp(self, date)
return fields.Datetime.to_string(date)