-
-
Notifications
You must be signed in to change notification settings - Fork 627
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
14 changed files
with
1,018 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
# -*- coding: utf-8 -*- | ||
############################################################################## | ||
# | ||
# Author: Leonardo Pistone | ||
# Copyright 2014 Camptocamp SA | ||
# | ||
# This program is free software: you can redistribute it and/or modify | ||
# it under the terms of the GNU Affero General Public License as | ||
# published by the Free Software Foundation, either version 3 of the | ||
# License, or (at your option) any later version. | ||
# | ||
# This program is distributed in the hope that it will be useful, | ||
# but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
# GNU Affero General Public License for more details. | ||
# | ||
# You should have received a copy of the GNU Affero General Public License | ||
# along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
# | ||
############################################################################## | ||
|
||
from . import model | ||
from . import wizard |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
# -*- coding: utf-8 -*- | ||
############################################################################## | ||
# | ||
# Author: Leonardo Pistone | ||
# Copyright 2014 Camptocamp SA | ||
# | ||
# This program is free software: you can redistribute it and/or modify | ||
# it under the terms of the GNU Affero General Public License as | ||
# published by the Free Software Foundation, either version 3 of the | ||
# License, or (at your option) any later version. | ||
# | ||
# This program is distributed in the hope that it will be useful, | ||
# but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
# GNU Affero General Public License for more details. | ||
# | ||
# You should have received a copy of the GNU Affero General Public License | ||
# along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
# | ||
############################################################################## | ||
{"name": "Stock Picking Compute Delivery Date", | ||
"version": "1.0", | ||
"author": "Camptocamp", | ||
"category": 'Warehouse Management', | ||
"website": "http://camptocamp.com", | ||
"description": """ | ||
Stock Picking Compute Delivery Date | ||
=================================== | ||
This module allows to recompute the delivery dates of outgoing Moves based on | ||
the dates of incoming Moves for the same product. That means that for every | ||
outgoing move we update the expected date based on our best guess based on the | ||
information we have. | ||
The method we use is different for Make to Order and Make to Stock products. | ||
In all cases, at the end we add the security margin defined for the company. | ||
Make to Order | ||
============= | ||
For every outgoing Move the system finds the corresponding incoming Move. That | ||
is identified by the 'move_dest_id' (Destination Move) field. In the usual | ||
case where the incoming Move is generated by a Purchase Order that in turn is | ||
generated by the Scheduler, that field is filled automatically by OpenERP. | ||
For that module we use only that field, so we do not need a dependency on the | ||
Purchase module. | ||
The date of the incoming Move, plus the Security Days defined in the Company, | ||
is then written in the outcoming Move. The existing expected date is not used | ||
and is overwritten. | ||
Make to Stock | ||
============= | ||
For Make To Stock products, the logic is more complex. | ||
First, for each product, the system takes the list of outgoing Moves, and the | ||
list of incoming Moves, each ordered by current expected date. | ||
It then loops over the outgoing Moves, and for each one, it tries to find at | ||
what time we will have enough stock to make the delivery. That information is | ||
based on the current stock, and on the day when we expect to receive | ||
deliveries of the same product. | ||
If incoming Moves run out, we stop processing the product and leave any | ||
remaining outgoing Moves untouched. We may decide to change that logic later. | ||
User Interface | ||
============== | ||
The process can be run in three ways: | ||
* Select a few lines from the Products tree view, and from "More" click | ||
"Compute delivery dates for all products". | ||
* Click Warehouse / Products / Compute all delivery dates. | ||
* A Scheduled action is provided, initially disabled. | ||
Possible future improvements | ||
============================ | ||
* Behave differently when incoming Moves run out | ||
* Use the Priority field to decide which outgoing Moves to process first (now | ||
only the expected date is used) | ||
""", | ||
"complexity": "normal", | ||
"depends": [ | ||
"sale_stock", | ||
"stock", | ||
], | ||
"data": [ | ||
'wizard/by_product.xml', | ||
'wizard/all_products.xml', | ||
'data/cron.xml', | ||
], | ||
"test": [ | ||
'test/setup_user.yml', | ||
'test/test_mts_1.yml', | ||
'test/test_mts_2.yml', | ||
'test/test_mto.yml', | ||
], | ||
"installable": True, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
<?xml version="1.0" encoding="utf-8"?> | ||
<openerp> | ||
<data noupdate="1"> | ||
|
||
<record forcecreate="True" id="cron_compute_all_delivery_dates" model="ir.cron"> | ||
<field name="name">Recompute dates of all deliveries</field> | ||
<field name="active" eval="False" /> | ||
<field name="user_id" ref="base.user_root"/> | ||
<field name="interval_number">1</field> | ||
<field name="interval_type">days</field> | ||
<field name="numbercall">-1</field> | ||
<field eval="False" name="doall" /> | ||
<field name="model">stock.picking.out</field> | ||
<field name="function">compute_all_delivery_dates</field> | ||
<field name="args">()</field> | ||
</record> | ||
|
||
</data> | ||
</openerp> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
# -*- coding: utf-8 -*- | ||
############################################################################## | ||
# | ||
# Author: Leonardo Pistone | ||
# Copyright 2014 Camptocamp SA | ||
# | ||
# This program is free software: you can redistribute it and/or modify | ||
# it under the terms of the GNU Affero General Public License as | ||
# published by the Free Software Foundation, either version 3 of the | ||
# License, or (at your option) any later version. | ||
# | ||
# This program is distributed in the hope that it will be useful, | ||
# but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
# GNU Affero General Public License for more details. | ||
# | ||
# You should have received a copy of the GNU Affero General Public License | ||
# along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
# | ||
############################################################################## | ||
|
||
from . import picking |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,169 @@ | ||
# -*- coding: utf-8 -*- | ||
############################################################################## | ||
# | ||
# Author: Leonardo Pistone | ||
# Copyright 2014 Camptocamp SA | ||
# | ||
# This program is free software: you can redistribute it and/or modify | ||
# it under the terms of the GNU Affero General Public License as | ||
# published by the Free Software Foundation, either version 3 of the | ||
# License, or (at your option) any later version. | ||
# | ||
# This program is distributed in the hope that it will be useful, | ||
# but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
# GNU Affero General Public License for more details. | ||
# | ||
# You should have received a copy of the GNU Affero General Public License | ||
# along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
# | ||
############################################################################## | ||
|
||
import logging | ||
import datetime as dt | ||
|
||
from openerp.osv import orm | ||
from openerp.tools import DEFAULT_SERVER_DATETIME_FORMAT as DT_FORMAT | ||
|
||
_logger = logging.getLogger(__name__) | ||
|
||
|
||
class PlanFinished(Exception): | ||
"""The available future stock is insufficient to handle all deliveries. | ||
After that is raised, we could stop processing the remaining deliveries, | ||
or consider that a failure of the whole process. | ||
Especially in the first case, you could argue this is an exception for | ||
control flow, and should then be factored out. | ||
""" | ||
pass | ||
|
||
|
||
class StockPickingOut(orm.Model): | ||
|
||
_inherit = 'stock.picking.out' | ||
|
||
def _security_delta(self, cr, uid, product, context=None): | ||
return dt.timedelta(days=product.company_id.security_lead or 0.0) | ||
|
||
def _availability_plan(self, cr, uid, product, context=None): | ||
move_obj = self.pool['stock.move'] | ||
|
||
stock_now = product.qty_available | ||
today = dt.datetime.today() | ||
security_delta = self._security_delta(cr, uid, product, context) | ||
plan = [{'date': today + security_delta, 'quantity': stock_now}] | ||
|
||
move_in_ids = move_obj.search(cr, uid, [ | ||
('product_id', '=', product.id), | ||
('picking_id.type', '=', 'in'), | ||
('state', 'in', ('confirmed', 'assigned', 'pending')), | ||
], order='date_expected', context=context) | ||
|
||
for move_in in move_obj.browse(cr, uid, move_in_ids, context=context): | ||
plan.append({ | ||
'date': dt.datetime.strptime(move_in.date_expected, DT_FORMAT) | ||
+ security_delta, | ||
'quantity': move_in.product_qty | ||
}) | ||
return iter(plan) | ||
|
||
def compute_delivery_dates(self, cr, uid, product, context=None): | ||
if product.procure_method == 'make_to_stock': | ||
return self.compute_mts_delivery_dates(cr, uid, product, context) | ||
else: | ||
return self.compute_mto_delivery_dates(cr, uid, product, context) | ||
|
||
def compute_mto_delivery_dates(self, cr, uid, product, context=None): | ||
move_obj = self.pool['stock.move'] | ||
security_delta = self._security_delta(cr, uid, product, context) | ||
|
||
move_out_ids = move_obj.search(cr, uid, [ | ||
('product_id', '=', product.id), | ||
('picking_id.type', '=', 'out'), | ||
('state', 'in', ('confirmed', 'assigned', 'pending')), | ||
], context=context) | ||
|
||
for move_out_id in move_out_ids: | ||
move_in_ids = move_obj.search(cr, uid, [ | ||
('move_dest_id', '=', move_out_id) | ||
], order="date_expected desc", context=context) | ||
if move_in_ids: | ||
move_in = move_obj.browse(cr, uid, move_in_ids[0], | ||
context=context) | ||
|
||
move_in_date = dt.datetime.strptime( | ||
move_in.date_expected, DT_FORMAT | ||
) | ||
new_date_str = dt.datetime.strftime( | ||
move_in_date + security_delta, DT_FORMAT | ||
) | ||
move_obj.write(cr, uid, move_out_id, { | ||
'date_expected': new_date_str | ||
}, context=context) | ||
|
||
def compute_mts_delivery_dates(self, cr, uid, product, context=None): | ||
move_obj = self.pool['stock.move'] | ||
|
||
plan = self._availability_plan(cr, uid, product, context=context) | ||
|
||
move_out_ids = move_obj.search(cr, uid, [ | ||
('product_id', '=', product.id), | ||
('picking_id.type', '=', 'out'), | ||
('state', 'in', ('confirmed', 'assigned', 'pending')), | ||
], order='date_expected', context=context) | ||
|
||
current_plan = plan.next() | ||
|
||
try: | ||
for move_out in move_obj.browse(cr, uid, move_out_ids, | ||
context=context): | ||
remaining_out_qty = move_out.product_qty | ||
|
||
while remaining_out_qty > 0.0: | ||
if current_plan['quantity'] >= remaining_out_qty: | ||
current_plan['quantity'] -= remaining_out_qty | ||
new_date_str = dt.datetime.strftime( | ||
current_plan['date'], DT_FORMAT | ||
) | ||
move_obj.write(cr, uid, move_out.id, { | ||
'date_expected': new_date_str, | ||
}, context=context) | ||
remaining_out_qty = 0.0 | ||
else: | ||
remaining_out_qty -= current_plan['quantity'] | ||
try: | ||
current_plan = plan.next() | ||
except StopIteration: | ||
raise PlanFinished | ||
except PlanFinished: | ||
_logger.debug( | ||
u'There is not enough planned stock to set dates for all ' | ||
u'outgoing moves. Remaining ones are left untouched.' | ||
) | ||
return True | ||
|
||
def compute_all_delivery_dates(self, cr, uid, context=None): | ||
move_obj = self.pool['stock.move'] | ||
prod_obj = self.pool['product.product'] | ||
|
||
moves_out_grouped = move_obj.read_group( | ||
cr, | ||
uid, | ||
domain=[ | ||
('picking_id.type', '=', 'out'), | ||
('state', 'in', ('confirmed', 'assigned', 'pending')) | ||
], | ||
fields=['product_id'], | ||
groupby=['product_id'], | ||
context=context, | ||
) | ||
|
||
product_ids = [g['product_id'][0] for g in moves_out_grouped] | ||
|
||
for product in prod_obj.browse(cr, uid, product_ids, context=context): | ||
self.compute_delivery_dates(cr, uid, product, context=context) | ||
|
||
return True |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
- | ||
I create a user to run the tests in this module | ||
- | ||
!record {model: res.users, id: res_users_emma}: | ||
name: Emma | ||
login: Emma | ||
groups_id: | ||
- stock.group_stock_user |
Oops, something went wrong.