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):
175 res = super(product_product,self).fields_view_get(cr, uid, view_id, view_type, context, toolbar)
176 if ('location' in context) and context['location']:
177 location_info = self.pool.get('stock.location').browse(cr, uid, context['location'])
178 fields=res.get('fields',{})
180 if location_info.usage == 'supplier':
181 if fields.get('virtual_available'):
182 res['fields']['virtual_available']['string'] = _('Futur Receptions')
183 if fields.get('qty_available'):
184 res['fields']['qty_available']['string'] = _('Received Qty')
186 if location_info.usage == 'internal':
187 if fields.get('virtual_available'):
188 res['fields']['virtual_available']['string'] = _('Futur Stock')
190 if location_info.usage == 'customer':
191 if fields.get('virtual_available'):
192 res['fields']['virtual_available']['string'] = _('Futur Deliveries')
193 if fields.get('qty_available'):
194 res['fields']['qty_available']['string'] = _('Delivered Qty')
196 if location_info.usage == 'inventory':
197 if fields.get('virtual_available'):
198 res['fields']['virtual_available']['string'] = _('Futur P&L')
199 res['fields']['qty_available']['string'] = _('P&L Qty')
201 if location_info.usage == 'procurement':
202 if fields.get('virtual_available'):
203 res['fields']['virtual_available']['string'] = _('Futur Qty')
204 if fields.get('qty_available'):
205 res['fields']['qty_available']['string'] = _('Unplanned Qty')
207 if location_info.usage == 'production':
208 if fields.get('virtual_available'):
209 res['fields']['virtual_available']['string'] = _('Futur Productions')
210 if fields.get('qty_available'):
211 res['fields']['qty_available']['string'] = _('Produced Qty')
217 class product_template(osv.osv):
218 _name = 'product.template'
219 _inherit = 'product.template'
221 'property_stock_procurement': fields.property(
224 relation='stock.location',
225 string="Procurement 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 by procurements"),
229 'property_stock_production': fields.property(
232 relation='stock.location',
233 string="Production Location",
236 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"),
237 'property_stock_inventory': fields.property(
240 relation='stock.location',
241 string="Inventory Location",
244 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"),
245 'property_stock_account_input': fields.property('account.account',
246 type='many2one', relation='account.account',
247 string='Stock Input Account', method=True, view_load=True,
248 help='This account will be used, instead of the default one, to value input stock'),
249 'property_stock_account_output': fields.property('account.account',
250 type='many2one', relation='account.account',
251 string='Stock Output Account', method=True, view_load=True,
252 help='This account will be used, instead of the default one, to value output stock'),
258 class product_category(osv.osv):
259 _inherit = 'product.category'
261 'property_stock_journal': fields.property('account.journal',
262 relation='account.journal', type='many2one',
263 string='Stock journal', method=True, view_load=True,
264 help="This journal will be used for the accounting move generated by stock move"),
265 'property_stock_account_input_categ': fields.property('account.account',
266 type='many2one', relation='account.account',
267 string='Stock Input Account', method=True, view_load=True,
268 help='This account will be used to value the input stock'),
269 'property_stock_account_output_categ': fields.property('account.account',
270 type='many2one', relation='account.account',
271 string='Stock Output Account', method=True, view_load=True,
272 help='This account will be used to value the output stock'),
277 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: