~unifield-team/unifield-wm/us-826

« back to all changes in this revision

Viewing changes to specific_rules/stock.py

  • Committer: matthieu.choplin at msf
  • Date: 2012-08-30 07:48:00 UTC
  • mto: This revision was merged to the branch mainline in revision 1118.
  • Revision ID: matthieu.choplin@geneva.msf.org-20120830074800-l442bu42mt0yzutn
[uf-1374]- change the write and create by an _sql_constraint on the financing contract check dates

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# -*- coding: utf-8 -*-
2
 
##############################################################################
3
 
#
4
 
#    Copyright (C) 2011 MSF, TeMPO Consulting
5
 
#
6
 
#    This program is free software: you can redistribute it and/or modify
7
 
#    it under the terms of the GNU Affero General Public License as
8
 
#    published by the Free Software Foundation, either version 3 of the
9
 
#    License, or (at your option) any later version.
10
 
#
11
 
#    This program is distributed in the hope that it will be useful,
12
 
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
 
#    GNU Affero General Public License for more details.
15
 
#
16
 
#    You should have received a copy of the GNU Affero General Public License
17
 
#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
18
 
#
19
 
##############################################################################
20
 
 
21
 
from osv import osv
22
 
from osv import fields
23
 
 
24
 
from tools.translate import _
25
 
import decimal_precision as dp
26
 
 
27
 
import time
28
 
 
29
 
class initial_stock_inventory(osv.osv):
30
 
    _name = 'initial.stock.inventory'
31
 
    _description = "Initial Stock Inventory"
32
 
    _inherit = 'stock.inventory'
33
 
    
34
 
    def unlink(self, cr, uid, ids, context=None):
35
 
        '''
36
 
        Prevent the deletion of a non-draft/cancel initial inventory
37
 
        '''
38
 
        for inv in self.browse(cr, uid, ids, context=context):
39
 
            if inv.state not in ('draft', 'cancel'):
40
 
                raise osv.except_osv(_('Error'), _('You cannot remove an initial inventory which is done'))
41
 
            
42
 
        return super(initial_stock_inventory, self).unlink(cr, uid, ids, context=context)
43
 
    
44
 
    _columns = {
45
 
        'inventory_line_id': fields.one2many('initial.stock.inventory.line', 'inventory_id', string='Inventory lines'),
46
 
        'move_ids': fields.many2many('stock.move', 'initial_stock_inventory_move_rel', 'inventory_id', 'move_id', 'Created Moves'),
47
 
        'sublist_id': fields.many2one('product.list', string='List/Sublist', ondelete='set null'),
48
 
        'nomen_manda_0': fields.many2one('product.nomenclature', 'Main Type', ondelete='set null'),
49
 
        'nomen_manda_1': fields.many2one('product.nomenclature', 'Group', ondelete='set null'),
50
 
        'nomen_manda_2': fields.many2one('product.nomenclature', 'Family', ondelete='set null'),
51
 
        'nomen_manda_3': fields.many2one('product.nomenclature', 'Root', ondelete='set null'),
52
 
    }
53
 
    
54
 
    def _inventory_line_hook(self, cr, uid, inventory_line, move_vals):
55
 
        '''
56
 
        Add the price in the stock move
57
 
        '''
58
 
        move_vals['price_unit'] = inventory_line.average_cost
59
 
        return super(initial_stock_inventory, self)._inventory_line_hook(cr, uid, inventory_line, move_vals)
60
 
    
61
 
    def action_confirm(self, cr, uid, ids, context=None):
62
 
        '''
63
 
        Override the action_confirm method to check the batch mgmt/perishable data
64
 
        '''
65
 
        if isinstance(ids, (int, long)):
66
 
            ids = [ids]
67
 
        
68
 
        product_dict = {}
69
 
        prodlot_obj = self.pool.get('stock.production.lot')
70
 
        product_obj = self.pool.get('product.product')
71
 
 
72
 
        self.check_integrity(cr, uid, ids, context=context)
73
 
        
74
 
        for inventory in self.browse(cr, uid, ids, context=context):
75
 
            # Prevent confirmation with no lines
76
 
            if len(inventory.inventory_line_id) == 0:
77
 
                raise osv.except_osv(_('Error'), _('Please enter at least one line in stock inventory before confirm it.'))
78
 
 
79
 
            for inventory_line in inventory.inventory_line_id:
80
 
                if inventory_line.product_id:
81
 
                    # Check product constrainsts
82
 
                    product_obj._get_restriction_error(cr, uid, [inventory_line.product_id.id], {'location_id': inventory_line.location_id.id}, context=context)
83
 
 
84
 
                # Don't check integrity on line with no quantity
85
 
                if inventory_line.product_qty == 0.0:
86
 
                    inventory_line.write({'dont_move': True})
87
 
                    continue
88
 
 
89
 
                # Check if there is two lines with the same product and with difference average cost
90
 
                if inventory_line.product_id.id not in product_dict:
91
 
                    product_dict.update({inventory_line.product_id.id: inventory_line.average_cost})
92
 
                elif product_dict[inventory_line.product_id.id] != inventory_line.average_cost:
93
 
                    raise osv.except_osv(_('Error'), _('You cannot have two lines for the product %s with different average cost.') % product_obj.name_get(cr, uid, [inventory_line.product_id.id], context=context)[0][1])
94
 
                
95
 
                # Returns error if the line is batch mandatory or perishable without prodlot
96
 
                if inventory_line.product_id.batch_management and not inventory_line.prodlot_name:
97
 
                    raise osv.except_osv(_('Error'), _('You must assign a Batch Number on the product %s.') % product_obj.name_get(cr, uid, [inventory_line.product_id.id])[0][1])
98
 
                elif inventory_line.product_id.perishable and not inventory_line.expiry_date:
99
 
                    raise osv.except_osv(_('Error'), _('You must assign an Expiry Date on the product %s.') % product_obj.name_get(cr, uid, [inventory_line.product_id.id])[0][1])
100
 
                        
101
 
                if inventory_line.product_id.batch_management:
102
 
                    # if no production lot, we create a new one
103
 
                    prodlot_ids = prodlot_obj.search(cr, uid, [('name', '=', inventory_line.prodlot_name),
104
 
                                                               ('type', '=', 'standard'),
105
 
                                                               ('product_id', '=', inventory_line.product_id.id)], context=context)
106
 
                    if prodlot_ids:
107
 
                        # Prevent creation of two batch with the same name/product but different expiry date
108
 
                        prodlot = prodlot_obj.browse(cr, uid, prodlot_ids[0])
109
 
                        if prodlot.life_date != inventory_line.expiry_date:
110
 
                            life_date = self.pool.get('date.tools').get_date_formatted(cr, uid, datetime=prodlot.life_date)
111
 
                            raise osv.except_osv(_('Error'), _('The batch number \'%s\' is already in the system but its expiry date is %s') % (prodlot.name, life_date))
112
 
                    prodlot_id = prodlot_ids and prodlot_ids[0] or False
113
 
                    # no prodlot, create a new one
114
 
                    if not prodlot_ids:
115
 
                        prodlot_id = prodlot_obj.create(cr, uid, {'name': inventory_line.prodlot_name,
116
 
                                                                  'type': 'standard',
117
 
                                                                  'life_date': inventory_line.expiry_date,
118
 
                                                                  'product_id': inventory_line.product_id.id}, context=context)
119
 
 
120
 
                    self.pool.get('initial.stock.inventory.line').write(cr, uid, [inventory_line.id], {'prod_lot_id': prodlot_id}, context=context)
121
 
 
122
 
                if inventory_line.product_id.perishable and not inventory_line.product_id.batch_management:
123
 
                    if not inventory_line.prodlot_name and inventory_line.expiry_date:
124
 
                        prodlot_ids = prodlot_obj.search(cr, uid, [
125
 
                            ('type', '=', 'internal'),
126
 
                            ('product_id', '=', inventory_line.product_id.id),
127
 
                            ('life_date', '=', inventory_line.expiry_date),
128
 
                        ], context=context)
129
 
                        if prodlot_ids:
130
 
                            self.pool.get('initial.stock.inventory.line').write(cr, uid, [inventory_line.id], {
131
 
                                'prodlot_name': prodlot_obj.read(cr, uid, prodlot_ids[0], ['name'], context=context)['name'],
132
 
                            }, context=context)
133
 
        
134
 
        return super(initial_stock_inventory, self).action_confirm(cr, uid, ids, context=context)
135
 
    
136
 
    def action_done(self, cr, uid, ids, context=None):
137
 
        """ Finish the inventory
138
 
        @return: True
139
 
        """
140
 
        if context is None:
141
 
            context = {}
142
 
            
143
 
        if isinstance(ids, (int, long)):
144
 
            ids = [ids]
145
 
 
146
 
        self.check_integrity(cr, uid, ids, context=context)
147
 
        
148
 
        move_obj = self.pool.get('stock.move')
149
 
        prod_obj = self.pool.get('product.product')
150
 
        sptc_obj = self.pool.get('standard.price.track.changes')
151
 
        for inv in self.browse(cr, uid, ids, context=context):
152
 
            # Set the cost price on product form with the new value, and process the stock move
153
 
            for move in inv.move_ids:
154
 
                new_std_price = move.price_unit
155
 
                sptc_obj.track_change(cr,
156
 
                                      uid,
157
 
                                      move.product_id.id,
158
 
                                      _('Initial stock inventory %s') % inv.name,
159
 
                                      vals={
160
 
                                          'standard_price': new_std_price,
161
 
                                          'old_price': move.product_id.standard_price,
162
 
                                      },
163
 
                                      context=context)
164
 
                prod_obj.write(cr, uid, move.product_id.id, {'standard_price': new_std_price}, context=context)
165
 
                move_obj.action_done(cr, uid, move.id, context=context)
166
 
 
167
 
            self.write(cr, uid, [inv.id], {'state':'done', 'date_done': time.strftime('%Y-%m-%d %H:%M:%S')}, context=context)
168
 
 
169
 
            self.infolog(cr, uid, 'The Initial stock inventory id:%s has been validated' % inv.id)
170
 
 
171
 
        return True
172
 
    
173
 
    def fill_lines(self, cr, uid, ids, context=None):
174
 
        '''
175
 
        Fill all lines according to defined nomenclature level and sublist
176
 
        '''
177
 
        if context is None:
178
 
            context = {}
179
 
            
180
 
        if isinstance(ids, (int, long)):
181
 
            ids = [ids]
182
 
            
183
 
        location_id = False
184
 
        wh_ids = self.pool.get('stock.warehouse').search(cr, uid, [])
185
 
        if wh_ids:
186
 
            location_id = self.pool.get('stock.warehouse').browse(cr, uid, wh_ids[0]).lot_stock_id.id
187
 
        for inventory in self.browse(cr, uid, ids, context=context):
188
 
            product_ids = []
189
 
            products = []
190
 
 
191
 
            nom = False
192
 
            # Get all products for the defined nomenclature
193
 
            if inventory.nomen_manda_3:
194
 
                nom = inventory.nomen_manda_3.id
195
 
                field = 'nomen_manda_3'
196
 
            elif inventory.nomen_manda_2:
197
 
                nom = inventory.nomen_manda_2.id
198
 
                field = 'nomen_manda_2'
199
 
            elif inventory.nomen_manda_1:
200
 
                nom = inventory.nomen_manda_1.id
201
 
                field = 'nomen_manda_1'
202
 
            elif inventory.nomen_manda_0:
203
 
                nom = inventory.nomen_manda_0.id
204
 
                field = 'nomen_manda_0'
205
 
            if nom:
206
 
                product_ids.extend(self.pool.get('product.product').search(cr, uid, [(field, '=', nom)], context=context))
207
 
 
208
 
            # Get all products for the defined list
209
 
            if inventory.sublist_id:
210
 
                for line in inventory.sublist_id.product_ids:
211
 
                    product_ids.append(line.name.id)
212
 
 
213
 
            # Check if products in already existing lines are in domain
214
 
            products = []
215
 
            for line in inventory.inventory_line_id:
216
 
                if line.product_id.id in product_ids:
217
 
                    products.append(line.product_id.id)
218
 
                else:
219
 
                    self.pool.get('initial.stock.inventory.line').unlink(cr, uid, line.id, context=context)
220
 
 
221
 
            c = context.copy()
222
 
            c.update({'location': location_id, 'compute_child': False, 'to_date': inventory.date})
223
 
            for product in self.pool.get('product.product').browse(cr, uid, product_ids, context=c):
224
 
                # Check if the product is not already on the report
225
 
                if product.type not in ('consu', 'service', 'service_recep') and product.id not in products:
226
 
                    batch_mandatory = product.batch_management
227
 
                    date_mandatory = product.perishable
228
 
                    values = {'product_id': product.id,
229
 
                              'uom_id': product.uom_id.id,
230
 
                              'product_qty': 0.00,
231
 
                              'average_cost': product.standard_price,
232
 
                              'hidden_batch_management_mandatory': batch_mandatory,
233
 
                              'hidden_perishable_mandatory': date_mandatory,
234
 
                              'inventory_id': inventory.id, }
235
 
                    v = self.pool.get('initial.stock.inventory.line').on_change_product_id(cr, uid, [], location_id, product.id, product.uom_id.id, False)['value']
236
 
                    # Remove product_qty from values because it has been computed before
237
 
                    v.pop('product_qty')
238
 
                    values.update(v)
239
 
                    if batch_mandatory:
240
 
                        values.update({'err_msg': 'You must assign a batch number'})
241
 
                    if date_mandatory:
242
 
                        values.update({'err_msg': 'You must assign an expiry date'})
243
 
                    self.pool.get('initial.stock.inventory.line').create(cr, uid, values)
244
 
        
245
 
        return {'type': 'ir.actions.act_window',
246
 
                'res_model': 'initial.stock.inventory',
247
 
                'view_type': 'form',
248
 
                'view_mode': 'form,tree',
249
 
                'res_id': ids[0],
250
 
                'target': 'dummy',
251
 
                'context': context}
252
 
        
253
 
    def get_nomen(self, cr, uid, id, field):
254
 
        return self.pool.get('product.nomenclature').get_nomen(cr, uid, self, id, field, context={'withnum': 1})
255
 
 
256
 
    def onChangeSearchNomenclature(self, cr, uid, id, position, type, nomen_manda_0, nomen_manda_1, nomen_manda_2, nomen_manda_3, num=True, context=None):
257
 
        return self.pool.get('product.product').onChangeSearchNomenclature(cr, uid, 0, position, type, nomen_manda_0, nomen_manda_1, nomen_manda_2, nomen_manda_3, False, context={'withnum': 1})
258
 
    
259
 
initial_stock_inventory()
260
 
 
261
 
 
262
 
class initial_stock_inventory_line(osv.osv):
263
 
    _name = 'initial.stock.inventory.line'
264
 
    _description = "Initial Stock Inventory Line"
265
 
    _inherit = 'stock.inventory.line'
266
 
    
267
 
    def _get_error_msg(self, cr, uid, ids, field_name, args, context=None):
268
 
        prodlot_obj = self.pool.get('stock.production.lot')
269
 
        dt_obj = self.pool.get('date.tools')
270
 
        res = {}
271
 
        
272
 
        for line in self.browse(cr, uid, ids, context=context):
273
 
            res[line.id] = ''
274
 
            if not line.location_id:
275
 
                res[line.id] = _('You must define a stock location')
276
 
            if line.hidden_batch_management_mandatory and not line.prodlot_name:
277
 
                res[line.id] = _('You must define a batch number')
278
 
            elif line.hidden_perishable_mandatory and not line.expiry_date:
279
 
                res[line.id] = _('You must define an expiry date')
280
 
            elif line.prodlot_name and line.expiry_date and line.product_id:
281
 
                prodlot_ids = prodlot_obj.search(cr, uid, [
282
 
                    ('name', '=', line.prodlot_name),
283
 
                    ('product_id', '=', line.product_id.id),
284
 
                ], context=context)
285
 
                if prodlot_ids:
286
 
                    prodlot = prodlot_obj.browse(cr, uid, prodlot_ids[0], context=context)
287
 
                    life_date = dt_obj.get_date_formatted(cr, uid, datetime=prodlot.life_date)
288
 
                    if prodlot.life_date != line.expiry_date:
289
 
                        res[line.id] = _('The batch number \'%s\' is already in the system but its expiry date is %s') % (line.prodlot_name, life_date)
290
 
 
291
 
        return res
292
 
 
293
 
    def _get_bm_perishable(self, cr, uid, ids, field_name, args, context=None):
294
 
        res = {}
295
 
 
296
 
        for line in self.browse(cr, uid, ids, context=context):
297
 
            res[line.id] = {
298
 
                'hidden_batch_management_mandatory': line.product_id.batch_management,
299
 
                'hidden_perishable_mandatory': line.product_id.perishable,
300
 
            }
301
 
 
302
 
        return res
303
 
 
304
 
    def _get_products(self, cr, uid, ids, context=None):
305
 
        inv_ids = self.pool.get('initial.stock.inventory').search(cr, uid, [
306
 
            ('state', 'not in', ['done', 'cancel']),
307
 
        ], context=context)
308
 
        return self.pool.get('initial.stock.inventory.line').search(cr, uid, [
309
 
            ('inventory_id', 'in', inv_ids),
310
 
            ('product_id', 'in', ids),
311
 
        ], context=context)
312
 
    
313
 
    _columns = {
314
 
        'inventory_id': fields.many2one('initial.stock.inventory', string='Inventory', ondelete='cascade'),
315
 
        'prodlot_name': fields.char(size=64, string='Batch'),
316
 
        'average_cost': fields.float(string='Initial average cost', digits_compute=dp.get_precision('Sale Price Computation'), required=True),
317
 
        'currency_id': fields.many2one('res.currency', string='Functional currency', readonly=True),
318
 
        'err_msg': fields.function(_get_error_msg, method=True, type='char', string='Message', store=False),
319
 
        'hidden_perishable_mandatory': fields.function(
320
 
            _get_bm_perishable,
321
 
            type='boolean',
322
 
            method=True,
323
 
            string='Hidden Flag for Perishable product',
324
 
            multi='bm_perishable',
325
 
            store={
326
 
                'initial.stock.inventory.line': (lambda self, cr, uid, ids, c=None: ids, ['product_id'], 10),
327
 
                'product.product': (_get_products, ['perishable'], 20),
328
 
            },
329
 
        ),
330
 
        'hidden_batch_management_mandatory': fields.function(
331
 
            _get_bm_perishable,
332
 
            type='boolean',
333
 
            method=True,
334
 
            string='Hidden Flag for Perishable product',
335
 
            multi='bm_perishable',
336
 
            store={
337
 
                'initial.stock.inventory.line': (lambda self, cr, uid, ids, c=None: ids, ['product_id'], 10),
338
 
                'product.product': (_get_products, ['batch_management'], 20),
339
 
            },
340
 
        ),
341
 
    }
342
 
    
343
 
    _defaults = {
344
 
        'currency_id': lambda obj, cr, uid, c: obj.pool.get('res.users').browse(cr, uid, uid).company_id.currency_id.id,
345
 
        'average_cost': lambda *a: 0.00,
346
 
        'product_qty': lambda *a: 0.00,
347
 
        'reason_type_id': lambda obj, cr, uid, c: obj.pool.get('ir.model.data').get_object_reference(cr, uid, 'reason_types_moves', 'reason_type_stock_initialization')[1],
348
 
    }
349
 
    
350
 
    def _check_batch_management(self, cr, uid, ids, context=None):
351
 
        '''
352
 
        check for batch management
353
 
        '''
354
 
        for obj in self.browse(cr, uid, ids, context=context):
355
 
            if obj.product_id.batch_management and obj.inventory_id.state not in ('draft', 'cancel'):
356
 
                if not obj.prod_lot_id or obj.prod_lot_id.type != 'standard':
357
 
                    return False
358
 
        return True
359
 
    
360
 
    def _check_perishable(self, cr, uid, ids, context=None):
361
 
        """
362
 
        check for perishable ONLY
363
 
        """
364
 
        for obj in self.browse(cr, uid, ids, context=context):
365
 
            if obj.product_id.perishable and not obj.product_id.batch_management and obj.inventory_id.state not in ('draft', 'cancel'):
366
 
                if (not obj.prod_lot_id and not obj.expiry_date) or (obj.prod_lot_id and obj.prod_lot_id.type != 'internal'):
367
 
                    return False
368
 
        return True
369
 
    
370
 
    def _check_prodlot_need(self, cr, uid, ids, context=None):
371
 
        """
372
 
        If the inv line has a prodlot but does not need one, return False.
373
 
        """
374
 
        for obj in self.browse(cr, uid, ids, context=context):
375
 
            if obj.prod_lot_id and obj.inventory_id.state not in ('draft', 'cancel'):
376
 
                if not obj.product_id.perishable and not obj.product_id.batch_management:
377
 
                    return False
378
 
        return True
379
 
    
380
 
    def _check_same_cost(self, cr, uid, ids, context=None):
381
 
        '''
382
 
        If the inv line has a different average cost than the other lines with the same product
383
 
        '''
384
 
        for obj in self.browse(cr, uid, ids, context=context):
385
 
            other_lines = self.search(cr, uid, [('product_qty', '!=', 0.00), ('inventory_id', '=', obj.inventory_id.id), ('product_id', '=', obj.product_id.id)], context=context)
386
 
            if other_lines and obj.product_qty != 0.00:
387
 
                cost = self.browse(cr, uid, other_lines[0], context=context).average_cost
388
 
                if cost != obj.average_cost:
389
 
                    raise osv.except_osv(_('Error'), _('You cannot have two lines with the product %s and different average cost.') % self.pool.get('product.product').name_get(cr, uid, [obj.product_id.id])[0][1])
390
 
                    return False
391
 
                
392
 
        return True
393
 
    
394
 
    _constraints = [(_check_batch_management,
395
 
                 'You must assign a Batch Number which corresponds to Batch Number Mandatory Products.',
396
 
                 ['prod_lot_id']),
397
 
                (_check_perishable,
398
 
                 'You must assign a Batch Numbre which corresponds to Expiry Date Mandatory Products.',
399
 
                 ['prod_lot_id']),
400
 
                (_check_prodlot_need,
401
 
                 'The selected product is neither Batch Number Mandatory nor Expiry Date Mandatory',
402
 
                 ['prod_lot_id']),
403
 
                (_check_same_cost,
404
 
                 'You cannot have two lines with the same product and different average cost.',
405
 
                 ['product_id', 'average_cost'])
406
 
                ]
407
 
    
408
 
    def product_change(self, cr, uid, ids, product_id, location_id, field_change, change_price=False, prodlot_id=False):
409
 
        '''
410
 
        Set the UoM with the default UoM of the product
411
 
        '''
412
 
        value = {'product_uom': False,
413
 
                 'hidden_perishable_mandatory': False,
414
 
                 'hidden_batch_management_mandatory': False,}
415
 
        
416
 
        if product_id:
417
 
            product_obj = self.pool.get('product.product')
418
 
            context = {}
419
 
            if location_id:
420
 
                context = {'location': location_id, 'compute_child': False}
421
 
                # Test the compatibility of the product with the location
422
 
                value, test = product_obj._on_change_restriction_error(cr, uid, product_id, field_name=field_change, values=value, vals={'location_id': location_id})
423
 
                if test:
424
 
                    return value
425
 
            if prodlot_id:
426
 
                context.update({'prodlot_id': prodlot_id})
427
 
            product = product_obj.browse(cr, uid, product_id, context=context)
428
 
            value.update({'product_uom': product.uom_id.id,
429
 
                          'hidden_perishable_mandatory': product.perishable,
430
 
                          'hidden_batch_management_mandatory': product.batch_management})
431
 
            if change_price:
432
 
                value.update({'average_cost': product.standard_price,})
433
 
 
434
 
            # Don't recompute the product qty according to batch because no selection of batch
435
 
#            if location_id:
436
 
#                value.update({'product_qty': product.qty_available})
437
 
            
438
 
        return {'value': value}
439
 
    
440
 
    def change_lot(self, cr, uid, ids, location_id, product, prod_lot_id, uom=False, to_date=False,):
441
 
        res = super(initial_stock_inventory_line, self).change_lot(cr, uid, ids, location_id, product, prod_lot_id, uom=uom, to_date=to_date)
442
 
        if 'warning' not in res:
443
 
            if 'value' not in res:
444
 
                res.update({'value': {}})
445
 
                
446
 
            res['value'].update({'err_msg': ''})
447
 
        
448
 
        return res
449
 
    
450
 
    def change_expiry(self, cr, uid, id, expiry_date, product_id, type_check, context=None):
451
 
        res = super(initial_stock_inventory_line, self).change_expiry(cr, uid, id, expiry_date, product_id, type_check, context=None)
452
 
        if 'warning' not in res:
453
 
            if 'value' not in res:
454
 
                res.udptae({'value': {}})
455
 
                
456
 
            res['value'].update({'err_msg': ''})
457
 
        
458
 
        return res
459
 
        
460
 
    def create(self, cr, uid, vals, context=None):
461
 
        '''
462
 
        Set the UoM with the default UoM of the product
463
 
        '''
464
 
        if vals.get('product_id', False):
465
 
            product = self.pool.get('product.product').browse(cr, uid, vals['product_id'], context=context)
466
 
            vals['product_uom'] = product.uom_id.id
467
 
        
468
 
        return super(initial_stock_inventory_line, self).create(cr, uid, vals, context=context)
469
 
    
470
 
    def write(self, cr, uid, ids, vals, context=None):
471
 
        '''
472
 
        Set the UoM with the default UoM of the product
473
 
        '''
474
 
        if vals.get('product_id', False):
475
 
            vals['product_uom'] = self.pool.get('product.product').browse(cr, uid, vals['product_id'], context=context).uom_id.id
476
 
            
477
 
        return super(initial_stock_inventory_line, self).write(cr, uid, ids, vals, context=context)
478
 
    
479
 
initial_stock_inventory_line()
480
 
 
481
 
class stock_cost_reevaluation(osv.osv):
482
 
    _name = 'stock.cost.reevaluation'
483
 
    _description = 'Cost reevaluation'
484
 
    
485
 
    _columns = {
486
 
        'name': fields.char(size=64, string='Reference', required=True, readonly=True, states={'draft': [('readonly', False)]}),
487
 
        'date': fields.date(string='Creation date', required=True, readonly=True, states={'draft': [('readonly', False)]}),
488
 
        'reevaluation_line_ids': fields.one2many('stock.cost.reevaluation.line', 'reevaluation_id', string='Lines',
489
 
                                                 readonly=True, states={'draft': [('readonly', False)]}),
490
 
        'state': fields.selection([('draft', 'Draft'), ('confirm', 'Confirmed'), ('done', 'Done'), ('cancel', 'Cancel')],
491
 
                                  string='State', readonly=True, required=True),
492
 
        'sublist_id': fields.many2one('product.list', string='List/Sublist', ondelete='set null'),
493
 
        'nomen_manda_0': fields.many2one('product.nomenclature', 'Main Type', ondelete='set null'),
494
 
        'nomen_manda_1': fields.many2one('product.nomenclature', 'Group', ondelete='set null'),
495
 
        'nomen_manda_2': fields.many2one('product.nomenclature', 'Family', ondelete='set null'),
496
 
        'nomen_manda_3': fields.many2one('product.nomenclature', 'Root', ondelete='set null'),
497
 
    }
498
 
    
499
 
    _defaults = {
500
 
        'state': lambda *a: 'draft',
501
 
        'date': lambda *a: time.strftime('%Y-%m-%d'),
502
 
    }
503
 
 
504
 
    _sql_constraints = [
505
 
        ('name_unique', "unique(name)", 'The Reference of the Product Cost Revaluation must be unique'),
506
 
    ]
507
 
 
508
 
    def copy(self, cr, uid, ids, default=None, context=None):
509
 
        '''
510
 
        Set the state to 'draft' and the creation date to the current date
511
 
        '''
512
 
        if not default:
513
 
            default = {}
514
 
        name = self.read(cr, uid, ids, ['name'])['name']
515
 
        i = 1
516
 
        new_name = '%s (copy %s)' % (name, i)
517
 
        while self.search_count(cr, uid, [('name', '=', new_name)]):
518
 
            i += 1
519
 
            new_name = '%s (copy %s)' % (name, i)
520
 
 
521
 
        if not 'state' in default:
522
 
            default.update({'state': 'draft'})
523
 
 
524
 
        default.update({'date': time.strftime('%Y-%m-%d'),
525
 
                        'name': new_name})
526
 
            
527
 
        return super(stock_cost_reevaluation, self).copy(cr, uid, ids, default=default, context=context)
528
 
 
529
 
    def action_confirm(self, cr, uid, ids, context=None):
530
 
        '''
531
 
        Confirm the cost reevaluation (don't change the price at this time)
532
 
        '''
533
 
        if isinstance(ids, (int, long)):
534
 
            ids = [ids]
535
 
        
536
 
        for obj in self.browse(cr, uid, ids, context=context):
537
 
            # Prevent confirmation without lines
538
 
            if len(obj.reevaluation_line_ids) == 0:
539
 
                raise osv.except_osv(_('Error'), _('Please enter at least one revaluation line before confirm it.'))
540
 
        
541
 
            # Check if there are two lines with the same product
542
 
            products = []
543
 
            for line in obj.reevaluation_line_ids:
544
 
                if line.product_id.id not in products:
545
 
                    products.append(line.product_id.id)
546
 
                else:
547
 
                    raise osv.except_osv(_('Error'), _('You cannot have two lines with the same product. (Product : [%s] %s)') % (line.product_id.default_code, line.product_id.name))
548
 
        
549
 
        return self.write(cr, uid, ids, {'state': 'confirm'}, context=context)
550
 
        
551
 
    def action_done(self, cr, uid, ids, context=None):
552
 
        '''
553
 
        Change the price of the products in the lines
554
 
        '''
555
 
        sptc_obj = self.pool.get('standard.price.track.changes')
556
 
 
557
 
        if isinstance(ids, (int, long)):
558
 
            ids = [ids]
559
 
        
560
 
        for obj in self.browse(cr, uid, ids, context=context):
561
 
            for line in obj.reevaluation_line_ids:
562
 
                sptc_obj.track_change(cr,
563
 
                                      uid,
564
 
                                      line.product_id.id,
565
 
                                      _('Product cost reevaluation %s') % obj.name,
566
 
                                      vals={
567
 
                                          'standard_price': line.average_cost,
568
 
                                          'old_price': line.product_id.standard_price,
569
 
                                      }, context=context)
570
 
                self.pool.get('product.product').write(cr, uid, line.product_id.id, {'standard_price': line.average_cost})
571
 
        
572
 
        return self.write(cr, uid, ids, {'state': 'done'}, context=context)
573
 
    
574
 
    def action_cancel(self, cr, uid, ids, context=None):
575
 
        '''
576
 
        Change the state of the document to cancel
577
 
        '''
578
 
        if isinstance(ids, (int, long)):
579
 
            ids = [ids]
580
 
            
581
 
        return self.write(cr, uid, ids, {'state': 'cancel'}, context=context)
582
 
    
583
 
    def action_cancel_draft(self, cr, uid, ids, context=None):
584
 
        '''
585
 
        Change the state of the document to draft
586
 
        '''
587
 
        if isinstance(ids, (int, long)):
588
 
            ids = [ids]
589
 
        
590
 
        return self.write(cr, uid, ids, {'state': 'draft'}, context=context)
591
 
    
592
 
    def fill_lines(self, cr, uid, ids, context=None):
593
 
        '''
594
 
        Fill all lines according to defined nomenclature level and sublist
595
 
        '''
596
 
        if context is None:
597
 
            context = {}
598
 
            
599
 
        if isinstance(ids, (int, long)):
600
 
            ids = [ids]
601
 
            
602
 
        for inventory in self.browse(cr, uid, ids, context=context):
603
 
            product_ids = []
604
 
            products = []
605
 
 
606
 
            nom = False
607
 
            # Get all products for the defined nomenclature
608
 
            if inventory.nomen_manda_3:
609
 
                nom = inventory.nomen_manda_3.id
610
 
                field = 'nomen_manda_3'
611
 
            elif inventory.nomen_manda_2:
612
 
                nom = inventory.nomen_manda_2.id
613
 
                field = 'nomen_manda_2'
614
 
            elif inventory.nomen_manda_1:
615
 
                nom = inventory.nomen_manda_1.id
616
 
                field = 'nomen_manda_1'
617
 
            elif inventory.nomen_manda_0:
618
 
                nom = inventory.nomen_manda_0.id
619
 
                field = 'nomen_manda_0'
620
 
            if nom:
621
 
                product_ids.extend(self.pool.get('product.product').search(cr, uid, [(field, '=', nom)], context=context))
622
 
 
623
 
            # Get all products for the defined list
624
 
            if inventory.sublist_id:
625
 
                for line in inventory.sublist_id.product_ids:
626
 
                    product_ids.append(line.name.id)
627
 
 
628
 
            # Check if products in already existing lines are in domain
629
 
            products = []
630
 
            for line in inventory.reevaluation_line_ids:
631
 
                if line.product_id.id in product_ids:
632
 
                    products.append(line.product_id.id)
633
 
                else:
634
 
                    self.pool.get('stock.cost.reevaluation.line').unlink(cr, uid, line.id, context=context)
635
 
 
636
 
            for product in self.pool.get('product.product').browse(cr, uid, product_ids):
637
 
                # Check if the product is not already on the report
638
 
                if product.id not in products:
639
 
                    values = {'product_id': product.id,
640
 
                              'average_cost': product.standard_price,
641
 
                              'reevaluation_id': inventory.id, }
642
 
                    self.pool.get('stock.cost.reevaluation.line').create(cr, uid, values)
643
 
        
644
 
        return {'type': 'ir.actions.act_window',
645
 
                'res_model': 'stock.cost.reevaluation',
646
 
                'view_type': 'form',
647
 
                'view_mode': 'form,tree',
648
 
                'res_id': ids[0],
649
 
                'target': 'dummy',
650
 
                'context': context}
651
 
        
652
 
    def get_nomen(self, cr, uid, id, field):
653
 
        return self.pool.get('product.nomenclature').get_nomen(cr, uid, self, id, field, context={'withnum': 1})
654
 
 
655
 
    def onChangeSearchNomenclature(self, cr, uid, id, position, type, nomen_manda_0, nomen_manda_1, nomen_manda_2, nomen_manda_3, num=True, context=None):
656
 
        return self.pool.get('product.product').onChangeSearchNomenclature(cr, uid, 0, position, type, nomen_manda_0, nomen_manda_1, nomen_manda_2, nomen_manda_3, False, context={'withnum': 1})
657
 
    
658
 
stock_cost_reevaluation()
659
 
 
660
 
class stock_cost_reevaluation_line(osv.osv):
661
 
    _name = 'stock.cost.reevaluation.line'
662
 
    _description = 'Cost reevaluation line'
663
 
    _rec_name = 'product_id'
664
 
    
665
 
    _columns = {
666
 
        'product_id': fields.many2one('product.product', string='Product', required=True),
667
 
        'average_cost': fields.float(string='Average cost', digits_compute=dp.get_precision('Sale Price Computation'), required=True),
668
 
        'currency_id': fields.many2one('res.currency', string='Currency', readonly=True),
669
 
        'reevaluation_id': fields.many2one('stock.cost.reevaluation', string='Header'),
670
 
    }
671
 
    
672
 
    _defaults = {
673
 
        'currency_id': lambda obj, cr, uid, c = {}: obj.pool.get('res.users').browse(cr, uid, uid).company_id.currency_id.id,
674
 
    }
675
 
    
676
 
    def product_id_change(self, cr, uid, ids, product_id, context=None):
677
 
        '''
678
 
        Change the average price with the cost price of the product
679
 
        '''
680
 
        if product_id:
681
 
            cost_price = self.pool.get('product.product').browse(cr, uid, product_id, context=context).standard_price
682
 
            return {'value': {'average_cost': cost_price}}
683
 
        
684
 
        return {'value': {'average_cost': 0.00}}
685
 
    
686
 
stock_cost_reevaluation_line()
687
 
 
688
 
class stock_move(osv.osv):
689
 
    _inherit = 'stock.move'
690
 
 
691
 
    _columns = {
692
 
        'init_inv_ids': fields.many2many('initial.stock.inventory', 'initial_stock_inventory_move_rel', 'move_id', 'inventory_id', 'Created Moves'),
693
 
    }
694
 
 
695
 
stock_move()