[IMP] stock_request: Add stock request order

This commit is contained in:
Enric Tobella
2018-04-09 10:32:58 +02:00
committed by hveficent
parent 3bea85804a
commit 77331c39cc
15 changed files with 926 additions and 71 deletions

View File

@@ -71,6 +71,7 @@ Contributors
------------
* Jordi Ballester (EFICENT) <jordi.ballester@eficent.com>.
* Enric Tobella <etobella@creublanca.es>
Maintainer
----------

View File

@@ -4,7 +4,7 @@
{
"name": "Stock Request",
"summary": "Internal request for stock",
"version": "11.0.1.0.0",
"version": "11.0.2.0.0",
"license": "LGPL-3",
"website": "https://github.com/stock-logistics-warehouse",
"author": "Eficent, "
@@ -20,7 +20,10 @@
"views/stock_request_allocation_views.xml",
"views/stock_move_views.xml",
"views/stock_picking_views.xml",
"data/stock_request_sequence_data.xml"
"views/stock_request_order_views.xml",
"views/res_config_settings_views.xml",
"views/stock_request_menu.xml",
"data/stock_request_sequence_data.xml",
],
"installable": True,
}

View File

@@ -10,5 +10,13 @@
<field name="company_id" eval="False"/>
</record>
<record id="seq_stock_request_order" model="ir.sequence">
<field name="name">Stock Request Order</field>
<field name="code">stock.request.order</field>
<field name="prefix">SRO/</field>
<field name="padding">5</field>
<field name="company_id" eval="False"/>
</record>
</data>
</odoo>

View File

@@ -1,7 +1,9 @@
from . import stock_request
from . import stock_request_allocation
from . import stock_request_order
from . import stock_move
from . import stock_picking
from . import procurement_rule
from . import stock_move_line
from . import res_config_settings

View File

@@ -0,0 +1,14 @@
# Copyright 2018 Creu Blanca
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html).
from odoo import fields, models
class ResConfigSettings(models.TransientModel):
_inherit = 'res.config.settings'
group_stock_request_order = fields.Boolean(
implied_group='stock_request.group_stock_request_order')
module_stock_request_purchase = fields.Boolean(
string='Stock requests for purchase')

View File

@@ -36,8 +36,9 @@ class StockRequest(models.Model):
@api.depends('product_id', 'product_uom_id', 'product_uom_qty')
def _compute_product_qty(self):
self.product_qty = self.product_uom_id._compute_quantity(
self.product_uom_qty, self.product_id.uom_id)
for rec in self:
rec.product_qty = rec.product_uom_id._compute_quantity(
rec.product_uom_qty, rec.product_id.uom_id)
name = fields.Char(
'Name', copy=False, required=True, readonly=True,
@@ -138,12 +139,24 @@ class StockRequest(models.Model):
route_id = fields.Many2one('stock.location.route', string='Route',
readonly=True,
states={'draft': [('readonly', False)]},
domain="[('id', 'in', route_ids)]",
ondelete='restrict')
route_ids = fields.Many2many(
'stock.location.route', string='Route',
compute='_compute_route_ids',
readonly=True,
)
allocation_ids = fields.One2many(comodel_name='stock.request.allocation',
inverse_name='stock_request_id',
string='Stock Request Allocation')
order_id = fields.Many2one(
'stock.request.order',
readonly=True,
)
_sql_constraints = [
('name_uniq', 'unique(name, company_id)',
'Stock Request name must be unique'),
@@ -177,6 +190,21 @@ class StockRequest(models.Model):
request.product_id.uom_id._compute_quantity(
open_qty, request.product_uom_id)
@api.depends('product_id', 'warehouse_id', 'location_id')
def _compute_route_ids(self):
for record in self:
routes = self.env['stock.location.route']
if record.product_id:
routes += record.product_id.mapped(
'route_ids') | record.product_id.mapped(
'categ_id').mapped('total_route_ids')
if record.warehouse_id:
routes |= self.env['stock.location.route'].search(
[('warehouse_ids', 'in', record.warehouse_id.ids)])
parents = record.get_parents().ids
record.route_ids = routes.filtered(lambda r: any(
p.location_id.id in parents for p in r.pull_ids))
def get_parents(self):
location = self.location_id.sudo()
result = location
@@ -196,26 +224,65 @@ class StockRequest(models.Model):
'same category than the default unit '
'of measure of the product'))
def _get_valid_routes(self):
routes = self.env['stock.location.route']
if self.product_id:
routes += self.product_id.mapped(
'route_ids') | self.product_id.mapped(
'categ_id').mapped('total_route_ids')
if self.warehouse_id:
routes |= self.env['stock.location.route'].search(
[('warehouse_ids', 'in', self.warehouse_id.ids)])
parents = self.get_parents().ids
routes = routes.filtered(lambda r: any(
p.location_id.id in parents for p in r.pull_ids))
return routes
@api.constrains('order_id', 'requested_by')
def check_order_requested_by(self):
if self.order_id and self.order_id.requested_by != self.requested_by:
raise ValidationError(_(
'Requested by must be equal to the order'
))
@api.constrains('order_id', 'warehouse_id')
def check_order_warehouse_id(self):
if self.order_id and self.order_id.warehouse_id != self.warehouse_id:
raise ValidationError(_(
'Warehouse must be equal to the order'
))
@api.constrains('order_id', 'location_id')
def check_order_location(self):
if self.order_id and self.order_id.location_id != self.location_id:
raise ValidationError(_(
'Location must be equal to the order'
))
@api.constrains('order_id', 'procurement_group_id')
def check_order_procurement_group(self):
if (
self.order_id and
self.order_id.procurement_group_id != self.procurement_group_id
):
raise ValidationError(_(
'Procurement group must be equal to the order'
))
@api.constrains('order_id', 'company_id')
def check_order_company(self):
if self.order_id and self.order_id.company_id != self.company_id:
raise ValidationError(_(
'Company must be equal to the order'
))
@api.constrains('order_id', 'expected_date')
def check_order_expected_date(self):
if self.order_id and self.order_id.expected_date != self.expected_date:
raise ValidationError(_(
'Expected date must be equal to the order'
))
@api.constrains('order_id', 'picking_policy')
def check_order_picking_policy(self):
if (
self.order_id and
self.order_id.picking_policy != self.picking_policy
):
raise ValidationError(_(
'The picking policy must be equal to the order'
))
@api.onchange('warehouse_id')
def onchange_warehouse_id(self):
""" Finds location id for changed warehouse. """
res = {'domain': {}}
routes = self._get_valid_routes()
res['domain']['route_id'] = [('id', 'in', routes.ids)]
if self.warehouse_id:
# search with sudo because the user may not have permissions
loc_wh = self.location_id.sudo().get_warehouse()
@@ -228,8 +295,6 @@ class StockRequest(models.Model):
@api.onchange('location_id')
def onchange_location_id(self):
res = {'domain': {}}
routes = self._get_valid_routes()
res['domain']['route_id'] = [('id', 'in', routes.ids)]
if self.location_id:
loc_wh = self.location_id.get_warehouse()
if self.warehouse_id != loc_wh:
@@ -254,8 +319,6 @@ class StockRequest(models.Model):
@api.onchange('product_id')
def onchange_product_id(self):
res = {'domain': {}}
routes = self._get_valid_routes()
res['domain']['route_id'] = [('id', 'in', routes.ids)]
if self.product_id:
self.product_uom_id = self.product_id.uom_id.id
res['domain']['product_uom_id'] = [
@@ -285,6 +348,8 @@ class StockRequest(models.Model):
def action_done(self):
self.state = 'done'
if self.order_id:
self.order_id.check_done()
return True
def check_done(self):
@@ -373,3 +438,9 @@ class StockRequest(models.Model):
(self.env.ref('stock.view_picking_form').id, 'form')]
action['res_id'] = pickings.id
return action
@api.multi
def unlink(self):
if self.filtered(lambda r: r.state != 'draft'):
raise UserError(_('Only requests on draft state can be unlinked'))
return super(StockRequest, self).unlink()

View File

@@ -0,0 +1,249 @@
# Copyright 2018 Creu Blanca
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html).
from odoo import api, fields, models, _
from odoo.exceptions import UserError
REQUEST_STATES = [
('draft', 'Draft'),
('open', 'In progress'),
('done', 'Done'),
('cancel', 'Cancelled')]
class StockRequestOrder(models.Model):
_name = 'stock.request.order'
_description = 'Stock Request Order'
_inherit = ['mail.thread', 'mail.activity.mixin']
@api.model
def default_get(self, fields):
res = super().default_get(fields)
warehouse = None
if 'warehouse_id' not in res and res.get('company_id'):
warehouse = self.env['stock.warehouse'].search(
[('company_id', '=', res['company_id'])], limit=1)
if warehouse:
res['warehouse_id'] = warehouse.id
res['location_id'] = warehouse.lot_stock_id.id
return res
def _get_default_requested_by(self):
return self.env['res.users'].browse(self.env.uid)
name = fields.Char(
'Name', copy=False, required=True, readonly=True,
states={'draft': [('readonly', False)]},
default=lambda self: self.env['ir.sequence'].next_by_code(
'stock.request.order'))
state = fields.Selection(selection=REQUEST_STATES, string='Status',
copy=False, default='draft', index=True,
readonly=True, track_visibility='onchange',
)
requested_by = fields.Many2one(
'res.users', 'Requested by', required=True,
track_visibility='onchange',
default=lambda s: s._get_default_requested_by(),
)
warehouse_id = fields.Many2one(
'stock.warehouse', 'Warehouse', readonly=True,
ondelete="cascade", required=True,
states={'draft': [('readonly', False)]})
location_id = fields.Many2one(
'stock.location', 'Location', readonly=True,
domain=[('usage', 'in', ['internal', 'transit'])],
ondelete="cascade", required=True,
states={'draft': [('readonly', False)]},
)
procurement_group_id = fields.Many2one(
'procurement.group', 'Procurement Group', readonly=True,
states={'draft': [('readonly', False)]},
help="Moves created through this stock request will be put in this "
"procurement group. If none is given, the moves generated by "
"procurement rules will be grouped into one big picking.",
)
company_id = fields.Many2one(
'res.company', 'Company', required=True, readonly=True,
states={'draft': [('readonly', False)]},
default=lambda self: self.env['res.company']._company_default_get(
'stock.request.order'),
)
expected_date = fields.Datetime(
'Expected date', default=fields.Datetime.now, index=True,
required=True, readonly=True,
states={'draft': [('readonly', False)]},
help="Date when you expect to receive the goods.",
)
picking_policy = fields.Selection([
('direct', 'Receive each product when available'),
('one', 'Receive all products at once')],
string='Shipping Policy', required=True, readonly=True,
states={'draft': [('readonly', False)]},
default='direct',
)
move_ids = fields.One2many(comodel_name='stock.move',
compute='_compute_move_ids',
string='Stock Moves', readonly=True,
)
picking_ids = fields.One2many('stock.picking',
compute='_compute_picking_ids',
string='Pickings', readonly=True,
)
picking_count = fields.Integer(string='Delivery Orders',
compute='_compute_picking_ids',
readonly=True,
)
stock_request_ids = fields.One2many(
'stock.request',
inverse_name='order_id',
)
stock_request_count = fields.Integer(
string='Stock requests',
compute='_compute_stock_request_count',
readonly=True,
)
_sql_constraints = [
('name_uniq', 'unique(name, company_id)',
'Stock Request name must be unique'),
]
@api.depends('stock_request_ids.allocation_ids')
def _compute_picking_ids(self):
for record in self:
record.picking_ids = record.stock_request_ids.mapped('picking_ids')
record.picking_count = len(record.picking_ids)
@api.depends('stock_request_ids')
def _compute_move_ids(self):
for record in self:
record.move_ids = record.stock_request_ids.mapped('move_ids')
@api.depends('stock_request_ids')
def _compute_stock_request_count(self):
for record in self:
record.stock_request_count = len(record.stock_request_ids)
@api.onchange('requested_by')
def onchange_requested_by(self):
self.change_childs()
@api.onchange('expected_date')
def onchange_expected_date(self):
self.change_childs()
@api.onchange('picking_policy')
def onchange_picking_policy(self):
self.change_childs()
@api.onchange('location_id')
def onchange_location_id(self):
if self.location_id:
loc_wh = self.location_id.sudo().get_warehouse()
if loc_wh and self.warehouse_id != loc_wh:
self.warehouse_id = loc_wh
self.with_context(
no_change_childs=True).onchange_warehouse_id()
self.change_childs()
@api.onchange('warehouse_id')
def onchange_warehouse_id(self):
if self.warehouse_id:
# search with sudo because the user may not have permissions
loc_wh = self.location_id.sudo().get_warehouse()
if self.warehouse_id != loc_wh:
self.location_id = self.warehouse_id.sudo().lot_stock_id
self.with_context(no_change_childs=True).onchange_location_id()
if self.warehouse_id.sudo().company_id != self.company_id:
self.company_id = self.warehouse_id.company_id
self.with_context(no_change_childs=True).onchange_company_id()
self.change_childs()
@api.onchange('procurement_group_id')
def onchange_procurement_group_id(self):
self.change_childs()
@api.onchange('company_id')
def onchange_company_id(self):
if self.company_id and (
not self.warehouse_id or
self.warehouse_id.sudo().company_id != self.company_id
):
self.warehouse_id = self.env['stock.warehouse'].search(
[('company_id', '=', self.company_id.id)], limit=1)
self.with_context(no_change_childs=True).onchange_warehouse_id()
self.change_childs()
return {
'domain': {
'warehouse_id': [('company_id', '=', self.company_id.id)]}}
def change_childs(self):
if not self._context.get('no_change_childs', False):
for line in self.stock_request_ids:
line.warehouse_id = self.warehouse_id
line.location_id = self.location_id
line.company_id = self.company_id
line.picking_policy = self.picking_policy
line.expected_date = self.expected_date
line.requested_by = self.requested_by
line.procurement_group_id = self.procurement_group_id
@api.multi
def action_confirm(self):
for line in self.stock_request_ids:
line.action_confirm()
self.state = 'open'
return True
def action_draft(self):
for line in self.stock_request_ids:
line.action_draft()
self.state = 'draft'
return True
def action_cancel(self):
for line in self.stock_request_ids:
line.action_cancel()
self.state = 'cancel'
return True
def action_done(self):
self.state = 'done'
return True
def check_done(self):
if not self.stock_request_ids.filtered(lambda r: r.state != 'done'):
self.action_done()
return
@api.multi
def action_view_transfer(self):
action = self.env.ref('stock.action_picking_tree_all').read()[0]
pickings = self.mapped('picking_ids')
if len(pickings) > 1:
action['domain'] = [('id', 'in', pickings.ids)]
elif pickings:
action['views'] = [
(self.env.ref('stock.view_picking_form').id, 'form')]
action['res_id'] = pickings.id
return action
@api.multi
def action_view_stock_requests(self):
action = self.env.ref(
'stock_request.action_stock_request_form').read()[0]
if len(self.stock_request_ids) > 1:
action['domain'] = [('order_id', 'in', self.ids)]
elif self.stock_request_ids:
action['views'] = [
(self.env.ref(
'stock_request.view_stock_request_form').id, 'form')]
action['res_id'] = self.stock_request_ids.id
return action
@api.multi
def unlink(self):
if self.filtered(lambda r: r.state != 'draft'):
raise UserError(_('Only orders on draft state can be unlinked'))
return super().unlink()

