mirror of
https://github.com/OCA/stock-logistics-warehouse.git
synced 2025-01-21 14:27:28 +02:00
Merge pull request #190 from management-and-acounting-on-line/stock-reserve-sale-port-9.0
Stock reserve sale port 9.0
This commit is contained in:
@@ -20,7 +20,7 @@
|
||||
##############################################################################
|
||||
|
||||
{'name': 'Stock Reserve Sales',
|
||||
'version': '8.0.1.0.0',
|
||||
'version': '9.0.1.0.0',
|
||||
'author': "Camptocamp,Odoo Community Association (OCA)",
|
||||
'category': 'Warehouse',
|
||||
'license': 'AGPL-3',
|
||||
@@ -31,13 +31,14 @@
|
||||
'stock_reserve',
|
||||
],
|
||||
'demo': [],
|
||||
'data': ['wizard/sale_stock_reserve_view.xml',
|
||||
'data': ['security/ir.model.access.csv',
|
||||
'wizard/sale_stock_reserve_view.xml',
|
||||
'view/sale.xml',
|
||||
'view/stock_reserve.xml',
|
||||
],
|
||||
'test': ['test/sale_reserve.yml',
|
||||
'test/sale_line_reserve.yml',
|
||||
],
|
||||
'installable': False,
|
||||
'installable': True,
|
||||
'auto_install': False,
|
||||
}
|
||||
|
||||
@@ -20,8 +20,10 @@
|
||||
##############################################################################
|
||||
|
||||
from openerp import models, fields, api
|
||||
from openerp.exceptions import except_orm
|
||||
from openerp.exceptions import UserError
|
||||
from openerp.tools.translate import _
|
||||
from openerp.tools.float_utils import float_compare
|
||||
import openerp.addons.decimal_precision as dp
|
||||
|
||||
|
||||
class SaleOrder(models.Model):
|
||||
@@ -31,7 +33,7 @@ class SaleOrder(models.Model):
|
||||
@api.depends('state',
|
||||
'order_line.reservation_ids',
|
||||
'order_line.is_stock_reservable')
|
||||
def _stock_reservation(self):
|
||||
def _compute_stock_reservation(self):
|
||||
for sale in self:
|
||||
has_stock_reservation = False
|
||||
is_stock_reservable = False
|
||||
@@ -46,29 +48,26 @@ class SaleOrder(models.Model):
|
||||
sale.has_stock_reservation = has_stock_reservation
|
||||
|
||||
has_stock_reservation = fields.Boolean(
|
||||
compute='_stock_reservation',
|
||||
compute='_compute_stock_reservation',
|
||||
readonly=True,
|
||||
multi='stock_reservation',
|
||||
store=True,
|
||||
string='Has Stock Reservations')
|
||||
is_stock_reservable = fields.Boolean(
|
||||
compute='_stock_reservation',
|
||||
compute='_compute_stock_reservation',
|
||||
readonly=True,
|
||||
multi='stock_reservation',
|
||||
store=True,
|
||||
string='Can Have Stock Reservations')
|
||||
|
||||
@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 = self.mapped('order_line')
|
||||
lines.release_stock_reservation()
|
||||
return True
|
||||
|
||||
@api.multi
|
||||
def action_button_confirm(self):
|
||||
def action_confirm(self):
|
||||
self.release_all_stock_reservation()
|
||||
return super(SaleOrder, self).action_button_confirm()
|
||||
return super(SaleOrder, self).action_confirm()
|
||||
|
||||
@api.multi
|
||||
def action_cancel(self):
|
||||
@@ -87,22 +86,22 @@ class SaleOrderLine(models.Model):
|
||||
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]
|
||||
product_route_ids = (product.route_ids.mapped('id') +
|
||||
product.categ_id.total_route_ids.mapped('id'))
|
||||
rules = ProcurementRule.search([('route_id', 'in', product_route_ids)],
|
||||
order='route_sequence, sequence',
|
||||
limit=1)
|
||||
|
||||
if not rules:
|
||||
warehouse = self.order_id.warehouse_id
|
||||
wh_routes = warehouse.route_ids
|
||||
wh_route_ids = [route.id for route in wh_routes]
|
||||
wh_route_ids = warehouse.route_ids.mapped('id')
|
||||
domain = ['|', ('warehouse_id', '=', warehouse.id),
|
||||
('warehouse_id', '=', False),
|
||||
('route_id', 'in', wh_route_ids)]
|
||||
|
||||
rules = ProcurementRule.search(domain,
|
||||
order='route_sequence, sequence')
|
||||
order='route_sequence, sequence',
|
||||
limit=1)
|
||||
|
||||
if rules:
|
||||
return rules[0]
|
||||
@@ -120,7 +119,7 @@ class SaleOrderLine(models.Model):
|
||||
@api.depends('state',
|
||||
'product_id.route_ids',
|
||||
'product_id.type')
|
||||
def _is_stock_reservable(self):
|
||||
def _compute_is_stock_reservation(self):
|
||||
for line in self:
|
||||
reservable = False
|
||||
if (not (line.state != 'draft' or
|
||||
@@ -137,98 +136,116 @@ class SaleOrderLine(models.Model):
|
||||
string='Stock Reservation',
|
||||
copy=False)
|
||||
is_stock_reservable = fields.Boolean(
|
||||
compute='_is_stock_reservable',
|
||||
compute='_compute_is_stock_reservation',
|
||||
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]
|
||||
reservations = self.env['stock.reservation'].browse(reserv_ids)
|
||||
reservations.release()
|
||||
def _prepare_stock_reservation(self, date_validity=False, note=False):
|
||||
self.ensure_one()
|
||||
|
||||
try:
|
||||
owner_id = self.stock_owner_id and self.stock_owner_id.id or False
|
||||
except AttributeError:
|
||||
owner_id = False
|
||||
# module sale_owner_stock_sourcing not installed, fine
|
||||
|
||||
return {'product_id': self.product_id.id,
|
||||
'product_uom': self.product_uom.id,
|
||||
'product_uom_qty': self.product_uom_qty,
|
||||
'date_validity': date_validity,
|
||||
'name': "%s (%s)" % (self.order_id.name, self.name),
|
||||
'note': note,
|
||||
'price_unit': self.price_unit,
|
||||
'sale_line_id': self.id,
|
||||
'restrict_partner_id': owner_id,
|
||||
}
|
||||
|
||||
@api.multi
|
||||
def acquire_stock_reservation(self, date_validity=False, note=False):
|
||||
reserv_obj = self.env['stock.reservation'].sudo()
|
||||
|
||||
reservations = reserv_obj.browse()
|
||||
for line in self:
|
||||
if not line.is_stock_reservable:
|
||||
continue
|
||||
|
||||
vals = line._prepare_stock_reservation(
|
||||
date_validity=date_validity, note=note)
|
||||
|
||||
# Place picking_type_id in context. This is required
|
||||
# to make reserve automaticaly find location_id and
|
||||
# location_dest_id
|
||||
pick_type_id = line.order_id.warehouse_id.int_type_id.id
|
||||
reserv_obj_ctx = reserv_obj.with_context(
|
||||
default_picking_type_id=pick_type_id)
|
||||
|
||||
reservations |= reserv_obj_ctx.create(vals)
|
||||
reservations.reserve()
|
||||
return True
|
||||
|
||||
def product_id_change(self, cr, uid, ids,
|
||||
pricelist,
|
||||
product,
|
||||
qty=0,
|
||||
uom=False,
|
||||
qty_uos=0,
|
||||
uos=False,
|
||||
name='',
|
||||
partner_id=False,
|
||||
lang=False,
|
||||
update_tax=True,
|
||||
date_order=False,
|
||||
packaging=False,
|
||||
fiscal_position=False,
|
||||
flag=False,
|
||||
context=None):
|
||||
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,
|
||||
packaging=packaging, fiscal_position=fiscal_position,
|
||||
flag=flag, context=context)
|
||||
if not ids: # warn only if we change an existing line
|
||||
return result
|
||||
assert len(ids) == 1, "Expected 1 ID, got %r" % ids
|
||||
line = self.browse(cr, uid, ids[0], context=context)
|
||||
if qty != line.product_uom_qty and line.reservation_ids:
|
||||
@api.multi
|
||||
def release_stock_reservation(self):
|
||||
self.mapped('reservation_ids').sudo().release()
|
||||
return True
|
||||
|
||||
@api.onchange('product_id', 'product_uom_qty')
|
||||
def onchange_product_id_qty(self):
|
||||
reserved_qty = sum(self.reservation_ids.mapped('product_uom_qty'))
|
||||
|
||||
precision_digits = dp.get_precision(
|
||||
'Product Unit of Measure')(self.env.cr)[1]
|
||||
qty_equal = float_compare(
|
||||
self.product_uom_qty, reserved_qty,
|
||||
precision_digits=precision_digits) == 0.0
|
||||
if not qty_equal and self.reservation_ids:
|
||||
msg = _("As you changed the quantity of the line, "
|
||||
"the quantity of the stock reservation will "
|
||||
"be automatically adjusted to %.2f.") % qty
|
||||
msg += "\n\n"
|
||||
result.setdefault('warning', {})
|
||||
if result['warning'].get('message'):
|
||||
result['warning']['message'] += msg
|
||||
else:
|
||||
result['warning'] = {
|
||||
"be automatically adjusted to %.2f."
|
||||
"") % self.product_uom_qty
|
||||
return {
|
||||
'warning': {
|
||||
'title': _('Configuration Error!'),
|
||||
'message': msg,
|
||||
}
|
||||
return result
|
||||
},
|
||||
}
|
||||
return {}
|
||||
|
||||
@api.multi
|
||||
def _update_reservation_price_qty(self):
|
||||
for line in self:
|
||||
if not line.reservation_ids:
|
||||
continue
|
||||
if len(line.reservation_ids) > 1:
|
||||
raise UserError(
|
||||
_('Several stock reservations are linked with the '
|
||||
'line. Impossible to adjust their quantity. '
|
||||
'Please release the reservation '
|
||||
'before changing the quantity.'))
|
||||
|
||||
line.reservation_ids.sudo().write({
|
||||
'price_unit': line.price_unit,
|
||||
'product_uom_qty': line.product_uom_qty,
|
||||
})
|
||||
|
||||
@api.multi
|
||||
def write(self, vals):
|
||||
block_on_reserve = ('product_id',
|
||||
'product_uom',
|
||||
'product_uos',
|
||||
'product_uom_id',
|
||||
'type')
|
||||
update_on_reserve = ('price_unit',
|
||||
'product_uom_qty',
|
||||
'product_uos_qty')
|
||||
)
|
||||
keys = set(vals.keys())
|
||||
test_block = keys.intersection(block_on_reserve)
|
||||
test_update = keys.intersection(update_on_reserve)
|
||||
if test_block:
|
||||
for line in self:
|
||||
if not line.reservation_ids:
|
||||
continue
|
||||
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.'))
|
||||
if test_block and len(self.mapped('reservation_ids')) > 0:
|
||||
raise UserError(
|
||||
_('You cannot change the product or unit of measure '
|
||||
'of lines with a stock reservation. '
|
||||
'Release the reservation '
|
||||
'before changing the product.'))
|
||||
res = super(SaleOrderLine, self).write(vals)
|
||||
if test_update:
|
||||
for line in self:
|
||||
if not line.reservation_ids:
|
||||
continue
|
||||
if len(line.reservation_ids) > 1:
|
||||
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.write(
|
||||
{'price_unit': line.price_unit,
|
||||
'product_uom_qty': line.product_uom_qty,
|
||||
'product_uos_qty': line.product_uos_qty,
|
||||
}
|
||||
)
|
||||
self._update_reservation_price_qty()
|
||||
return res
|
||||
|
||||
@@ -37,6 +37,5 @@ class StockReservation(models.Model):
|
||||
|
||||
@api.multi
|
||||
def release(self):
|
||||
for rec in self:
|
||||
rec.sale_line_id = False
|
||||
self.write({'sale_line_id': False})
|
||||
return super(StockReservation, self).release()
|
||||
|
||||
3
stock_reserve_sale/security/ir.model.access.csv
Normal file
3
stock_reserve_sale/security/ir.model.access.csv
Normal file
@@ -0,0 +1,3 @@
|
||||
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
|
||||
access_stock_reservation_sale_user,stock.reservation sale_user,stock_reserve.model_stock_reservation,base.group_sale_salesman,1,0,0,0
|
||||
|
||||
|
@@ -12,8 +12,6 @@
|
||||
uom_po_id: product.product_uom_kgm
|
||||
valuation: real_time
|
||||
cost_method: average
|
||||
property_stock_account_input: account.o_expense
|
||||
property_stock_account_output: account.o_income
|
||||
-
|
||||
I update the current stock of the yogurt with 10 kgm
|
||||
-
|
||||
@@ -27,18 +25,18 @@
|
||||
-
|
||||
In order to test reservation of the sales order, I create a sales order
|
||||
-
|
||||
!record {model: sale.order, id: sale_reserve_02}:
|
||||
!record {model: sale.order, id: sale_reserve_04}:
|
||||
partner_id: base.res_partner_2
|
||||
payment_term: account.account_payment_term
|
||||
payment_term_id: account.account_payment_term
|
||||
-
|
||||
And I create a sales order line
|
||||
-
|
||||
!record {model: sale.order.line, id: sale_line_reserve_02_01, view: sale.view_order_line_tree}:
|
||||
name: Yogurt
|
||||
name: "Yogurt"
|
||||
product_id: product_yogurt
|
||||
product_uom_qty: 4
|
||||
product_uom: product.product_uom_kgm
|
||||
order_id: sale_reserve_02
|
||||
order_id: sale_reserve_04
|
||||
-
|
||||
And I create a stock reserve for this line
|
||||
-
|
||||
@@ -54,7 +52,7 @@
|
||||
-
|
||||
!python {model: product.product}: |
|
||||
product = self.browse(cr, uid, ref('product_yogurt'), context=context)
|
||||
assert product.virtual_available == 6, "Stock is not updated."
|
||||
assert product.virtual_available == 6, "Stock is not updated. (%s)" % product.virtual_available
|
||||
-
|
||||
I set product_12 to MTO (doesn't work)
|
||||
-
|
||||
@@ -72,8 +70,8 @@
|
||||
And I create a MTO sales order line
|
||||
-
|
||||
!record {model: sale.order.line, id: sale_line_reserve_02_02, view: sale.view_order_line_tree}:
|
||||
order_id: sale_reserve_02
|
||||
name: Mouse, Wireless
|
||||
order_id: sale_reserve_04
|
||||
name: "Mouse, Wireless"
|
||||
product_id: product.product_product_12
|
||||
product_uom_qty: 4
|
||||
product_uom: product.product_uom_unit
|
||||
|
||||
@@ -21,8 +21,6 @@
|
||||
uom_po_id: product.product_uom_kgm
|
||||
valuation: real_time
|
||||
cost_method: average
|
||||
property_stock_account_input: account.o_expense
|
||||
property_stock_account_output: account.o_income
|
||||
-
|
||||
I update the current stock of the Gelato with 10 kgm
|
||||
-
|
||||
@@ -38,11 +36,13 @@
|
||||
-
|
||||
!record {model: sale.order, id: sale_reserve_01}:
|
||||
partner_id: base.res_partner_2
|
||||
payment_term: account.account_payment_term
|
||||
payment_term_id: account.account_payment_term
|
||||
order_line:
|
||||
- product_id: product_gelato
|
||||
name: Gelato
|
||||
product_uom_qty: 4
|
||||
- product_id: product_gelato
|
||||
name: Gelato
|
||||
product_uom_qty: 1
|
||||
-
|
||||
I call the wizard to reserve the products of the sales order
|
||||
@@ -71,3 +71,79 @@
|
||||
!python {model: product.product, id: product_gelato}: |
|
||||
from nose.tools import *
|
||||
assert_almost_equal(self.virtual_available, 10.0)
|
||||
|
||||
|
||||
-
|
||||
In order to test reservation of the sales order, I create a sales order
|
||||
-
|
||||
!record {model: sale.order, id: sale_reserve_02_test_confirm}:
|
||||
partner_id: base.res_partner_2
|
||||
payment_term_id: account.account_payment_term
|
||||
order_line:
|
||||
- product_id: product_gelato
|
||||
name: Gelato
|
||||
product_uom_qty: 4
|
||||
-
|
||||
I call the wizard to reserve the products of the sales order
|
||||
-
|
||||
!python {model: sale.stock.reserve}: |
|
||||
active_id = ref('sale_reserve_02_test_confirm')
|
||||
context['active_id'] = active_id
|
||||
context['active_ids'] = [active_id]
|
||||
context['active_model'] = 'sale.order'
|
||||
wizard_id = self.create(cr, uid, {}, context=context)
|
||||
self.button_reserve(cr, uid, [wizard_id], context=context)
|
||||
|
||||
-
|
||||
I confirm sale order and test that reservations are released
|
||||
-
|
||||
!python {model: sale.order}: |
|
||||
active_id = ref('sale_reserve_02_test_confirm')
|
||||
order = self.browse(cr, uid, active_id, context=context)
|
||||
reservations = order.mapped('order_line.reservation_ids')
|
||||
assert reservations.mapped('state') == ['assigned'], "wrong reservations state"
|
||||
order.action_confirm()
|
||||
assert reservations.mapped('state') == ['cancel'], "wrong reservations state"
|
||||
assert len(reservations.mapped('sale_line_id')) == 0, "bindings to sale lines are cleaned"
|
||||
|
||||
-
|
||||
I cancel sale order (2)
|
||||
-
|
||||
!python {model: sale.order}: |
|
||||
active_id = ref('sale_reserve_02_test_confirm')
|
||||
self.action_cancel(cr, uid, active_id, context=context)
|
||||
|
||||
|
||||
-
|
||||
In order to test reservation of the sales order, I create a sales order
|
||||
-
|
||||
!record {model: sale.order, id: sale_reserve_03_cancel}:
|
||||
partner_id: base.res_partner_2
|
||||
payment_term_id: account.account_payment_term
|
||||
order_line:
|
||||
- product_id: product_gelato
|
||||
name: Gelato
|
||||
product_uom_qty: 4
|
||||
-
|
||||
I call the wizard to reserve the products of the sales order
|
||||
-
|
||||
!python {model: sale.stock.reserve}: |
|
||||
active_id = ref('sale_reserve_03_cancel')
|
||||
context['active_id'] = active_id
|
||||
context['active_ids'] = [active_id]
|
||||
context['active_model'] = 'sale.order'
|
||||
wizard_id = self.create(cr, uid, {}, context=context)
|
||||
self.button_reserve(cr, uid, [wizard_id], context=context)
|
||||
|
||||
-
|
||||
I cancel sale order and tet that reservations are released
|
||||
-
|
||||
!python {model: sale.order}: |
|
||||
active_id = ref('sale_reserve_03_cancel')
|
||||
order = self.browse(cr, uid, active_id, context=context)
|
||||
reservations = order.mapped('order_line.reservation_ids')
|
||||
assert reservations.mapped('state') == ['assigned'], "wrong reservations state"
|
||||
order.action_cancel()
|
||||
assert reservations.mapped('state') == ['cancel'], "wrong reservations state"
|
||||
assert len(reservations.mapped('sale_line_id')) == 0, "bindings to sale lines are cleaned"
|
||||
|
||||
|
||||
@@ -5,16 +5,21 @@
|
||||
<record id="view_order_form_reserve" model="ir.ui.view">
|
||||
<field name="name">sale.order.form.reserve</field>
|
||||
<field name="model">sale.order</field>
|
||||
<field name="inherit_id" ref="sale_stock.view_order_form_inherit"/>
|
||||
<field name="inherit_id" ref="sale_stock.view_order_form_inherit_sale_stock"/>
|
||||
<field name="arch" type="xml">
|
||||
<button name="action_quotation_send" position="before">
|
||||
<field name="is_stock_reservable" invisible="1"/>
|
||||
<field name="has_stock_reservation" invisible="1"/>
|
||||
<button name="%(action_sale_stock_reserve)d"
|
||||
type="action"
|
||||
string="Reserve Stock"
|
||||
help="Pre-book products from stock"
|
||||
attrs="{'invisible': [('is_stock_reservable', '=', False)]}"
|
||||
/>
|
||||
<button name="release_all_stock_reservation"
|
||||
type="object"
|
||||
string="Release Reservations"
|
||||
attrs="{'invisible': [('has_stock_reservation', '=', False)]}"/>
|
||||
</button>
|
||||
|
||||
<field name="order_line" position="attributes">
|
||||
@@ -23,6 +28,7 @@
|
||||
|
||||
<xpath expr="//field[@name='order_line']/form//field[@name='state']" position="before">
|
||||
<field name="reservation_ids" invisible="1"/>
|
||||
<field name="is_stock_reservable" invisible="1"/>
|
||||
<button name="%(action_sale_stock_reserve)d"
|
||||
type="action"
|
||||
string="Reserve Stock"
|
||||
@@ -50,7 +56,7 @@
|
||||
attrs="{'invisible': [('reservation_ids', '=', [])]}" />
|
||||
</xpath>
|
||||
|
||||
<field name="invoiced" position="before">
|
||||
<xpath expr="//group[@name='sales_person']" position="inside">
|
||||
<label for="has_stock_reservation"/>
|
||||
<div>
|
||||
<field name="has_stock_reservation"/>
|
||||
@@ -59,7 +65,7 @@
|
||||
type="object" class="oe_link"
|
||||
attrs="{'invisible': [('has_stock_reservation', '=', False)]}"/>
|
||||
</div>
|
||||
</field>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
|
||||
@@ -19,114 +19,40 @@
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
from openerp import models, fields, api, exceptions
|
||||
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()
|
||||
|
||||
def _default_owner(self):
|
||||
"""If sale_owner_stock_sourcing is installed, it adds an owner field
|
||||
on sale order lines. Use it.
|
||||
|
||||
"""
|
||||
model = self.env[self.env.context['active_model']]
|
||||
if model._name == 'sale.order':
|
||||
lines = model.browse(self.env.context['active_id']).order_line
|
||||
else:
|
||||
lines = model.browse(self.env.context['active_ids'])
|
||||
|
||||
try:
|
||||
owners = set([l.stock_owner_id for l in lines])
|
||||
except AttributeError:
|
||||
return self.env['res.partner']
|
||||
# module sale_owner_stock_sourcing not installed, fine
|
||||
|
||||
if len(owners) == 1:
|
||||
return owners.pop()
|
||||
elif len(owners) > 1:
|
||||
raise exceptions.Warning(
|
||||
'The lines have different owners. Please reserve them '
|
||||
'individually with the reserve button on each one.')
|
||||
|
||||
return self.env['res.partner']
|
||||
|
||||
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')
|
||||
owner_id = fields.Many2one('res.partner', 'Stock Owner',
|
||||
default=_default_owner)
|
||||
|
||||
@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,
|
||||
'restrict_partner_id': self.owner_id.id,
|
||||
}
|
||||
@api.model
|
||||
def _get_so_lines(self):
|
||||
active_model = self.env.context.get('active_model')
|
||||
active_ids = self.env.context.get('active_ids')
|
||||
active_id = self.env.context.get('active_id')
|
||||
|
||||
@api.multi
|
||||
def stock_reserve(self, line_ids):
|
||||
self.ensure_one()
|
||||
if active_model == 'sale.order' and active_id:
|
||||
lines = self.env[active_model].browse(active_id).order_line
|
||||
elif active_model == 'sale.order.line' and active_ids:
|
||||
lines = self.env[active_model].browse(active_ids)
|
||||
else:
|
||||
lines = self.env['sale.order.line'].browse()
|
||||
|
||||
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
|
||||
return lines
|
||||
|
||||
@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]
|
||||
lines = self._get_so_lines()
|
||||
if lines:
|
||||
lines.acquire_stock_reservation(date_validity=self.date_validity,
|
||||
note=self.note)
|
||||
|
||||
if active_model == 'sale.order.line':
|
||||
line_ids = active_ids
|
||||
|
||||
self.stock_reserve(line_ids)
|
||||
return close
|
||||
return {'type': 'ir.actions.act_window_close'}
|
||||
|
||||
@@ -13,10 +13,7 @@
|
||||
the reservation will be released once the date has passed.
|
||||
</p>
|
||||
<group>
|
||||
<field name="location_id"/>
|
||||
<field name="location_dest_id"/>
|
||||
<field name="date_validity"/>
|
||||
<field name="owner_id" groups="stock.group_tracking_owner"/>
|
||||
</group>
|
||||
<group name="note" string="Notes">
|
||||
<field name="note" nolabel="1"/>
|
||||
|
||||
Reference in New Issue
Block a user