mejora el stock picking batch de odoo desde OCA/stock_logistics_workflow

This commit is contained in:
santiky 2022-04-22 18:42:08 +02:00
parent 8a755662d7
commit 448f889f12
Signed by: snt
GPG key ID: A9FD34930EADBE71
41 changed files with 6541 additions and 0 deletions

View file

@ -0,0 +1,7 @@
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
from . import res_config_settings
from . import stock_batch_picking
from . import product_product
from . import stock_picking
from . import stock_warehouse

View file

@ -0,0 +1,11 @@
# Copyright 2012-2014 Alexandre Fayolle, Camptocamp SA
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
from odoo import fields, models
class Product(models.Model):
_inherit = "product.product"
# TODO: Integrate in existent field
description_warehouse = fields.Text('Warehouse Description',
translate=True)

View file

@ -0,0 +1,20 @@
# Copyright 2019 Camptocamp - Iryna Vyshnevska
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
from odoo import fields, models
class ResConfigSettings(models.TransientModel):
_inherit = 'res.config.settings'
use_oca_batch_validation = fields.Boolean(
string='Use OCA approach to validate Picking Batch',
related="company_id.use_oca_batch_validation",
readonly=False,
)
class Company(models.Model):
_inherit = "res.company"
use_oca_batch_validation = fields.Boolean()

View file

@ -0,0 +1,261 @@
# Copyright 2012-2014 Alexandre Fayolle, Camptocamp SA
# Copyright 2018-2020 Tecnativa - Carlos Dauden
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
from odoo import _, api, fields, models
from odoo.exceptions import UserError
class StockBatchPicking(models.Model):
""" This object allow to manage multiple stock.picking at the same time.
"""
# renamed stock.batch.picking -> stock.picking.batch
_inherit = ['stock.picking.batch', 'mail.thread', 'mail.activity.mixin']
_name = 'stock.picking.batch'
name = fields.Char(
index=True,
unique=True,
states={'draft': [('readonly', False)]},
default=lambda self: self.env['ir.sequence'].next_by_code(
'stock.picking.batch'
),
)
# added state to be compatible with picking_ids
state = fields.Selection(
selection_add=[
('assigned', 'Available'),
],
readonly=True, index=True,
help='the state of the batch picking. '
'Workflow is draft -> in_progress/assigned -> done or cancel',
)
date = fields.Date(
string='Date',
required=True, readonly=True, index=True,
states={
'draft': [('readonly', False)],
'in_progress': [('readonly', False)]
},
default=fields.Date.context_today,
help='date on which the batch picking is to be processed',
)
user_id = fields.Many2one(
comodel_name='res.users',
string='Picker',
readonly=True, index=True,
states={
'draft': [('readonly', False)],
'in_progress': [('readonly', False)]
},
help='the user to which the pickings are assigned',
old_name='picker_id',
)
use_oca_batch_validation = fields.Boolean(
default=lambda self: self.env.user.company_id.use_oca_batch_validation,
copy=False,
)
picking_ids = fields.One2many(
string='Pickings',
readonly=True,
states={'draft': [('readonly', False)]},
help='List of picking managed by this batch.',
)
# TODO add comment to this field
active_picking_ids = fields.One2many(
string="Active Pickings",
comodel_name='stock.picking',
inverse_name='batch_id',
readonly=True,
domain=[('state', 'not in', ('cancel', 'done'))],
)
notes = fields.Text('Notes', help='free form remarks')
move_lines = fields.Many2many(
comodel_name='stock.move',
readonly=True,
string='Operations',
compute='_compute_move_lines',
)
move_line_ids = fields.Many2many(
comodel_name='stock.move.line',
string='Detailed operations',
compute='_compute_move_line_ids',
# HACK: Allow to write sml fields from this model
inverse=lambda self: self,
)
entire_package_ids = fields.Many2many(
comodel_name='stock.quant.package',
compute='_compute_entire_package_ids',
help='Those are the entire packages of a picking shown in the view of '
'operations',
)
entire_package_detail_ids = fields.Many2many(
comodel_name='stock.quant.package',
compute='_compute_entire_package_ids',
help='Those are the entire packages of a picking shown in the view of '
'detailed operations',
)
picking_count = fields.Integer(
string='# Pickings',
compute='_compute_picking_count',
)
@api.depends('picking_ids')
def _compute_move_lines(self):
for batch in self:
if batch.use_oca_batch_validation:
batch.move_lines = batch.picking_ids.mapped("move_lines")
@api.depends('picking_ids')
def _compute_move_line_ids(self):
for batch in self:
if batch.use_oca_batch_validation:
batch.move_line_ids = batch.picking_ids.mapped(
'move_line_ids'
)
@api.depends('picking_ids')
def _compute_entire_package_ids(self):
for batch in self:
if batch.use_oca_batch_validation:
batch.update({
'entire_package_ids': batch.picking_ids.mapped(
'entire_package_ids'),
'entire_package_detail_ids': batch.picking_ids.mapped(
'entire_package_detail_ids'),
})
def _compute_picking_count(self):
"""Calculate number of pickings."""
groups = self.env['stock.picking'].read_group(
domain=[('batch_id', 'in', self.ids)],
fields=['batch_id'],
groupby=['batch_id'],
)
counts = {g['batch_id'][0]: g['batch_id_count'] for g in groups}
for batch in self:
batch.picking_count = counts.get(batch.id, 0)
def get_not_empties(self):
""" Return all batches in this recordset
for which picking_ids is not empty.
:raise UserError: If all batches are empty.
"""
if not self.mapped('picking_ids'):
if len(self) == 1:
message = _('This Batch has no pickings')
else:
message = _('These Batches have no pickings')
raise UserError(message)
return self.filtered(lambda b: len(b.picking_ids) != 0)
def verify_state(self, expected_state=None):
""" Check if batches states must be changed based on pickings states.
If all pickings are canceled, batch must be canceled.
If all pickings are canceled or done, batch must be done.
If all pickings are canceled or done or *expected_state*,
batch must be *expected_state*.
:return: True if batches states has been changed.
"""
expected_states = {'done', 'cancel'}
if expected_state is not None:
expected_states.add(expected_state)
all_good = True
for batch in self.filtered(lambda b: b.state not in expected_states):
states = set(batch.mapped('picking_ids.state'))
if not states or states == {'cancel'}:
batch.state = 'cancel'
elif states == {'done'} or states == {'done', 'cancel'}:
batch.state = 'done'
elif states.issubset(expected_states):
batch.state = expected_state
else:
all_good = False
return all_good
@api.multi
def action_cancel(self):
""" Call action_cancel for all batches pickings
and set batches states to cancel too.
"""
for batch in self:
if not batch.picking_ids:
batch.write({'state': 'cancel'})
else:
if not batch.verify_state():
batch.picking_ids.action_cancel()
@api.multi
def action_assign(self):
""" Check if batches pickings are available.
"""
batches = self.get_not_empties()
if not batches.verify_state('in_progress'):
mass_wiz = self.env['stock.picking.mass.action'].create({
'check_availability': True,
'picking_ids': [
(6, 0, batches.mapped('active_picking_ids').ids)
]
})
return mass_wiz.mass_action()
@api.multi
def action_transfer(self):
""" Create wizard to process all active pickings in these batches
"""
batches = self.get_not_empties()
if not batches.verify_state():
mass_wiz = self.env['stock.picking.mass.action'].create({
'transfer': True,
'picking_ids': [
(6, 0, batches.mapped('active_picking_ids').ids)
],
})
return mass_wiz.mass_action()
@api.multi
def action_print_picking(self):
pickings = self.mapped('picking_ids')
if not pickings:
raise UserError(_('Nothing to print.'))
return self.env.ref(
'stock_picking_batch_extended.action_report_batch_picking'
).report_action(self)
@api.multi
def remove_undone_pickings(self):
""" Remove of this batch all pickings which state is not done / cancel.
"""
self.mapped('active_picking_ids').write({'batch_id': False})
self.verify_state()
@api.multi
def action_view_stock_picking(self):
"""This function returns an action that display existing pickings of
given batch picking.
"""
self.ensure_one()
pickings = self.mapped('picking_ids')
action = self.env.ref('stock.action_picking_tree_all').read([])[0]
action['domain'] = [('id', 'in', pickings.ids)]
return action

