Files
stock-logistics-warehouse/stock_request/models/stock_request_order.py
hveficent 957559d0f2 [12.0][IMP] stock_request:
* Add readme autogenerated
* Add some hooks
* Improve test inheritance (reduce testing time)
* Don't allow qty <= 0
* SR expected_date = order expected_date if SRO
2021-04-19 15:50:02 +07:00

322 lines
12 KiB
Python

# 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, ValidationError, AccessError
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']
_order = 'id desc'
@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='/')
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)]},
)
allow_virtual_location = fields.Boolean(
related='company_id.stock_request_allow_virtual_loc',
readonly=True,
)
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('allow_virtual_location')
def onchange_allow_virtual_location(self):
if self.allow_virtual_location:
return {'domain': {'location_id': []}}
@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.model
def create(self, vals):
upd_vals = vals.copy()
if upd_vals.get('name', '/') == '/':
upd_vals['name'] = self.env['ir.sequence'].next_by_code(
'stock.request.order')
return super().create(upd_vals)
@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()
@api.constrains('warehouse_id', 'company_id')
def _check_warehouse_company(self):
if any(request.warehouse_id.company_id !=
request.company_id for request in self):
raise ValidationError(
_('The company of the stock request must match with '
'that of the warehouse.'))
@api.constrains('location_id', 'company_id')
def _check_location_company(self):
if any(request.location_id.company_id and
request.location_id.company_id !=
request.company_id for request in self):
raise ValidationError(
_('The company of the stock request must match with '
'that of the location.'))
@api.model
def _create_from_product_multiselect(self, products):
if not products:
return False
if products._name not in ('product.product', 'product.template'):
raise ValidationError(
_("This action only works in the context of products"))
if products._name == 'product.template':
# search instead of mapped so we don't include archived variants
products = self.env['product.product'].search([
('product_tmpl_id', 'in', products.ids)
])
expected = self.default_get(['expected_date'])['expected_date']
try:
order = self.env['stock.request.order'].create(dict(
expected_date=expected,
stock_request_ids=[(0, 0, dict(
product_id=product.id,
product_uom_id=product.uom_id.id,
product_uom_qty=1.0,
expected_date=expected,
)) for product in products]
))
except AccessError:
# TODO: if there is a nice way to hide the action from the
# Action-menu if the user doesn't have the necessary rights,
# that would be a better way of doing this
raise UserError(_(
"Unfortunately it seems you do not have the necessary rights "
"for creating stock requests. Please contact your "
"administrator."))
action = self.env.ref('stock_request.stock_request_order_action'
).read()[0]
action['views'] = [(
self.env.ref('stock_request.stock_request_order_form').id, 'form')]
action['res_id'] = order.id
return action