Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions sale_order_rental_stock_forecast/README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
:alt: License: AGPL-3

=================================
Sale order rental stock forecast
=================================

* Scheluded for generate "product st ock forecast", modified to search
the biggest "expected end date" of the sales orders.


Bug Tracker
===========

Bugs are tracked on `GitHub Issues
<https://github.com/avanzosc/sale-addons/issues>`_. In case of trouble, please
check there if your issue has already been reported. If you spotted it first,
help us smash it by providing detailed and welcomed feedback.

Credits
=======

Contributors
------------
* Ana Juaristi <[email protected]>
* Alfredo de la Fuente <[email protected]>

Do not contact contributors directly about support or help with technical issues.
1 change: 1 addition & 0 deletions sale_order_rental_stock_forecast/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from . import models
18 changes: 18 additions & 0 deletions sale_order_rental_stock_forecast/__manifest__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Copyright 2019 Alfredo de la Fuente - AvanzOSC
# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html
{
"name": "Sale Order Rental Stock Forecast",
"version": "12.0.1.0.0",
"license": "AGPL-3",
"depends": [
"sale_order_rental",
"stock_forecast",
],
"author": "AvanzOSC",
"website": "http://www.avanzosc.es",
"category": "Warehouse",
"data": [
],
"installable": True,
'auto_install': True
}
2 changes: 2 additions & 0 deletions sale_order_rental_stock_forecast/models/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
from . import product_product_stock_forecast
from . import sale_order
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Copyright 2019 Alfredo de la Fuente - AvanzOSC
# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html
from odoo import models


class ProductProductStockForecast(models.Model):
_inherit = 'product.product.stock.forecast'

def _catch_max_fec_for_calc_qty_per_day(self, moves):
date_expected = super(
ProductProductStockForecast,
self)._catch_max_fec_for_calc_qty_per_day(moves)
cond = [('expected_end_date', '!=', False)]
sales = self.env['sale.order'].search(cond)
if sales:
sale = max(sales, key=lambda x: x.expected_end_date)
if sale.expected_end_date > date_expected:
return sale.expected_end_date
return date_expected
18 changes: 18 additions & 0 deletions sale_order_rental_stock_forecast/models/sale_order.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Copyright 2019 Alfredo de la Fuente - AvanzOSC
# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html
from odoo import models


class SaleOrder(models.Model):
_inherit = 'sale.order'

def action_view_products_stock_forecast_from_sale(self):
res = super(
SaleOrder, self).action_view_products_stock_forecast_from_sale()
if (self.expected_delivery_date and self.expected_end_date and
res.get('domain', False)):
cond = [('product_id', 'in', self.sale_product_ids.ids),
('date', '>=', self.expected_delivery_date),
('date', '<=', self.expected_end_date)]
res['domain'] = cond
return res
1 change: 1 addition & 0 deletions sale_order_rental_stock_forecast/tests/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from . import test_sale_order_rental_stock_forecast
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
# -*- coding: utf-8 -*-
# (c) 2016 Alfredo de la Fuente - AvanzOSC
# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html
from odoo.tests.common import TransactionCase
from openerp import fields
from dateutil.relativedelta import relativedelta


class TestSaleOrderRentalStockForecast(TransactionCase):

