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

« back to all changes in this revision

Viewing changes to specific_rules/stock.py

  • Committer: Quentin THEURET
  • Date: 2016-03-04 11:30:53 UTC
  • Revision ID: qt@tempo-consulting.fr-20160304113053-abbygzrfvf0zpuxw
US-826 [FIX] Parent_id is an integer, not a many2one

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()