View file

@ -0,0 +1,79 @@
# Copyright 2016 Cyril Gaudin, Camptocamp SA
# Copyright 2018 Tecnativa - Carlos Dauden
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
from odoo import api, fields, models
from odoo.tools import float_is_zero
class StockPicking(models.Model):
_inherit = "stock.picking"
batch_id = fields.Many2one(
string='Batch',
domain="[('state', '=', 'draft')]",
)
@api.multi
def action_cancel(self):
"""In addition to what the method in the parent class does,
cancel the batches for which all pickings are cancelled
"""
result = super(StockPicking, self).action_cancel()
self.mapped('batch_id').verify_state()
return result
@api.multi
def action_assign(self):
"""In addition to what the method in the parent class does,
Changed batches states to assigned if all picking are assigned.
"""
result = super(StockPicking, self).action_assign()
self.mapped('batch_id').verify_state('assigned')
return result
@api.multi
def action_done(self):
"""In addition to what the method in the parent class does,
Changed batches states to done if all picking are done.
"""
result = super(StockPicking, self).action_done()
self.mapped('batch_id').verify_state()
return result
def force_transfer(self, force_qty=True):
""" Do the picking transfer (by calling action_done)
If *force_qty* is True, force the transfer for all product_uom_qty
when qty_done is 0.
Otherwise, process only pack operation with qty_done.
If a picking has no qty_done filled, we released it from his batch
"""
for pick in self:
if pick.state != 'assigned':
pick.action_assign()
if pick.state != 'assigned':
continue
if force_qty:
for pack in pick.move_line_ids:
pack.qty_done = pack.product_uom_qty
else:
if all(
float_is_zero(
pack.qty_done,
precision_rounding=pack.product_uom_id.rounding)
for pack in pick.move_line_ids):
# No qties to process, release out of the batch
pick.batch_id = False
continue
else:
for pack in pick.move_line_ids:
if not pack.qty_done:
pack.unlink()
pick.action_done()

View file

@ -0,0 +1,14 @@
# Copyright 2016 Camptocamp SA
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
from odoo import fields, models
class StockWarehouse(models.Model):
_inherit = 'stock.warehouse'
default_user_id = fields.Many2one(
'res.users', 'Default Picker',
help='the user to which the batch pickings are assigned by default',
index=True,
)