Update for new Transaction

This commit is contained in:
Cédric Krier
2010-08-19 22:22:44 +02:00
parent ada56fe0cb
commit cd62ea725f
6 changed files with 931 additions and 1157 deletions
+1 -1
View File
@@ -4,7 +4,7 @@ Installing trytond_stock
Prerequisites
-------------
* Python 2.4 or later (http://www.python.org/)
* Python 2.5 or later (http://www.python.org/)
* trytond (http://www.tryton.org/)
* trytond_party (http://www.tryton.org/)
* trytond_product (http://www.tryton.org/)
+69 -93
View File
@@ -1,10 +1,11 @@
#This file is part of Tryton. The COPYRIGHT file at the top level
#of this repository contains the full copyright notices and license terms.
'Inventory'
from __future__ import with_statement
from trytond.model import ModelWorkflow, ModelView, ModelSQL, fields
from trytond.wizard import Wizard
from trytond.pyson import Not, Equal, Eval, Or, Bool
from trytond.backend import TableHandler
from trytond.transaction import Transaction
STATES = {
'readonly': Not(Equal(Eval('state'), 'draft')),
@@ -47,60 +48,56 @@ class Inventory(ModelWorkflow, ModelSQL, ModelView):
super(Inventory, self).__init__()
self._order.insert(0, ('date', 'DESC'))
def init(self, cursor, module_name):
super(Inventory, self).init(cursor, module_name)
def init(self, module_name):
super(Inventory, self).init(module_name)
cursor = Transaction().cursor
# Add index on create_date
table = TableHandler(cursor, self, module_name)
table.index_action('create_date', action='add')
def default_state(self, cursor, user, context=None):
def default_state(self):
return 'draft'
def default_date(self, cursor, user, context=None):
def default_date(self):
date_obj = self.pool.get('ir.date')
return date_obj.today(cursor, user, context=context)
return date_obj.today()
def default_company(self, cursor, user, context=None):
if context is None:
context = {}
if context.get('company'):
return context['company']
return False
def default_company(self):
return Transaction().context.get('company') or False
def default_lost_found(self, cursor, user, context=None):
def default_lost_found(self):
location_obj = self.pool.get('stock.location')
location_ids = location_obj.search(cursor, user,
self.lost_found.domain, context=context)
location_ids = location_obj.search(self.lost_found.domain)
if len(location_ids) == 1:
return location_ids[0]
return False
def set_state_draft(self, cursor, user, inventory_id, context=None):
self.write(cursor, user, inventory_id, {
def set_state_draft(self, inventory_id):
self.write(inventory_id, {
'state': 'draft',
}, context=context)
})
def set_state_cancel(self, cursor, user, inventory_id, context=None):
def set_state_cancel(self, inventory_id):
line_obj = self.pool.get("stock.inventory.line")
inventory = self.browse(cursor, user, inventory_id, context=context)
line_obj.cancel_move(cursor, user, inventory.lines, context=context)
self.write(cursor, user, inventory_id, {
inventory = self.browse(inventory_id)
line_obj.cancel_move(inventory.lines)
self.write(inventory_id, {
'state': 'cancel',
}, context=context)
})
def set_state_done(self, cursor, user, inventory_id, context=None):
def set_state_done(self, inventory_id):
date_obj = self.pool.get('ir.date')
line_obj = self.pool.get('stock.inventory.line')
inventory = self.browse(cursor, user, inventory_id, context=context)
inventory = self.browse(inventory_id)
for line in inventory.lines:
line_obj.create_move(cursor, user, line, context=context)
self.write(cursor, user, inventory_id, {
line_obj.create_move(line)
self.write(inventory_id, {
'state': 'done',
}, context=context)
})
def copy(self, cursor, user, ids, default=None, context=None):
def copy(self, ids, default=None):
date_obj = self.pool.get('ir.date')
line_obj = self.pool.get('stock.inventory.line')
@@ -112,31 +109,28 @@ class Inventory(ModelWorkflow, ModelSQL, ModelView):
if default is None:
default = {}
default = default.copy()
default['date'] = date_obj.today(cursor, user, context=context)
default['date'] = date_obj.today()
default['lines'] = False
new_ids = []
for inventory in self.browse(cursor, user, ids, context=context):
new_id = super(Inventory, self).copy(cursor, user, inventory.id,
default=default, context=context)
line_obj.copy(cursor, user, [x.id for x in inventory.lines],
for inventory in self.browse(ids):
new_id = super(Inventory, self).copy(inventory.id, default=default)
line_obj.copy([x.id for x in inventory.lines],
default={
'inventory': new_id,
'move': False,
}, context=context)
self.complete_lines(cursor, user, new_id, context=context)
})
self.complete_lines(new_id)
new_ids.append(new_id)
if int_id:
return new_ids[0]
return new_ids
def complete_lines(self, cursor, user, ids, context=None):
def complete_lines(self, ids):
'''
Complete or update the inventories
:param cursor: the database cursor
:param user: the user id
:param ids: the ids of stock.inventory
:param context: the context
'''
@@ -147,22 +141,18 @@ class Inventory(ModelWorkflow, ModelSQL, ModelView):
if isinstance(ids, (int, long)):
ids = [ids]
inventories = self.browse(cursor, user, ids,
context=context)
context = context and context.copy() or {}
inventories = self.browse(ids)
for inventory in inventories:
# Compute product quantities
ctx = context and context.copy() or {}
ctx['stock_date_end'] = inventory.date
pbl = product_obj.products_by_location(
cursor, user, [inventory.location.id], context=ctx)
with Transaction().set_context(stock_date_end=inventory.date):
pbl = product_obj.products_by_location(
[inventory.location.id])
# Index some data
product2uom = {}
product2type = {}
for product in product_obj.browse(cursor, user,
[line[1] for line in pbl], context=context):
for product in product_obj.browse([line[1] for line in pbl]):
product2uom[product.id] = product.default_uom.id
product2type[product.id] = product.type
@@ -174,7 +164,7 @@ class Inventory(ModelWorkflow, ModelSQL, ModelView):
for line in inventory.lines:
if not (line.product.active and
line.product.type == 'stockable'):
line_obj.delete(cursor, user, line.id, context=context)
line_obj.delete(line.id)
continue
if line.product.id in product_qty:
quantity, uom_id = product_qty.pop(line.product.id)
@@ -182,21 +172,18 @@ class Inventory(ModelWorkflow, ModelSQL, ModelView):
quantity, uom_id = 0.0, product2uom[line.product.id]
else:
quantity, uom_id = 0.0, line.product.default_uom.id
values = line_obj.update_values4complete(cursor, user,
line, quantity, uom_id, context=context)
values = line_obj.update_values4complete(line, quantity, uom_id)
if values:
line_obj.write(cursor, user, line.id, values,
context=context)
line_obj.write(line.id, values)
# Create lines if needed
for product_id in product_qty:
if product2type[product_id] != 'stockable':
continue
quantity, uom_id = product_qty[product_id]
values = line_obj.create_values4complete(cursor, user,
product_id, inventory, quantity, uom_id,
context=context)
line_obj.create(cursor, user, values, context=context)
values = line_obj.create_values4complete(product_id, inventory,
quantity, uom_id)
line_obj.create(values)
Inventory()
@@ -231,53 +218,48 @@ class InventoryLine(ModelSQL, ModelView):
]
self._order.insert(0, ('product', 'ASC'))
def default_unit_digits(self, cursor, user, context=None):
def default_unit_digits(self):
return 2
def on_change_product(self, cursor, user, vals, context=None):
def on_change_product(self, vals):
product_obj = self.pool.get('product.product')
uom_obj = self.pool.get('product.uom')
res = {}
res['unit_digits'] = 2
if vals.get('product'):
product = product_obj.browse(cursor, user, vals['product'],
context=context)
product = product_obj.browse(vals['product'])
res['uom'] = product.default_uom.id
res['uom.rec_name'] = product.default_uom.rec_name
res['unit_digits'] = product.default_uom.digits
return res
def get_uom(self, cursor, user, ids, name, context=None):
def get_uom(self, ids, name):
res = {}
for line in self.browse(cursor, user, ids, context=context):
for line in self.browse(ids):
res[line.id] = line.product.default_uom.id
return res
def get_unit_digits(self, cursor, user, ids, name, context=None):
def get_unit_digits(self, ids, name):
res = {}
for line in self.browse(cursor, user, ids, context=context):
for line in self.browse(ids):
res[line.id] = line.product.default_uom.digits
return res
def cancel_move(self, cursor, user, lines, context=None):
def cancel_move(self, lines):
move_obj = self.pool.get('stock.move')
move_obj.write(
cursor, user, [l.move.id for l in lines if l.move], {'state': 'cancel'},
context=context)
move_obj.delete(
cursor, user, [l.move.id for l in lines if l.move], context=context)
self.write(
cursor, user, [l.id for l in lines if l.move], {'move': False},
context=context)
move_obj.write( [l.move.id for l in lines if l.move], {
'state': 'cancel',
})
move_obj.delete([l.move.id for l in lines if l.move])
self.write([l.id for l in lines if l.move], {
'move': False,
})
def create_move(self, cursor, user, line, context=None):
def create_move(self, line):
'''
Create move for an inventory line
:param cursor: the database cursor
:param user: the user id
:param line: a BrowseRecord of inventory.line
:param context: the context
:return: the stock.move id or None
'''
move_obj = self.pool.get('stock.move')
@@ -290,7 +272,7 @@ class InventoryLine(ModelSQL, ModelView):
(from_location, to_location, delta_qty) = \
(to_location, from_location, -delta_qty)
move_id = move_obj.create(cursor, user, {
move_id = move_obj.create({
'from_location': from_location,
'to_location': to_location,
'quantity': delta_qty,
@@ -299,21 +281,19 @@ class InventoryLine(ModelSQL, ModelView):
'company': line.inventory.company.id,
'state': 'done',
'effective_date': line.inventory.date,
}, context=context)
self.write(cursor, user, line.id, {'move': move_id}, context=context)
})
self.write(line.id, {
'move': move_id,
})
return move_id
def update_values4complete(self, cursor, user, line, quantity, uom_id,
context=None):
def update_values4complete(self, line, quantity, uom_id):
'''
Return update values to complete inventory
:param cursor: the database cursor
:param user: the user id
:param line: a BrowseRecord of inventory.line
:param quantity: the actual product quantity for the inventory location
:param uom_id: the UoM id of the product line
:param context: the context
:return: a dictionary
'''
res = {}
@@ -328,18 +308,14 @@ class InventoryLine(ModelSQL, ModelView):
res['quantity'] = max(quantity, 0.0)
return res
def create_values4complete(self, cursor, user, product_id, inventory,
quantity, uom_id, context=None):
def create_values4complete(self, product_id, inventory, quantity, uom_id):
'''
Return create values to complete inventory
:param cursor: the database cursor
:param user: the user id
:param product_id: the product.product id
:param inventory: a BrowseRecord of inventory.inventory
:param quantity: the actual product quantity for the inventory location
:param uom_id: the UoM id of the product_id
:param context: the context
:return: a dictionary
'''
return {
@@ -366,9 +342,9 @@ class CompleteInventory(Wizard):
},
}
def _complete(self, cursor, user, data, context=None):
def _complete(self, data):
inventory_obj = self.pool.get('stock.inventory')
inventory_obj.complete_lines(cursor, user, data['ids'], context=context)
inventory_obj.complete_lines(data['ids'])
return {}
+106 -121
View File
@@ -1,11 +1,12 @@
#This file is part of Tryton. The COPYRIGHT file at the top level of
#this repository contains the full copyright notices and license terms.
"Wharehouse"
from __future__ import with_statement
import datetime
from trytond.model import ModelView, ModelSQL, fields
from trytond.wizard import Wizard
from trytond.backend import TableHandler
from trytond.pyson import Not, Bool, Eval, Equal, PYSONEncoder, Date
import datetime
from trytond.transaction import Transaction
STATES = {
'readonly': Not(Bool(Eval('active'))),
@@ -90,110 +91,101 @@ class Location(ModelSQL, ModelView):
'child_of_warehouse': 'Location "%s" must be a child of warehouse "%s"!',
})
def init(self, cursor, module_name):
super(Location, self).init(cursor, module_name)
def init(self, module_name):
super(Location, self).init(module_name)
cursor = Transaction().cursor
table = TableHandler(cursor, self, module_name)
table.index_action(['left', 'right'], 'add')
def check_type_for_moves(self, cursor, user, ids):
def check_type_for_moves(self, ids):
""" Check locations with moves have types compatible with moves. """
invalid_move_types = ['warehouse', 'view']
move_obj = self.pool.get('stock.move')
for location in self.browse(cursor, user, ids):
for location in self.browse(ids):
if location.type in invalid_move_types and \
move_obj.search(cursor, user, ['OR',
move_obj.search(['OR',
('to_location', '=', location.id),
('from_location', '=', location.id)]):
('from_location', '=', location.id),
]):
return False
return True
def default_active(self, cursor, user, context=None):
def default_active(self):
return True
def default_left(self, cursor, user, context=None):
def default_left(self):
return 0
def default_right(self, cursor, user, context=None):
def default_right(self):
return 0
def default_type(self, cursor, user, context=None):
def default_type(self):
return 'storage'
def check_xml_record(self, cursor, user, ids, values, context=None):
def check_xml_record(self, ids, values):
return True
def search_rec_name(self, cursor, user, name, clause, context=None):
ids = self.search(cursor, user, [('code', '=', clause[2])],
order=[], context=context)
def search_rec_name(self, name, clause):
ids = self.search([
('code', '=', clause[2]),
], order=[])
if ids:
return [('id', 'in', ids)]
return [(self._rec_name,) + clause[1:]]
def get_quantity(self, cursor, user, ids, name, context=None):
def get_quantity(self, ids, name):
product_obj = self.pool.get('product.product')
date_obj = self.pool.get('ir.date')
if not context:
context = {}
if (not context.get('product')) \
or not (isinstance(context['product'], (int, long))):
if (not Transaction().context.get('product')) \
or not (isinstance(Transaction().context['product'],
(int, long))):
return dict([(i, 0) for i in ids])
ctx = context.copy()
ctx['active_test'] = False
if not product_obj.search(cursor, user, [
('id', '=', context['product']),
], context=ctx):
return dict([(i, 0) for i in ids])
with Transaction().set_context(active_test=False):
if not product_obj.search([
('id', '=', Transaction().context['product']),
]):
return dict([(i, 0) for i in ids])
if name == 'quantity' and \
context.get('stock_date_end') > \
date_obj.today(cursor, user, context=context):
context = context.copy()
context['stock_date_end'] = date_obj.today(
cursor, user, context=context)
context = {}
if (name == 'quantity'
and Transaction().context.get('stock_date_end') >
date_obj.today()):
context['stock_date_end'] = date_obj.today()
if name == 'forecast_quantity':
context = context.copy()
context['forecast'] = True
if not context.get('stock_date_end'):
if not Transaction().context.get('stock_date_end'):
context['stock_date_end'] = datetime.date.max
pbl = product_obj.products_by_location(cursor, user, location_ids=ids,
product_ids=[context['product']], with_childs=True, skip_zero=False,
context=context).iteritems()
with Transaction().set_context(context):
pbl = product_obj.products_by_location(location_ids=ids,
product_ids=[Transaction().context['product']],
with_childs=True, skip_zero=False).iteritems()
return dict([(loc,qty) for (loc,prod), qty in pbl])
def view_header_get(self, cursor, user, value, view_type='form',
context=None):
def view_header_get(self, value, view_type='form'):
product_obj = self.pool.get('product.product')
if context is None:
context = {}
ctx = context.copy()
ctx['active_test'] = False
if context.get('product') \
and isinstance(context['product'], (int, long)) \
and product_obj.search(cursor, user, [
('id', '=', context['product']),
], context=ctx):
product = product_obj.browse(cursor, user, context['product'],
context=context)
return value + ': ' + product.rec_name + \
' (' + product.default_uom.rec_name + ')'
if (Transaction().context.get('product')
and isinstance(Transaction().context['product'], (int, long))):
with Transaction().set_context(active_test=False):
product_ids = product_obj.search([
('id', '=', Transaction().context['product']),
], limit=1)
if product_ids:
product = product_obj.browse(product_ids[0])
return value + ': ' + product.rec_name + \
' (' + product.default_uom.rec_name + ')'
return value
def _set_warehouse_parent(self, cursor, user, locations, context=None):
def _set_warehouse_parent(self, locations):
'''
Set the parent of child location of warehouse if not set
:param cursor: the database cursor
:param user: the user id
:param locations: a BrowseRecordList of locations
:param context: the context
:return: a list with updated location ids
'''
location_ids = set()
@@ -207,35 +199,32 @@ class Location(ModelSQL, ModelView):
location_ids.add(location.storage_location.id)
location_ids = list(location_ids)
if location_ids:
self.write(cursor, user, location_ids, {
self.write(location_ids, {
'parent': location.id,
}, context=context)
})
def create(self, cursor, user, vals, context=None):
res = super(Location, self).create(cursor, user, vals, context=context)
locations = self.browse(cursor, user, [res], context=context)
self._set_warehouse_parent(cursor, user, locations, context=context)
def create(self, vals):
res = super(Location, self).create(vals)
locations = self.browse([res])
self._set_warehouse_parent(locations)
return res
def write(self, cursor, user, ids, vals, context=None):
res = super(Location, self).write(cursor, user, ids, vals,
context=context)
def write(self, ids, vals):
res = super(Location, self).write(ids, vals)
if isinstance(ids, (int, long)):
ids = [ids]
locations = self.browse(cursor, user, ids, context=context)
self._set_warehouse_parent(cursor, user, locations, context=context)
locations = self.browse(ids)
self._set_warehouse_parent(locations)
check_wh = self.search(
cursor, user,
[('type', '=', 'warehouse'),
['OR',
('storage_location', 'in', ids),
('input_location', 'in', ids),
('output_location', 'in', ids)
]],
context=context)
check_wh = self.search([
('type', '=', 'warehouse'),
['OR',
('storage_location', 'in', ids),
('input_location', 'in', ids),
('output_location', 'in', ids),
]])
warehouses = self.browse(cursor, user, check_wh, context=context)
warehouses = self.browse(check_wh)
fields = ('storage_location', 'input_location', 'output_location')
wh2childs = {}
for warehouse in warehouses:
@@ -243,19 +232,16 @@ class Location(ModelSQL, ModelView):
for location in locations:
if location.id not in in_out_sto:
continue
childs = wh2childs.setdefault(
warehouse.id,
self.search(
cursor, user, [('parent', 'child_of', warehouse.id)],
context=context))
childs = wh2childs.setdefault(warehouse.id, self.search([
('parent', 'child_of', warehouse.id),
]))
if location.id not in childs:
self.raise_user_error(
cursor, 'child_of_warehouse',
(location.name, warehouse.name), context=context)
self.raise_user_error('child_of_warehouse',
(location.name, warehouse.name))
return res
def copy(self, cursor, user, ids, default=None, context=None):
def copy(self, ids, default=None):
if default is None:
default = {}
int_id = False
@@ -267,7 +253,7 @@ class Location(ModelSQL, ModelView):
default['right'] = 0
res = []
locations = self.browse(cursor, user, ids, context=context)
locations = self.browse(ids)
for location in locations:
if location.type == 'warehouse':
@@ -278,32 +264,31 @@ class Location(ModelSQL, ModelView):
wh_default['storage_location'] = False
wh_default['childs'] = False
new_id = super(Location, self).copy(
cursor, user, location.id, default=wh_default,
context=context)
new_id = super(Location, self).copy(location.id,
default=wh_default)
child_context = context and context.copy() or {}
child_context['cp_warehouse_locations'] = {
'input_location': location.input_location.id,
'output_location': location.output_location.id,
'storage_location': location.storage_location.id}
child_context['cp_warehouse_id'] = new_id
self.copy(
cursor, user, [c.id for c in location.childs],
default={'parent':new_id}, context=child_context)
self.write(
cursor, user, new_id, {'type': 'warehouse'}, context=context)
with Transaction().set_context(
cp_warehouse_locations={
'input_location': location.input_location.id,
'output_location': location.output_location.id,
'storage_location': location.storage_location.id,
},
cp_warehouse_id=new_id):
self.copy([c.id for c in location.childs],
default={'parent': new_id})
self.write(new_id, {
'type': 'warehouse',
})
else:
new_id = super(Location, self).copy(
cursor, user, location.id, default=default, context=context)
warehouse_locations = context.get('cp_warehouse_locations', {})
new_id = super(Location, self).copy(location.id,
default=default)
warehouse_locations = Transaction().context.get(
'cp_warehouse_locations') or {}
if location.id in warehouse_locations.values():
for field, loc_id in warehouse_locations.iteritems():
if loc_id == location.id:
self.write(
cursor, user, context['cp_warehouse_id'],
{field: new_id}, context=context)
self.write(Transaction().context['cp_warehouse_id'],
{field: new_id})
res.append(new_id)
@@ -335,9 +320,9 @@ class ChooseStockDateInit(ModelView):
'* An empty value is an infinite date in the future.\n'\
'* A date in the past will provide historical values.')
def default_forecast_date(self, cursor, user, context=None):
def default_forecast_date(self):
date_obj = self.pool.get('ir.date')
return date_obj.today(cursor, user, context=context)
return date_obj.today()
ChooseStockDateInit()
@@ -365,21 +350,21 @@ class OpenProduct(Wizard):
},
}
def _action_open_product(self, cursor, user, data, context=None):
def _action_open_product(self, data):
model_data_obj = self.pool.get('ir.model.data')
act_window_obj = self.pool.get('ir.action.act_window')
act_window_id = model_data_obj.get_id(cursor, user, 'stock',
'act_product_by_location', context=context)
res = act_window_obj.read(cursor, user, act_window_id, context=context)
act_window_id = model_data_obj.get_id('stock',
'act_product_by_location')
res = act_window_obj.read(act_window_id)
ctx = {}
ctx['locations'] = data['ids']
context = {}
context['locations'] = data['ids']
if data['form']['forecast_date']:
date = data['form']['forecast_date']
else:
date = datetime.date.max
ctx['stock_date_end'] = Date(date.year, date.month, date.day)
res['pyson_context'] = PYSONEncoder().encode(ctx)
context['stock_date_end'] = Date(date.year, date.month, date.day)
res['pyson_context'] = PYSONEncoder().encode(context)
return res
+166 -256
View File
@@ -1,10 +1,11 @@
#This file is part of Tryton. The COPYRIGHT file at the top level of
#this repository contains the full copyright notices and license terms.
"Move"
from __future__ import with_statement
from decimal import Decimal
from trytond.model import ModelView, ModelSQL, fields, OPERATORS
from trytond.backend import TableHandler
from trytond.pyson import In, Eval, Not, In, Equal, If, Get, Bool
from decimal import Decimal
from trytond.transaction import Transaction
STATES = {
'readonly': In(Eval('state'), ['cancel', 'done']),
@@ -117,7 +118,8 @@ class Move(ModelSQL, ModelView):
'service_product': 'You can not use service products for a move!',
})
def init(self, cursor, module_name):
def init(self, module_name):
cursor = Transaction().cursor
# Migration from 1.2: packing renamed into shipment
table = TableHandler(cursor, self, module_name)
table.drop_constraint('check_packing')
@@ -128,7 +130,7 @@ class Move(ModelSQL, ModelView):
table.index_action(old_column, action='remove')
table.drop_fk(old_column)
table.column_rename(old_column, new_column)
super(Move, self).init(cursor, module_name)
super(Move, self).init(module_name)
# Migration from 1.0 check_packing_in_out has been removed
table = TableHandler(cursor, self, module_name)
@@ -137,225 +139,179 @@ class Move(ModelSQL, ModelView):
# Add index on create_date
table.index_action('create_date', action='add')
def default_planned_date(self, cursor, user, context=None):
if context and context.get('planned_date'):
return context.get('planned_date')
def default_planned_date(self):
return Transaction().context.get('planned_date') or False
def default_to_location(self, cursor, user, context=None):
def default_to_location(self):
location_obj = self.pool.get('stock.location')
party_obj = self.pool.get('party.party')
res = False
if context is None:
context = {}
warehouse = None
if context.get('warehouse'):
warehouse = location_obj.browse(cursor, user, context['warehouse'],
context=context)
if Transaction().context.get('warehouse'):
warehouse = location_obj.browse(Transaction().context['warehouse'])
if context.get('type', '') == 'inventory_in':
if Transaction().context.get('type', '') == 'inventory_in':
if warehouse:
res = warehouse.storage_location.id
elif context.get('type', '') == 'inventory_out':
elif Transaction().context.get('type', '') == 'inventory_out':
if warehouse:
res = warehouse.output_location.id
elif context.get('type', '') == 'incoming':
elif Transaction().context.get('type', '') == 'incoming':
if warehouse:
res = warehouse.input_location.id
elif context.get('type', '') == 'outgoing':
if context.get('customer'):
customer = party_obj.browse(cursor, user, context['customer'],
context=context)
elif Transaction().context.get('type', '') == 'outgoing':
if Transaction().context.get('customer'):
customer = party_obj.browse(Transaction().context['customer'])
res = customer.customer_location.id
if context.get('to_location'):
res = context.get('to_location')
if Transaction().context.get('to_location'):
res = Transaction().context['to_location']
return res
def default_from_location(self, cursor, user, context=None):
def default_from_location(self):
location_obj = self.pool.get('stock.location')
party_obj = self.pool.get('party.party')
res = False
if context is None:
context = {}
warehouse = None
if context.get('warehouse'):
warehouse = location_obj.browse(cursor, user, context['warehouse'],
context=context)
if Transaction().context.get('warehouse'):
warehouse = location_obj.browse(Transaction().context['warehouse'])
if context.get('type', '') == 'inventory_in':
if Transaction().context.get('type', '') == 'inventory_in':
if warehouse:
res = warehouse.input_location.id
elif context.get('type', '') == 'inventory_out':
elif Transaction().context.get('type', '') == 'inventory_out':
if warehouse:
res = warehouse.storage_location.id
elif context.get('type', '') == 'outgoing':
elif Transaction().context.get('type', '') == 'outgoing':
if warehouse:
res = warehouse.output_location.id
elif context.get('type', '') == 'incoming':
if context.get('supplier'):
supplier = party_obj.browse(cursor, user, context['supplier'],
context=context)
elif Transaction().context.get('type', '') == 'incoming':
if Transaction().context.get('supplier'):
supplier = party_obj.browse(Transaction().context['supplier'])
res = supplier.supplier_location.id
elif context.get('customer'):
customer = party_obj.browse(cursor, user, context['customer'],
context=context)
elif Transaction().context.get('customer'):
customer = party_obj.browse(Transaction().context['customer'])
res = customer.customer_location.id
if context.get('from_location'):
res = context.get('from_location')
if Transaction().context.get('from_location'):
res = Transaction().context['from_location']
return res
def default_state(self, cursor, user, context=None):
def default_state(self):
return 'draft'
def default_company(self, cursor, user, context=None):
if context is None:
context = {}
if context.get('company'):
return context['company']
return False
def default_company(self):
return Transaction().context.get('company') or False
def default_currency(self, cursor, user, context=None):
def default_currency(self):
company_obj = self.pool.get('company.company')
currency_obj = self.pool.get('currency.currency')
if context is None:
context = {}
company = None
if context.get('company'):
company = company_obj.browse(cursor, user, context['company'],
context=context)
company = Transaction().context.get('company')
if company:
company = company_obj.browse(company)
return company.currency.id
return False
def on_change_with_unit_digits(self, cursor, user, vals, context=None):
def on_change_with_unit_digits(self, vals):
uom_obj = self.pool.get('product.uom')
if vals.get('uom'):
uom = uom_obj.browse(cursor, user, vals['uom'],
context=context)
uom = uom_obj.browse(vals['uom'])
return uom.digits
return 2
def get_unit_digits(self, cursor, user, ids, name, context=None):
def get_unit_digits(self, ids, name):
res = {}
for move in self.browse(cursor, user, ids, context=context):
for move in self.browse(ids):
res[move.id] = move.uom.digits
return res
def on_change_product(self, cursor, user, vals, context=None):
def on_change_product(self, vals):
product_obj = self.pool.get('product.product')
uom_obj = self.pool.get('product.uom')
currency_obj = self.pool.get('currency.currency')
company_obj = self.pool.get('company.company')
location_obj = self.pool.get('stock.location')
if context is None:
context = {}
res = {
'unit_price': Decimal('0.0'),
}
if vals.get('product'):
product = product_obj.browse(cursor, user, vals['product'],
context=context)
product = product_obj.browse(vals['product'])
res['uom'] = product.default_uom.id
res['uom.rec_name'] = product.default_uom.rec_name
res['unit_digits'] = product.default_uom.digits
to_location = None
if vals.get('to_location'):
to_location = location_obj.browse(cursor, user,
vals['to_location'], context=context)
to_location = location_obj.browse(vals['to_location'])
if to_location and to_location.type == 'storage':
unit_price = product.cost_price
if vals.get('uom') and vals['uom'] != product.default_uom.id:
uom = uom_obj.browse(cursor, user, vals['uom'],
context=context)
unit_price = uom_obj.compute_price(cursor, user,
product.default_uom, unit_price, uom,
context=context)
uom = uom_obj.browse(vals['uom'])
unit_price = uom_obj.compute_price(product.default_uom,
unit_price, uom)
if vals.get('currency') and vals.get('company'):
currency = currency_obj.browse(cursor, user,
vals['currency'], context=context)
company = company_obj.browse(cursor, user,
vals['company'], context=context)
unit_price = currency_obj.compute(cursor, user,
company.currency, unit_price, currency,
round=False, context=context)
currency = currency_obj.browse(vals['currency'])
company = company_obj.browse(vals['company'])
unit_price = currency_obj.compute(company.currency,
unit_price, currency, round=False)
res['unit_price'] = unit_price
return res
def on_change_uom(self, cursor, user, vals, context=None):
def on_change_uom(self, vals):
product_obj = self.pool.get('product.product')
uom_obj = self.pool.get('product.uom')
currency_obj = self.pool.get('currency.currency')
company_obj = self.pool.get('company.company')
location_obj = self.pool.get('stock.location')
if context is None:
context = {}
res = {
'unit_price': Decimal('0.0'),
}
if vals.get('product'):
product = product_obj.browse(cursor, user, vals['product'],
context=context)
product = product_obj.browse(vals['product'])
to_location = None
if vals.get('to_location'):
to_location = location_obj.browse(cursor, user,
vals['to_location'], context=context)
to_location = location_obj.browse(vals['to_location'])
if to_location and to_location.type == 'storage':
unit_price = product.cost_price
if vals.get('uom') and vals['uom'] != product.default_uom.id:
uom = uom_obj.browse(cursor, user, vals['uom'],
context=context)
unit_price = uom_obj.compute_price(cursor, user,
product.default_uom, unit_price, uom,
context=context)
uom = uom_obj.browse(vals['uom'])
unit_price = uom_obj.compute_price(product.default_uom,
unit_price, uom)
if vals.get('currency') and vals.get('company'):
currency = currency_obj.browse(cursor, user,
vals['currency'], context=context)
company = company_obj.browse(cursor, user,
vals['company'], context=context)
unit_price = currency_obj.compute(cursor, user,
company.currency, unit_price, currency,
round=False, context=context)
currency = currency_obj.browse(vals['currency'])
company = company_obj.browse(vals['company'])
unit_price = currency_obj.compute(company.currency,
unit_price, currency, round=False)
res['unit_price'] = unit_price
return res
def default_unit_price_required(self, cursor, user, context=None):
from_location = self.default_from_location(cursor, user,
context=context)
to_location = self.default_to_location(cursor,user,
context=context)
def default_unit_price_required(self):
from_location = self.default_from_location()
to_location = self.default_to_location()
vals = {
'from_location': from_location,
'to_location': to_location,
}
return self.on_change_with_unit_price_required(cursor, user,
vals, context=context)
return self.on_change_with_unit_price_required(vals)
def on_change_with_unit_price_required(self, cursor, user, vals,
context=None):
def on_change_with_unit_price_required(self, vals):
location_obj = self.pool.get('stock.location')
if vals.get('from_location'):
from_location = location_obj.browse(cursor, user,
vals['from_location'], context=context)
from_location = location_obj.browse(vals['from_location'])
if from_location.type == 'supplier':
return True
if vals.get('to_location'):
to_location = location_obj.browse(cursor, user,
vals['to_location'], context=context)
to_location = location_obj.browse(vals['to_location'])
if to_location.type == 'customer':
return True
return False
def get_unit_price_required(self, cursor, user, ids, name, context=None):
def get_unit_price_required(self, ids, name):
res = {}
for move in self.browse(cursor, user, ids, context=context):
for move in self.browse(ids):
res[move.id] = False
if move.from_location.type == 'supplier':
res[move.id] = True
@@ -363,26 +319,26 @@ class Move(ModelSQL, ModelView):
res[move.id] = True
return res
def check_product_type(self, cursor, user, ids):
for move in self.browse(cursor, user, ids):
def check_product_type(self, ids):
for move in self.browse(ids):
if move.product.type == 'service':
return False
return True
def get_rec_name(self, cursor, user, ids, name, context=None):
def get_rec_name(self, ids, name):
if not ids:
return {}
res = {}
moves = self.browse(cursor, user, ids, context=context)
moves = self.browse(ids)
for m in moves:
res[m.id] = "%s%s %s" % (m.quantity, m.uom.symbol, m.product.rec_name)
return res
def search_rec_name(self, cursor, user, name, clause, context=None):
def search_rec_name(self, name, clause):
return [('product',) + clause[1:]]
def search(self, cursor, user, args, offset=0, limit=None, order=None,
context=None, count=False, query_string=False):
def search(self, args, offset=0, limit=None, order=None, count=False,
query_string=False):
location_obj = self.pool.get('stock.location')
args = args[:]
@@ -396,8 +352,7 @@ class Move(ModelSQL, ModelView):
args[i][0] == 'to_location_warehouse':
location_id = False
if args[i][2]:
location = location_obj.browse(cursor, user,
args[i][2], context=context)
location = location_obj.browse(args[i][2])
if location.type == 'warehouse':
location_id = location.input_location.id
args[i] = ('to_location', args[i][1], location_id)
@@ -405,17 +360,14 @@ class Move(ModelSQL, ModelView):
process_args(args[i])
i += 1
process_args(args)
return super(Move, self).search(cursor, user, args, offset=offset,
limit=limit, order=order, context=context, count=count,
query_string=query_string)
return super(Move, self).search(args, offset=offset, limit=limit,
order=order, count=count, query_string=query_string)
def _update_product_cost_price(self, cursor, user, product_id, quantity, uom,
unit_price, currency, company, context=None):
def _update_product_cost_price(self, product_id, quantity, uom, unit_price,
currency, company):
"""
Update the cost price on the given product
:param cursor: the database cursor
:param user: the user id
:param product_id: the id of the product
:param quantity: the quantity of the product, positive if incoming and
negative if outgoing
@@ -423,7 +375,6 @@ class Move(ModelSQL, ModelView):
:param unit_price: the unit price of the product
:param currency: the currency of the unit price
:param company: the company id ot a BrowseRecord of the company
:param context: the context
"""
uom_obj = self.pool.get('product.uom')
product_obj = self.pool.get('product.product')
@@ -433,31 +384,28 @@ class Move(ModelSQL, ModelView):
company_obj = self.pool.get('company.company')
date_obj = self.pool.get('ir.date')
if context is None:
context = {}
if isinstance(uom, (int, long)):
uom = uom_obj.browse(cursor, user, uom, context=context)
uom = uom_obj.browse(uom)
if isinstance(company, (int, long)):
company = company_obj.browse(cursor, user, company, context=context)
company = company_obj.browse(company)
ctx = context and context.copy() or {}
ctx['locations'] = location_obj.search(
cursor, user, [('type', '=', 'storage')], context=context)
ctx['stock_date_end'] = date_obj.today(cursor, user, context=context)
product = product_obj.browse(cursor, user, product_id, context=ctx)
qty = uom_obj.compute_qty(
cursor, user, uom, quantity, product.default_uom, context=context)
context = {}
context['locations'] = location_obj.search([
('type', '=', 'storage'),
])
context['stock_date_end'] = date_obj.today()
with Transaction().set_context(context):
product = product_obj.browse(product_id)
qty = uom_obj.compute_qty(uom, quantity, product.default_uom)
qty = Decimal(str(qty))
product_qty = Decimal(str(product.template.quantity))
# convert wrt currency
unit_price = currency_obj.compute(
cursor, user, currency, unit_price, company.currency,
round=False, context=context)
unit_price = currency_obj.compute(currency, unit_price,
company.currency, round=False)
# convert wrt to the uom
unit_price = uom_obj.compute_price(
cursor, user, uom, unit_price, product.default_uom, context=context)
unit_price = uom_obj.compute_price(uom, unit_price,
product.default_uom)
if product_qty + qty != Decimal('0.0'):
new_cost_price = (
(product.cost_price * product_qty) + (unit_price * qty)
@@ -472,13 +420,12 @@ class Move(ModelSQL, ModelView):
new_cost_price = new_cost_price.quantize(
Decimal(str(10.0**-digits[1])))
ctx = context.copy()
ctx['user'] = user
product_obj.write(
cursor, 0, product.id, {'cost_price': new_cost_price},
context=ctx)
with Transaction().set_user(0, set_context=True):
product_obj.write(product.id, {
'cost_price': new_cost_price,
})
def create(self, cursor, user, vals, context=None):
def create(self, vals):
location_obj = self.pool.get('stock.location')
product_obj = self.pool.get('product.product')
date_obj = self.pool.get('ir.date')
@@ -487,125 +434,101 @@ class Move(ModelSQL, ModelView):
if vals.get('state') == 'done':
if not vals.get('effective_date'):
vals['effective_date'] = date_obj.today(cursor, user,
context=context)
from_location = location_obj.browse(cursor, user,
vals['from_location'], context=context)
to_location = location_obj.browse(cursor, user,
vals['to_location'], context=context)
product = product_obj.browse(cursor, user, vals['product'],
context=context)
vals['effective_date'] = date_obj.today()
from_location = location_obj.browse(vals['from_location'])
to_location = location_obj.browse(vals['to_location'])
product = product_obj.browse(vals['product'])
if from_location.type == 'supplier' \
and product.cost_price_method == 'average':
self._update_product_cost_price(
cursor, user, vals['product'], vals['quantity'],
vals['uom'], vals['unit_price'], vals['currency'],
vals['company'], context=context)
self._update_product_cost_price(vals['product'],
vals['quantity'], vals['uom'], vals['unit_price'],
vals['currency'], vals['company'])
if to_location.type == 'supplier' \
and product.cost_price_method == 'average':
self._update_product_cost_price(
cursor, user, vals['product'], -vals['quantity'],
vals['uom'], vals['unit_price'], vals['currency'],
vals['company'], context=context)
self._update_product_cost_price(vals['product'],
-vals['quantity'], vals['uom'], vals['unit_price'],
vals['currency'], vals['company'])
if not vals.get('cost_price'):
# Re-read product to get the updated cost_price
product = product_obj.browse(cursor, user, vals['product'],
context=context)
product = product_obj.browse(vals['product'])
vals['cost_price'] = product.cost_price
elif vals.get('state') == 'assigned':
if not vals.get('effective_date'):
vals['effective_date'] = date_obj.today(cursor, user,
context=context)
return super(Move, self).create(cursor, user, vals, context=context)
vals['effective_date'] = date_obj.today()
return super(Move, self).create(vals)
def write(self, cursor, user, ids, vals, context=None):
def write(self, ids, vals):
date_obj = self.pool.get('ir.date')
if context is None:
context = {}
if isinstance(ids, (int, long)):
ids = [ids]
if 'state' in vals:
for move in self.browse(cursor, user, ids, context=context):
for move in self.browse(ids):
if vals['state'] == 'cancel':
vals['effective_date'] = False
if move.from_location.type == 'supplier' \
and move.state != 'cancel' \
and move.product.cost_price_method == 'average':
self._update_product_cost_price(
cursor, user, move.product.id, -move.quantity,
move.uom, move.unit_price, move.currency,
move.company, context=context)
self._update_product_cost_price(move.product.id,
-move.quantity, move.uom, move.unit_price,
move.currency, move.company)
if move.to_location.type == 'supplier' \
and move.state != 'cancel' \
and move.product.cost_price_method == 'average':
self._update_product_cost_price(
cursor, user, move.product.id, move.quantity,
move.uom, move.unit_price, move.currency,
move.company, context=context)
self._update_product_cost_price(move.product.id,
move.quantity, move.uom, move.unit_price,
move.currency, move.company)
elif vals['state'] == 'draft':
if move.state == 'done':
self.raise_user_error(cursor, 'set_state_draft',
context=context)
self.raise_user_error('set_state_draft')
elif vals['state'] == 'assigned':
if move.state in ('cancel', 'done'):
self.raise_user_error(cursor, 'set_state_assigned',
context=context)
vals['effective_date'] = date_obj.today(cursor, user,
context=context)
self.raise_user_error('set_state_assigned')
vals['effective_date'] = date_obj.today()
elif vals['state'] == 'done':
if move.state in ('cancel'):
self.raise_user_error(cursor, 'set_state_done',
context=context)
vals['effective_date'] = date_obj.today(cursor, user,
context=context)
self.raise_user_error('set_state_done')
vals['effective_date'] = date_obj.today()
if move.from_location.type == 'supplier' \
and move.state != 'done' \
and move.product.cost_price_method == 'average':
self._update_product_cost_price(
cursor, user, move.product.id, move.quantity,
move.uom, move.unit_price, move.currency,
move.company, context=context)
self._update_product_cost_price(move.product.id,
move.quantity, move.uom, move.unit_price,
move.currency, move.company)
if move.to_location.type == 'supplier' \
and move.state != 'done' \
and move.product.cost_price_method == 'average':
self._update_product_cost_price(
cursor, user, move.product.id, -move.quantity,
move.uom, move.unit_price, move.currency,
move.company, context=context)
res = super(Move, self).write(cursor, user, ids, vals, context=context)
self._update_product_cost_price( move.product.id,
-move.quantity, move.uom, move.unit_price,
move.currency, move.company)
res = super(Move, self).write(ids, vals)
if vals.get('state', '') == 'done':
#Re-read the move because cost_price has been updated
for move in self.browse(cursor, user, ids, context=context):
for move in self.browse(ids):
if not move.cost_price:
self.write(cursor, user, move.id, {
self.write(move.id, {
'cost_price': move.product.cost_price,
}, context=context)
})
return res
def delete(self, cursor, user, ids, context=None):
for move in self.browse(cursor, user, ids, context=context):
def delete(self, ids):
for move in self.browse(ids):
if move.state not in ('draft', 'cancel'):
self.raise_user_error(cursor, 'del_draft_cancel',
context=context)
return super(Move, self).delete(cursor, user, ids, context=context)
self.raise_user_error('del_draft_cancel')
return super(Move, self).delete(ids)
def pick_product(self, cursor, user, move, location_quantities, context=None):
def pick_product(self, move, location_quantities):
"""
Pick the product across the location. Naive (fast) implementation.
:param cursor: the database cursor
:param user: the user id
:param move: a BrowseRecord of stock.move
:param location_quantities: a list of tuple (location, available_qty)
where location is a BrowseRecord of stock.location.
:param context: the context
:return: a list of tuple (location, quantity) for quantities
that can be picked
"""
@@ -627,15 +550,12 @@ class Move(ModelSQL, ModelView):
return to_pick
return to_pick
def assign_try(self, cursor, user, moves, context=None):
def assign_try(self, moves):
'''
Try to assign moves.
It will split the moves to assign as much possible.
:param cursor: the database cursor
:param user: the user id
:param moves: a BrowseRecordList of stock.move to assign
:param context: the context
:return: True if succeed or False if not
'''
product_obj = self.pool.get('product.product')
@@ -643,22 +563,16 @@ class Move(ModelSQL, ModelView):
date_obj = self.pool.get('ir.date')
location_obj = self.pool.get('stock.location')
if context is None:
context = {}
Transaction().cursor.lock(self._table)
cursor.lock(self._table)
local_ctx = context and context.copy() or {}
local_ctx['stock_date_end'] = date_obj.today(cursor, user,
context=context)
local_ctx['stock_assign'] = True
location_ids = location_obj.search(cursor, user, [
location_ids = location_obj.search([
('parent', 'child_of', [x.from_location.id for x in moves]),
], context=context)
pbl = product_obj.products_by_location(cursor, user,
location_ids=location_ids,
product_ids=[m.product.id for m in moves],
context=local_ctx)
])
with Transaction().set_context(
stock_date_end=date_obj.today(),
stock_assign=True):
pbl = product_obj.products_by_location(location_ids=location_ids,
product_ids=[m.product.id for m in moves])
success = True
for move in moves:
@@ -666,19 +580,17 @@ class Move(ModelSQL, ModelView):
continue
to_location = move.to_location
location_qties = {}
child_ids = location_obj.search(cursor, user, [
child_ids = location_obj.search([
('parent', 'child_of', [move.from_location.id]),
], context=context)
for location in location_obj.browse(cursor, user, child_ids,
context=context):
])
for location in location_obj.browse(child_ids):
if (location.id, move.product.id) in pbl:
location_qties[location] = uom_obj.compute_qty(
cursor, user, move.product.default_uom,
pbl[(location.id, move.product.id)], move.uom,
round=False, context=context)
move.product.default_uom,
pbl[(location.id, move.product.id)], move.uom,
round=False)
to_pick = self.pick_product(
cursor, user, move, location_qties, context=context)
to_pick = self.pick_product(move, location_qties)
picked_qties = 0.0
for _, qty in to_pick:
@@ -687,9 +599,9 @@ class Move(ModelSQL, ModelView):
if picked_qties < move.quantity:
success = False
first = False
self.write(cursor, user, move.id, {
self.write(move.id, {
'quantity': move.quantity - picked_qties,
}, context=context)
})
else:
first = True
for from_location, qty in to_pick:
@@ -699,15 +611,13 @@ class Move(ModelSQL, ModelView):
'state': 'assigned',
}
if first:
self.write(cursor, user, move.id, values, context=context)
self.write(move.id, values)
first = False
else:
move_id = self.copy(cursor, user, move.id, default=values,
context=context)
move_id = self.copy(move.id, default=values)
qty_default_uom = uom_obj.compute_qty(
cursor, user, move.uom, qty, move.product.default_uom,
round=False, context=context)
qty_default_uom = uom_obj.compute_qty(move.uom, qty,
move.product.default_uom, round=False)
pbl[(from_location.id, move.product.id)] = \
pbl.get((from_location.id, move.product.id), 0.0) - qty_default_uom
+88 -103
View File
@@ -1,9 +1,11 @@
#This file is part of Tryton. The COPYRIGHT file at the top level of
#this repository contains the full copyright notices and license terms.
from __future__ import with_statement
import datetime
from trytond.model import ModelView, ModelSQL, fields
from trytond.wizard import Wizard
from trytond.pyson import PYSONEncoder
import datetime
from trytond.transaction import Transaction
class Template(ModelSQL, ModelView):
@@ -13,12 +15,12 @@ class Template(ModelSQL, ModelView):
forecast_quantity = fields.Function(fields.Float('Forecast Quantity'),
'get_quantity')
def get_quantity(self, cursor, user, ids, name, context=None):
def get_quantity(self, ids, name):
res = {}
if name not in ('quantity', 'forecast_quantity'):
raise Exception('Bad argument')
for template in self.browse(cursor, user, ids, context=context):
for template in self.browse(ids):
res[template.id] = 0.0
for product in template.products:
res[template.id] += product[name]
@@ -31,29 +33,27 @@ class Template(ModelSQL, ModelView):
'a product which is associated to stock moves.',
})
def write(self, cursor, user, ids, vals, context=None):
def write(self, ids, vals):
move_obj = self.pool.get('stock.move')
cursor = Transaction().cursor
if not vals.get("default_uom"):
return super(Template, self).write(cursor, user, ids, vals,
context=context)
return super(Template, self).write(ids, vals)
for i in range(0, len(ids), cursor.IN_MAX):
sub_ids = ids[i:i + cursor.IN_MAX]
res = self.search(cursor, user, [
res = self.search([
('id', 'in', sub_ids),
('default_uom', '!=', vals['default_uom']),
], context=context)
])
if res:
res = move_obj.search(cursor, user, [
res = move_obj.search([
('product.template', 'in', res),
], limit=1, context=context)
], limit=1)
if res:
self.raise_user_error(cursor, 'change_default_uom',
context=context)
self.raise_user_error('change_default_uom')
return super(Template, self).write(cursor, user, ids, vals,
context=context)
return super(Template, self).write(ids, vals)
Template()
@@ -66,32 +66,30 @@ class Product(ModelSQL, ModelView):
forecast_quantity = fields.Function(fields.Float('Forecast Quantity'),
'get_quantity', searcher='search_quantity')
def get_quantity(self, cursor, user, ids, name, context=None):
def get_quantity(self, ids, name):
date_obj = self.pool.get('ir.date')
if not (context and context.get('locations')):
return dict([(id, 0.0) for id in ids])
if not Transaction().context.get('locations'):
return dict((id, 0.0) for id in ids)
if name == 'quantity' and \
context.get('stock_date_end') and \
context.get('stock_date_end') > \
date_obj.today(cursor, user, context=context):
context = context.copy()
context['stock_date_end'] = date_obj.today(
cursor, user, context=context)
context = {}
if (name == 'quantity'
and Transaction().context.get('stock_date_end')
and Transaction().context.get('stock_date_end') >
date_obj.today()):
context['stock_date_end'] = date_obj.today()
if name == 'forecast_quantity':
context = context.copy()
context['forecast'] = True
if not context.get('stock_date_end'):
if not Transaction().context.get('stock_date_end'):
context['stock_date_end'] = datetime.date.max
pbl = self.products_by_location(cursor, user,
location_ids=context['locations'], product_ids=ids,
with_childs=True, context=context)
with Transaction().set_context(context):
pbl = self.products_by_location(
location_ids=Transaction().context['locations'],
product_ids=ids, with_childs=True)
res = {}.fromkeys(ids, 0.0)
for location in context['locations']:
for location in Transaction().context['locations']:
for product in ids:
res[product] += pbl.get((location, product), 0.0)
return res
@@ -107,30 +105,28 @@ class Product(ModelSQL, ModelView):
operator= "=="
return (eval(str(value) + operator + str(operand)))
def search_quantity(self, cursor, user, name, domain=None, context=None):
def search_quantity(self, name, domain=None):
date_obj = self.pool.get('ir.date')
if not (context and context.get('locations') and domain):
if not (Transaction().context.get('locations') and domain):
return []
if name == 'quantity' and \
context.get('stock_date_end') and \
context.get('stock_date_end') > \
date_obj.today(cursor, user, context=context):
context = context.copy()
context['stock_date_end'] = date_obj.today(
cursor, user, context=context)
context = {}
if (name == 'quantity'
and Transaction().context.get('stock_date_end')
and Transaction().context.get('stock_date_end') >
date_obj.today()):
context['stock_date_end'] = date_obj.today()
if name == 'forecast_quantity':
context = context.copy()
context['forecast'] = True
if not context.get('stock_date_end'):
if not Transaction().context.get('stock_date_end'):
context['stock_date_end'] = datetime.date.max
pbl = self.products_by_location(
cursor, user, location_ids=context['locations'], with_childs=True,
skip_zero=False, context=context).iteritems()
with Transaction().set_context(context):
pbl = self.products_by_location(
location_ids=Transaction().context['locations'],
with_childs=True, skip_zero=False).iteritems()
processed_lines = []
for (location, product), quantity in pbl:
@@ -144,20 +140,13 @@ class Product(ModelSQL, ModelView):
def products_by_location(self, cursor, user, location_ids,
product_ids=None, with_childs=False, skip_zero=True, context=None):
def products_by_location(self, location_ids, product_ids=None,
with_childs=False, skip_zero=True):
"""
Compute for each location and product the stock quantity in the default
uom of the product.
:param cursor: the database cursor
:param user: the user id
:param location_ids: the ids of locations
:param product_ids: the ids of the products
if None all products are used
:param with_childs: a boolean to compute also for child locations
:param skip_zero: a boolean to list also items with zero quantity
:param context: the context with keys:
The context with keys:
stock_date_end: if set the date of the stock computation.
stock_date_start: if set return the delta of the stock
between the two dates, (ignored if stock_date_end is
@@ -169,6 +158,12 @@ class Product(ModelSQL, ModelView):
stock_skip_warehouse: if set, quantities on a warehouse are no
more quantities of all child locations but quantities of the
storage zone.
:param location_ids: the ids of locations
:param product_ids: the ids of the products
if None all products are used
:param with_childs: a boolean to compute also for child locations
:param skip_zero: a boolean to list also items with zero quantity
:return: a dictionary with (location id, product id) as key
and quantity as value
"""
@@ -178,22 +173,21 @@ class Product(ModelSQL, ModelView):
location_obj = self.pool.get('stock.location')
date_obj = self.pool.get('ir.date')
today = date_obj.today(cursor, user, context=context)
today = date_obj.today()
if not location_ids:
return {}
if context is None:
context= {}
cursor = Transaction().cursor
context = Transaction().context.copy()
# Skip warehouse location in favor of their storage location
# to compute quantities. Keep track of which ids to remove
# and to add after the query.
location_ids = set(location_ids)
storage_to_remove = set()
wh_to_add = {}
for location in location_obj.browse(
cursor, user, location_ids, context=context):
if location.type == 'warehouse' \
and context.get('stock_skip_warehouse'):
for location in location_obj.browse(location_ids):
if (location.type == 'warehouse'
and Transaction().context.get('stock_skip_warehouse')):
location_ids.remove(location.id)
if location.storage_location.id not in location_ids:
storage_to_remove.add(location.storage_location.id)
@@ -201,17 +195,15 @@ class Product(ModelSQL, ModelView):
wh_to_add[location.id] = location.storage_location.id
location_ids = list(location_ids)
move_query, move_val = rule_obj.domain_get(cursor, user, 'stock.move',
context=context)
move_query, move_val = rule_obj.domain_get('stock.move')
if not context.get('stock_date_end'):
context = context.copy()
context['stock_date_end'] = datetime.date.max
# date end in the past or today: filter on state done
if context['stock_date_end'] < today or \
(context['stock_date_end'] == today \
and not context.get('forecast')):
if (context['stock_date_end'] < today
or (context['stock_date_end'] == today
and not context.get('forecast'))):
state_date_clause = \
'('\
'(state in (%s, %s)) '\
@@ -359,9 +351,9 @@ class Product(ModelSQL, ModelView):
])
if with_childs:
query, args = location_obj.search(cursor, user, [
query, args = location_obj.search([
('parent', 'child_of', location_ids),
], context=context, query_string=True, order=[])
], query_string=True, order=[])
where_clause = " IN (" + query + ") "
where_vals = args
else:
@@ -442,27 +434,24 @@ class Product(ModelSQL, ModelView):
if line[position] not in id_list:
id_list.append(line[position])
uom_by_id = dict([(x.id, x) for x in uom_obj.browse(
cursor, user, uom_ids, context=context)])
uom_by_id = dict((x.id, x) for x in uom_obj.browse(uom_ids))
default_uom = dict((x.id, x.default_uom) for x in product_obj.browse(
cursor, user, product_ids or res_product_ids, context=context))
product_ids or res_product_ids))
for line in raw_lines:
location, product, uom, quantity = line
key = (location, product)
res.setdefault(key, 0.0)
res[key] += uom_obj.compute_qty(cursor, user, uom_by_id[uom],
quantity, default_uom[product], round=False,
context=context)
res[key] += uom_obj.compute_qty(uom_by_id[uom], quantity,
default_uom[product], round=False)
# Propagate quantities on from child locations to their parents
if with_childs:
# Fetch all child locations
all_location_ids = location_obj.search(
cursor, user, [('parent', 'child_of', location_ids)],
context=context)
locations = location_obj.browse(cursor, user, all_location_ids,
context=context)
all_location_ids = location_obj.search([
('parent', 'child_of', location_ids),
])
locations = location_obj.browse(all_location_ids)
# Generate a set of locations without childs and a dict
# giving the parent of each location.
leafs = set(all_location_ids)
@@ -492,9 +481,8 @@ class Product(ModelSQL, ModelView):
# Round quantities
for location, product in res:
key = (location, product)
res[key] = uom_obj.compute_qty(cursor, user, default_uom[product],
res[key], default_uom[product], round=True,
context=context)
res[key] = uom_obj.compute_qty(default_uom[product], res[key],
default_uom[product], round=True)
# Complete result with missing products if asked
if not skip_zero:
@@ -502,8 +490,7 @@ class Product(ModelSQL, ModelView):
if product_ids:
all_product_ids = product_ids
else:
all_product_ids = self.pool.get("product.product").search(
cursor, user, [], context=context)
all_product_ids = self.pool.get("product.product").search([])
keys = ((l,p) for l in location_ids for p in all_product_ids)
for location_id, product_id in keys:
if (location_id, product_id) not in res:
@@ -519,13 +506,11 @@ class Product(ModelSQL, ModelView):
return res
def view_header_get(self, cursor, user, value, view_type='form',
context=None):
if not context.get('locations'):
def view_header_get(self, value, view_type='form'):
if not Transaction().context.get('locations'):
return value
location_obj = self.pool.get('stock.location')
locations = location_obj.browse(cursor, user, context.get('locations'),
context=context)
locations = location_obj.browse(Transaction().context.get('locations'))
return value + " (" + ",".join(l.name for l in locations) + ")"
Product()
@@ -540,9 +525,9 @@ class ChooseStockDateInit(ModelView):
'* An empty value is an infinite date in the future.\n'\
'* A date in the past will provide historical values.')
def default_forecast_date(self, cursor, user, context=None):
def default_forecast_date(self):
date_obj = self.pool.get('ir.date')
return date_obj.today(cursor, user, context=context)
return date_obj.today()
ChooseStockDateInit()
@@ -569,20 +554,20 @@ class OpenLocation(Wizard):
},
}
def _action_open_location(self, cursor, user, data, context=None):
def _action_open_location(self, data):
model_data_obj = self.pool.get('ir.model.data')
act_window_obj = self.pool.get('ir.action.act_window')
act_window_id = model_data_obj.get_id(cursor, user, 'stock',
'act_location_quantity_tree', context=context)
res = act_window_obj.read(cursor, user, act_window_id, context=context)
act_window_id = model_data_obj.get_id('stock',
'act_location_quantity_tree')
res = act_window_obj.read(act_window_id)
ctx = {}
ctx['product'] = data['id']
context = {}
context['product'] = data['id']
if data['form']['forecast_date']:
ctx['stock_date_end'] = data['form']['forecast_date']
context['stock_date_end'] = data['form']['forecast_date']
else:
ctx['stock_date_end'] = datetime.date.max
res['pyson_context'] = PYSONEncoder().encode(ctx)
context['stock_date_end'] = datetime.date.max
res['pyson_context'] = PYSONEncoder().encode(context)
return res
+501 -583
View File
File diff suppressed because it is too large Load Diff