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] [NEW] stock_orderpoint_manual_procurement #210

Merged
Merged
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
67 changes: 67 additions & 0 deletions stock_orderpoint_manual_procurement/README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
.. 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

===================================
Stock Orderpoint Manual Procurement
===================================

This module allows users to manually start procurements from the list of
reordering rules, based on the quantity that is recommended to be procured.


Usage
=====

Go to 'Configuration / Reordering Rules' and review the quantity recommended
to be procured. You can now start the procurement for a single or a list of
reordering rules.

The recommended quantity to procure is adjusted to the procurement unit of
measure indicated in the reordering rule.

If you want users to be able to change the recommended quantity to procure,
you should assign them to the security group 'Change quantity in manual
procurements from reordering rules', under 'Settings / Users / Users'.

.. 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.

Credits
=======

Images
------

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

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

* Jordi Ballester Alomar <jordi.ballester@eficent.com>


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.
7 changes: 7 additions & 0 deletions stock_orderpoint_manual_procurement/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# -*- coding: utf-8 -*-
# Copyright 2016 Eficent Business and IT Consulting Services S.L.
# (http://www.eficent.com)
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).

from . import models
from . import wizards
24 changes: 24 additions & 0 deletions stock_orderpoint_manual_procurement/__openerp__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# -*- coding: utf-8 -*-
# Copyright 2016 Eficent Business and IT Consulting Services S.L.
# (http://www.eficent.com)
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
{
"name": "Stock Orderpoint manual procurement",
"summary": "Allows to create procurement orders from orderpoints instead "
"of relying only on the scheduler",
"version": "8.0.1.0.0",
"author": "Eficent Business and IT Consulting Services S.L,"
"Odoo Community Association (OCA)",
"website": "https://www.odoo-community.org",
"category": "Warehouse Management",
"depends": ["stock",
"stock_orderpoint_uom"],
"data": ["security/stock_orderpoint_manual_procurement_security.xml",
"wizards/make_procurement_orderpoint_view.xml",
"views/procurement_order_view.xml",
"views/stock_warehouse_orderpoint_view.xml"
],
"license": "AGPL-3",
'installable': True,
'application': True,
}
6 changes: 6 additions & 0 deletions stock_orderpoint_manual_procurement/models/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# -*- coding: utf-8 -*-
# Copyright 2016 Eficent Business and IT Consulting Services S.L.
# (http://www.eficent.com)
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).

from . import stock_warehouse_orderpoint
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# -*- coding: utf-8 -*-
# Copyright 2016 Eficent Business and IT Consulting Services S.L.
# (http://www.eficent.com)
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).

from openerp import api, fields, models
from datetime import datetime
from openerp.addons import decimal_precision as dp
from openerp.tools import float_compare, float_round


UNIT = dp.get_precision('Product Unit of Measure')


class StockWarehouseOrderpoint(models.Model):
_inherit = 'stock.warehouse.orderpoint'

@api.multi
@api.depends("product_min_qty", "product_id", "qty_multiple")
def _compute_procure_recommended(self):
procurement_model = self.env['procurement.order']
for op in self:
op.procure_recommended_date = \
procurement_model._get_orderpoint_date_planned(
op, datetime.today())
procure_recommended_qty = 0.0
prods = procurement_model._product_virtual_get(op)
if prods is None:
continue
if float_compare(prods, op.product_min_qty,
precision_rounding=op.product_uom.rounding) < 0:
qty = max(op.product_min_qty, op.product_max_qty) - prods
Copy link
Contributor

Choose a reason for hiding this comment

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

I've created a Reordering rule for the product [DVD] Blank DVD-RW with the following values:
Minimum Quantity 5.000
Maximum Quantity 25.000
Quantity Multiple 1.000
Unit of measure: Dozens
The quantity on hand is 0.00 and the forecasted quantity is 0.00 dozens.

When I create a procurement and I select the product and then the unit of measure, the quantity proposed is the maximum quantity in the reordering rule and the unit of measure is Dozens.

It works as expected, however why do we choose the maximum instead of the minimum?

