diff --git a/stock_request/README.rst b/stock_request/README.rst index f3d0cca06..34182e2ed 100644 --- a/stock_request/README.rst +++ b/stock_request/README.rst @@ -71,6 +71,7 @@ Contributors ------------ * Jordi Ballester (EFICENT) . +* Enric Tobella Maintainer ---------- diff --git a/stock_request/__manifest__.py b/stock_request/__manifest__.py index 309374eb5..c0c885040 100644 --- a/stock_request/__manifest__.py +++ b/stock_request/__manifest__.py @@ -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, } diff --git a/stock_request/data/stock_request_sequence_data.xml b/stock_request/data/stock_request_sequence_data.xml index 3f2f9fd8a..985be8057 100644 --- a/stock_request/data/stock_request_sequence_data.xml +++ b/stock_request/data/stock_request_sequence_data.xml @@ -10,5 +10,13 @@ + + Stock Request Order + stock.request.order + SRO/ + 5 + + + diff --git a/stock_request/models/__init__.py b/stock_request/models/__init__.py index a7e453e0c..36e050a25 100644 --- a/stock_request/models/__init__.py +++ b/stock_request/models/__init__.py @@ -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 diff --git a/stock_request/models/res_config_settings.py b/stock_request/models/res_config_settings.py new file mode 100644 index 000000000..74220d880 --- /dev/null +++ b/stock_request/models/res_config_settings.py @@ -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') diff --git a/stock_request/models/stock_request.py b/stock_request/models/stock_request.py index af8615424..3833890f9 100644 --- a/stock_request/models/stock_request.py +++ b/stock_request/models/stock_request.py @@ -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() diff --git a/stock_request/models/stock_request_order.py b/stock_request/models/stock_request_order.py new file mode 100644 index 000000000..cd76013de --- /dev/null +++ b/stock_request/models/stock_request_order.py @@ -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() diff --git a/stock_request/security/ir.model.access.csv b/stock_request/security/ir.model.access.csv index ca66a1a53..4a92b001b 100644 --- a/stock_request/security/ir.model.access.csv +++ b/stock_request/security/ir.model.access.csv @@ -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 diff --git a/stock_request/security/stock_request_security.xml b/stock_request/security/stock_request_security.xml index ecb96c13e..827788eb5 100644 --- a/stock_request/security/stock_request_security.xml +++ b/stock_request/security/stock_request_security.xml @@ -21,6 +21,11 @@ + + Stock Request Order + + + @@ -67,6 +72,46 @@ + + + stock_request_order multi-company + + + ['|',('company_id','=',False),('company_id','child_of',[user.company_id.id])] + + + + Follow Stock Request Order + + + + + + + ['|',('requested_by','=',user.id), + ('message_partner_ids', 'in', [user.partner_id.id])] + + + + Stock Request Order User + + + + + + + [('requested_by','=',user.id)] + + + + Stock Request Manager + + + + + + + diff --git a/stock_request/static/description/icon.png b/stock_request/static/description/icon.png index a7c11c7a1..c31ecfd9f 100644 Binary files a/stock_request/static/description/icon.png and b/stock_request/static/description/icon.png differ diff --git a/stock_request/tests/test_stock_request.py b/stock_request/tests/test_stock_request.py index ddd31e9c5..4a1f431b4 100644 --- a/stock_request/tests/test_stock_request.py +++ b/stock_request/tests/test_stock_request.py @@ -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) diff --git a/stock_request/views/res_config_settings_views.xml b/stock_request/views/res_config_settings_views.xml new file mode 100644 index 000000000..d1fafc2d1 --- /dev/null +++ b/stock_request/views/res_config_settings_views.xml @@ -0,0 +1,60 @@ + + + + + + + + res.config.settings.view.form.inherit.medical + res.config.settings + + + +
+
+
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+
+
+
+
+ + + Settings + ir.actions.act_window + res.config.settings + form + inline + {'module' : 'stock_request'} + + + +
diff --git a/stock_request/views/stock_request_menu.xml b/stock_request/views/stock_request_menu.xml new file mode 100644 index 000000000..c90445b14 --- /dev/null +++ b/stock_request/views/stock_request_menu.xml @@ -0,0 +1,27 @@ + + + + + + + + + + + diff --git a/stock_request/views/stock_request_order_views.xml b/stock_request/views/stock_request_order_views.xml new file mode 100644 index 000000000..444e3b902 --- /dev/null +++ b/stock_request/views/stock_request_order_views.xml @@ -0,0 +1,137 @@ + + + + + stock.request.order.tree + stock.request.order + + + + + + + + + + + + stock.request.order.form + stock.request.order + +
+
+
+ +
+ + + +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + +
+
+
+
+ + + Stock request orders + ir.actions.act_window + stock.request.order + form + tree,form + + +
diff --git a/stock_request/views/stock_request_views.xml b/stock_request/views/stock_request_views.xml index 9734edc16..2e748a712 100644 --- a/stock_request/views/stock_request_views.xml +++ b/stock_request/views/stock_request_views.xml @@ -82,6 +82,7 @@ + @@ -93,6 +94,7 @@ + @@ -128,16 +130,4 @@ - - - -