Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[8.0] ADD Putaway strategy allowing to set a location per product. #151

Merged
merged 12 commits into from
Sep 29, 2016
Merged
89 changes: 89 additions & 0 deletions stock_putaway_product/README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
.. 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

============================
Putaway strategy per product
============================

This module extends the functionality of the odoo putaway strategy.
It defines a new type of putaway strategy where users can set a specific
stock location per product.

On the product form, the case, rack, location fields are replaced with a
specific putaway strategy and location id for the product.

A putaway strategy can be used to ensure that incoming products will be
stored in the location set on the product form.

Copy link
Sponsor Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would add:

A recommended set-up is to create a separate Putaway Strategy per Warehouse. This will ensure that the same product will be placed in the appropiate location in each warehouse it is received.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This suggestion is attended in the next line.

A recommended set-up is to create a separate putaway strategy for each
warehouse. This will ensure that the same product will be placed in the
appropriate location in each warehouse it is received.

Installation
============

To install this module, just click the install button.

Configuration
=============

To configure this module, you need to:

#. Go to Settings -> Configuration -> Warehouse
#. Enable "Manage multiple locations and warehouses'
#. Enable "Manage advanced routes for your warehouse"
#. Go to Warehouse -> Configuration -> Locations
#. On the main inventory location of your warehouse,
set a new putaway strategy.
#. For the new putaway strategy, select 'Fixed per product location'
as method

Usage
=====

To use this module, you need to:

#. Select the proper stock locations for each product on the product form
on the "Inventory" tab

.. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas
:alt: Try me on Runbot
:target: https://runbot.odoo-community.org/runbot/153/8.0

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

Bugs are tracked on `GitHub Issues
<https://github.com/OCA/stock-logistics-warehouse/issues>`_. In
case of trouble, please
check there if your issue has already been reported. If you spotted it first,
help us smashing it by providing a detailed and welcomed `feedback
<https://github.com/OCA/stock-logistics-warehouse/issues/new?body=module:%20stock_putaway_product%0Aversion:%208.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.

Images
------

* Odoo Community Association: `Icon <https://github.com/OCA/maintainer-tools/blob/master/template/module/static/description/icon.svg>`_.

Contributors
------------

* Jos De Graeve - Apertoso N.V. <Jos.DeGraeve@apertoso.be>


Maintainer
----------

.. image:: https://odoo-community.org/logo.png
:alt: Odoo Community Association
:target: https://odoo-community.org

This module is maintained by the OCA.

OCA, or the Odoo Community Association, is a nonprofit organization whose
mission is to support the collaborative development of Odoo features and
promote its widespread use.

To contribute to this module, please visit https://odoo-community.org.

6 changes: 6 additions & 0 deletions stock_putaway_product/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# -*- coding: utf-8 -*-
# © 2016 Jos De Graeve - Apertoso N.V. <Jos.DeGraeve@apertoso.be>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from . import models
from . import tests
from . import wizard
26 changes: 26 additions & 0 deletions stock_putaway_product/__openerp__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# -*- coding: utf-8 -*-
# © 2016 Jos De Graeve - Apertoso N.V. <Jos.DeGraeve@apertoso.be>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
{
'name': 'Putaway strategy per product',
'summary': 'Set a product location and put-away strategy per product',
'version': '8.0.1.0.0',
'category': 'Inventory',
'website': 'http://www.apertoso.be',
'author': 'Apertoso N.V., Odoo Community Association (OCA)',
'license': 'AGPL-3',
'applicaton': False,
'installable': True,
'depends': [
'product',
'stock'
],
'data': [
'views/product.xml',
'views/product_putaway.xml',
'security/ir.model.access.csv',
],
'demo': [
'demo/product_putaway.xml',
]
}
29 changes: 29 additions & 0 deletions stock_putaway_product/demo/product_putaway.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>

<record id="product_putaway_per_product_wh"
model="product.putaway">
<field name="name">WH - Putaway Per Product</field>
<field name="method">per_product</field>
</record>

<record id="stock.stock_location_stock"
model="stock.location">
<field name="putaway_strategy_id"
ref="product_putaway_per_product_wh"/>
</record>

<record id="product_putaway_strategy_product_4"
model="stock.product.putaway.strategy">
<field name="product_product_id"
ref="product.product_product_4"/>
<field name="product_template_id"
ref="product.product_product_4_product_template"/>
<field name="putaway_id"
ref="product_putaway_per_product_wh"/>
<field name="fixed_location_id"
ref="stock.stock_location_components"/>
</record>
</data>
</openerp>
4 changes: 4 additions & 0 deletions stock_putaway_product/models/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# -*- coding: utf-8 -*-

from . import product_putaway
from . import product
25 changes: 25 additions & 0 deletions stock_putaway_product/models/product.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# -*- coding: utf-8 -*-

import logging

from openerp import models, fields

_logger = logging.getLogger(__name__)


class ProductTemplate(models.Model):
_inherit = 'product.template'

product_putaway_ids = fields.One2many(
comodel_name='stock.product.putaway.strategy',
inverse_name='product_template_id',
string="Product stock locations")

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@JosDeGraeve After some tests (and intense reflection to understand :-)), it does not seem to work with variants... With this related field, its impossible to create put-away strategy for two variants on same product.

Example: With demo data enable, iPad Retina Display already has a put-away strategy for variant 16 GB, White, 2.4 GHz. If you try to create a strategy for another variant, it will raise a Constraint Error (on unique(putaway_id,product_product_id)).

The One2many widget try to add the new strategy on product.template.product_putaway_ids but with related, it seems that he write the new strategy on first variant (regardless of the product_product_id's value in putaway.strategy) .

I am a new developer Odoo, then a confirmation from an expert would be appreciated but I think you cannot write on this field...


class ProductProduct(models.Model):
_inherit = 'product.product'

product_putaway_ids = fields.One2many(
comodel_name='stock.product.putaway.strategy',
inverse_name='product_product_id',
string="Product stock locations")
80 changes: 80 additions & 0 deletions stock_putaway_product/models/product_putaway.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
# -*- coding: utf-8 -*-
# © 2016 Jos De Graeve - Apertoso N.V. <Jos.DeGraeve@apertoso.be>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).

import logging

from openerp import models, fields, api, _

_logger = logging.getLogger(__name__)


class ProductPutawayStrategy(models.Model):
_inherit = 'product.putaway'

@api.model
def _get_putaway_options(self):
ret = super(ProductPutawayStrategy, self)._get_putaway_options()
return ret + [('per_product', 'Fixed per product location')]

product_location_ids = fields.One2many(
comodel_name='stock.product.putaway.strategy',
inverse_name='putaway_id',
string='Fixed per product location',
copy=True)
method = fields.Selection(
selection=_get_putaway_options,
string="Method",
required=True)

@api.model
def putaway_apply(self, putaway_strategy, product):
if putaway_strategy.method == 'per_product':
strategy_domain = [
('putaway_id', '=', putaway_strategy.id),
('product_product_id', '=', product.id),
]
for strategy in putaway_strategy.product_location_ids.search(
strategy_domain, limit=1):
return strategy.fixed_location_id.id
else:
return super(ProductPutawayStrategy, self).putaway_apply(
putaway_strategy, product)


class FixedPutawayStrat(models.Model):
_name = 'stock.product.putaway.strategy'
_rec_name = 'product_product_id'

_sql_constraints = [(
'putaway_product_unique',
'unique(putaway_id,product_product_id)',
_('There can only be one fixed location per product!')
)]

@api.model
def get_default_inventory_id(self):
return self.env['stock.inventory'].search(
[('id', '=', self.env.context.get('active_id', False))])

putaway_id = fields.Many2one(
comodel_name='product.putaway',
sting='Put Away Method',
required=True,
select=True)
product_template_id = fields.Many2one(
comodel_name='product.template',
string='Product Template',
select=True,
required=True)
product_product_id = fields.Many2one(
comodel_name='product.product',
string='Product Variant',
required=True,
select=True,
domain=[('product_tmpl_id', '=', 'product_template_id.id')])
fixed_location_id = fields.Many2one(
comodel_name='stock.location',
string='Location',
required=True,
domain=[('usage', '=', 'internal')])
3 changes: 3 additions & 0 deletions stock_putaway_product/security/ir.model.access.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_stock_product_putaway_strategy,stock_product_putaway_strategy managers,model_stock_product_putaway_strategy,stock.group_stock_manager,1,1,1,1
access_stock_product_putaway_user,stock_product_putaway_strategy user,model_stock_product_putaway_strategy,stock.group_stock_user,1,0,0,0
5 changes: 5 additions & 0 deletions stock_putaway_product/tests/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# -*- coding: utf-8 -*-
# © 2016 Jos De Graeve - Apertoso N.V. <Jos.DeGraeve@apertoso.be>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).

from . import test_product_putaway
43 changes: 43 additions & 0 deletions stock_putaway_product/tests/test_product_putaway.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# -*- coding: utf-8 -*-
# © 2016 Jos De Graeve - Apertoso N.V. <Jos.DeGraeve@apertoso.be>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).

from openerp.tests import common


class TestProductPutaway(common.TransactionCase):
# Check if "per_product" is a valid putaway method
def test_01_putaway_methods(self):
field_method = self.env[
'product.putaway']._fields.get('method')
self.assertIn('per_product', field_method.get_values(self.env))

def test_02_putway_apply(self):
putaway_per_product = self.browse_ref(
'stock_putaway_product.product_putaway_per_product_wh')
product_ipad = self.browse_ref(
'product.product_product_4')
location_shelf1 = self.browse_ref(
'stock.stock_location_components')

self.assertEqual(
self.env['product.putaway'].putaway_apply(
putaway_per_product, product_ipad),
location_shelf1.id)

def test_03_stock_change_product_qty_default(self):
product_ipad = self.browse_ref(
'product.product_product_4')
location_shelf1 = self.browse_ref(
'stock.stock_location_components')

wiz_obj = self.env['stock.change.product.qty']

test_context = {
'active_model': 'product.product',
'active_id': product_ipad.id,
}
wiz_instance = wiz_obj.with_context(test_context).create({})
self.assertEqual(
wiz_instance.location_id,
location_shelf1)
Loading