Copy link
Member

Choose a reason for hiding this comment

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

@aheficent minimum qty is a trigger to let procurement engine start procuring. So if the stock level drop below minimum qty it will trigger procurement. Max qty will tell procurement engine how many will it procure. Here are few links regarding stock level:

https://www.lokad.com/min-max-inventory-planning-definition
http://accountlearning.blogspot.co.id/2010/05/minimum-level-or-safety-stock-and-its.html

reste = op.qty_multiple > 0 and qty % op.qty_multiple or 0.0
if float_compare(
reste, 0.0,
precision_rounding=op.product_uom.rounding) > 0:
qty += op.qty_multiple - reste

if float_compare(
qty, 0.0,
precision_rounding=op.product_uom.rounding) <= 0:
continue

qty -= op.subtract_procurements(op)

qty_rounded = float_round(
qty, precision_rounding=op.product_uom.rounding)
if qty_rounded > 0:
procure_recommended_qty = qty_rounded
if op.procure_uom_id:
product_qty = op.procure_uom_id._compute_qty(
op.product_id.uom_id.id, procure_recommended_qty,
op.procure_uom_id.id)
else:
product_qty = procure_recommended_qty

op.procure_recommended_qty = product_qty

procure_recommended_qty = fields.Float(
string='Procure recommendation',
compute="_compute_procure_recommended",
digits=UNIT)
procure_recommended_date = fields.Date(
string='Request Date',
compute="_compute_procure_recommended")
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data noupdate="0">

<record id="group_change_orderpoint_procure_qty" model="res.groups">
<field name="name">Change quantity in manual procurements from reordering rules</field>
<field name="implied_ids" eval="[(4, ref('base.group_user'))]"/>
<field name="category_id" ref="base.module_category_warehouse_management"/>
</record>

</data>
</openerp>
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
7 changes: 7 additions & 0 deletions stock_orderpoint_manual_procurement/tests/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# -*- coding: utf-8 -*-
# Copyright 2016 Eficent Business and IT Consulting Services S.L.
# (http://www.eficent.com)
# Copyright 2016 Serpent Consulting Services Pvt. Ltd.
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).

from . import test_stock_orderpoint_manual_procurement
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
# -*- coding: utf-8 -*-
# Copyright 2016 Eficent Business and IT Consulting Services S.L.
# (http://www.eficent.com)
# Copyright 2016 Serpent Consulting Services Pvt. Ltd.
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).

from openerp.tests import common


class TestStockWarehouseOrderpoint(common.TransactionCase):

def setUp(self):
super(TestStockWarehouseOrderpoint, self).setUp()

# Refs
self.group_stock_manager = self.env.ref('stock.group_stock_manager')
self.group_change_procure_qty = self.env.ref(
'stock_orderpoint_manual_procurement.'
'group_change_orderpoint_procure_qty')
self.company1 = self.env.ref('base.main_company')

# Get required Model
self.reordering_rule_model = self.env['stock.warehouse.orderpoint']
self.product_model = self.env['product.product']
self.user_model = self.env['res.users']
self.product_ctg_model = self.env['product.category']
self.stock_change_model = self.env['stock.change.product.qty']
self.make_procurement_orderpoint_model =\
self.env['make.procurement.orderpoint']

# Create users
self.user = self._create_user('user_1',
[self.group_stock_manager,
self.group_change_procure_qty],
self.company1)
# Get required Model data
self.product_uom = self.env.ref('product.product_uom_unit')
self.location = self.env.ref('stock.stock_location_stock')
self.product = self.env.ref('product.product_product_7')

# Create Product category and Product
self.product_ctg = self._create_product_category()
self.product = self._create_product()

# Add default quantity
quantity = 20.00
self._update_product_qty(self.product, self.location, quantity)

# Create Reordering Rule
self.reorder = self.create_orderpoint()

def _create_user(self, login, groups, company):
""" Create a user."""
group_ids = [group.id for group in groups]
user = \
self.user_model.with_context({'no_reset_password': True}).create({
'name': 'Test User',
'login': login,
'password': 'demo',
'email': 'test@yourcompany.com',
'company_id': company.id,
'company_ids': [(4, company.id)],
'groups_id': [(6, 0, group_ids)]
})
return user

def _create_product_category(self):
"""Create a Product Category."""
product_ctg = self.product_ctg_model.create({
'name': 'test_product_ctg',
'type': 'normal',
})
return product_ctg

def _create_product(self):
"""Create a Product."""
product = self.product_model.create({
'name': 'Test Product',
'categ_id': self.product_ctg.id,
'type': 'product',
'uom_id': self.product_uom.id,
})
return product

def _update_product_qty(self, product, location, quantity):
"""Update Product quantity."""
change_product_qty = self.stock_change_model.create({
'location_id': location.id,
'product_id': product.id,
'new_quantity': quantity,
})
change_product_qty.change_product_qty()
return change_product_qty

def create_orderpoint(self):
"""Create a Reordering Rule"""
reorder = self.reordering_rule_model.sudo(self.user).create({
'name': 'Order-point',
'product_id': self.product.id,
'product_min_qty': '100',
'product_max_qty': '500',
'qty_multiple': '1'
})
return reorder

def create_orderpoint_procurement(self):
"""Make Procurement from Reordering Rule"""
context = {
'active_model': 'stock.warehouse.orderpoint',
'active_ids': self.reorder.ids,
'active_id': self.reorder.id
}
wizard = self.make_procurement_orderpoint_model.sudo(self.user).\
with_context(context).create({})
wizard.make_procurement()
return wizard

def test_security(self):
"""Test Manual Procurement created from Order-Point"""

# Create Manual Procurement from order-point procured quantity
self.create_orderpoint_procurement()

# Assert that Procurement is created with the desired quantity
self.assertTrue(self.reorder.procurement_ids)
self.assertEqual(self.reorder.product_id.id,
self.reorder.procurement_ids.product_id.id)
self.assertEqual(self.reorder.name,
self.reorder.procurement_ids.origin)
self.assertNotEqual(self.reorder.procure_recommended_qty,
self.reorder.procurement_ids.product_qty)
self.assertEqual(self.reorder.procurement_ids.product_qty,
480.0)
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?xml version="1.0"?>
<openerp>
<data>

<record id="view_procurement_filter" model="ir.ui.view">
<field name="name">procurement.order.select</field>
<field name="model">procurement.order</field>
<field name="inherit_id"
ref="procurement.view_procurement_filter"/>
<field name="arch" type="xml">
<field name="origin" position="after">
<field name="orderpoint_id"/>
</field>

</field>
</record>

</data>
</openerp>
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?xml version="1.0"?>
<openerp>
<data>

<record id="view_warehouse_orderpoint_tree" model="ir.ui.view">
<field name="name">stock.warehouse.orderpoint.tree</field>
<field name="model">stock.warehouse.orderpoint</field>
<field name="inherit_id"
ref="stock.view_warehouse_orderpoint_tree"/>
<field name="arch" type="xml">
<field name="product_uom" position="after">
<field name="procurement_ids" invisible="1"/>
<field name="procure_recommended_qty"/>
<field name="procure_recommended_date"/>
<button string="Create Procurement"
name="%(stock_orderpoint_manual_procurement.act_make_procurement_from_orderpoint)d"
icon="gtk-execute" type="action"/>
<button string="Procurements"
name="%(procurement.procurement_action)d"
attrs="{'invisible':[('procurement_ids', '=', False)]}"
icon="gtk-open" type="action"
domain="[('orderpoint_id','=', active_id)]"
context="{'search_default_orderpoint_id': [active_id]}"/>
</field>
</field>
</record>

</data>
</openerp>
6 changes: 6 additions & 0 deletions stock_orderpoint_manual_procurement/wizards/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# -*- coding: utf-8 -*-
# Copyright 2016 Eficent Business and IT Consulting Services S.L.
# (http://www.eficent.com)
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).

from . import make_procurement_orderpoint
Loading