mirror of
https://github.com/OCA/stock-logistics-warehouse.git
synced 2025-01-21 14:27:28 +02:00
add stock_request in 11.0
This commit is contained in:
committed by
Jesús Alan Ramos Rodríguez
parent
5fad77ce43
commit
92fd123b59
63
stock_request_purchase/README.rst
Normal file
63
stock_request_purchase/README.rst
Normal file
@@ -0,0 +1,63 @@
|
||||
.. image:: https://img.shields.io/badge/licence-LGPL--3-blue.svg
|
||||
:target: https://www.gnu.org/licenses/lgpl-3.0-standalone.html
|
||||
:alt: License: LGPL-3
|
||||
|
||||
======================
|
||||
Stock Request Purchase
|
||||
======================
|
||||
|
||||
This module allows for users to be able to display purchase orders that have
|
||||
been created as a consequence of Stock Requests.
|
||||
|
||||
|
||||
Usage
|
||||
=====
|
||||
|
||||
In case that the confirmation of the Stock Request results in an immediate
|
||||
Purchase Order, the user will be able to display the PO's from the Stock
|
||||
Request form view.
|
||||
|
||||
|
||||
.. 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/11.0
|
||||
|
||||
|
||||
Known issues / Roadmap
|
||||
======================
|
||||
|
||||
* When a Stock Request is cancelled, it does not cancel the quantity included
|
||||
in the Purchase Order.
|
||||
|
||||
|
||||
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 smash it by providing detailed and welcomed feedback.
|
||||
|
||||
|
||||
Credits
|
||||
=======
|
||||
|
||||
Contributors
|
||||
------------
|
||||
|
||||
* Jordi Ballester <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.
|
||||
2
stock_request_purchase/__init__.py
Normal file
2
stock_request_purchase/__init__.py
Normal file
@@ -0,0 +1,2 @@
|
||||
|
||||
from . import models
|
||||
24
stock_request_purchase/__manifest__.py
Normal file
24
stock_request_purchase/__manifest__.py
Normal file
@@ -0,0 +1,24 @@
|
||||
# Copyright 2017 Eficent Business and IT Consulting Services, S.L.
|
||||
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html).
|
||||
|
||||
{
|
||||
"name": "Stock Request Purchase",
|
||||
"summary": "Internal request for stock",
|
||||
"version": "11.0.1.0.0",
|
||||
"license": "LGPL-3",
|
||||
"website": "https://github.com/stock-logistics-warehouse",
|
||||
"author": "Eficent, "
|
||||
"Odoo Community Association (OCA)",
|
||||
"category": "Warehouse Management",
|
||||
"depends": [
|
||||
"stock_request",
|
||||
"purchase",
|
||||
"purchase_procurement_run_buy_hook"
|
||||
],
|
||||
"data": [
|
||||
"security/ir.model.access.csv",
|
||||
"views/stock_request_views.xml",
|
||||
"views/purchase_order_views.xml",
|
||||
],
|
||||
"installable": True,
|
||||
}
|
||||
5
stock_request_purchase/models/__init__.py
Normal file
5
stock_request_purchase/models/__init__.py
Normal file
@@ -0,0 +1,5 @@
|
||||
|
||||
from . import purchase_order
|
||||
from . import purchase_order_line
|
||||
from . import procurement_rule
|
||||
from . import stock_request
|
||||
27
stock_request_purchase/models/procurement_rule.py
Normal file
27
stock_request_purchase/models/procurement_rule.py
Normal file
@@ -0,0 +1,27 @@
|
||||
# Copyright 2017 Eficent Business and IT Consulting Services, S.L.
|
||||
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html).
|
||||
|
||||
from odoo import models
|
||||
|
||||
|
||||
class ProcurementRule(models.Model):
|
||||
_inherit = 'procurement.rule'
|
||||
|
||||
def _prepare_purchase_order_line(self, product_id, product_qty,
|
||||
product_uom, values, po, supplier):
|
||||
vals = super(ProcurementRule, self)._prepare_purchase_order_line(
|
||||
product_id, product_qty, product_uom, values, po, supplier)
|
||||
if 'stock_request_id' in values:
|
||||
vals['stock_request_ids'] = [(4, values['stock_request_id'])]
|
||||
return vals
|
||||
|
||||
def _prepare_purchase_order_line_update(self, line,
|
||||
procurement_uom_po_qty,
|
||||
price_unit, values):
|
||||
vals = super(ProcurementRule,
|
||||
self)._prepare_purchase_order_line_update(
|
||||
line, procurement_uom_po_qty, price_unit, values
|
||||
)
|
||||
if 'stock_request_id' in values:
|
||||
vals['stock_request_ids'] = [(4, values['stock_request_id'])]
|
||||
return vals
|
||||
37
stock_request_purchase/models/purchase_order.py
Normal file
37
stock_request_purchase/models/purchase_order.py
Normal file
@@ -0,0 +1,37 @@
|
||||
# Copyright 2017 Eficent Business and IT Consulting Services, S.L.
|
||||
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html).
|
||||
|
||||
from odoo import api, fields, models
|
||||
|
||||
|
||||
class PurchaseOrder(models.Model):
|
||||
_inherit = 'purchase.order'
|
||||
|
||||
stock_request_ids = fields.Many2many(comodel_name='stock.request',
|
||||
string='Stock Requests',
|
||||
compute='_compute_stock_request_ids')
|
||||
stock_request_count = fields.Integer('Stock Request #',
|
||||
compute='_compute_stock_request_ids')
|
||||
|
||||
@api.depends('order_line')
|
||||
def _compute_stock_request_ids(self):
|
||||
for rec in self:
|
||||
rec.stock_request_ids = rec.order_line.mapped('stock_request_ids')
|
||||
rec.stock_request_count = len(rec.stock_request_ids)
|
||||
|
||||
def action_view_stock_request(self):
|
||||
"""
|
||||
:return dict: dictionary value for created view
|
||||
"""
|
||||
action = self.env.ref(
|
||||
'stock_request.action_stock_request_form').read()[0]
|
||||
|
||||
requests = self.mapped('stock_request_ids')
|
||||
if len(requests) > 1:
|
||||
action['domain'] = [('id', 'in', requests.ids)]
|
||||
elif requests:
|
||||
action['views'] = [
|
||||
(self.env.ref('stock_request.view_stock_request_form').id,
|
||||
'form')]
|
||||
action['res_id'] = requests.id
|
||||
return action
|
||||
23
stock_request_purchase/models/purchase_order_line.py
Normal file
23
stock_request_purchase/models/purchase_order_line.py
Normal file
@@ -0,0 +1,23 @@
|
||||
# Copyright 2017 Eficent Business and IT Consulting Services, S.L.
|
||||
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html).
|
||||
|
||||
from odoo import api, fields, models
|
||||
|
||||
|
||||
class PurchaseOrderLine(models.Model):
|
||||
_inherit = 'purchase.order.line'
|
||||
|
||||
stock_request_ids = fields.Many2many(comodel_name='stock.request',
|
||||
string='Stock Requests', copy=False)
|
||||
|
||||
@api.multi
|
||||
def _prepare_stock_moves(self, picking):
|
||||
res = super(PurchaseOrderLine, self)._prepare_stock_moves(picking)
|
||||
|
||||
for re in res:
|
||||
re['allocation_ids'] = [
|
||||
(0, 0, {
|
||||
'stock_request_id': request.id,
|
||||
'requested_product_uom_qty': request.product_qty,
|
||||
}) for request in self.stock_request_ids]
|
||||
return res
|
||||
38
stock_request_purchase/models/stock_request.py
Normal file
38
stock_request_purchase/models/stock_request.py
Normal file
@@ -0,0 +1,38 @@
|
||||
# Copyright 2017 Eficent Business and IT Consulting Services, S.L.
|
||||
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html).
|
||||
|
||||
from odoo import api, fields, models
|
||||
|
||||
|
||||
class StockRequest(models.Model):
|
||||
_inherit = "stock.request"
|
||||
|
||||
purchase_ids = fields.One2many('purchase.order',
|
||||
compute='_compute_purchase_ids',
|
||||
string='Pickings', readonly=True)
|
||||
purchase_count = fields.Integer(string='Purchase count',
|
||||
compute='_compute_purchase_ids',
|
||||
readonly=True)
|
||||
purchase_line_ids = fields.Many2many('purchase.order.line',
|
||||
string='Purchase Order Lines',
|
||||
readonly=True, copy=False)
|
||||
|
||||
@api.depends('purchase_line_ids')
|
||||
def _compute_purchase_ids(self):
|
||||
for request in self.sudo():
|
||||
request.purchase_ids = request.purchase_line_ids.mapped('order_id')
|
||||
request.purchase_count = len(request.purchase_ids)
|
||||
|
||||
@api.multi
|
||||
def action_view_purchase(self):
|
||||
action = self.env.ref(
|
||||
'purchase.purchase_order_action_generic').read()[0]
|
||||
|
||||
purchases = self.mapped('purchase_ids')
|
||||
if len(purchases) > 1:
|
||||
action['domain'] = [('id', 'in', purchases.ids)]
|
||||
elif purchases:
|
||||
action['views'] = [
|
||||
(self.env.ref('purchase.purchase_order_form').id, 'form')]
|
||||
action['res_id'] = purchases.id
|
||||
return action
|
||||
3
stock_request_purchase/security/ir.model.access.csv
Normal file
3
stock_request_purchase/security/ir.model.access.csv
Normal file
@@ -0,0 +1,3 @@
|
||||
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
|
||||
access_stock_request_purchase_user,stock.request purchase user,stock_request.model_stock_request,purchase.group_purchase_user,1,0,0,0
|
||||
access_stock_request_allocation_purchase_user,stock request allocation purchase user,stock_request.model_stock_request_allocation,purchase.group_purchase_user,1,0,0,0
|
||||
|
BIN
stock_request_purchase/static/description/icon.png
Normal file
BIN
stock_request_purchase/static/description/icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.7 KiB |
2
stock_request_purchase/tests/__init__.py
Normal file
2
stock_request_purchase/tests/__init__.py
Normal file
@@ -0,0 +1,2 @@
|
||||
|
||||
from . import test_stock_request_purchase
|
||||
199
stock_request_purchase/tests/test_stock_request_purchase.py
Normal file
199
stock_request_purchase/tests/test_stock_request_purchase.py
Normal file
@@ -0,0 +1,199 @@
|
||||
# Copyright 2017 Eficent Business and IT Consulting Services S.L.
|
||||
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl-3.0).
|
||||
|
||||
from odoo.tests import common
|
||||
|
||||
|
||||
class TestStockRequestPurchase(common.TransactionCase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestStockRequestPurchase, self).setUp()
|
||||
|
||||
# common models
|
||||
self.stock_request = self.env['stock.request']
|
||||
|
||||
# refs
|
||||
self.stock_request_user_group = \
|
||||
self.env.ref('stock_request.group_stock_request_user')
|
||||
self.stock_request_manager_group = \
|
||||
self.env.ref('stock_request.group_stock_request_manager')
|
||||
self.main_company = self.env.ref('base.main_company')
|
||||
self.warehouse = self.env.ref('stock.warehouse0')
|
||||
self.categ_unit = self.env.ref('product.product_uom_categ_unit')
|
||||
|
||||
# common data
|
||||
self.company_2 = self.env['res.company'].create({
|
||||
'name': 'Comp2',
|
||||
})
|
||||
self.wh2 = self.env['stock.warehouse'].search(
|
||||
[('company_id', '=', self.company_2.id)], limit=1)
|
||||
self.stock_request_user = self._create_user(
|
||||
'stock_request_user',
|
||||
[self.stock_request_user_group.id],
|
||||
[self.main_company.id, self.company_2.id])
|
||||
self.stock_request_manager = self._create_user(
|
||||
'stock_request_manager',
|
||||
[self.stock_request_manager_group.id],
|
||||
[self.main_company.id, self.company_2.id])
|
||||
self.route_buy = self.warehouse.buy_pull_id.route_id
|
||||
self.supplier = self.env['res.partner'].create({
|
||||
'name': 'Supplier',
|
||||
'supplier': True,
|
||||
})
|
||||
self.product = self._create_product('SH', 'Shoes', False)
|
||||
|
||||
self.uom_dozen = self.env['product.uom'].create({
|
||||
'name': 'Test-DozenA',
|
||||
'category_id': self.categ_unit.id,
|
||||
'factor_inv': 12,
|
||||
'uom_type': 'bigger',
|
||||
'rounding': 0.001})
|
||||
|
||||
def _create_user(self, name, group_ids, company_ids):
|
||||
return self.env['res.users'].with_context(
|
||||
{'no_reset_password': True}).create(
|
||||
{'name': name,
|
||||
'password': 'demo',
|
||||
'login': name,
|
||||
'email': '@'.join([name, '@test.com']),
|
||||
'groups_id': [(6, 0, group_ids)],
|
||||
'company_ids': [(6, 0, company_ids)]
|
||||
})
|
||||
|
||||
def _create_product(self, default_code, name, company_id):
|
||||
return self.env['product.product'].create({
|
||||
'name': name,
|
||||
'default_code': default_code,
|
||||
'uom_id': self.env.ref('product.product_uom_unit').id,
|
||||
'company_id': company_id,
|
||||
'type': 'product',
|
||||
'route_ids': [(6, 0, self.route_buy.ids)],
|
||||
'seller_ids': [(0, 0, {'name': self.supplier.id, 'delay': 5})]
|
||||
})
|
||||
|
||||
def test_create_request_01(self):
|
||||
"""Single Stock request with buy rule"""
|
||||
vals = {
|
||||
'product_id': self.product.id,
|
||||
'product_uom_id': self.product.uom_id.id,
|
||||
'product_uom_qty': 5.0,
|
||||
'company_id': self.main_company.id,
|
||||
'warehouse_id': self.warehouse.id,
|
||||
'location_id': self.warehouse.lot_stock_id.id,
|
||||
}
|
||||
|
||||
stock_request = self.stock_request.sudo(
|
||||
self.stock_request_user).create(vals)
|
||||
stock_request.action_confirm()
|
||||
|
||||
self.assertEqual(stock_request.state, 'open')
|
||||
|
||||
stock_request.refresh()
|
||||
|
||||
self.assertEqual(len(stock_request.sudo().purchase_ids), 1)
|
||||
self.assertEqual(len(stock_request.picking_ids), 0)
|
||||
self.assertEqual(len(stock_request.move_ids), 0)
|
||||
self.assertEqual(stock_request.qty_in_progress, 0.0)
|
||||
|
||||
purchase = stock_request.sudo().purchase_ids[0]
|
||||
purchase.button_confirm()
|
||||
picking = purchase.picking_ids[0]
|
||||
picking.action_confirm()
|
||||
|
||||
self.assertEqual(stock_request.qty_in_progress, 5.0)
|
||||
self.assertEqual(stock_request.qty_done, 0.0)
|
||||
|
||||
picking.action_assign()
|
||||
packout1 = picking.move_line_ids[0]
|
||||
packout1.qty_done = 5
|
||||
picking.action_done()
|
||||
|
||||
self.assertEqual(stock_request.qty_in_progress, 0.0)
|
||||
self.assertEqual(stock_request.qty_done,
|
||||
stock_request.product_uom_qty)
|
||||
self.assertEqual(stock_request.state, 'done')
|
||||
|
||||
def test_create_request_02(self):
|
||||
"""Multiple Stock requests with buy rule"""
|
||||
vals = {
|
||||
'product_id': self.product.id,
|
||||
'product_uom_id': self.product.uom_id.id,
|
||||
'product_uom_qty': 5.0,
|
||||
'company_id': self.main_company.id,
|
||||
'warehouse_id': self.warehouse.id,
|
||||
'location_id': self.warehouse.lot_stock_id.id,
|
||||
}
|
||||
|
||||
stock_request_1 = self.stock_request.sudo(
|
||||
self.stock_request_user).create(vals)
|
||||
stock_request_2 = self.stock_request.sudo(
|
||||
self.stock_request_manager).create(vals)
|
||||
|
||||
stock_request_1.action_confirm()
|
||||
self.assertEqual(sum(stock_request_1.sudo().purchase_line_ids.mapped(
|
||||
'product_qty')), 5)
|
||||
|
||||
stock_request_2.action_confirm()
|
||||
|
||||
self.assertEqual(sum(stock_request_2.sudo().purchase_line_ids.mapped(
|
||||
'product_qty')), 10)
|
||||
|
||||
stock_request_1.refresh()
|
||||
stock_request_2.refresh()
|
||||
|
||||
self.assertEqual(len(stock_request_1.sudo().purchase_ids), 1)
|
||||
self.assertEqual(len(stock_request_2.sudo().purchase_ids), 1)
|
||||
self.assertEqual(len(stock_request_1.sudo().purchase_ids), 1)
|
||||
self.assertEqual(len(stock_request_2.sudo().purchase_line_ids), 1)
|
||||
self.assertEqual(stock_request_1.sudo().purchase_ids,
|
||||
stock_request_2.sudo().purchase_ids)
|
||||
self.assertEqual(stock_request_1.sudo().purchase_line_ids,
|
||||
stock_request_2.sudo().purchase_line_ids)
|
||||
|
||||
purchase = stock_request_1.sudo().purchase_ids[0]
|
||||
|
||||
purchase.button_confirm()
|
||||
picking = purchase.picking_ids[0]
|
||||
picking.action_confirm()
|
||||
|
||||
self.assertEqual(stock_request_1.qty_in_progress, 5.0)
|
||||
self.assertEqual(stock_request_1.qty_done, 0.0)
|
||||
self.assertEqual(stock_request_2.qty_in_progress, 5.0)
|
||||
self.assertEqual(stock_request_2.qty_done, 0.0)
|
||||
|
||||
picking.action_assign()
|
||||
packout1 = picking.move_line_ids[0]
|
||||
packout1.qty_done = 10
|
||||
picking.action_done()
|
||||
|
||||
self.assertEqual(stock_request_1.qty_in_progress, 0.0)
|
||||
self.assertEqual(stock_request_1.qty_done,
|
||||
stock_request_1.product_uom_qty)
|
||||
|
||||
self.assertEqual(stock_request_2.qty_in_progress, 0.0)
|
||||
self.assertEqual(stock_request_2.qty_done,
|
||||
stock_request_2.product_uom_qty)
|
||||
|
||||
def test_view_actions(self):
|
||||
vals = {
|
||||
'product_id': self.product.id,
|
||||
'product_uom_id': self.product.uom_id.id,
|
||||
'product_uom_qty': 5.0,
|
||||
'company_id': self.main_company.id,
|
||||
'warehouse_id': self.warehouse.id,
|
||||
'location_id': self.warehouse.lot_stock_id.id,
|
||||
}
|
||||
|
||||
stock_request = self.stock_request.sudo().create(vals)
|
||||
stock_request.action_confirm()
|
||||
|
||||
action = stock_request.action_view_purchase()
|
||||
|
||||
self.assertEqual(action['domain'], '[]')
|
||||
self.assertEqual('views' in action.keys(), True)
|
||||
self.assertEqual(action['res_id'],
|
||||
stock_request.purchase_ids[0].id)
|
||||
|
||||
action = stock_request.purchase_ids[0].action_view_stock_request()
|
||||
self.assertEqual(action['type'], 'ir.actions.act_window')
|
||||
self.assertEqual(action['res_id'], stock_request.id)
|
||||
43
stock_request_purchase/views/purchase_order_views.xml
Normal file
43
stock_request_purchase/views/purchase_order_views.xml
Normal file
@@ -0,0 +1,43 @@
|
||||
<?xml version="1.0"?>
|
||||
<!-- Copyright 2016 Eficent Business and IT Consulting Services S.L.
|
||||
License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl-3.0) -->
|
||||
<odoo>
|
||||
<record id="purchase_order_form" model="ir.ui.view">
|
||||
<field name="name">purchase.order.form</field>
|
||||
<field name="model">purchase.order</field>
|
||||
<field name="inherit_id" ref="purchase.purchase_order_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//div[@name='button_box']" position="inside">
|
||||
<button type="object"
|
||||
name="action_view_stock_request"
|
||||
class="oe_stat_button"
|
||||
icon="fa-chain"
|
||||
attrs="{'invisible':[('stock_request_ids', '=', [])]}">
|
||||
<field name="stock_request_count" widget="statinfo"
|
||||
string="Stock Requests"/>
|
||||
<field name="stock_request_ids" invisible="1"/>
|
||||
</button>
|
||||
</xpath>
|
||||
<xpath expr="//field[@name='order_line']/form/sheet/notebook"
|
||||
position="inside">
|
||||
<page name="stock_requests" string="Stock Requests"
|
||||
groups="stock_request.group_stock_request_user">
|
||||
<field name="stock_request_ids"/>
|
||||
</page>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="purchase_order_line_form2" model="ir.ui.view">
|
||||
<field name="name">purchase.order.line.form2</field>
|
||||
<field name="model">purchase.order.line</field>
|
||||
<field name="inherit_id" ref="purchase.purchase_order_line_form2"/>
|
||||
<field name="arch" type="xml">
|
||||
<field name="name" position="after">
|
||||
<separator string="Stock Requests"/>
|
||||
<field name="stock_request_ids"/>
|
||||
</field>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
26
stock_request_purchase/views/stock_request_views.xml
Normal file
26
stock_request_purchase/views/stock_request_views.xml
Normal file
@@ -0,0 +1,26 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright 2017 Eficent
|
||||
License LGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -->
|
||||
<odoo>
|
||||
|
||||
<record id="view_stock_request_form" model="ir.ui.view">
|
||||
<field name="name">stock.request.form</field>
|
||||
<field name="model">stock.request</field>
|
||||
<field name="inherit_id" ref="stock_request.view_stock_request_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<div name="button_box" position="inside">
|
||||
<field name="purchase_ids" invisible="1"/>
|
||||
<button type="object"
|
||||
name="action_view_purchase"
|
||||
class="oe_stat_button"
|
||||
icon="fa-truck"
|
||||
attrs="{'invisible': [('purchase_count', '=', 0)]}"
|
||||
groups="purchase.group_purchase_user">
|
||||
<field name="purchase_count" widget="statinfo"
|
||||
string="Purchase"/>
|
||||
</button>
|
||||
</div>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
Reference in New Issue
Block a user