View File

@@ -5,9 +5,9 @@ access_stock_request_stock_user,stock.request stock user,model_stock_request,sto
access_stock_request_allocation_user,stock request allocation user,model_stock_request_allocation,group_stock_request_user,1,1,1,1
access_stock_request_allocation_manager,stock request allocation manager,model_stock_request_allocation,group_stock_request_manager,1,1,1,1
access_stock_request_allocation_stock_user,stock.request.allocation stock user,model_stock_request_allocation,stock.group_stock_user,1,0,0,0
access_stock_request_manager,stock request manager,model_stock_request,group_stock_request_manager,1,1,1,1
access_stock_warehouse_user,stock.warehouse.user,stock.model_stock_warehouse,group_stock_request_user,1,0,0,0
access_stock_location_user,stock.location.user,stock.model_stock_location,group_stock_request_user,1,0,0,0
access_stock_location_request_manager,stock.location request manager,stock.model_stock_location,group_stock_request_manager,1,0,0,0
access_procurement_rule_request_manager,procurement_rule request_manager,stock.model_procurement_rule,group_stock_request_manager,1,0,0,0
access_procurement_rule,procurement.rule.flow,stock.model_procurement_rule,group_stock_request_user,1,0,0,0
access_stock_request_order_user,stock request user,model_stock_request_order,stock_request.group_stock_request_user,1,1,1,0
access_stock_request_order_manager,stock request manager,model_stock_request_order,stock_request.group_stock_request_manager,1,1,1,1
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
5 access_stock_request_allocation_user stock request allocation user model_stock_request_allocation group_stock_request_user 1 1 1 1
6 access_stock_request_allocation_manager stock request allocation manager model_stock_request_allocation group_stock_request_manager 1 1 1 1
7 access_stock_request_allocation_stock_user stock.request.allocation stock user model_stock_request_allocation stock.group_stock_user 1 0 0 0
access_stock_request_manager stock request manager model_stock_request group_stock_request_manager 1 1 1 1
access_stock_warehouse_user stock.warehouse.user stock.model_stock_warehouse group_stock_request_user 1 0 0 0
8 access_stock_location_user stock.location.user stock.model_stock_location group_stock_request_user 1 0 0 0
9 access_stock_location_request_manager stock.location request manager stock.model_stock_location group_stock_request_manager 1 0 0 0
10 access_procurement_rule_request_manager procurement_rule request_manager stock.model_procurement_rule group_stock_request_manager 1 0 0 0
11 access_procurement_rule procurement.rule.flow stock.model_procurement_rule group_stock_request_user 1 0 0 0
12 access_stock_request_order_user stock request user model_stock_request_order stock_request.group_stock_request_user 1 1 1 0
13 access_stock_request_order_manager stock request manager model_stock_request_order stock_request.group_stock_request_manager 1 1 1 1

View File

@@ -21,6 +21,11 @@
<field name="category_id" ref="module_category_stock_request"/>
</record>
<record id="group_stock_request_order" model="res.groups">
<field name="name">Stock Request Order</field>
<field name="category_id" ref="base.module_category_hidden"/>
</record>
<record id="stock.group_stock_user" model="res.groups">
<field name="implied_ids"
eval="[(4, ref('group_stock_request_user'))]"/>
@@ -67,6 +72,46 @@
<field name="perm_create" eval="True"/>
<field name="perm_unlink" eval="True"/>
</record>
<record model="ir.rule" id="stock_request_order_picking_rule">
<field name="name">stock_request_order multi-company</field>
<field name="model_id" ref="model_stock_request_order"/>
<field name="global" eval="True"/>
<field name="domain_force">['|',('company_id','=',False),('company_id','child_of',[user.company_id.id])]</field>
</record>
<record id="stock_request_order_followers_rule" model="ir.rule">
<field name="name">Follow Stock Request Order</field>
<field name="model_id" ref="model_stock_request_order"/>
<field name="groups" eval="[(6,0, [ref('stock_request.group_stock_request_user')])]"/>
<field name="perm_read" eval="True"/>
<field name="perm_write" eval="False"/>
<field name="perm_create" eval="False"/>
<field name="perm_unlink" eval="False"/>
<field name="domain_force">['|',('requested_by','=',user.id),
('message_partner_ids', 'in', [user.partner_id.id])]</field>
</record>
<record id="stock_request_order_rule" model="ir.rule">
<field name="name">Stock Request Order User</field>
<field name="model_id" ref="model_stock_request_order"/>
<field name="groups" eval="[(6,0, [ref('stock_request.group_stock_request_user')])]"/>
<field name="perm_read" eval="True"/>
<field name="perm_write" eval="True"/>
<field name="perm_create" eval="True"/>
<field name="perm_unlink" eval="True"/>
<field name="domain_force">[('requested_by','=',user.id)]</field>
</record>
<record id="stock_request_order_manager_rule" model="ir.rule">
<field name="name">Stock Request Manager</field>
<field name="model_id" ref="model_stock_request_order"/>
<field name="groups" eval="[(6,0, [ref('stock_request.group_stock_request_manager')])]"/>
<field name="perm_read" eval="True"/>
<field name="perm_write" eval="True"/>
<field name="perm_create" eval="True"/>
<field name="perm_unlink" eval="True"/>
</record>
</data>
</odoo>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.7 KiB

After

Width:  |  Height:  |  Size: 7.3 KiB

View File

@@ -2,11 +2,10 @@
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl-3.0).
from odoo.tests import common
from odoo import exceptions
from odoo import fields, exceptions
class TestStockRequest(common.TransactionCase):
def setUp(self):
super(TestStockRequest, self).setUp()
@@ -92,7 +91,6 @@ class TestStockRequest(common.TransactionCase):
})
def test_defaults(self):
vals = {
'product_id': self.product.id,
'product_uom_id': self.product.uom_id.id,
@@ -111,6 +109,80 @@ class TestStockRequest(common.TransactionCase):
self.assertEqual(
stock_request.location_id, self.warehouse.lot_stock_id)
def test_defaults_order(self):
vals = {}
order = self.env['stock.request.order'].sudo(
self.stock_request_user.id).with_context(
company_id=self.main_company.id).create(vals)
self.assertEqual(
order.requested_by, self.stock_request_user)
self.assertEqual(
order.warehouse_id, self.warehouse)
self.assertEqual(
order.location_id, self.warehouse.lot_stock_id)
def test_onchanges_order(self):
vals = {
'company_id': self.main_company.id,
'warehouse_id': self.warehouse.id,
'location_id': self.warehouse.lot_stock_id.id,
'stock_request_ids': [(0, 0, {
'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,
})]
}
order = self.env['stock.request.order'].sudo(
self.stock_request_user).new(vals)
self.stock_request_user.company_id = self.company_2
order.company_id = self.company_2
order.onchange_company_id()
stock_request = order.stock_request_ids
self.assertEqual(order.warehouse_id, self.wh2)
self.assertEqual(order.location_id, self.wh2.lot_stock_id)
self.assertEqual(order.warehouse_id, stock_request.warehouse_id)
procurement_group = self.env['procurement.group'].create({
'name': 'TEST'
})
order.procurement_group_id = procurement_group
order.onchange_procurement_group_id()
self.assertEqual(
order.procurement_group_id,
order.stock_request_ids.procurement_group_id)
order.procurement_group_id = procurement_group
order.onchange_procurement_group_id()
self.assertEqual(
order.procurement_group_id,
order.stock_request_ids.procurement_group_id)
order.picking_policy = 'one'
order.onchange_picking_policy()
self.assertEqual(
order.picking_policy,
order.stock_request_ids.picking_policy)
order.expected_date = fields.Date.today()
order.onchange_expected_date()
self.assertEqual(
order.expected_date,
order.stock_request_ids.expected_date)
order.requested_by = self.stock_request_manager
order.onchange_requested_by()
self.assertEqual(
order.requested_by,
order.stock_request_ids.requested_by)
def test_onchanges(self):
self.product.route_ids = [(6, 0, self.route.ids)]
vals = {
@@ -123,11 +195,8 @@ class TestStockRequest(common.TransactionCase):
stock_request.product_id = self.product
vals = stock_request.default_get(['warehouse_id', 'company_id'])
stock_request.update(vals)
res = stock_request.onchange_product_id()
self.assertTrue(res['domain']['route_id'])
routes = self.env['stock.location.route'].search(
res['domain']['route_id'])
self.assertIn(self.route.id, routes.ids)
stock_request.onchange_product_id()
self.assertIn(self.route.id, stock_request.route_ids.ids)
self.stock_request_user.company_id = self.company_2
stock_request.company_id = self.company_2
@@ -162,11 +231,12 @@ class TestStockRequest(common.TransactionCase):
# Test onchange_warehouse_id
wh2_2 = self.env['stock.warehouse'].with_context(
company_id=self.company_2.id).create({
'name': 'C2_2',
'code': 'C2_2',
'company_id': self.company_2.id
})
company_id=self.company_2.id
).create({
'name': 'C2_2',
'code': 'C2_2',
'company_id': self.company_2.id
})
stock_request.warehouse_id = wh2_2
stock_request.onchange_warehouse_id()
@@ -181,6 +251,146 @@ class TestStockRequest(common.TransactionCase):
self.assertEqual(
stock_request.location_id, self.warehouse.lot_stock_id)
def test_stock_request_order_validations_01(self):
vals = {
'company_id': self.main_company.id,
'warehouse_id': self.wh2.id,
'location_id': self.warehouse.lot_stock_id.id,
'stock_request_ids': [(0, 0, {
'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,
})]
}
# Select a UoM that is incompatible with the product's UoM
with self.assertRaises(exceptions.ValidationError):
self.env['stock.request.order'].sudo(
self.stock_request_user).create(vals)
def test_stock_request_order_validations_02(self):
vals = {
'company_id': self.main_company.id,
'warehouse_id': self.warehouse.id,
'location_id': self.wh2.lot_stock_id.id,
'stock_request_ids': [(0, 0, {
'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,
})]
}
# Select a UoM that is incompatible with the product's UoM
with self.assertRaises(exceptions.ValidationError):
self.env['stock.request.order'].sudo(
self.stock_request_user).create(vals)
def test_stock_request_order_validations_03(self):
vals = {
'company_id': self.main_company.id,
'warehouse_id': self.warehouse.id,
'location_id': self.warehouse.lot_stock_id.id,
'requested_by': self.stock_request_user.id,
'stock_request_ids': [(0, 0, {
'product_id': self.product.id,
'product_uom_id': self.product.uom_id.id,
'product_uom_qty': 5.0,
'requested_by': self.stock_request_manager.id,
'company_id': self.main_company.id,
'warehouse_id': self.warehouse.id,
'location_id': self.warehouse.lot_stock_id.id,
})]
}
# Select a UoM that is incompatible with the product's UoM
with self.assertRaises(exceptions.ValidationError):
self.env['stock.request.order'].sudo(
self.stock_request_user).create(vals)
def test_stock_request_order_validations_04(self):
procurement_group = self.env['procurement.group'].create({
'name': 'Procurement',
})
vals = {
'company_id': self.main_company.id,
'warehouse_id': self.warehouse.id,
'location_id': self.warehouse.lot_stock_id.id,
'procurement_group_id': procurement_group.id,
'stock_request_ids': [(0, 0, {
'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,
})]
}
# Select a UoM that is incompatible with the product's UoM
with self.assertRaises(exceptions.ValidationError):
self.env['stock.request.order'].sudo(
self.stock_request_user).create(vals)
def test_stock_request_order_validations_05(self):
vals = {
'company_id': self.main_company.id,
'warehouse_id': self.warehouse.id,
'location_id': self.warehouse.lot_stock_id.id,
'expected_date': fields.Date.today(),
'stock_request_ids': [(0, 0, {
'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,
})]
}
# Select a UoM that is incompatible with the product's UoM
with self.assertRaises(exceptions.ValidationError):
self.env['stock.request.order'].sudo(
self.stock_request_user).create(vals)
def test_stock_request_order_validations_06(self):
vals = {
'company_id': self.company_2.id,
'warehouse_id': self.warehouse.id,
'location_id': self.warehouse.lot_stock_id.id,
'stock_request_ids': [(0, 0, {
'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,
})]
}
# Select a UoM that is incompatible with the product's UoM
with self.assertRaises(exceptions.ValidationError):
self.env['stock.request.order'].sudo().create(vals)
def test_stock_request_order_validations_07(self):
vals = {
'company_id': self.main_company.id,
'warehouse_id': self.warehouse.id,
'location_id': self.warehouse.lot_stock_id.id,
'picking_policy': 'one',
'stock_request_ids': [(0, 0, {
'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,
})]
}
# Select a UoM that is incompatible with the product's UoM
with self.assertRaises(exceptions.ValidationError):
self.env['stock.request.order'].sudo(
self.stock_request_user).create(vals)
def test_stock_request_validations_01(self):
vals = {
'product_id': self.product.id,
@@ -213,22 +423,32 @@ class TestStockRequest(common.TransactionCase):
stock_request.action_confirm()
def test_create_request_01(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_ids': [(0, 0, {
'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(
order = self.env['stock.request.order'].sudo(
self.stock_request_user).create(vals)
stock_request = order.stock_request_ids
self.product.route_ids = [(6, 0, self.route.ids)]
stock_request.action_confirm()
order.action_confirm()
self.assertEqual(order.state, 'open')
self.assertEqual(stock_request.state, 'open')
self.assertEqual(len(order.sudo().picking_ids), 1)
self.assertEqual(len(order.sudo().move_ids), 1)
self.assertEqual(len(stock_request.sudo().picking_ids), 1)
self.assertEqual(len(stock_request.sudo().move_ids), 1)
self.assertEqual(stock_request.sudo().move_ids[0].location_dest_id,
@@ -250,6 +470,7 @@ class TestStockRequest(common.TransactionCase):
self.assertEqual(stock_request.qty_in_progress, 0.0)
self.assertEqual(stock_request.qty_done,
stock_request.product_uom_qty)
self.assertEqual(order.state, 'done')
self.assertEqual(stock_request.state, 'done')
def test_create_request_02(self):
@@ -330,19 +551,27 @@ class TestStockRequest(common.TransactionCase):
def test_cancel_request(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_ids': [(0, 0, {
'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(
order = self.env['stock.request.order'].sudo(
self.stock_request_user).create(vals)
self.product.route_ids = [(6, 0, self.route.ids)]
stock_request.action_confirm()
order.action_confirm()
stock_request = order.stock_request_ids
self.assertEqual(len(order.sudo().picking_ids), 1)
self.assertEqual(len(order.sudo().move_ids), 1)
self.assertEqual(len(stock_request.sudo().picking_ids), 1)
self.assertEqual(len(stock_request.sudo().move_ids), 1)
self.assertEqual(stock_request.sudo().move_ids[0].location_dest_id,
@@ -358,37 +587,56 @@ class TestStockRequest(common.TransactionCase):
self.assertEqual(stock_request.qty_in_progress, 5.0)
self.assertEqual(stock_request.qty_done, 0.0)
picking.action_assign()
stock_request.action_cancel()
order.action_cancel()
self.assertEqual(stock_request.qty_in_progress, 0.0)
self.assertEqual(stock_request.qty_done, 0.0)
self.assertEqual(len(stock_request.sudo().picking_ids), 0)
# Set the request back to draft
stock_request.action_draft()
order.action_draft()
self.assertEqual(order.state, 'draft')
self.assertEqual(stock_request.state, 'draft')
# Re-confirm. We expect new pickings to be created
stock_request.action_confirm()
order.action_confirm()
self.assertEqual(len(stock_request.sudo().picking_ids), 1)
self.assertEqual(len(stock_request.sudo().move_ids), 2)
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_ids': [(0, 0, {
'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)
order = self.env['stock.request.order'].sudo().create(vals)
self.product.route_ids = [(6, 0, self.route.ids)]
stock_request.action_confirm()
action = stock_request.action_view_transfer()
order.action_confirm()
stock_request = order.stock_request_ids
self.assertTrue(stock_request.picking_ids)
self.assertTrue(order.picking_ids)
action = order.action_view_transfer()
self.assertEqual('domain' in action.keys(), True)
self.assertEqual('views' in action.keys(), True)
self.assertEqual(action['res_id'], order.picking_ids[0].id)
action = order.action_view_stock_requests()
self.assertEqual('domain' in action.keys(), True)
self.assertEqual('views' in action.keys(), True)
self.assertEqual(action['res_id'], stock_request[0].id)
action = stock_request.action_view_transfer()
self.assertEqual('domain' in action.keys(), True)
self.assertEqual('views' in action.keys(), True)
self.assertEqual(action['res_id'], stock_request.picking_ids[0].id)

View File

@@ -0,0 +1,60 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!--
Copyright 2018 Creu Blanca
License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html).
-->
<odoo>
<record id="res_config_settings_view_form" model="ir.ui.view">
<field name="name">res.config.settings.view.form.inherit.medical</field>
<field name="model">res.config.settings</field>
<field name="inherit_id" ref="base.res_config_settings_view_form"/>
<field name="arch" type="xml">
<xpath expr="//div[hasclass('settings')]" position="inside">
<div class="app_settings_block"
data-string="Stock Request" data-key="stock_request"
groups="stock_request.group_stock_request_manager">
<div class="row mt16 o_settings_container" id="stock_request">
<div class="col-xs-12 col-md-6 o_setting_box">
<div class="o_setting_left_pane">
<field name="group_stock_request_order"/>
</div>
<div class="o_setting_right_pane">
<label string="Enable orders"
for="group_stock_request_order"/>
<div class="text-muted">
Activates Stock Request Orders
</div>
</div>
</div>
<div class="col-xs-12 col-md-6 o_setting_box">
<div class="o_setting_left_pane">
<field name="module_stock_request_purchase"/>
</div>
<div class="o_setting_right_pane">
<label string="Enable Stock Requests for Purchases"
for="module_stock_request_purchase"/>
<div class="text-muted">
Use Purchases with Stock Requests
</div>
</div>
</div>
</div>
</div>
</xpath>
</field>
</record>
<record id="action_stock_request_config" model="ir.actions.act_window">
<field name="name">Settings</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">res.config.settings</field>
<field name="view_mode">form</field>
<field name="target">inline</field>
<field name="context">{'module' : 'stock_request'}</field>
</record>
</odoo>

View File

@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="UTF-8" ?>
<odoo>
<menuitem
id="menu_stock_request_root"
name="Stock Requests"
groups="stock_request.group_stock_request_user,stock_request.group_stock_request_manager"
sequence="100"/>
<menuitem
id="menu_stock_request"
action="action_stock_request_form"
name="Stock Requests" parent="menu_stock_request_root"
sequence="20"/>
<menuitem id="stock_request_order_menu"
name="Stock Requests orders"
parent="menu_stock_request_root"
action="stock_request_order_action"
groups="group_stock_request_order"
sequence="10"/>
<menuitem id="menu_stock_request_config" name="Settings"
parent="menu_stock_request_root"
sequence="99" action="action_stock_request_config"
groups="base.group_system"/>
</odoo>

View File

@@ -0,0 +1,137 @@
<?xml version="1.0" encoding="UTF-8" ?>
<odoo>
<record model="ir.ui.view" id="stock_request_order_tree">
<field name="name">stock.request.order.tree</field>
<field name="model">stock.request.order</field>
<field name="arch" type="xml">
<tree string="Stock requests">
<field name="name"/>
<field name="warehouse_id"
groups="stock.group_stock_multi_locations"/>
<field name="location_id"
groups="stock.group_stock_multi_locations"/>
<field name="state"/>
</tree>
</field>
</record>
<record model="ir.ui.view" id="stock_request_order_form">
<field name="name">stock.request.order.form</field>
<field name="model">stock.request.order</field>
<field name="arch" type="xml">
<form string="Stock request">
<header>
<button name="action_confirm"
string="Confirm" type="object"
attrs="{'invisible': [('state', 'not in', ['draft'])]}"/>
<button name="action_cancel" states="draft,open"
type="object" string="Cancel"/>
<button name="action_draft" states="cancel" type="object"
string="Set to Draft"/>
<field name="state" widget="statusbar"/>
</header>
<sheet>
<div class="oe_button_box" name="button_box">
<field name="picking_ids" invisible="1"/>
<button type="object"
name="action_view_transfer"
class="oe_stat_button"
icon="fa-truck"
attrs="{'invisible': [('picking_count', '=', 0)]}"
groups="stock.group_stock_user">
<field name="picking_count" widget="statinfo"
string="Transfers"/>
</button>
<button type="object"
name="action_view_stock_requests"
class="oe_stat_button"
icon="fa-chain"
attrs="{'invisible': [('state', '=', 'draft')]}"
groups="stock.group_stock_user"
>
<field name="stock_request_count" widget="statinfo"
string="Stock requests"/>
</button>
</div>
<div class="oe_title">
<label string="Stock Request"/>
<h1>
<field name="name" readonly="1"/>
</h1>
</div>
<group>
<group>
<field name="expected_date"/>
<field name="picking_policy"/>
</group>
<group>
<field name="warehouse_id" widget="selection"
groups="stock.group_stock_multi_locations"/>
<field name="location_id"
groups="stock.group_stock_multi_locations"/>
<field name="procurement_group_id"
groups="stock.group_adv_location"/>
<field name="company_id"
groups="base.group_multi_company"
options="{'no_create': True}"/>
</group>
</group>
<notebook>
<page name="items" string="Items">
<field name="stock_request_ids" context="{
'default_expected_date':expected_date,
'default_picking_policy': picking_policy,
'default_warehouse_id': warehouse_id,
'default_location_id': location_id,
'default_procurement_group_id': procurement_group_id,
'default_company_id': company_id,
'default_state': state,
}" attrs="{'readonly': [('state', '!=', 'draft')]}">
<tree editable="bottom">
<field name="name" readonly="1"/>
<field name="product_id"/>
<field name="product_uom_id"
options="{'no_open': True, 'no_create': True}"
groups="product.group_uom"/>
<field name="route_id"
options="{'no_create': True}"
groups="stock.group_stock_multi_locations"/>
<field name="route_ids" invisible="1"/>
<field name="product_uom_qty"/>
<field name="qty_in_progress"/>
<field name="qty_done"/>
<field name="expected_date" invisible="1"/>
<field name="picking_policy"
invisible="1"/>
<field name="warehouse_id" invisible="1"/>
<field name="location_id" invisible="1"/>
<field name="procurement_group_id"
invisible="1"/>
<field name="company_id" invisible="1"/>
<field name="state"/>
</tree>
</field>
</page>
</notebook>
</sheet>
<div class="oe_chatter">
<field name="message_follower_ids"
widget="mail_followers"/>
<field name="activity_ids" widget="mail_activity"/>
<field name="message_ids" widget="mail_thread"/>
</div>
</form>
</field>
</record>
<record model="ir.actions.act_window"
id="stock_request_order_action">
<field name="name">Stock request orders</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">stock.request.order</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form</field>
</record>
</odoo>

View File

@@ -82,6 +82,7 @@
</div>
<group>
<group>
<field name="order_id" readonly="1"/>
<field name="product_id"/>
<field name="expected_date"/>
<field name="picking_policy"/>
@@ -93,6 +94,7 @@
<field name="location_id" groups="stock.group_stock_multi_locations"/>
<field name="route_id"
options="{'no_create': True}" groups="stock.group_stock_multi_locations"/>
<field name="route_ids" invisible="1"/>
<field name="procurement_group_id"
groups="stock.group_adv_location"/>
<field name="company_id" groups="base.group_multi_company" options="{'no_create': True}"/>
@@ -128,16 +130,4 @@
</field>
</record>
<menuitem
id="menu_stock_request_root"
name="Stock Requests"
groups="stock_request.group_stock_request_user,stock_request.group_stock_request_manager"
sequence="100"/>
<menuitem
id="menu_stock_request"
action="action_stock_request_form"
name="Stock Requests" parent="menu_stock_request_root"
sequence="10"/>
</odoo>