From e6c6b15a1d97ef16dfb0c09d332eb2fa9cb4ed67 Mon Sep 17 00:00:00 2001 From: Bertrand Chenal Date: Fri, 11 Jul 2008 23:20:17 +0200 Subject: [PATCH] Improved stock computation behaviour --- inventory.py | 6 +-- location.py | 29 ++++++------- location.xml | 4 +- product.py | 115 +++++++++++++++++++++++++++++++-------------------- product.xml | 4 +- 5 files changed, 93 insertions(+), 65 deletions(-) diff --git a/inventory.py b/inventory.py index c8d92d29b0..3ea8e3bc88 100644 --- a/inventory.py +++ b/inventory.py @@ -18,7 +18,7 @@ class Inventory(OSV): location = fields.Many2One( 'stock.location', 'Location', required=True, domain="[('type', '=', 'storage')]", states=STATES) - date = fields.Date('Date',) + date = fields.Date('Date') lost_found = fields.Many2One( 'stock.location', 'Lost and Found', required=True, domain="[('type', '=', 'lost_found')]", states=STATES) @@ -188,9 +188,9 @@ class CompleteInventory(Wizard): # Compute product quantities if inventory.date: context = context.copy() - context['forecast_date'] = inventory.date + context['stock_date'] = inventory.date pbl = product_obj.products_by_location( - cursor, user, [inventory.location.id], forecast=True, + cursor, user, [inventory.location.id], context=context) else: pbl = product_obj.products_by_location( diff --git a/location.py b/location.py index 9f4541f6ed..f5e7889d09 100644 --- a/location.py +++ b/location.py @@ -2,6 +2,7 @@ "Wharehouse" from trytond.osv import fields, OSV from trytond.wizard import Wizard, WizardOSV +import datetime STATES = { 'readonly': "not active", @@ -86,14 +87,13 @@ class Location(OSV): return dict([(i,0) for i in ids]) product_obj = self.pool.get('product.product') all_ids = self.search(cursor, user, [('parent', 'child_of', ids)]) - if name == 'forecast_quantity': - pbl = product_obj.products_by_location( - cursor, user, location_ids=all_ids, - product_ids=[context['product']], forecast=True, context=context) - else: - pbl = product_obj.products_by_location( - cursor, user, location_ids=all_ids, - product_ids=[context['product']], context=context) + if name != 'forecast_quantity' and context.get('stock_date'): + if context['stock_date'] != datetime.date.today(): + context = context.copy() + del context['stock_date'] + pbl = product_obj.products_by_location( + cursor, user, location_ids=all_ids, + product_ids=[context['product']], context=context) qty_by_ltn = dict([(i['location'], i['quantity']) for i in pbl]) to_compute = dict.fromkeys(all_ids, True) locations = self.browse(cursor, user, all_ids, context=context) @@ -141,14 +141,14 @@ class Party(OSV): Party() -class ChooseForecatsDateInit(WizardOSV): - _name = 'stock.location_forecast_date.init' +class ChooseStockDateInit(WizardOSV): + _name = 'stock.location_stock_date.init' forecast_date = fields.Date( 'Forecast Date', help='Allow to compute expected '\ 'stock quantities for this date.\n'\ '* An empty value is an infinite date in the future.\n'\ '* A date in the past will provide historical values.') -ChooseForecatsDateInit() +ChooseStockDateInit() class OpenProduct(Wizard): @@ -158,7 +158,7 @@ class OpenProduct(Wizard): 'init': { 'result': { 'type': 'form', - 'object': 'stock.location_forecast_date.init', + 'object': 'stock.location_stock_date.init', 'state': [ ('end', 'Cancel', 'tryton-cancel'), ('open', 'Open', 'tryton-ok', True), @@ -186,9 +186,10 @@ class OpenProduct(Wizard): context=context) res = act_window_obj.read(cursor, user, model_data.db_id, context=context) - context = {'locations': data['ids']} + if context == None: context = {} + context['locations'] = data['ids'] if data['form']['forecast_date']: - context['forecast_date'] = data['form']['forecast_date'] + context['stock_date'] = data['form']['forecast_date'] res['context'] = str(context) return res diff --git a/location.xml b/location.xml index 041c0d0469..9c2197372c 100644 --- a/location.xml +++ b/location.xml @@ -170,8 +170,8 @@ lost_found - - stock.location_forecast_date.init + + stock.location_stock_date.init form =", "<=", ">", "<", "!="): return False @@ -56,26 +55,32 @@ class Product(OSV): context=context) if name == 'forecast_quantity': pbl = self.products_by_location( - cursor, user, location_ids=location_ids, forecast=True, - context=context) + cursor, user, location_ids=location_ids, + skip_zero=False, context=context) for line in pbl: line['forecast_quantity'] = line['quantity'] del line['quantity'] else: + if context.get('stock_date'): + if context['stock_date'] != datetime.date.today(): + context = context.copy() + del context['stock_date'] pbl = self.products_by_location( - cursor, user, location_ids=location_ids, context=context) - + cursor, user, location_ids=location_ids, + skip_zero=False, context=context) res= [line['product'] for line in pbl \ if self._eval_domain(line, domain)] return [('id', 'in', res)] def raw_products_by_location(self, cursor, user, location_ids, - product_ids=None, forecast=False, context=None): + product_ids=None, context=None): """ Return a list like : [(location, product, uom, qty)] for each location and product given as argument. Null qty are not - returned and the tuple (location, product,uom) is unique. + returned and the tuple (location, product,uom) is unique. The + key stock_date in context can be used to compute stock for + other date than today. """ if not location_ids: return [] @@ -83,8 +88,8 @@ class Product(OSV): rule_obj = self.pool.get('ir.rule') move_query, move_val = rule_obj.domain_get(cursor, user, 'stock.move') - if forecast and not (context.get('forecast_date') and \ - context['forecast_date'] < datetime.date.today()): + if context.get('stock_date') and \ + context['stock_date'] > datetime.date.today(): in_states = ['done', 'assigned', 'draft'] out_states = ['done', 'assigned', 'draft'] else: @@ -121,15 +126,15 @@ class Product(OSV): ",".join(["%s" for i in product_ids]) + ")" where_vals += product_ids - if forecast and context.get('forecast_date'): - where_clause += ' AND ('\ - '(effective_date IS NULL '\ - 'AND ( planned_date <= %s or planned_date IS NULL)) '\ - 'OR effective_date <= %s'\ - ')' - where_date = [context['forecast_date'],context['forecast_date']] + where_clause += ' AND ('\ + '(effective_date IS NULL '\ + 'AND ( planned_date <= %s or planned_date IS NULL)) '\ + 'OR effective_date <= %s'\ + ')' + if context.get('stock_date'): + where_date = [context['stock_date'], context['stock_date']] else: - where_date = [] + where_date = [datetime.date.today(), datetime.date.today()] cursor.execute(select_clause % ( ",".join(["%s" for i in in_states]), where_clause, ",".join(["%s" for i in out_states]), where_clause), @@ -139,8 +144,14 @@ class Product(OSV): return cursor.fetchall() def products_by_location(self, cursor, user, location_ids, - product_ids=None, forecast=False, context=None): - + product_ids=None, skip_zero=True, context=None): + """ + Return a list like : [{location: 1, product: 1, uom: 1, qty: + 2}] for each location and product given as argument. The key + stock_date in context can be used to compute stock for other + date than today. If skip_zero, list item with quantity equal + to zero are not returned. + """ uom_obj = self.pool.get("product.uom") product_obj = self.pool.get("product.product") @@ -149,27 +160,42 @@ class Product(OSV): if not product_ids: product_ids = product_obj.search(cursor, user, [], context=context) + else: + product_ids = product_ids[:] uom_ids = uom_obj.search(cursor, user, [], context=context) uom_by_id = dict((x.id, x) for x in uom_obj.browse( cursor, user, uom_ids, context=context)) - default_uom = dict((x.id, x.default_uom) for x in product_obj.browse( + default_uom = dict((p.id, p.default_uom) for p in product_obj.browse( cursor, user, product_ids, context=context)) - res = {} + res_dict = {} for line in self.raw_products_by_location( - cursor, user, location_ids, product_ids, forecast=forecast, - context=context): + cursor, user, location_ids, product_ids, context=context): location, product, uom, quantity= line key = (location, product, default_uom[product].id) - res.setdefault(key, 0.0) - res[key] += uom_obj.compute_qty( - cursor, user, uom_by_id[uom], quantity, default_uom[product], - context=context) - return [{'location': key[0], - 'product':key[1], + res_dict.setdefault(key, 0.0) + res_dict[key] += uom_obj.compute_qty( + cursor, user, uom_by_id[uom], quantity, + default_uom[product], context=context) + # Remove seen products from product_ids + if key[1] in product_ids: + product_ids.remove(key[1]) + + res = [{'location': key[0], + 'product': key[1], 'uom': key[2], - 'quantity': val} for key, val in res.iteritems()] + 'quantity': val} for key, val in res_dict.iteritems()] + # Complete result with missing products if asked + if not skip_zero: + for location_id in location_ids: + for product_id in product_ids: + res.append({'location': location_id, + 'product': product_id, + 'uom': default_uom[product_id].id, + 'quantity': 0.0}) + + return res def view_header_get(self, cursor, user, value, view_type='form', context=None): @@ -183,14 +209,14 @@ class Product(OSV): Product() -class ChooseForecatsDateInit(WizardOSV): - _name = 'stock.product_forecast_date.init' +class ChooseStockDateInit(WizardOSV): + _name = 'stock.product_stock_date.init' forecast_date = fields.Date( 'Forecast Date', help='Allow to compute expected '\ 'stock quantities for this date.\n'\ '* An empty value is an infinite date in the future.\n'\ '* A date in the past will provide historical values.') -ChooseForecatsDateInit() +ChooseStockDateInit() class OpenLocation(Wizard): 'Products by Locations' @@ -199,7 +225,7 @@ class OpenLocation(Wizard): 'init': { 'result': { 'type': 'form', - 'object': 'stock.product_forecast_date.init', + 'object': 'stock.product_stock_date.init', 'state': [ ('end', 'Cancel', 'tryton-cancel'), ('open', 'Open', 'tryton-ok', True), @@ -227,9 +253,10 @@ class OpenLocation(Wizard): context=context) res = act_window_obj.read(cursor, user, model_data.db_id, context=context) - context = {'product': data['id']} + if context == None: context = {} + context['product'] = data['id'] if data['form']['forecast_date']: - context['forecast_date'] = data['form']['forecast_date'] + context['stock_date'] = data['form']['forecast_date'] res['context'] = str(context) return res diff --git a/product.xml b/product.xml index d878b62229..08cb3618d9 100644 --- a/product.xml +++ b/product.xml @@ -57,8 +57,8 @@ - - product_forecast_date.init + + product_stock_date.init form