mirror of
https://github.com/OCA/stock-logistics-warehouse.git
synced 2025-01-21 14:27:28 +02:00
Merge pull request #14 from yvaucher/8.0-stock_reserve_sale
8.0 port module stock_reserve_sale
This commit is contained in:
@@ -1,110 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
# Author: Guewen Baconnier
|
||||
# Copyright 2013 Camptocamp SA
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
# published by the Free Software Foundation, either version 3 of the
|
||||
# License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
from openerp.osv import orm, fields
|
||||
|
||||
|
||||
class sale_stock_reserve(orm.TransientModel):
|
||||
_name = 'sale.stock.reserve'
|
||||
|
||||
_columns = {
|
||||
'location_id': fields.many2one(
|
||||
'stock.location',
|
||||
'Source Location',
|
||||
required=True),
|
||||
'location_dest_id': fields.many2one(
|
||||
'stock.location',
|
||||
'Reservation Location',
|
||||
required=True,
|
||||
help="Location where the system will reserve the "
|
||||
"products."),
|
||||
'date_validity': fields.date(
|
||||
"Validity Date",
|
||||
help="If a date is given, the reservations will be released "
|
||||
"at the end of the validity."),
|
||||
'note': fields.text('Notes'),
|
||||
}
|
||||
|
||||
def _default_location_id(self, cr, uid, context=None):
|
||||
reserv_obj = self.pool.get('stock.reservation')
|
||||
return reserv_obj._default_location_id(cr, uid, context=context)
|
||||
|
||||
def _default_location_dest_id(self, cr, uid, context=None):
|
||||
reserv_obj = self.pool.get('stock.reservation')
|
||||
return reserv_obj._default_location_dest_id(cr, uid, context=context)
|
||||
|
||||
_defaults = {
|
||||
'location_id': _default_location_id,
|
||||
'location_dest_id': _default_location_dest_id,
|
||||
}
|
||||
|
||||
def _prepare_stock_reservation(self, cr, uid, form, line, context=None):
|
||||
product_uos = line.product_uos.id if line.product_uos else False
|
||||
return {'product_id': line.product_id.id,
|
||||
'product_uom': line.product_uom.id,
|
||||
'product_qty': line.product_uom_qty,
|
||||
'date_validity': form.date_validity,
|
||||
'name': "{} ({})".format(line.order_id.name, line.name),
|
||||
'location_id': form.location_id.id,
|
||||
'location_dest_id': form.location_dest_id.id,
|
||||
'note': form.note,
|
||||
'product_uos_qty': line.product_uos_qty,
|
||||
'product_uos': product_uos,
|
||||
'price_unit': line.price_unit,
|
||||
'sale_line_id': line.id,
|
||||
}
|
||||
|
||||
def stock_reserve(self, cr, uid, ids, line_ids, context=None):
|
||||
assert len(ids) == 1, "Expected 1 ID, got %r" % ids
|
||||
reserv_obj = self.pool.get('stock.reservation')
|
||||
line_obj = self.pool.get('sale.order.line')
|
||||
|
||||
form = self.browse(cr, uid, ids[0], context=context)
|
||||
lines = line_obj.browse(cr, uid, line_ids, context=context)
|
||||
for line in lines:
|
||||
if not line.is_stock_reservable:
|
||||
continue
|
||||
vals = self._prepare_stock_reservation(cr, uid, form, line,
|
||||
context=context)
|
||||
reserv_id = reserv_obj.create(cr, uid, vals, context=context)
|
||||
reserv_obj.reserve(cr, uid, [reserv_id], context=context)
|
||||
return True
|
||||
|
||||
def button_reserve(self, cr, uid, ids, context=None):
|
||||
assert len(ids) == 1, "Expected 1 ID, got %r" % ids
|
||||
if context is None:
|
||||
context = {}
|
||||
close = {'type': 'ir.actions.act_window_close'}
|
||||
active_model = context.get('active_model')
|
||||
active_ids = context.get('active_ids')
|
||||
if not (active_model and active_ids):
|
||||
return close
|
||||
|
||||
if active_model == 'sale.order':
|
||||
sale_obj = self.pool.get('sale.order')
|
||||
sales = sale_obj.browse(cr, uid, active_ids, context=context)
|
||||
line_ids = [line.id for sale in sales for line in sale.order_line]
|
||||
|
||||
if active_model == 'sale.order.line':
|
||||
line_ids = active_ids
|
||||
|
||||
self.stock_reserve(cr, uid, ids, line_ids, context=context)
|
||||
return close
|
||||
@@ -57,9 +57,9 @@ insufficient at the order date, you may want to install the
|
||||
'view/sale.xml',
|
||||
'view/stock_reserve.xml',
|
||||
],
|
||||
'auto_install': False,
|
||||
'test': ['test/sale_reserve.yml',
|
||||
'test/sale_line_reserve.yml',
|
||||
],
|
||||
'installable': False,
|
||||
'installable': True,
|
||||
'auto_install': False,
|
||||
}
|
||||
@@ -19,102 +19,134 @@
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
from openerp.osv import orm, fields
|
||||
from openerp import models, fields, api
|
||||
from openerp.exceptions import except_orm
|
||||
from openerp.tools.translate import _
|
||||
|
||||
|
||||
class sale_order(orm.Model):
|
||||
class SaleOrder(models.Model):
|
||||
_inherit = 'sale.order'
|
||||
|
||||
def _stock_reservation(self, cr, uid, ids, fields, args, context=None):
|
||||
result = {}
|
||||
for order_id in ids:
|
||||
result[order_id] = {'has_stock_reservation': False,
|
||||
'is_stock_reservable': False}
|
||||
for sale in self.browse(cr, uid, ids, context=context):
|
||||
@api.multi
|
||||
@api.depends('state',
|
||||
'order_line.reservation_ids',
|
||||
'order_line.is_stock_reservable')
|
||||
def _stock_reservation(self):
|
||||
for sale in self:
|
||||
has_stock_reservation = False
|
||||
is_stock_reservable = False
|
||||
for line in sale.order_line:
|
||||
if line.reservation_ids:
|
||||
result[sale.id]['has_stock_reservation'] = True
|
||||
has_stock_reservation = True
|
||||
if line.is_stock_reservable:
|
||||
result[sale.id]['is_stock_reservable'] = True
|
||||
is_stock_reservable = True
|
||||
if sale.state not in ('draft', 'sent'):
|
||||
result[sale.id]['is_stock_reservable'] = False
|
||||
return result
|
||||
is_stock_reservable = False
|
||||
sale.is_stock_reservable = is_stock_reservable
|
||||
sale.has_stock_reservation = has_stock_reservation
|
||||
|
||||
_columns = {
|
||||
'has_stock_reservation': fields.function(
|
||||
_stock_reservation,
|
||||
type='boolean',
|
||||
readonly=True,
|
||||
multi='stock_reservation',
|
||||
string='Has Stock Reservations'),
|
||||
'is_stock_reservable': fields.function(
|
||||
_stock_reservation,
|
||||
type='boolean',
|
||||
readonly=True,
|
||||
multi='stock_reservation',
|
||||
string='Can Have Stock Reservations'),
|
||||
}
|
||||
has_stock_reservation = fields.Boolean(
|
||||
compute='_stock_reservation',
|
||||
readonly=True,
|
||||
multi='stock_reservation',
|
||||
store=True,
|
||||
string='Has Stock Reservations')
|
||||
is_stock_reservable = fields.Boolean(
|
||||
compute='_stock_reservation',
|
||||
readonly=True,
|
||||
multi='stock_reservation',
|
||||
store=True,
|
||||
string='Can Have Stock Reservations')
|
||||
|
||||
def release_all_stock_reservation(self, cr, uid, ids, context=None):
|
||||
sales = self.browse(cr, uid, ids, context=context)
|
||||
line_ids = [line.id for sale in sales for line in sale.order_line]
|
||||
line_obj = self.pool.get('sale.order.line')
|
||||
line_obj.release_stock_reservation(cr, uid, line_ids, context=context)
|
||||
@api.multi
|
||||
def release_all_stock_reservation(self):
|
||||
line_ids = [line.id for order in self for line in order.order_line]
|
||||
lines = self.env['sale.order.line'].browse(line_ids)
|
||||
lines.release_stock_reservation()
|
||||
return True
|
||||
|
||||
def action_button_confirm(self, cr, uid, ids, context=None):
|
||||
self.release_all_stock_reservation(cr, uid, ids, context=context)
|
||||
return super(sale_order, self).action_button_confirm(
|
||||
cr, uid, ids, context=context)
|
||||
@api.multi
|
||||
def action_button_confirm(self):
|
||||
self.release_all_stock_reservation()
|
||||
return super(SaleOrder, self).action_button_confirm()
|
||||
|
||||
def action_cancel(self, cr, uid, ids, context=None):
|
||||
self.release_all_stock_reservation(cr, uid, ids, context=context)
|
||||
return super(sale_order, self).action_cancel(
|
||||
cr, uid, ids, context=context)
|
||||
@api.multi
|
||||
def action_cancel(self):
|
||||
self.release_all_stock_reservation()
|
||||
return super(SaleOrder, self).action_cancel()
|
||||
|
||||
|
||||
class sale_order_line(orm.Model):
|
||||
class SaleOrderLine(models.Model):
|
||||
_inherit = 'sale.order.line'
|
||||
|
||||
def _is_stock_reservable(self, cr, uid, ids, fields, args, context=None):
|
||||
result = {}.fromkeys(ids, False)
|
||||
for line in self.browse(cr, uid, ids, context=context):
|
||||
if line.state != 'draft':
|
||||
continue
|
||||
if line.type == 'make_to_order':
|
||||
continue
|
||||
if (not line.product_id or line.product_id.type == 'service'):
|
||||
continue
|
||||
if not line.reservation_ids:
|
||||
result[line.id] = True
|
||||
return result
|
||||
@api.multi
|
||||
def _get_line_rule(self):
|
||||
""" Get applicable rule for this product
|
||||
|
||||
_columns = {
|
||||
'reservation_ids': fields.one2many(
|
||||
'stock.reservation',
|
||||
'sale_line_id',
|
||||
string='Stock Reservation'),
|
||||
'is_stock_reservable': fields.function(
|
||||
_is_stock_reservable,
|
||||
type='boolean',
|
||||
readonly=True,
|
||||
string='Can be reserved'),
|
||||
}
|
||||
Reproduce get suitable rule from procurement
|
||||
to predict source location """
|
||||
ProcurementRule = self.env['procurement.rule']
|
||||
product = self.product_id
|
||||
product_route_ids = [x.id for x in product.route_ids +
|
||||
product.categ_id.total_route_ids]
|
||||
rules = ProcurementRule.search([('route_id', 'in', product_route_ids)],
|
||||
order='route_sequence, sequence',
|
||||
limit=1)
|
||||
|
||||
def copy_data(self, cr, uid, id, default=None, context=None):
|
||||
if default is None:
|
||||
default = {}
|
||||
default['reservation_ids'] = False
|
||||
return super(sale_order_line, self).copy_data(
|
||||
cr, uid, id, default=default, context=context)
|
||||
if not rules:
|
||||
warehouse = self.order_id.warehouse_id
|
||||
wh_routes = warehouse.route_ids
|
||||
wh_route_ids = [route.id for route in wh_routes]
|
||||
domain = ['|', ('warehouse_id', '=', warehouse.id),
|
||||
('warehouse_id', '=', False),
|
||||
('route_id', 'in', wh_route_ids)]
|
||||
|
||||
def release_stock_reservation(self, cr, uid, ids, context=None):
|
||||
lines = self.browse(cr, uid, ids, context=context)
|
||||
reserv_ids = [reserv.id for line in lines
|
||||
rules = ProcurementRule.search(domain,
|
||||
order='route_sequence, sequence')
|
||||
|
||||
if rules:
|
||||
return rules[0]
|
||||
return False
|
||||
|
||||
@api.multi
|
||||
def _get_procure_method(self):
|
||||
""" Get procure_method depending on product routes """
|
||||
rule = self._get_line_rule()
|
||||
if rule:
|
||||
return rule.procure_method
|
||||
return False
|
||||
|
||||
@api.multi
|
||||
@api.depends('state',
|
||||
'product_id.route_ids',
|
||||
'product_id.type')
|
||||
def _is_stock_reservable(self):
|
||||
for line in self:
|
||||
reservable = False
|
||||
if (not (line.state != 'draft'
|
||||
or line._get_procure_method() == 'make_to_order'
|
||||
or not line.product_id
|
||||
or line.product_id.type == 'service')
|
||||
and not line.reservation_ids):
|
||||
reservable = True
|
||||
line.is_stock_reservable = reservable
|
||||
|
||||
reservation_ids = fields.One2many(
|
||||
'stock.reservation',
|
||||
'sale_line_id',
|
||||
string='Stock Reservation',
|
||||
copy=False)
|
||||
is_stock_reservable = fields.Boolean(
|
||||
compute='_is_stock_reservable',
|
||||
readonly=True,
|
||||
string='Can be reserved')
|
||||
|
||||
@api.multi
|
||||
def release_stock_reservation(self):
|
||||
reserv_ids = [reserv.id for line in self
|
||||
for reserv in line.reservation_ids]
|
||||
reserv_obj = self.pool.get('stock.reservation')
|
||||
reserv_obj.release(cr, uid, reserv_ids, context=context)
|
||||
reservations = self.env['stock.reservation'].browse(reserv_ids)
|
||||
reservations.release()
|
||||
return True
|
||||
|
||||
def product_id_change(self, cr, uid, ids,
|
||||
@@ -133,7 +165,7 @@ class sale_order_line(orm.Model):
|
||||
fiscal_position=False,
|
||||
flag=False,
|
||||
context=None):
|
||||
result = super(sale_order_line, self).product_id_change(
|
||||
result = super(SaleOrderLine, self).product_id_change(
|
||||
cr, uid, ids, pricelist, product, qty=qty, uom=uom,
|
||||
qty_uos=qty_uos, uos=uos, name=name, partner_id=partner_id,
|
||||
lang=lang, update_tax=update_tax, date_order=date_order,
|
||||
@@ -158,7 +190,8 @@ class sale_order_line(orm.Model):
|
||||
}
|
||||
return result
|
||||
|
||||
def write(self, cr, uid, ids, vals, context=None):
|
||||
@api.multi
|
||||
def write(self, vals):
|
||||
block_on_reserve = ('product_id',
|
||||
'product_uom',
|
||||
'product_uos',
|
||||
@@ -170,33 +203,31 @@ class sale_order_line(orm.Model):
|
||||
test_block = keys.intersection(block_on_reserve)
|
||||
test_update = keys.intersection(update_on_reserve)
|
||||
if test_block:
|
||||
for line in self.browse(cr, uid, ids, context=context):
|
||||
for line in self:
|
||||
if not line.reservation_ids:
|
||||
continue
|
||||
raise orm.except_orm(
|
||||
raise except_orm(
|
||||
_('Error'),
|
||||
_('You cannot change the product or unit of measure '
|
||||
'of lines with a stock reservation. '
|
||||
'Release the reservation '
|
||||
'before changing the product.'))
|
||||
res = super(sale_order_line, self).write(cr, uid, ids,
|
||||
vals,
|
||||
context=context)
|
||||
res = super(SaleOrderLine, self).write(vals)
|
||||
if test_update:
|
||||
for line in self.browse(cr, uid, ids, context=context):
|
||||
for line in self:
|
||||
if not line.reservation_ids:
|
||||
continue
|
||||
if len(line.reservation_ids) > 1:
|
||||
raise orm.except_orm(
|
||||
raise except_orm(
|
||||
_('Error'),
|
||||
_('Several stock reservations are linked with the '
|
||||
'line. Impossible to adjust their quantity. '
|
||||
'Please release the reservation '
|
||||
'before changing the quantity.'))
|
||||
|
||||
line.reservation_ids[0].write(
|
||||
line.reservation_ids.write(
|
||||
{'price_unit': line.price_unit,
|
||||
'product_qty': line.product_uom_qty,
|
||||
'product_uom_qty': line.product_uom_qty,
|
||||
'product_uos_qty': line.product_uos_qty,
|
||||
}
|
||||
)
|
||||
@@ -19,32 +19,24 @@
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
from openerp.osv import orm, fields
|
||||
from openerp import models, fields, api
|
||||
|
||||
|
||||
class stock_reservation(orm.Model):
|
||||
class StockReservation(models.Model):
|
||||
_inherit = 'stock.reservation'
|
||||
|
||||
_columns = {
|
||||
'sale_line_id': fields.many2one(
|
||||
'sale.order.line',
|
||||
string='Sale Order Line',
|
||||
ondelete='cascade'),
|
||||
'sale_id': fields.related(
|
||||
'sale_line_id', 'order_id',
|
||||
type='many2one',
|
||||
relation='sale.order',
|
||||
string='Sale Order')
|
||||
}
|
||||
sale_line_id = fields.Many2one(
|
||||
'sale.order.line',
|
||||
string='Sale Order Line',
|
||||
ondelete='cascade',
|
||||
copy=False)
|
||||
sale_id = fields.Many2one(
|
||||
'sale.order',
|
||||
string='Sale Order',
|
||||
related='sale_line_id.order_id')
|
||||
|
||||
def release(self, cr, uid, ids, context=None):
|
||||
self.write(cr, uid, ids, {'sale_line_id': False}, context=context)
|
||||
return super(stock_reservation, self).release(
|
||||
cr, uid, ids, context=context)
|
||||
|
||||
def copy_data(self, cr, uid, id, default=None, context=None):
|
||||
if default is None:
|
||||
default = {}
|
||||
default['sale_line_id'] = False
|
||||
return super(stock_reservation, self).copy_data(
|
||||
cr, uid, id, default=default, context=context)
|
||||
@api.multi
|
||||
def release(self):
|
||||
for rec in self:
|
||||
rec.sale_line_id = False
|
||||
return super(StockReservation, self).release()
|
||||
@@ -10,7 +10,6 @@
|
||||
standard_price: 70.0
|
||||
uom_id: product.product_uom_kgm
|
||||
uom_po_id: product.product_uom_kgm
|
||||
procure_method: make_to_stock
|
||||
valuation: real_time
|
||||
cost_method: average
|
||||
property_stock_account_input: account.o_expense
|
||||
@@ -60,6 +59,19 @@
|
||||
!python {model: product.product}: |
|
||||
product = self.browse(cr, uid, ref('product_yogurt'), context=context)
|
||||
assert product.virtual_available == 6, "Stock is not updated."
|
||||
-
|
||||
I set product_12 to MTO (doesn't work)
|
||||
-
|
||||
!record {model: product.product, id: product.product_product_12}:
|
||||
route_ids:
|
||||
- stock.route_warehouse0_mto
|
||||
-
|
||||
I set MTO for real
|
||||
-
|
||||
!python {model: product.product}: |
|
||||
product = self.browse(cr, uid, ref('product.product_product_12'), context=context)
|
||||
self.write(cr, uid, ref('product.product_product_12'),
|
||||
{'route_ids': [(6, False, [ref('stock.route_warehouse0_mto')])]}, context=context)
|
||||
-
|
||||
And I create a MTO sales order line
|
||||
-
|
||||
@@ -67,9 +79,8 @@
|
||||
order_id: sale_reserve_02
|
||||
name: Mouse, Wireless
|
||||
product_id: product.product_product_12
|
||||
type: make_to_order
|
||||
product_uom_qty: 4
|
||||
product_uom: product.product_uom_kgm
|
||||
product_uom: product.product_uom_unit
|
||||
-
|
||||
And I try to create a stock reserve for this MTO line
|
||||
-
|
||||
@@ -10,7 +10,6 @@
|
||||
standard_price: 70.0
|
||||
uom_id: product.product_uom_kgm
|
||||
uom_po_id: product.product_uom_kgm
|
||||
procure_method: make_to_stock
|
||||
valuation: real_time
|
||||
cost_method: average
|
||||
property_stock_account_input: account.o_expense
|
||||
103
stock_reserve_sale/wizard/sale_stock_reserve.py
Normal file
103
stock_reserve_sale/wizard/sale_stock_reserve.py
Normal file
@@ -0,0 +1,103 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
# Author: Guewen Baconnier
|
||||
# Copyright 2013 Camptocamp SA
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
# published by the Free Software Foundation, either version 3 of the
|
||||
# License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
from openerp import models, fields, api
|
||||
|
||||
|
||||
class SaleStockReserve(models.TransientModel):
|
||||
_name = 'sale.stock.reserve'
|
||||
|
||||
@api.model
|
||||
def _default_location_id(self):
|
||||
return self.env['stock.reservation']._default_location_id()
|
||||
|
||||
@api.model
|
||||
def _default_location_dest_id(self):
|
||||
return self.env['stock.reservation']._default_location_dest_id()
|
||||
|
||||
location_id = fields.Many2one(
|
||||
'stock.location',
|
||||
'Source Location',
|
||||
required=True,
|
||||
default=_default_location_id)
|
||||
location_dest_id = fields.Many2one(
|
||||
'stock.location',
|
||||
'Reservation Location',
|
||||
required=True,
|
||||
help="Location where the system will reserve the "
|
||||
"products.",
|
||||
default=_default_location_dest_id)
|
||||
date_validity = fields.Date(
|
||||
"Validity Date",
|
||||
help="If a date is given, the reservations will be released "
|
||||
"at the end of the validity.")
|
||||
note = fields.Text('Notes')
|
||||
|
||||
@api.multi
|
||||
def _prepare_stock_reservation(self, line):
|
||||
self.ensure_one()
|
||||
product_uos = line.product_uos.id if line.product_uos else False
|
||||
return {'product_id': line.product_id.id,
|
||||
'product_uom': line.product_uom.id,
|
||||
'product_uom_qty': line.product_uom_qty,
|
||||
'date_validity': self.date_validity,
|
||||
'name': "%s (%s)" % (line.order_id.name, line.name),
|
||||
'location_id': self.location_id.id,
|
||||
'location_dest_id': self.location_dest_id.id,
|
||||
'note': self.note,
|
||||
'product_uos_qty': line.product_uos_qty,
|
||||
'product_uos': product_uos,
|
||||
'price_unit': line.price_unit,
|
||||
'sale_line_id': line.id,
|
||||
}
|
||||
|
||||
@api.multi
|
||||
def stock_reserve(self, line_ids):
|
||||
self.ensure_one()
|
||||
|
||||
lines = self.env['sale.order.line'].browse(line_ids)
|
||||
for line in lines:
|
||||
if not line.is_stock_reservable:
|
||||
continue
|
||||
vals = self._prepare_stock_reservation(line)
|
||||
reserv = self.env['stock.reservation'].create(vals)
|
||||
reserv.reserve()
|
||||
return True
|
||||
|
||||
@api.multi
|
||||
def button_reserve(self):
|
||||
env = self.env
|
||||
self.ensure_one()
|
||||
close = {'type': 'ir.actions.act_window_close'}
|
||||
active_model = env.context.get('active_model')
|
||||
active_ids = env.context.get('active_ids')
|
||||
if not (active_model and active_ids):
|
||||
return close
|
||||
|
||||
if active_model == 'sale.order':
|
||||
sales = env['sale.order'].browse(active_ids)
|
||||
line_ids = [line.id for sale in sales for line in sale.order_line]
|
||||
|
||||
if active_model == 'sale.order.line':
|
||||
line_ids = active_ids
|
||||
|
||||
self.stock_reserve(line_ids)
|
||||
return close
|
||||
Reference in New Issue
Block a user