1 # -*- encoding: utf-8 -*-
2 ##############################################################################
4 # OpenERP, Open Source Management Solution
5 # Copyright (C) 2004-2008 Tiny SPRL (<http://tiny.be>). All Rights Reserved
8 # This program is free software: you can redistribute it and/or modify
9 # it under the terms of the GNU General Public License as published by
10 # the Free Software Foundation, either version 3 of the License, or
11 # (at your option) any later version.
13 # This program is distributed in the hope that it will be useful,
14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 # GNU General Public License for more details.
18 # You should have received a copy of the GNU General Public License
19 # along with this program. If not, see <http://www.gnu.org/licenses/>.
21 ##############################################################################
23 from osv import fields, osv
24 from tools.translate import _
27 class product_product(osv.osv):
28 _inherit = "product.product"
29 def view_header_get(self, cr, user, view_id, view_type, context):
30 res = super(product_product, self).view_header_get(cr, user, view_id, view_type, context)
32 if (context.get('location', False)):
33 return _('Products: ')+self.pool.get('stock.location').browse(cr, user, context['location'], context).name
36 def get_product_available(self,cr,uid,ids,context=None):
39 states=context.get('states',[])
40 what=context.get('what',())
42 ids = self.search(cr, uid, [])
43 res = {}.fromkeys(ids, 0.0)
47 if context.get('shop', False):
48 cr.execute('select warehouse_id from sale_shop where id=%s', (int(context['shop']),))
51 context['warehouse'] = res2[0]
53 if context.get('warehouse', False):
54 cr.execute('select lot_stock_id from stock_warehouse where id=%s', (int(context['warehouse']),))
57 context['location'] = res2[0]
59 if context.get('location', False):
60 if type(context['location']) == type(1):
61 location_ids = [context['location']]
63 location_ids = context['location']
65 cr.execute("select lot_stock_id from stock_warehouse")
66 location_ids = [id for (id,) in cr.fetchall()]
68 # build the list of ids of children of the location given by id
69 child_location_ids = self.pool.get('stock.location').search(cr, uid, [('location_id', 'child_of', location_ids)])
70 location_ids= len(child_location_ids) and child_location_ids or location_ids
72 states_str = ','.join(map(lambda s: "'%s'" % s, states))
75 for product in self.browse(cr, uid, ids, context=context):
76 product2uom[product.id] = product.uom_id.id
78 prod_ids_str = ','.join(map(str, ids))
79 location_ids_str = ','.join(map(str, location_ids))
83 from_date=context.get('from_date',False)
84 to_date=context.get('to_date',False)
86 if from_date and to_date:
87 date_str="date_planned>='%s' and date_planned<='%s'"%(from_date,to_date)
89 date_str="date_planned>='%s'"%(from_date)
91 date_str="date_planned<='%s'"%(to_date)
94 # all moves from a location out of the set to a location in the set
96 'select sum(product_qty), product_id, product_uom '\
98 'where location_id not in ('+location_ids_str+') '\
99 'and location_dest_id in ('+location_ids_str+') '\
100 'and product_id in ('+prod_ids_str+') '\
101 'and state in ('+states_str+') '+ (date_str and 'and '+date_str+' ' or '') +''\
102 'group by product_id,product_uom'
104 results = cr.fetchall()
106 # all moves from a location in the set to a location out of the set
108 'select sum(product_qty), product_id, product_uom '\
110 'where location_id in ('+location_ids_str+') '\
111 'and location_dest_id not in ('+location_ids_str+') '\
112 'and product_id in ('+prod_ids_str+') '\
113 'and state in ('+states_str+') '+ (date_str and 'and '+date_str+' ' or '') + ''\
114 'group by product_id,product_uom'
116 results2 = cr.fetchall()
117 uom_obj = self.pool.get('product.uom')
118 for amount, prod_id, prod_uom in results:
119 amount = uom_obj._compute_qty(cr, uid, prod_uom, amount,
120 context.get('uom', False) or product2uom[prod_id])
121 res[prod_id] += amount
122 for amount, prod_id, prod_uom in results2:
123 amount = uom_obj._compute_qty(cr, uid, prod_uom, amount,
124 context.get('uom', False) or product2uom[prod_id])
125 res[prod_id] -= amount
128 def _product_available(self, cr, uid, ids, field_names=None, arg=False, context={}):
133 res[id] = {}.fromkeys(field_names, 0.0)
134 for f in field_names:
136 if f=='qty_available':
137 c.update({ 'states':('done',), 'what':('in', 'out') })
138 if f=='virtual_available':
139 c.update({ 'states':('confirmed','waiting','assigned','done'), 'what':('in', 'out') })
140 if f=='incoming_qty':
141 c.update({ 'states':('confirmed','waiting','assigned'), 'what':('in',) })
142 if f=='outgoing_qty':
143 c.update({ 'states':('confirmed','waiting','assigned'), 'what':('out',) })
144 stock=self.get_product_available(cr,uid,ids,context=c)
146 res[id][f] = stock.get(id, 0.0)
150 'qty_available': fields.function(_product_available, method=True, type='float', string='Real Stock', help="Current quantities of products in selected locations or all internal if none have been selected.", multi='qty_available'),
151 'virtual_available': fields.function(_product_available, method=True, type='float', string='Virtual Stock', help="Futur stock for this product according to the selected location or all internal if none have been selected. Computed as: Real Stock - Outgoing + Incoming.", multi='qty_available'),
152 'incoming_qty': fields.function(_product_available, method=True, type='float', string='Incoming', help="Quantities of products that are planned to arrive in selected locations or all internal if none have been selected.", multi='qty_available'),
153 'outgoing_qty': fields.function(_product_available, method=True, type='float', string='Outgoing', help="Quantities of products that are planned to leave in selected locations or all internal if none have been selected.", multi='qty_available'),
154 'track_production' : fields.boolean('Track Production Lots' , help="Force to use a Production Lot during production order"),
155 'track_incoming' : fields.boolean('Track Incomming Lots', help="Force to use a Production Lot during receptions"),
156 'track_outgoing' : fields.boolean('Track Outging Lots', help="Force to use a Production Lot during deliveries"),
158 def fields_view_get(self, cr, uid, view_id=None, view_type='form', context=None, toolbar=False):
159 res = super(product_product,self).fields_view_get(cr, uid, view_id, view_type, context, toolbar)
160 if ('location' in context) and context['location']:
161 location_info = self.pool.get('stock.location').browse(cr, uid, context['location'])
162 fields=res.get('fields',{})
164 if location_info.usage == 'supplier':
165 if fields.get('virtual_available'):
166 res['fields']['virtual_available']['string'] = _('Futur Receptions')
167 if fields.get('qty_available'):
168 res['fields']['qty_available']['string'] = _('Received Qty')
170 if location_info.usage == 'internal':
171 if fields.get('virtual_available'):
172 res['fields']['virtual_available']['string'] = _('Futur Stock')
174 if location_info.usage == 'customer':
175 if fields.get('virtual_available'):
176 res['fields']['virtual_available']['string'] = _('Futur Deliveries')
177 if fields.get('qty_available'):
178 res['fields']['qty_available']['string'] = _('Delivered Qty')
180 if location_info.usage == 'inventory':
181 if fields.get('virtual_available'):
182 res['fields']['virtual_available']['string'] = _('Futur P&L')
183 res['fields']['qty_available']['string'] = _('P&L Qty')
185 if location_info.usage == 'procurement':
186 if fields.get('virtual_available'):
187 res['fields']['virtual_available']['string'] = _('Futur Qty')
188 if fields.get('qty_available'):
189 res['fields']['qty_available']['string'] = _('Unplanned Qty')
191 if location_info.usage == 'production':
192 if fields.get('virtual_available'):
193 res['fields']['virtual_available']['string'] = _('Futur Productions')
194 if fields.get('qty_available'):
195 res['fields']['qty_available']['string'] = _('Produced Qty')
201 class product_template(osv.osv):
202 _name = 'product.template'
203 _inherit = 'product.template'
205 'property_stock_procurement': fields.property(
208 relation='stock.location',
209 string="Procurement Location",
212 help="For the current product (template), this stock location will be used, instead of the default one, as the source location for stock moves generated by procurements"),
213 'property_stock_production': fields.property(
216 relation='stock.location',
217 string="Production Location",
220 help="For the current product (template), this stock location will be used, instead of the default one, as the source location for stock moves generated by production orders"),
221 'property_stock_inventory': fields.property(
224 relation='stock.location',
225 string="Inventory Location",
228 help="For the current product (template), this stock location will be used, instead of the default one, as the source location for stock moves generated when you do an inventory"),
229 'property_stock_account_input': fields.property('account.account',
230 type='many2one', relation='account.account',
231 string='Stock Input Account', method=True, view_load=True,
232 help='This account will be used, instead of the default one, to value input stock'),
233 'property_stock_account_output': fields.property('account.account',
234 type='many2one', relation='account.account',
235 string='Stock Output Account', method=True, view_load=True,
236 help='This account will be used, instead of the default one, to value output stock'),
242 class product_category(osv.osv):
243 _inherit = 'product.category'
245 'property_stock_journal': fields.property('account.journal',
246 relation='account.journal', type='many2one',
247 string='Stock journal', method=True, view_load=True,
248 help="This journal will be used for the accounting move generated by stock move"),
249 'property_stock_account_input_categ': fields.property('account.account',
250 type='many2one', relation='account.account',
251 string='Stock Input Account', method=True, view_load=True,
252 help='This account will be used to value the input stock'),
253 'property_stock_account_output_categ': fields.property('account.account',
254 type='many2one', relation='account.account',
255 string='Stock Output Account', method=True, view_load=True,
256 help='This account will be used to value the output stock'),
261 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: