Skip to content

Commit

Permalink
[IMP] stock_return_request: lot suggestions
Browse files Browse the repository at this point in the history
  • Loading branch information
chienandalu authored and PrapassornS committed Nov 19, 2020
1 parent 6578c7b commit 227613c
Show file tree
Hide file tree
Showing 8 changed files with 157 additions and 5 deletions.
1 change: 1 addition & 0 deletions stock_return_request/__init__.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
from . import models
from . import wizard
from .hooks import pre_init_hook, post_init_hook
1 change: 1 addition & 0 deletions stock_return_request/__manifest__.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
'data/stock_return_request_data.xml',
'views/stock_return_request_views.xml',
'report/stock_return_report.xml',
'wizard/suggest_return_request.xml',
],
"pre_init_hook": "pre_init_hook",
"post_init_hook": "post_init_hook",
Expand Down
12 changes: 12 additions & 0 deletions stock_return_request/models/stock_move.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,15 @@ def _compute_qty_returnable(self):
move.returned_move_ids._compute_qty_returnable()
move.qty_returnable = move.quantity_done - sum(
move.returned_move_ids.mapped('qty_returnable'))

def _get_lot_returnable_qty(self, lot_id, qty=0):
"""Looks for chained returned moves to compute how much quantity
from the original can be returned for a given lot"""
for move in self.filtered(lambda x: x.state not in ['draft', 'cancel']):
mls = move.move_line_ids.filtered(lambda x: x.lot_id == lot_id)
if move.state == 'done':
qty += sum(mls.mapped('qty_done'))
else:
qty += sum(mls.mapped('product_uom_qty'))
qty -= move.returned_move_ids._get_lot_returnable_qty(lot_id)
return qty
19 changes: 14 additions & 5 deletions stock_return_request/models/stock_return_request.py
Original file line number Diff line number Diff line change
Expand Up @@ -415,10 +415,9 @@ def _get_moves_domain(self):
('origin_returned_move_id', '=', False),
('qty_returnable', '>', 0),
('product_id', '=', self.product_id.id),
('picking_id.partner_id', 'child_of',
self.request_id.partner_id.commercial_partner_id.id),
('move_line_ids.lot_id', '=', self.lot_id.id),
]
if not self.env.context.get('ignore_rr_lots'):
domain += [('move_line_ids.lot_id', '=', self.lot_id.id)]
if self.request_id.from_date:
domain += [('date', '>=', self.request_id.from_date)]
if self.request_id.picking_types:
Expand Down Expand Up @@ -516,8 +515,8 @@ def create(self, values):
Please first validate the first return request with this
product before creating a new one.
""") % (res.product_id.display_name,
res.return_from_location.display_name,
res.return_to_location.display_name,
res.request_id.return_from_location.display_name,
res.request_id.return_to_location.display_name,
res.request_id.partner_id.name))
return res

Expand All @@ -538,3 +537,13 @@ def onchange_product_id(self):
res = self.env['stock.quant'].read_group(search_args, ['quantity'], [])
max_quantity = res[0]['quantity']
self.max_quantity = max_quantity

def action_lot_suggestion(self):
return {
'name': _('Suggest Lot'),
'type': 'ir.actions.act_window',
'res_model': 'suggest.return.request.lot',
'view_type': 'form',
'view_mode': 'form',
'target': 'new',
}
1 change: 1 addition & 0 deletions stock_return_request/views/stock_return_request_views.xml
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@
<field name="product_id"/>
<field name="product_uom_id" readonly="1"/>
<field name="tracking" invisible="1"/>
<button name="action_lot_suggestion" type="object" icon="fa-list-ol" class="btn-link oe_edit_only" attrs="{'invisible': [('tracking', '=', 'none')]}"/>
<field name="lot_id" domain="[('product_id', '=', product_id)]" groups="stock.group_production_lot" context="{'default_product_id': product_id}" attrs="{'required': [('tracking', '!=', 'none')]}"/>
<field name="quantity"/>
<field name="max_quantity" readonly="1"/>
Expand Down
1 change: 1 addition & 0 deletions stock_return_request/wizard/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from . import suggest_return_request_lot
28 changes: 28 additions & 0 deletions stock_return_request/wizard/suggest_return_request.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>

<record id="stock_return_request_line_suggest_lot" model="ir.ui.view">
<field name="name">Suggested Lots for this Return Request Line</field>
<field name="model">suggest.return.request.lot</field>
<field name="arch" type="xml">
<form>
<field name="lot_suggestion_mode"/>
<field name="suggested_lot" attrs="{'invisible': [('lot_suggestion_mode', '=', 'detail')]}"/>
<field name="suggested_lot_detail" attrs="{'invisible': [('lot_suggestion_mode', '=', 'sum')]}"/>
<footer>
<button name="action_confirm" type="object" string="Accept" class="oe_highlight"/>
<button special="cancel" string="Cancel"/>
</footer>
</form>
</field>
</record>

<record id="act_stock_return_request_line_suggest_lot" model="ir.actions.act_window">
<field name="name">Suggested Lots for this Return Request Line</field>
<field name="res_model">suggest.return.request.lot</field>
<field name="view_type">form</field>
<field name="view_mode">form</field>
<field name="target">new</field>
</record>

</odoo>
99 changes: 99 additions & 0 deletions stock_return_request/wizard/suggest_return_request_lot.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
# Copyright 2020 Tecnativa - David Vidal
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from odoo import api, fields, models


class SuggestReturnRequestLot(models.TransientModel):
_name = "suggest.return.request.lot"
_description = "Suggest lots for the return request line"

request_line_id = fields.Many2one(
comodel_name="stock.return.request.line",
default=lambda self: self._default_request_line_id(),
required=True,
readonly=True,
ondelete="cascade",
)
lot_suggestion_mode = fields.Selection(
selection=[
('sum', 'Total by lot'),
('detail', 'Total by move'),
],
default='sum',
)
suggested_lot = fields.Selection(
selection="_get_suggested_lots_selection",
string="Suggested Lots",
help="You can return these lots",
)
suggested_lot_detail = fields.Selection(
selection="_get_suggested_lots_detail_selection",
string="Suggested Lots",
help="You can return these lots",
)

@api.model
def _default_request_line_id(self):
if (self.env.context.get('active_model', False) !=
'stock.return.request.line'):
return False
return self.env.context.get('active_id', False)

def _get_suggested_lots_data(self):
"""Returns dict with returnable lots and qty"""
if (
self.env.context.get("active_model", False)
!= "stock.return.request.line"
):
return (False, False)
request_line = (
self.request_line_id or
self.request_line_id.browse(self.env.context.get('active_id')))
if not request_line:
return (False, False)
moves = self.env["stock.move"].search(
request_line.with_context(ignore_rr_lots=True)._get_moves_domain(),
order=request_line.request_id.return_order
)
suggested_lots_totals = {}
suggested_lots_moves = {}
for line in moves.mapped("move_line_ids"):
qty = line.move_id._get_lot_returnable_qty(line.lot_id)
suggested_lots_moves[line] = qty
suggested_lots_totals.setdefault(line.lot_id, 0)
suggested_lots_totals[line.lot_id] += qty
return (suggested_lots_totals, suggested_lots_moves)

def _get_suggested_lots_selection(self):
"""Return selection tuple with lots selections and qtys"""
suggested_lots, suggested_lots_moves = self._get_suggested_lots_data()
if not suggested_lots:
return
if self.lot_suggestion_mode == 'detail':
return [(
ml.lot_id.id, '{} - {} - {}'.format(
ml.date, ml.name, suggested_lots_moves[ml])
) for ml in suggested_lots_moves.keys()]
return [(
x.id, '{} - {}'.format(x.name, suggested_lots[x]))
for x in suggested_lots.keys()]

def _get_suggested_lots_detail_selection(self):
"""Return selection tuple with lots selections and qtys"""
suggested_lots, suggested_lots_moves = self._get_suggested_lots_data()
if not suggested_lots_moves:
return
return [(
ml.lot_id.id, '{} - {} - {} - {}'.format(
ml.date, ml.lot_id.name,
ml.reference, suggested_lots_moves[ml])
) for ml in suggested_lots_moves.keys()]

def action_confirm(self):
# TODO: In v12, selection keys are forced to strings, so the should be
# converted to integers in order to write the into the register
if self.lot_suggestion_mode == 'sum' and self.suggested_lot:
self.request_line_id.lot_id = self.suggested_lot
elif (self.lot_suggestion_mode == 'detail' and
self.suggested_lot_detail):
self.request_line_id.lot_id = self.suggested_lot_detail

0 comments on commit 227613c

Please sign in to comment.