mirror of
https://github.com/OCA/stock-logistics-warehouse.git
synced 2025-01-21 14:27:28 +02:00
[IMP] stock_reserve: add views, now able to reserve and release a move, added a default 'Reservation Stock' location
This commit is contained in:
@@ -32,7 +32,7 @@ Stock Reserve
|
||||
=============
|
||||
|
||||
Allows to create stock reservation on a product or a selection of
|
||||
products. The reservations can be monitored and lifter on the product
|
||||
products. The reservations can be monitored and lifted on the product
|
||||
view. Each reservation can have a validity date, once reached, the
|
||||
reservation is automatically lifted.
|
||||
|
||||
@@ -40,7 +40,9 @@ reservation is automatically lifted.
|
||||
'depends': ['stock',
|
||||
],
|
||||
'demo': [],
|
||||
'data': [],
|
||||
'data': ['view/stock_reserve.xml',
|
||||
'data/stock_data.xml',
|
||||
],
|
||||
'auto_install': False,
|
||||
'test': [],
|
||||
'installable': True,
|
||||
|
||||
9
stock_reserve/data/stock_data.xml
Normal file
9
stock_reserve/data/stock_data.xml
Normal file
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<openerp>
|
||||
<data noupdate="1">
|
||||
<record id="stock_location_reservation" model="stock.location">
|
||||
<field name="name">Reservation Stock</field>
|
||||
<field name="location_id" ref="stock.stock_location_company"/>
|
||||
</record>
|
||||
</data>
|
||||
</openerp>
|
||||
@@ -19,18 +19,12 @@
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
from datetime import datetime
|
||||
|
||||
from openerp.osv import orm, fields
|
||||
from openerp.tools import DEFAULT_SERVER_DATETIME_FORMAT as DT_FMT
|
||||
|
||||
|
||||
class stock_reservation(orm.Model):
|
||||
""" Allow to reserve products.
|
||||
|
||||
A stock reservation is basically a stock move,
|
||||
but the reservation is handled by this model using
|
||||
a ``_inherits``.
|
||||
"""
|
||||
_name = 'stock.reservation'
|
||||
_description = 'Stock Reservation'
|
||||
@@ -38,39 +32,17 @@ class stock_reservation(orm.Model):
|
||||
|
||||
_columns = {
|
||||
'move_id': fields.many2one('stock.move',
|
||||
'Move',
|
||||
readonly=True,
|
||||
'Reservation Move',
|
||||
required=True,
|
||||
readonly=True,
|
||||
ondelete='cascade',
|
||||
select=1),
|
||||
'date_validity': fields.datetime('Validity Date'),
|
||||
}
|
||||
|
||||
def _prepare_reserve(self, cr, uid, product_id, quantity, location_id,
|
||||
date_validity=None, context=None):
|
||||
product_obj = self.pool.get('product.product')
|
||||
product = product_obj.browse(cr, uid, product_id, context=context)
|
||||
location_dest_id = self._get_reservation_location(
|
||||
cr, uid, product_id, context=context)
|
||||
vals = {'name': 'Reservation', # sequence?
|
||||
'product_id': product_id,
|
||||
'product_qty': quantity,
|
||||
'product_uom': product.uom_id.id,
|
||||
'location_id': location_id,
|
||||
'location_dest_id': location_dest_id,
|
||||
'price_unit': product.standard_price or 0.0,
|
||||
}
|
||||
if date_validity:
|
||||
vals.update({'date_validity': date_validity,
|
||||
'date': date_validity,
|
||||
'date_expected': date_validity,
|
||||
})
|
||||
else:
|
||||
today = datetime.now().strftime(DT_FMT)
|
||||
vals.update({'date': today,
|
||||
'date_expected': today,
|
||||
})
|
||||
return vals
|
||||
_defaults = {
|
||||
'type': 'internal',
|
||||
}
|
||||
|
||||
def _get_reservation_location(self, cr, uid, product_id, context=None):
|
||||
""" Returns the appropriate destination location to
|
||||
@@ -78,42 +50,30 @@ class stock_reservation(orm.Model):
|
||||
"""
|
||||
return 1
|
||||
|
||||
def reserve(self, cr, uid, product_id, quantity, location_id,
|
||||
date_validity=None, context=None):
|
||||
""" Reserve a product.
|
||||
def reserve(self, cr, uid, ids, context=None):
|
||||
""" Confirm a reservation
|
||||
|
||||
The reservation is done using the default UOM of the product.
|
||||
A date until which the product is reserved can be specified.
|
||||
|
||||
:param product_id: id of the product to reserve
|
||||
:param quantity: quantity of products to reserve
|
||||
:param location_id: source location for the reservation
|
||||
:param date_validity: optional datetime until which the reservation
|
||||
is valid
|
||||
:returns: id of the ``stock.reservation`` created
|
||||
"""
|
||||
vals = self._prepare_reserve(cr, uid, product_id, quantity,
|
||||
location_id,
|
||||
date_validity=date_validity,
|
||||
context=context)
|
||||
reservation_id = self.create(cr, uid, vals, context=context)
|
||||
move_id = self.read(cr, uid,
|
||||
reservation_id,
|
||||
['move_id'],
|
||||
context=context,
|
||||
load='_classic_write')['move_id']
|
||||
move_obj = self.pool.get('stock.move')
|
||||
move_obj.action_confirm(cr, uid, [move_id], context=context)
|
||||
# TODO: if no quantity in the location, it will stay 'confirmed'
|
||||
# after action_confirm(), how to handle that?
|
||||
move_obj.action_assign(cr, uid, [move_id])
|
||||
return reservation_id
|
||||
reservations = self.browse(cr, uid, ids, context=context)
|
||||
move_ids = [reserv.move_id.id for reserv in reservations]
|
||||
move_obj.action_confirm(cr, uid, move_ids, context=context)
|
||||
move_obj.force_assign(cr, uid, move_ids, context=context)
|
||||
move_obj.action_done(cr, uid, move_ids, context=context)
|
||||
return True
|
||||
|
||||
def release(self, cr, uid, ids, context=None):
|
||||
""" Release a reservation """
|
||||
return self.unlink(cr, uid, ids, context=context)
|
||||
|
||||
def create(self, cr, uid, vals, context=None):
|
||||
# TODO
|
||||
return super(stock_reservation, self).create(cr, uid, vals,
|
||||
context=context)
|
||||
|
||||
def unlink(self, cr, uid, ids, context=None):
|
||||
""" Release the reservation """
|
||||
if isinstance(ids, (int, long)):
|
||||
ids = [ids]
|
||||
reservations = self.read(cr, uid, ids, ['move_id'],
|
||||
@@ -121,4 +81,39 @@ class stock_reservation(orm.Model):
|
||||
move_obj = self.pool.get('stock.move')
|
||||
move_ids = [reserv['move_id'] for reserv in reservations]
|
||||
move_obj.action_cancel(cr, uid, move_ids, context=context)
|
||||
return super(stock_reservation, self).unlink(cr, uid, ids, context=context)
|
||||
return super(stock_reservation, self).unlink(cr, uid, ids,
|
||||
context=context)
|
||||
|
||||
def onchange_product_id(self, cr, uid, ids, prod_id=False, loc_id=False,
|
||||
loc_dest_id=False, partner_id=False):
|
||||
""" On change of product id, if finds UoM, UoS,
|
||||
quantity and UoS quantity.
|
||||
"""
|
||||
move_obj = self.pool.get('stock.move')
|
||||
if ids:
|
||||
reserv = self.read(cr, uid, ids, ['move_id'], load='_classic_write')
|
||||
move_ids = [rv['move_id'] for rv in reserv]
|
||||
else:
|
||||
move_ids = []
|
||||
return move_obj.onchange_product_id(
|
||||
cr, uid, move_ids, prod_id=prod_id, loc_id=loc_id,
|
||||
loc_dest_id=loc_dest_id, partner_id=partner_id)
|
||||
|
||||
def onchange_move_type(self, cr, uid, ids, type, context=None):
|
||||
""" On change of move type gives source and destination location.
|
||||
"""
|
||||
move_obj = self.pool.get('stock.move')
|
||||
location_obj = self.pool.get('stock.location')
|
||||
data_obj = self.pool.get('ir.model.data')
|
||||
result = move_obj.onchange_move_type(cr, uid, ids, type,
|
||||
context=context)
|
||||
get_ref = data_obj.get_object_reference
|
||||
try:
|
||||
__, dest_location_id = get_ref(cr, uid, 'stock_reserve',
|
||||
'stock_location_reservation')
|
||||
location_obj.check_access_rule(cr, uid, [dest_location_id],
|
||||
'read', context=context)
|
||||
except (orm.except_orm, ValueError):
|
||||
dest_location_id = False
|
||||
result['value']['location_dest_id'] = dest_location_id
|
||||
return result
|
||||
|
||||
118
stock_reserve/view/stock_reserve.xml
Normal file
118
stock_reserve/view/stock_reserve.xml
Normal file
@@ -0,0 +1,118 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<openerp>
|
||||
<data noupdate="0">
|
||||
<record id="view_stock_reservation_form" model="ir.ui.view">
|
||||
<field name="name">stock.reservation.form</field>
|
||||
<field name="model">stock.reservation</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Stock Reservations" version="7.0">
|
||||
<header>
|
||||
<button name="reserve" type="object"
|
||||
string="Reserve"
|
||||
class="oe_highlight"
|
||||
states="draft"/>
|
||||
<button name="release" type="object"
|
||||
string="Release"
|
||||
class="oe_highlight"
|
||||
states="assigned,confirmed,done"/>
|
||||
<field name="state" widget="statusbar"
|
||||
statusbar_visible="draft,confirm"/>
|
||||
</header>
|
||||
<sheet>
|
||||
<group>
|
||||
<group name="main_grp" string="Details">
|
||||
<field name="product_id"
|
||||
on_change="onchange_product_id(product_id, location_id, location_dest_id, False)"
|
||||
/>
|
||||
<label for="product_qty" />
|
||||
<div>
|
||||
<field name="product_qty"
|
||||
class="oe_inline"/>
|
||||
<field name="product_uom"
|
||||
groups="product.group_uom" class="oe_inline"/>
|
||||
</div>
|
||||
<field name="date_validity" />
|
||||
<field name="name"/>
|
||||
<field name="company_id"
|
||||
groups="base.group_multi_company"
|
||||
widget="selection"/>
|
||||
</group>
|
||||
<group name="origin_grp" string="Origin">
|
||||
<field name="type" readonly="1" invisible="1"
|
||||
on_change="onchange_move_type(type)"/>
|
||||
<field name="location_id" groups="stock.group_locations"/>
|
||||
<field name="create_date" groups="base.group_no_one"/>
|
||||
</group>
|
||||
<group name="destination_grp" string="Destination">
|
||||
<field name="location_dest_id"
|
||||
groups="stock.group_locations"/>
|
||||
<field name="date_expected"
|
||||
attrs="{'invisible': [('state', '=', 'done')]}"/>
|
||||
<field name="date" attrs="{'invisible': [('state', '!=', 'done')]}"/>
|
||||
</group>
|
||||
<group name="note" string="Notes">
|
||||
<field name="note" nolabel="1"/>
|
||||
</group>
|
||||
</group>
|
||||
</sheet>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="view_stock_reservation_tree" model="ir.ui.view">
|
||||
<field name="name">stock.reservation.tree</field>
|
||||
<field name="model">stock.reservation</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="Stock Reservations" version="7.0"
|
||||
colors="blue:state == 'draft'" >
|
||||
<field name="move_id" />
|
||||
<field name="product_id" />
|
||||
<field name="product_qty" />
|
||||
<field name="product_uom" />
|
||||
<field name="date_validity" />
|
||||
<field name="state"/>
|
||||
<button name="reserve" type="object"
|
||||
string="Reserve"
|
||||
icon="terp-gtk-jump-to-ltr"
|
||||
states="draft"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="view_stock_reservation_search" model="ir.ui.view">
|
||||
<field name="name">stock.reservation.search</field>
|
||||
<field name="model">stock.reservation</field>
|
||||
<field name="arch" type="xml">
|
||||
<search string="Stock Reservations" version="7.0">
|
||||
<field name="move_id" />
|
||||
<field name="product_id" />
|
||||
<field name="state"/>
|
||||
</search>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="action_stock_reservation" model="ir.actions.act_window">
|
||||
<field name="name">Stock Reservations</field>
|
||||
<field name="res_model">stock.reservation</field>
|
||||
<field name="type">ir.actions.act_window</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="view_id" ref="view_stock_reservation_tree"/>
|
||||
<field name="search_view_id" ref="view_stock_reservation_search"/>
|
||||
<field name="context">{}</field>
|
||||
<field name="help" type="html">
|
||||
<p class="oe_view_nocontent_create">
|
||||
Click to create a stock reservation.
|
||||
</p><p>
|
||||
This menu allow you to prepare and reserve some quantities
|
||||
of products.
|
||||
</p>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<menuitem action="action_stock_reservation"
|
||||
id="menu_action_stock_reservation"
|
||||
parent="stock.menu_stock_products_moves"
|
||||
sequence="10"
|
||||
groups="stock.group_locations"/>
|
||||
</data>
|
||||
</openerp>
|
||||
Reference in New Issue
Block a user