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
25 class product_product(osv.osv):
26 _inherit = "product.product"
27 def view_header_get(self, cr, user, view_id, view_type, context):
28 res = super(product_product, self).view_header_get(cr, user, view_id, view_type, context)
30 if (context.get('location', False)):
31 return _('Products: ')+self.pool.get('stock.location').browse(cr, user, context['location'], context).name
34 def get_product_available(self,cr,uid,ids,context=None):
37 states=context.get('states',[])
38 what=context.get('what',())
40 ids = self.search(cr, uid, [])
41 res = {}.fromkeys(ids, 0.0)
45 if context.get('shop', False):
46 cr.execute('select warehouse_id from sale_shop where id=%d', (int(context['shop']),))
49 context['warehouse'] = res2[0]
51 if context.get('warehouse', False):
52 cr.execute('select lot_stock_id from stock_warehouse where id=%d', (int(context['warehouse']),))
55 context['location'] = res2[0]
57 if context.get('location', False):
58 if type(context['location']) == type(1):
59 location_ids = [context['location']]
61 location_ids = context['location']
63 cr.execute("select lot_stock_id from stock_warehouse")
64 location_ids = [id for (id,) in cr.fetchall()]
66 # build the list of ids of children of the location given by id
67 child_location_ids = self.pool.get('stock.location').search(cr, uid, [('location_id', 'child_of', location_ids)])
68 location_ids= len(child_location_ids) and child_location_ids or location_ids
70 states_str = ','.join(map(lambda s: "'%s'" % s, states))
73 for product in self.browse(cr, uid, ids, context=context):
74 product2uom[product.id] = product.uom_id.id
76 prod_ids_str = ','.join(map(str, ids))
77 location_ids_str = ','.join(map(str, location_ids))
81 from_date=context.get('from_date',False)
82 to_date=context.get('to_date',False)
84 if from_date and to_date:
85 date_str="date_planned>='%s' and date_planned<='%s'"%(from_date,to_date)
87 date_str="date_planned>='%s'"%(from_date)
89 date_str="date_planned<='%s'"%(to_date)
92 # all moves from a location out of the set to a location in the set
94 'select sum(product_qty), product_id, product_uom '\
96 'where location_id not in ('+location_ids_str+') '\
97 'and location_dest_id in ('+location_ids_str+') '\
98 'and product_id in ('+prod_ids_str+') '\
99 'and state in ('+states_str+') '+ (date_str and 'and '+date_str+' ' or '') +''\
100 'group by product_id,product_uom'
102 results = cr.fetchall()
104 # all moves from a location in the set to a location out of the set
106 'select sum(product_qty), product_id, product_uom '\
108 'where location_id in ('+location_ids_str+') '\
109 'and location_dest_id not in ('+location_ids_str+') '\
110 'and product_id in ('+prod_ids_str+') '\
111 'and state in ('+states_str+') '+ (date_str and 'and '+date_str+' ' or '') + ''\
112 'group by product_id,product_uom'
114 results2 = cr.fetchall()
115 uom_obj = self.pool.get('product.uom')
116 for amount, prod_id, prod_uom in results:
117 amount = uom_obj._compute_qty(cr, uid, prod_uom, amount,
118 context.get('uom', False) or product2uom[prod_id])
119 res[prod_id] += amount
120 for amount, prod_id, prod_uom in results2:
121 amount = uom_obj._compute_qty(cr, uid, prod_uom, amount,
122 context.get('uom', False) or product2uom[prod_id])
123 res[prod_id] -= amount
126 def _product_available(self, cr, uid, ids, field_names=None, arg=False, context={}):
131 res[id] = {}.fromkeys(field_names, 0.0)
132 for f in field_names:
134 if f=='qty_available':
135 c.update({ 'states':('done',), 'what':('in', 'out') })
136 if f=='virtual_available':
137 c.update({ 'states':('confirmed','waiting','assigned','done'), 'what':('in', 'out') })
138 if f=='incoming_qty':
139 c.update({ 'states':('confirmed','waiting','assigned'), 'what':('in',) })
140 if f=='outgoing_qty':
141 c.update({ 'states':('confirmed','waiting','assigned'), 'what':('out',) })
142 stock=self.get_product_available(cr,uid,ids,context=c)
144 res[id][f] = stock.get(id, 0.0)
148 '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'),
149 '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'),
150 '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'),
151 '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'),
152 'track_production' : fields.boolean('Track Production Lots' , help="Force to use a Production Lot during production order"),
153 'track_incoming' : fields.boolean('Track Incomming Lots', help="Force to use a Production Lot during receptions"),
154 'track_outgoing' : fields.boolean('Track Outging Lots', help="Force to use a Production Lot during deliveries"),
156 def fields_view_get(self, cr, uid, view_id=None, view_type='form', context=None, toolbar=False):
157 res = super(product_product,self).fields_view_get(cr, uid, view_id, view_type, context, toolbar)
158 if ('location' in context) and context['location']:
159 location_info = self.pool.get('stock.location').browse(cr, uid, context['location'])
160 fields=res.get('fields',{})
162 if location_info.usage == 'supplier':
163 if fields.get('virtual_available'):
164 res['fields']['virtual_available']['string'] = 'Futur Receptions'
165 if fields.get('qty_available'):
166 res['fields']['qty_available']['string'] = 'Received Qty'
168 if location_info.usage == 'internal':
169 if fields.get('virtual_available'):
170 res['fields']['virtual_available']['string'] = 'Futur Stock'
172 if location_info.usage == 'customer':
173 if fields.get('virtual_available'):
174 res['fields']['virtual_available']['string'] = 'Futur Deliveries'
175 if fields.get('qty_available'):
176 res['fields']['qty_available']['string'] = 'Delivered Qty'
178 if location_info.usage == 'inventory':
179 if fields.get('virtual_available'):
180 res['fields']['virtual_available']['string'] = 'Futur P&L'
181 res['fields']['qty_available']['string'] = 'P&L Qty'
183 if location_info.usage == 'procurement':
184 if fields.get('virtual_available'):
185 res['fields']['virtual_available']['string'] = 'Futur Qty'
186 if fields.get('qty_available'):
187 res['fields']['qty_available']['string'] = 'Unplanned Qty'
189 if location_info.usage == 'production':
190 if fields.get('virtual_available'):
191 res['fields']['virtual_available']['string'] = 'Futur Productions'
192 if fields.get('qty_available'):
193 res['fields']['qty_available']['string'] = 'Produced Qty'
199 class product_template(osv.osv):
200 _name = 'product.template'
201 _inherit = 'product.template'
203 'property_stock_procurement': fields.property(
206 relation='stock.location',
207 string="Procurement Location",
210 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"),
211 'property_stock_production': fields.property(
214 relation='stock.location',
215 string="Production 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 production orders"),
219 'property_stock_inventory': fields.property(
222 relation='stock.location',
223 string="Inventory 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 when you do an inventory"),
227 'property_stock_account_input': fields.property('account.account',
228 type='many2one', relation='account.account',
229 string='Stock Input Account', method=True, view_load=True,
230 help='This account will be used, instead of the default one, to value input stock'),
231 'property_stock_account_output': fields.property('account.account',
232 type='many2one', relation='account.account',
233 string='Stock Output Account', method=True, view_load=True,
234 help='This account will be used, instead of the default one, to value output stock'),
240 class product_category(osv.osv):
241 _inherit = 'product.category'
243 'property_stock_journal': fields.property('account.journal',
244 relation='account.journal', type='many2one',
245 string='Stock journal', method=True, view_load=True,
246 help="This journal will be used for the accounting move generated by stock move"),
247 'property_stock_account_input_categ': 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 to value the input stock'),
251 'property_stock_account_output_categ': 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 to value the output stock'),
259 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: