1 # -*- encoding: utf-8 -*-
2 ##############################################################################
4 # Copyright (c) 2004-2008 TINY SPRL. (http://tiny.be) All Rights Reserved.
8 # WARNING: This program as such is intended to be used by professional
9 # programmers who take the whole responsability of assessing all potential
10 # consequences resulting from its eventual inadequacies and bugs
11 # End users who are looking for a ready-to-use solution with commercial
12 # garantees and support are strongly adviced to contract a Free Software
15 # This program is Free Software; you can redistribute it and/or
16 # modify it under the terms of the GNU General Public License
17 # as published by the Free Software Foundation; either version 2
18 # of the License, or (at your option) any later version.
20 # This program is distributed in the hope that it will be useful,
21 # but WITHOUT ANY WARRANTY; without even the implied warranty of
22 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 # GNU General Public License for more details.
25 # You should have received a copy of the GNU General Public License
26 # along with this program; if not, write to the Free Software
27 # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
29 ##############################################################################
31 from osv import fields, osv
33 class product_product(osv.osv):
34 _inherit = "product.product"
35 def view_header_get(self, cr, user, view_id, view_type, context):
36 res = super(product_product, self).view_header_get(cr, user, view_id, view_type, context)
38 if (context.get('location', False)):
39 return _('Products: ')+self.pool.get('stock.location').browse(cr, user, context['location'], context).name
42 def get_product_available(self,cr,uid,ids,context=None):
45 states=context.get('states',[])
46 what=context.get('what',())
48 ids = self.search(cr, uid, [])
49 res = {}.fromkeys(ids, 0.0)
53 if context.get('shop', False):
54 cr.execute('select warehouse_id from sale_shop where id=%d', (int(context['shop']),))
57 context['warehouse'] = res2[0]
59 if context.get('warehouse', False):
60 cr.execute('select lot_stock_id from stock_warehouse where id=%d', (int(context['warehouse']),))
63 context['location'] = res2[0]
65 if context.get('location', False):
66 if type(context['location']) == type(1):
67 location_ids = [context['location']]
69 location_ids = context['location']
71 cr.execute("select lot_stock_id from stock_warehouse")
72 location_ids = [id for (id,) in cr.fetchall()]
74 # build the list of ids of children of the location given by id
75 child_location_ids = self.pool.get('stock.location').search(cr, uid, [('location_id', 'child_of', location_ids)])
76 location_ids= len(child_location_ids) and child_location_ids or location_ids
78 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
84 prod_ids_str = ','.join(map(str, ids))
85 location_ids_str = ','.join(map(str, location_ids))
89 from_date=context.get('from_date',False)
90 to_date=context.get('to_date',False)
92 if from_date and to_date:
93 date_str="date_planned>='%s' and date_planned<='%s'"%(from_date,to_date)
95 date_str="date_planned>='%s'"%(from_date)
97 date_str="date_planned<='%s'"%(to_date)
100 # all moves from a location out of the set to a location in the set
102 'select sum(product_qty), product_id, product_uom '\
104 'where location_id not in ('+location_ids_str+') '\
105 'and location_dest_id in ('+location_ids_str+') '\
106 'and product_id in ('+prod_ids_str+') '\
107 'and state in ('+states_str+') '+ (date_str and 'and '+date_str+' ' or '') +''\
108 'group by product_id,product_uom'
110 results = cr.fetchall()
112 # all moves from a location in the set to a location out of the set
114 'select sum(product_qty), product_id, product_uom '\
116 'where location_id in ('+location_ids_str+') '\
117 'and location_dest_id not in ('+location_ids_str+') '\
118 'and product_id in ('+prod_ids_str+') '\
119 'and state in ('+states_str+') '+ (date_str and 'and '+date_str+' ' or '') + ''\
120 'group by product_id,product_uom'
122 results2 = cr.fetchall()
123 uom_obj = self.pool.get('product.uom')
124 for amount, prod_id, prod_uom in results:
125 amount = uom_obj._compute_qty(cr, uid, prod_uom, amount,
126 context.get('uom', False) or product2uom[prod_id])
127 res[prod_id] += amount
128 for amount, prod_id, prod_uom in results2:
129 amount = uom_obj._compute_qty(cr, uid, prod_uom, amount,
130 context.get('uom', False) or product2uom[prod_id])
131 res[prod_id] -= amount
134 def _product_available(self, cr, uid, ids, field_names=None, arg=False, context={}):
139 res[id] = {}.fromkeys(field_names, 0.0)
140 for f in field_names:
142 if f=='qty_available':
143 c.update({ 'states':('done',), 'what':('in', 'out') })
144 if f=='virtual_available':
145 c.update({ 'states':('confirmed','waiting','assigned','done'), 'what':('in', 'out') })
146 if f=='incoming_qty':
147 c.update({ 'states':('confirmed','waiting','assigned'), 'what':('in',) })
148 if f=='outgoing_qty':
149 c.update({ 'states':('confirmed','waiting','assigned'), 'what':('out',) })
150 stock=self.get_product_available(cr,uid,ids,context=c)
152 res[id][f] = stock.get(id, 0.0)
156 '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'),
157 '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'),
158 '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'),
159 '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'),
160 'track_production' : fields.boolean('Track Production Lots' , help="Force to use a Production Lot during production order"),
161 'track_incoming' : fields.boolean('Track Incomming Lots', help="Force to use a Production Lot during receptions"),
162 'track_outgoing' : fields.boolean('Track Outging Lots', help="Force to use a Production Lot during deliveries"),
164 def fields_view_get(self, cr, uid, view_id=None, view_type='form', context=None, toolbar=False):
165 res = super(product_product,self).fields_view_get(cr, uid, view_id, view_type, context, toolbar)
166 if ('location' in context) and context['location']:
167 location_info = self.pool.get('stock.location').browse(cr, uid, context['location'])
168 fields=res.get('fields',{})
170 if location_info.usage == 'supplier':
171 if fields.get('virtual_available'):
172 res['fields']['virtual_available']['string'] = 'Futur Receptions'
173 if fields.get('qty_available'):
174 res['fields']['qty_available']['string'] = 'Received Qty'
176 if location_info.usage == 'internal':
177 if fields.get('virtual_available'):
178 res['fields']['virtual_available']['string'] = 'Futur Stock'
180 if location_info.usage == 'customer':
181 if fields.get('virtual_available'):
182 res['fields']['virtual_available']['string'] = 'Futur Deliveries'
183 if fields.get('qty_available'):
184 res['fields']['qty_available']['string'] = 'Delivered Qty'
186 if location_info.usage == 'inventory':
187 if fields.get('virtual_available'):
188 res['fields']['virtual_available']['string'] = 'Futur P&L'
189 res['fields']['qty_available']['string'] = 'P&L Qty'
191 if location_info.usage == 'procurement':
192 if fields.get('virtual_available'):
193 res['fields']['virtual_available']['string'] = 'Futur Qty'
194 if fields.get('qty_available'):
195 res['fields']['qty_available']['string'] = 'Unplanned Qty'
197 if location_info.usage == 'production':
198 if fields.get('virtual_available'):
199 res['fields']['virtual_available']['string'] = 'Futur Productions'
200 if fields.get('qty_available'):
201 res['fields']['qty_available']['string'] = 'Produced Qty'
207 class product_template(osv.osv):
208 _name = 'product.template'
209 _inherit = 'product.template'
211 'property_stock_procurement': fields.property(
214 relation='stock.location',
215 string="Procurement Location",
218 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"),
219 'property_stock_production': fields.property(
222 relation='stock.location',
223 string="Production Location",
226 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"),
227 'property_stock_inventory': fields.property(
230 relation='stock.location',
231 string="Inventory Location",
234 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"),
235 'property_stock_account_input': fields.property('account.account',
236 type='many2one', relation='account.account',
237 string='Stock Input Account', method=True, view_load=True,
238 help='This account will be used, instead of the default one, to value input stock'),
239 'property_stock_account_output': fields.property('account.account',
240 type='many2one', relation='account.account',
241 string='Stock Output Account', method=True, view_load=True,
242 help='This account will be used, instead of the default one, to value output stock'),
248 class product_category(osv.osv):
249 _inherit = 'product.category'
251 'property_stock_journal': fields.property('account.journal',
252 relation='account.journal', type='many2one',
253 string='Stock journal', method=True, view_load=True,
254 help="This journal will be used for the accounting move generated by stock move"),
255 'property_stock_account_input_categ': fields.property('account.account',
256 type='many2one', relation='account.account',
257 string='Stock Input Account', method=True, view_load=True,
258 help='This account will be used to value the input stock'),
259 'property_stock_account_output_categ': fields.property('account.account',
260 type='many2one', relation='account.account',
261 string='Stock Output Account', method=True, view_load=True,
262 help='This account will be used to value the output stock'),
267 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: