1 # -*- encoding: utf-8 -*-
2 ##############################################################################
4 # OpenERP, Open Source Management Solution
5 # Copyright (C) 2004-2009 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']
66 wids = self.pool.get('stock.warehouse').search(cr, uid, [], context=context)
67 for w in self.pool.get('stock.warehouse').browse(cr, uid, wids, context=context):
68 location_ids.append(w.lot_stock_id.id)
70 # build the list of ids of children of the location given by id
71 if context.get('compute_child',True):
72 child_location_ids = self.pool.get('stock.location').search(cr, uid, [('location_id', 'child_of', location_ids)])
73 location_ids= len(child_location_ids) and child_location_ids or location_ids
75 location_ids= location_ids
77 states_str = ','.join(map(lambda s: "'%s'" % s, states))
81 for product in self.browse(cr, uid, ids, context=context):
82 product2uom[product.id] = product.uom_id.id
83 uoms_o[product.uom_id.id] = product.uom_id
85 prod_ids_str = ','.join(map(str, ids))
86 location_ids_str = ','.join(map(str, location_ids))
90 from_date=context.get('from_date',False)
91 to_date=context.get('to_date',False)
93 if from_date and to_date:
94 date_str="date_planned>='%s' and date_planned<='%s'"%(from_date,to_date)
96 date_str="date_planned>='%s'"%(from_date)
98 date_str="date_planned<='%s'"%(to_date)
101 # all moves from a location out of the set to a location in the set
103 'select sum(product_qty), product_id, product_uom '\
105 'where location_id not in ('+location_ids_str+') '\
106 'and location_dest_id in ('+location_ids_str+') '\
107 'and product_id in ('+prod_ids_str+') '\
108 'and state in ('+states_str+') '+ (date_str and 'and '+date_str+' ' or '') +''\
109 'group by product_id,product_uom'
111 results = cr.fetchall()
113 # all moves from a location in the set to a location out of the set
115 'select sum(product_qty), product_id, product_uom '\
117 'where location_id in ('+location_ids_str+') '\
118 'and location_dest_id not in ('+location_ids_str+') '\
119 'and product_id in ('+prod_ids_str+') '\
120 'and state in ('+states_str+') '+ (date_str and 'and '+date_str+' ' or '') + ''\
121 'group by product_id,product_uom'
123 results2 = cr.fetchall()
124 uom_obj = self.pool.get('product.uom')
125 uoms = map(lambda x: x[2], results) + map(lambda x: x[2], results2)
126 if context.get('uom', False):
127 uoms += [context['uom']]
129 uoms = filter(lambda x: x not in uoms_o.keys(), uoms)
131 uoms = uom_obj.browse(cr, uid, list(set(uoms)), context=context)
134 for amount, prod_id, prod_uom in results:
135 amount = uom_obj._compute_qty_obj(cr, uid, uoms_o[prod_uom], amount,
136 uoms_o[context.get('uom', False) or product2uom[prod_id]])
137 res[prod_id] += amount
138 for amount, prod_id, prod_uom in results2:
139 amount = uom_obj._compute_qty_obj(cr, uid, uoms_o[prod_uom], amount,
140 uoms_o[context.get('uom', False) or product2uom[prod_id]])
141 res[prod_id] -= amount
144 def _product_available(self, cr, uid, ids, field_names=None, arg=False, context={}):
149 res[id] = {}.fromkeys(field_names, 0.0)
150 for f in field_names:
152 if f=='qty_available':
153 c.update({ 'states':('done',), 'what':('in', 'out') })
154 if f=='virtual_available':
155 c.update({ 'states':('confirmed','waiting','assigned','done'), 'what':('in', 'out') })
156 if f=='incoming_qty':
157 c.update({ 'states':('confirmed','waiting','assigned'), 'what':('in',) })
158 if f=='outgoing_qty':
159 c.update({ 'states':('confirmed','waiting','assigned'), 'what':('out',) })
160 stock=self.get_product_available(cr,uid,ids,context=c)
162 res[id][f] = stock.get(id, 0.0)
166 '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'),
167 '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'),
168 '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'),
169 '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'),
170 'track_production' : fields.boolean('Track Production Lots' , help="Force to use a Production Lot during production order"),
171 'track_incoming' : fields.boolean('Track Incomming Lots', help="Force to use a Production Lot during receptions"),
172 'track_outgoing' : fields.boolean('Track Outging Lots', help="Force to use a Production Lot during deliveries"),
174 def fields_view_get(self, cr, uid, view_id=None, view_type='form', context=None, toolbar=False):
177 res = super(product_product,self).fields_view_get(cr, uid, view_id, view_type, context, toolbar)
178 if context.get('location'):
179 location_info = self.pool.get('stock.location').browse(cr, uid, context['location'])
180 fields=res.get('fields',{})
182 if location_info.usage == 'supplier':
183 if fields.get('virtual_available'):
184 res['fields']['virtual_available']['string'] = _('Futur Receptions')
185 if fields.get('qty_available'):
186 res['fields']['qty_available']['string'] = _('Received Qty')
188 if location_info.usage == 'internal':
189 if fields.get('virtual_available'):
190 res['fields']['virtual_available']['string'] = _('Futur Stock')
192 if location_info.usage == 'customer':
193 if fields.get('virtual_available'):
194 res['fields']['virtual_available']['string'] = _('Futur Deliveries')
195 if fields.get('qty_available'):
196 res['fields']['qty_available']['string'] = _('Delivered Qty')
198 if location_info.usage == 'inventory':
199 if fields.get('virtual_available'):
200 res['fields']['virtual_available']['string'] = _('Futur P&L')
201 res['fields']['qty_available']['string'] = _('P&L Qty')
203 if location_info.usage == 'procurement':
204 if fields.get('virtual_available'):
205 res['fields']['virtual_available']['string'] = _('Futur Qty')
206 if fields.get('qty_available'):
207 res['fields']['qty_available']['string'] = _('Unplanned Qty')
209 if location_info.usage == 'production':
210 if fields.get('virtual_available'):
211 res['fields']['virtual_available']['string'] = _('Futur Productions')
212 if fields.get('qty_available'):
213 res['fields']['qty_available']['string'] = _('Produced Qty')
219 class product_template(osv.osv):
220 _name = 'product.template'
221 _inherit = 'product.template'
223 'property_stock_procurement': fields.property(
226 relation='stock.location',
227 string="Procurement Location",
230 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"),
231 'property_stock_production': fields.property(
234 relation='stock.location',
235 string="Production Location",
238 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"),
239 'property_stock_inventory': fields.property(
242 relation='stock.location',
243 string="Inventory Location",
246 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"),
247 'property_stock_account_input': fields.property('account.account',
248 type='many2one', relation='account.account',
249 string='Stock Input Account', method=True, view_load=True,
250 help='This account will be used, instead of the default one, to value input stock'),
251 'property_stock_account_output': fields.property('account.account',
252 type='many2one', relation='account.account',
253 string='Stock Output Account', method=True, view_load=True,
254 help='This account will be used, instead of the default one, to value output stock'),
260 class product_category(osv.osv):
261 _inherit = 'product.category'
263 'property_stock_journal': fields.property('account.journal',
264 relation='account.journal', type='many2one',
265 string='Stock journal', method=True, view_load=True,
266 help="This journal will be used for the accounting move generated by stock move"),
267 'property_stock_account_input_categ': fields.property('account.account',
268 type='many2one', relation='account.account',
269 string='Stock Input Account', method=True, view_load=True,
270 help='This account will be used to value the input stock'),
271 'property_stock_account_output_categ': fields.property('account.account',
272 type='many2one', relation='account.account',
273 string='Stock Output Account', method=True, view_load=True,
274 help='This account will be used to value the output stock'),
279 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: