[REF] stock, inventory: post moves at the inventory closing date + removal of the constraints because it's not needed anymore now

bzr revid: qdp-launchpad@openerp.com-20140408160144-nb5b7rjxygbblud3
This commit is contained in:
Quentin (OpenERP) 2014-04-08 18:01:44 +02:00
parent f8aef0b59e
commit c788088617
3 changed files with 64 additions and 56 deletions

View File

@ -587,32 +587,6 @@ class stock_quant(osv.osv):
raise osv.except_osv(_('Error'), _('You cannot move to a location of type view %s.') % (location.name))
return True
def _check_open_inventory_location(self, cr, uid, ids, context=None):
""" Check if there is an inventory running for the locations implied
"""
inventory_obj = self.pool.get('stock.inventory')
for quant in self.browse(cr, uid, ids, context=context):
loc = quant.location_id
parent_location_ids = []
while loc:
parent_location_ids.append(loc.id)
loc = loc.location_id
domain = [('state', '=', 'confirm'), ('location_id', 'in', parent_location_ids), '|', ('product_id', '=', False), ('product_id', '=', quant.product_id.id)]
domain += ['|', ('lot_id', '=', False), ('lot_id', '=', quant.lot_id.id)]
domain += ['|', ('partner_id', '=', False), ('partner_id', '=', quant.owner_id.id)]
domain += ['|', ('package_id', '=', False), ('package_id', '=', quant.package_id.id)]
inventory_ids = inventory_obj.search(cr, uid, domain, context=context)
if inventory_ids:
inventory = inventory_obj.browse(cr, uid, inventory_ids[0], context=context)
raise osv.except_osv(_('Error! Location on inventory'),
_('There exists an inventory conflicting with your operation :\n%s') % inventory.name)
return True
_constraints = [
(_check_open_inventory_location,
"A Physical Inventory is being conducted at this location", ['location_id']),
]
def write(self, cr, uid, ids, vals, context=None):
#check the inventory constraint before the write
if isinstance(ids, (int, long)):
@ -2253,8 +2227,7 @@ class stock_move(osv.osv):
# Check the packages have been placed in the correct locations
self._check_package_from_moves(cr, uid, ids, context=context)
# Apply on picking
done_date = context.get('force_date', time.strftime(DEFAULT_SERVER_DATETIME_FORMAT))
self.write(cr, uid, ids, {'state': 'done', 'date': done_date}, context=context)
self.write(cr, uid, ids, {'state': 'done', 'date': time.strftime(DEFAULT_SERVER_DATETIME_FORMAT)}, context=context)
self.pool.get('procurement.order').check(cr, uid, procurement_ids, context=context)
#check picking state to set the date_done is needed
done_picking = []
@ -2427,6 +2400,7 @@ class stock_inventory(osv.osv):
'name': fields.char('Inventory Reference', size=64, required=True, readonly=True, states={'draft': [('readonly', False)]}, help="Inventory Name."),
'date': fields.datetime('Inventory Date', required=True, readonly=True, states={'draft': [('readonly', False)]}, help="The date that will be used for the validation date of the stock move related to this inventory (and for the valuation accounting entries, if any)"),
'line_ids': fields.one2many('stock.inventory.line', 'inventory_id', 'Inventories', readonly=False, states={'done': [('readonly', True)]}, help="Inventory Lines."),
'theoretical_line_ids': fields.one2many('stock.theoretical.inventory.line', 'inventory_id', 'Theoretical Inventory Lines', readonly=False, states={'done': [('readonly', True)]}),
'move_ids': fields.one2many('stock.move', 'inventory_id', 'Created Moves', help="Inventory Moves.", states={'done': [('readonly', True)]}),
'state': fields.selection(INVENTORY_STATE_SELECTION, 'Status', readonly=True, select=True),
'company_id': fields.many2one('res.company', 'Company', required=True, select=True, readonly=True, states={'draft': [('readonly', False)]}),
@ -2479,25 +2453,23 @@ class stock_inventory(osv.osv):
""" Finish the inventory
@return: True
"""
if context is None:
context = {}
move_obj = self.pool.get('stock.move')
for inv in self.browse(cr, uid, ids, context=context):
for inventory_line in inv.line_ids:
if inventory_line.product_qty < 0 and inventory_line.product_qty != inventory_line.th_qty:
raise osv.except_osv(_('Warning'), _('You cannot set a negative product quantity in an inventory line:\n\t%s - qty: %s' % (inventory_line.product_id.name, inventory_line.product_qty)))
if not inv.move_ids:
self.action_check(cr, uid, [inv.id], context=context)
self.action_check(cr, uid, [inv.id], context=context)
inv.refresh()
self.write(cr, uid, [inv.id], {'state': 'done'}, context=context)
#The inventory is posted as a single step which means quants cannot be moved from an internal location to another using an inventory
#as they will be moved to inventory loss, and other quants will be created to the encoded quant location. This is a normal behavior
#as quants cannot be reuse from inventory location (users can still manually move the products before/after the inventory if they want).
ctx = context.copy()
ctx['force_date'] = inv.date
move_obj.action_done(cr, uid, [x.id for x in inv.move_ids], context=ctx)
self.post_inventory(cr, uid, inv, context=context)
return True
def post_inventory(self, cr, uid, inv, context=None):
#The inventory is posted as a single step which means quants cannot be moved from an internal location to another using an inventory
#as they will be moved to inventory loss, and other quants will be created to the encoded quant location. This is a normal behavior
#as quants cannot be reuse from inventory location (users can still manually move the products before/after the inventory if they want).
move_obj = self.pool.get('stock.move')
move_obj.action_done(cr, uid, [x.id for x in inv.move_ids], context=context)
def _create_stock_move(self, cr, uid, inventory, todo_line, context=None):
stock_move_obj = self.pool.get('stock.move')
product_obj = self.pool.get('product.product')
@ -2537,7 +2509,7 @@ class stock_inventory(osv.osv):
move_ids = [move.id for move in inventory.move_ids]
stock_move_obj.unlink(cr, uid, move_ids, context=context)
#compute what should be in the inventory lines
theorical_lines = self._get_inventory_lines(cr, uid, inventory, context=context)
theorical_lines = self._get_th_inventory_lines(cr, uid, inventory, context=context)
for line in inventory.line_ids:
#compare the inventory lines to the theorical ones and store the diff in theorical_lines
inventory_line_obj._resolve_inventory_line(cr, uid, line, theorical_lines, context=context)
@ -2558,31 +2530,23 @@ class stock_inventory(osv.osv):
def action_cancel_inventory(self, cr, uid, ids, context=None):
self.action_cancel_draft(cr, uid, ids, context=context)
def check_inventory_date(self, cr, uid, inventory, context=None):
domain = ['|', ('location_id', 'child_of', [inventory.location_id.id]), ('location_dest_id', 'child_of', [inventory.location_id.id]), ('date', '>', inventory.date), '|', ('state', 'in', ['assigned', 'done']), '&', ('state', 'in', ['confirmed', 'waiting']), ('partially_available', '=', True)]
if inventory.product_id:
domain += [('product_id', '=', inventory.product_id.id)]
return self.pool.get('stock.move').search(cr, uid, domain, context=context)
def prepare_inventory(self, cr, uid, ids, context=None):
inventory_line_obj = self.pool.get('stock.inventory.line')
th_inventory_line_obj = self.pool.get('stock.theoretical.inventory.line')
for inventory in self.browse(cr, uid, ids, context=context):
#check inventory start date is allowed
if inventory.date > time.strftime(DEFAULT_SERVER_DATETIME_FORMAT):
raise osv.except_osv(_('Error!'), _('It\'s impossible to confirm an inventory in the future. Please change the inventory date to proceed further.'))
conflicting_move_ids = self.check_inventory_date(cr, uid, inventory, context=context)
error_message = ""
for move in self.pool.get('stock.move').browse(cr, uid, conflicting_move_ids, context=context):
error_message += _("\n * Date: %s - %s %s - From: %s To: %s State: %s") % (move.date, move.product_uom_qty, move.product_uom.name, move.location_id.name, move.location_dest_id.name, move.state)
if conflicting_move_ids:
raise osv.except_osv(_('Error!'), _('Stock moves exist made/scheduled after the inventory date which are conflicting with its settings. Please unreserve them or change the inventory date to proceed further.\n\n%s') % (error_message))
#clean the existing inventory lines before redoing an inventory proposal
line_ids = [line.id for line in inventory.line_ids]
inventory_line_obj.unlink(cr, uid, line_ids, context=context)
th_line_ids = [line.id for line in inventory.theoretical_line_ids]
th_inventory_line_obj.unlink(cr, uid, th_line_ids, context=context)
#compute the inventory lines and create them
vals = self._get_inventory_lines(cr, uid, inventory, context=context)
for product_line in vals:
inventory_line_obj.create(cr, uid, product_line, context=context)
th_inventory_line_obj.create(cr, uid, product_line, context=context)
return self.write(cr, uid, ids, {'state': 'confirm'})
def _get_inventory_lines(self, cr, uid, inventory, context=None):
@ -2622,6 +2586,20 @@ class stock_inventory(osv.osv):
vals.append(product_line)
return vals
def _get_th_inventory_lines(self, cr, uid, inventory, context=None):
th_vals = []
for th_line in inventory.theoretical_inventory_lines:
th_vals.append({
'product_id': th_line.product_id.id,
'product_qty': th_line.product_qty,
'location_id': th_line.location_id.id,
'prod_lot_id': th_line.prod_lot_id.id,
'package_id': th_line.package_id.id,
'partner_id': th_line.partner_id.id
})
return th_vals
class stock_inventory_line(osv.osv):
_name = "stock.inventory.line"
_description = "Inventory Line"
@ -2716,6 +2694,12 @@ class stock_inventory_line(osv.osv):
return {'value': {'th_qty': th_qty, 'product_uom_id': uom or obj_product.uom_id.id}}
class stock_theoretical_inventory_line(osv.osv):
""" Simple copy of the inventory line table used to keep track of the theoretical inventory lines that should be recorded in inventories. It's filled at the time an inventory is started and checked and the time it's done to see the difference and apply stock move accordingly. We could have used another relation between inventory and inventory lines but having a second table is easier to make sure we don't introduce any side effect """
_inherit = "stock.inventory.line"
_name = "stock.theoretical.inventory.line"
#----------------------------------------------------------
# Stock Warehouse
#----------------------------------------------------------

View File

@ -19,14 +19,28 @@
#
##############################################################################
import time
from openerp.osv import fields, osv
from openerp.tools.translate import _
from openerp import SUPERUSER_ID
import logging
_logger = logging.getLogger(__name__)
class stock_inventory(osv.osv):
_inherit = "stock.inventory"
_columns = {
'period_id': fields.many2one('account.period', 'Force Valuation Period', help="Choose the accounting period where you want to value the stock moves created by the inventory instead of the default one (chosen by the inventory end date)"),
}
def post_inventory(self, cr, uid, inv, context=None):
if context is None:
context = {}
ctx = context.copy()
if inv.period_id:
ctx['force_period'] = inv.period_id.id
return super(stock_inventory, self).post_inventory(cr, uid, inv, context=ctx)
#----------------------------------------------------------
# Stock Location
#----------------------------------------------------------
@ -227,9 +241,10 @@ class stock_quant(osv.osv):
move_obj = self.pool.get('account.move')
for cost, qty in quant_cost_qty.items():
move_lines = self._prepare_account_move_line(cr, uid, move, qty, cost, credit_account_id, debit_account_id, context=context)
period_id = context.get('force_period', self.pool.get('account.period').find(cr, uid, move.date, context=context)[0])
return move_obj.create(cr, uid, {'journal_id': journal_id,
'line_id': move_lines,
'period_id': self.pool.get('account.period').find(cr, uid, move.date, context=context)[0],
'period_id': period_id,
'date': move.date,
'ref': move.picking_id and move.picking_id.name}, context=context)

View File

@ -1,6 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<record id="view_inventory_form_inherit" model="ir.ui.view">
<field name="name">stock.inventory.form.inherit</field>
<field name="model">stock.inventory</field>
<field name="inherit_id" ref="stock.view_inventory_form"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='date']" position="after">
<field name="period_id"/>
</xpath>
</field>
</record>
<record id="view_location_form_inherit" model="ir.ui.view">
<field name="name">stock.location.form.inherit</field>
@ -16,7 +26,6 @@
</field>
</record>
<record id="view_picking_inherit_form2" model="ir.ui.view">
<field name="name">stock.picking.form.inherit</field>
<field name="model">stock.picking</field>