def setUp(self):
super(TestSaleOrderRentalStockForecast, self).setUp()
self.forecast_obj = self.env['product.product.stock.forecast']
wiz_obj = self.env['stock.immediate.transfer']
location_obj = self.env['stock.location']
partner_obj = self.env['res.partner']
picking_obj = self.env['stock.picking']
cond = [('code', '=', 'incoming')]
self.in_picking_type = self.env['stock.picking.type'].search(
cond, limit=1)
self.location_supplier = location_obj.search(
[('usage', '=', 'supplier')], limit=1)
self.location_customer = location_obj.search(
[('usage', '=', 'customer')], limit=1)
cond = [('code', '=', 'outgoing')]
self.out_picking_type = self.env['stock.picking.type'].search(
cond, limit=1)
cond = [('supplier', '=', True)]
self.supplier = partner_obj.search(cond, limit=1)
cond = [('customer', '=', True)]
self.customer = partner_obj.search(cond, limit=1)
vals = {'name': 'Product for TestSaleOrderRentalStockForecast',
'type': 'product'}
self.product = self.env['product.product'].create(vals)
picking_vals = {
'partner_id': self.supplier.id,
'picking_type_id': self.in_picking_type.id,
'location_id': self.location_supplier.id,
'location_dest_id':
self.in_picking_type.default_location_dest_id.id}
picking_line_vals = {
'product_id': self.product.id,
'name': self.product.name,
'product_uom_qty': 20,
'product_uom': self.product.uom_id.id}
picking_vals['move_lines'] = [(0, 0, picking_line_vals)]
self.picking = picking_obj.create(picking_vals)
self.picking.action_confirm()
wizard_vals = {'pick_ids': [(6, 0, self.picking.ids)]}
wizard = wiz_obj.create(wizard_vals)
wizard.process()
date_expected = (fields.Datetime.from_string(
self.picking.move_lines[0].date_expected) - relativedelta(years=1))
date_expected_without_hour = (fields.Datetime.from_string(
self.picking.move_lines[0].date_expected_without_hour) -
relativedelta(years=1))
self.picking.move_lines[0].write(
{'date_expected': date_expected,
'date_expected_without_hour': date_expected_without_hour})
self.picking2 = self.picking.copy()
self.picking2.move_lines[0].product_uom_qty = 5
self.picking2.action_confirm()
wizard_vals = {'pick_ids': [(6, 0, self.picking2.ids)]}
wizard = wiz_obj.create(wizard_vals)
wizard.process()
date_expected = (fields.Datetime.from_string(
self.picking2.move_lines[0].date_expected) +
relativedelta(years=1))
date_expected_without_hour = (fields.Datetime.from_string(
self.picking2.move_lines[0].date_expected_without_hour) +
relativedelta(years=1))
self.picking2.move_lines[0].write(
{'date_expected': date_expected,
'date_expected_without_hour': date_expected_without_hour})
picking_vals = {
'partner_id': self.customer.id,
'picking_type_id': self.out_picking_type.id,
'location_id': self.out_picking_type.default_location_src_id.id,
'location_dest_id': self.location_customer.id}
picking_line_vals = {
'product_id': self.product.id,
'name': self.product.name,
'product_uom_qty': 2,
'product_uom': self.product.uom_id.id}
picking_vals['move_lines'] = [(0, 0, picking_line_vals)]
self.out_picking = picking_obj.create(picking_vals)
self.out_picking.action_confirm()
self.out_picking.move_lines[0].write(
{'date_expected': self.picking2.move_lines[0].date_expected,
'date_expected_without_hour':
self.picking2.move_lines[0].date_expected_without_hour})
self.today = fields.Date.from_string(fields.Date.today())
self.today_10 = (self.today + relativedelta(days=10))
sale_vals = {
'partner_id': self.customer.id,
'expected_delivery_date': self.today,
'expected_end_date': self.today_10}
sale_line_vals = {
'product_id': self.product.id,
'name': self.product.name,
'originator_id': 1,
'product_uom': self.product.uom_id.id,
'price_unit': 25.0}
sale_vals['order_line'] = [(0, 0, sale_line_vals)]
self.sale = self.env['sale.order'].create(sale_vals)

def test_sale_order_rental_stock_forecast(self):
domain = [('product_id', 'in', [self.product.id]),
('date', '>=', self.today),
('date', '<=', self.today_10)]
res = self.sale.action_view_products_stock_forecast_from_sale()
self.assertEqual(res.get('domain'), domain)
cond = [('product_id', '=', self.product.id)]
forecasts = self.forecast_obj.search(cond)
forecasts = forecasts.filtered(
lambda c: c.qty_available != 0 or c.incoming_qty != 0 or
c.outgoing_qty != 0 or c.virtual_available != 0)
maxforecast = max(forecasts, key=lambda x: x.date)
self.assertEqual(maxforecast.date, self.today_10)
self.assertEqual(maxforecast.qty_available, 25.0)
self.assertEqual(maxforecast.incoming_qty, 0.0)
self.assertEqual(maxforecast.outgoing_qty, 2.0)
self.assertEqual(maxforecast.virtual_available, 23.0)