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

« back to all changes in this revision

Viewing changes to kit/wizard/kit_selection_sale.py

  • Committer: duy.vo at msf
  • Date: 2012-09-18 21:05:00 UTC
  • mfrom: (1175 unifield-wm)
  • mto: This revision was merged to the branch mainline in revision 1188.
  • Revision ID: duy.vo@geneva.msf.org-20120918210500-qw8i815ns8xtk3bv
UF-1055: Merged with trunk 1175

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# -*- coding: utf-8 -*-
 
2
##############################################################################
 
3
#
 
4
#    OpenERP, Open Source Management Solution
 
5
#    Copyright (C) Copyright (C) 2011 MSF, TeMPO Consulting.
 
6
#
 
7
#    This program is free software: you can redistribute it and/or modify
 
8
#    it under the terms of the GNU Affero General Public License as
 
9
#    published by the Free Software Foundation, either version 3 of the
 
10
#    License, or (at your option) any later version.
 
11
#
 
12
#    This program is distributed in the hope that it will be useful,
 
13
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
 
14
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
15
#    GNU Affero General Public License for more details.
 
16
#
 
17
#    You should have received a copy of the GNU Affero General Public License
 
18
#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
19
#
 
20
##############################################################################
 
21
 
 
22
from osv import fields, osv
 
23
from tools.translate import _
 
24
import decimal_precision as dp
 
25
 
 
26
import netsvc
 
27
 
 
28
from msf_outgoing import INTEGRITY_STATUS_SELECTION
 
29
 
 
30
class kit_selection_sale(osv.osv_memory):
 
31
    '''
 
32
    kit selection
 
33
    '''
 
34
    _name = "kit.selection.sale"
 
35
    
 
36
    _columns = {'product_id_kit_selection_sale': fields.many2one('product.product', string='Kit Product', readonly=True),
 
37
                'kit_id_kit_selection_sale': fields.many2one('composition.kit', string='Theoretical Kit'),
 
38
                'order_line_id_kit_selection_sale': fields.many2one('sale.order.line', string='Sale Order Line', readonly=True, required=True),
 
39
                # one2many
 
40
                'product_ids_kit_selection_sale': fields.one2many('kit.selection.sale.line', 'wizard_id_kit_selection_sale_line', string='Replacement Products'),
 
41
                # related fields
 
42
                'partner_id_kit_selection_sale': fields.related('order_line_id_kit_selection_sale', 'order_id', 'partner_id', string='Partner', type='many2one', relation='res.partner', readonly=True, store=False),
 
43
                'pricelist_id_kit_selection_sale': fields.related('order_line_id_kit_selection_sale', 'order_id', 'pricelist_id', string='Currency', type='many2one', relation='product.pricelist', readonly=True, store=False),
 
44
                'warehouse_id_kit_selection_sale': fields.related('order_line_id_kit_selection_sale', 'order_id', 'warehouse_id', string='Warehouse', type='many2one', relation='stock.warehouse', readonly=True, store=False),
 
45
                }
 
46
    
 
47
    _defaults = {'product_id_kit_selection_sale': lambda s, cr, uid, c: c.get('product_id', False),
 
48
                 'order_line_id_kit_selection_sale': lambda s, cr, uid, c: c.get('active_ids') and c.get('active_ids')[0] or False,
 
49
                 }
 
50
    
 
51
    def import_items(self, cr, uid, ids, context=None):
 
52
        '''
 
53
        import lines into product_ids_kit_selection_sale
 
54
        '''
 
55
        # objects
 
56
        line_obj = self.pool.get('kit.selection.sale.line')
 
57
        # purchase order line id
 
58
        sol_ids = context['active_ids']
 
59
        
 
60
        for obj in self.browse(cr, uid, ids, context=context):
 
61
            if not obj.kit_id_kit_selection_sale:
 
62
                raise osv.except_osv(_('Warning !'), _('A theoretical version should be selected.'))
 
63
            if obj.kit_id_kit_selection_sale.state != 'completed':
 
64
                raise osv.except_osv(_('Warning !'), _('The theoretical version must be completed.'))
 
65
            for item in obj.kit_id_kit_selection_sale.composition_item_ids:
 
66
                values = {'order_line_id_kit_selection_sale_line': obj.order_line_id_kit_selection_sale.id,
 
67
                          'wizard_id_kit_selection_sale_line': obj.id,
 
68
                          'product_id_kit_selection_sale_line': item.item_product_id.id,
 
69
                          'qty_kit_selection_sale_line': item.item_qty,
 
70
                          'uom_id_kit_selection_sale_line': item.item_uom_id.id,
 
71
                          }
 
72
                line_obj.create(cr, uid, values, context=dict(context, sol_ids=context['active_ids']))
 
73
        return self.pool.get('wizard').open_wizard(cr, uid, sol_ids, type='update', context=context)
 
74
    
 
75
    def validate_lines(self, cr, uid, ids, context=None):
 
76
        '''
 
77
        validate the lines
 
78
        
 
79
        - qty > 0.0 (must_be_greater_than_0)
 
80
        - unit price > 0.0
 
81
        
 
82
        return True or False
 
83
        '''
 
84
        # objects
 
85
        prod_obj = self.pool.get('product.product')
 
86
        lot_obj = self.pool.get('stock.production.lot')
 
87
        # errors
 
88
        errors = {'must_be_greater_than_0': False,
 
89
                  'price_must_be_greater_than_0': False,
 
90
                  }
 
91
        for obj in self.browse(cr, uid, ids, context=context):
 
92
            for item in obj.product_ids_kit_selection_sale:
 
93
                # reset the integrity status
 
94
                item.write({'integrity_status': 'empty'}, context=context)
 
95
                # qty
 
96
                if item.qty_kit_selection_sale_line <= 0.0:
 
97
                    # qty is needed
 
98
                    errors.update(must_be_greater_than_0=True)
 
99
                    item.write({'integrity_status': 'must_be_greater_than_0'}, context=context)
 
100
                # unit price
 
101
                if item.price_unit_kit_selection_sale_line <= 0.0:
 
102
                    # unit price is needed
 
103
                    errors.update(price_must_be_greater_than_0=True)
 
104
                    item.write({'integrity_status': 'price_must_be_greater_than_0'}, context=context)
 
105
        # check the encountered errors
 
106
        return all([not x for x in errors.values()])
 
107
 
 
108
    def do_de_kitting(self, cr, uid, ids, context=None):
 
109
        '''
 
110
        create a sale order line for each kit item and delete the selected kit purchase order line
 
111
        '''
 
112
        # quick integrity check
 
113
        assert context, 'No context defined, problem on method call'
 
114
        if isinstance(ids, (int, long)):
 
115
            ids = [ids]
 
116
        # objects
 
117
        so_obj = self.pool.get('sale.order')
 
118
        sol_obj = self.pool.get('sale.order.line')
 
119
        # id of corresponding sale order line
 
120
        sol_ids = context['active_ids']
 
121
        sol_id = context['active_ids'][0]
 
122
        sol = sol_obj.browse(cr, uid, sol_id, context=context)
 
123
        if sol.order_id.state not in ['draft', 'validated']:
 
124
            raise osv.except_osv(_('Warning !'), _('Sale order line kit replacement with components function is only available for Draft and Validated states.'))
 
125
        # integrity constraint
 
126
        integrity_check = self.validate_lines(cr, uid, ids, context=context)
 
127
        if not integrity_check:
 
128
            # the windows must be updated to trigger tree colors
 
129
            return self.pool.get('wizard').open_wizard(cr, uid, sol_ids, type='update', context=context)
 
130
        # process
 
131
        ctx_keep_info = context.copy()
 
132
        ctx_keep_info['keepDateAndDistrib'] = True
 
133
        for obj in self.browse(cr, uid, ids, context=context):
 
134
            if not len(obj.product_ids_kit_selection_sale):
 
135
                raise osv.except_osv(_('Warning !'), _('Replacement Items must be selected.'))
 
136
            # to keep a link to previous line (for copy) and as a flag to write in the first loop
 
137
            last_line_id = False
 
138
            # for each item from the product_ids_kit_selection_sale
 
139
            for item_v in obj.product_ids_kit_selection_sale:
 
140
                # price unit is mandatory
 
141
                if item_v.price_unit_kit_selection_sale_line <= 0.0:
 
142
                    raise osv.except_osv(_('Warning !'), _('Unit Price must be specified for each line.'))
 
143
                # selected product_id
 
144
                product_id = item_v.product_id_kit_selection_sale_line.id
 
145
                # selected qty
 
146
                qty = item_v.qty_kit_selection_sale_line
 
147
                if qty <= 0.0:
 
148
                    raise osv.except_osv(_('Warning !'), _('Quantity must be greater than 0.0.'))
 
149
                # selected uom
 
150
                uom_id = item_v.uom_id_kit_selection_sale_line.id
 
151
                # price unit
 
152
                price_unit = item_v.price_unit_kit_selection_sale_line
 
153
                # call purchase order line on change function
 
154
                data = self.pool.get('kit.selection.sale.line')._call_sol_on_change(cr, uid, ids, product_id, qty, uom_id, price_unit,
 
155
                                                                               type='product_id_change',
 
156
                                                                               context=dict(context, sol_ids=context['active_ids']))
 
157
                # common dictionary of data
 
158
                values = {'product_id': product_id,
 
159
                          'price_unit': price_unit,
 
160
                          'product_uom': uom_id,
 
161
                          'product_uom_qty': sol.product_uom_qty*qty,
 
162
                          'product_uos': uom_id,
 
163
                          'product_uos_qty': sol.product_uos_qty*qty,
 
164
                          'default_code': data['value']['default_code'],
 
165
                          'name': data['value']['name'],
 
166
                          'default_name': data['value']['default_name'],
 
167
                          }
 
168
                
 
169
                # following new sequencing policy, we check if resequencing occur (behavior 1).
 
170
                # if not (behavior 2), the split line keeps the same line number as original line
 
171
                if not sol_obj.allow_resequencing(cr, uid, [obj.order_line_id_kit_selection_sale.id], context=context):
 
172
                    # set default value for line_number as the same as original line
 
173
                    values.update({'line_number': obj.order_line_id_kit_selection_sale.line_number})
 
174
                
 
175
                if last_line_id:
 
176
                    # the existing purchase order line has already been updated, we create a new one
 
177
                    # copy the original purchase order line
 
178
                    last_line_id = sol_obj.copy(cr, uid, last_line_id, values, context=ctx_keep_info)
 
179
                    # as so *line* state is draft anyhow, we do not need to process the created line
 
180
                else:
 
181
                    # first item to be treated, we update the existing line
 
182
                    last_line_id = obj.order_line_id_kit_selection_sale.id
 
183
                    sol_obj.write(cr, uid, [last_line_id], values, context=context)
 
184
                
 
185
        return {'type': 'ir.actions.act_window_close'}
 
186
    
 
187
kit_selection_sale()
 
188
 
 
189
 
 
190
class kit_selection_sale_line(osv.osv_memory):
 
191
    '''
 
192
    substitute items
 
193
    '''
 
194
    _name = 'kit.selection.sale.line'
 
195
    
 
196
    def create(self, cr, uid, vals, context=None):
 
197
        '''
 
198
        default price unit from sol on_change function
 
199
        '''
 
200
        # objects
 
201
        sol_obj = self.pool.get('sale.order.line')
 
202
        # id of corresponding sale order line
 
203
        sol_id = context.get('active_ids', False) and context['active_ids'][0] or False
 
204
        if sol_id and ('price_unit_kit_selection_sale_line' not in vals or vals.get('price_unit_kit_selection_sale_line') == 0.0):
 
205
            sol = sol_obj.browse(cr, uid, sol_id, context=context)
 
206
            # selected product_id
 
207
            product_id = vals.get('product_id_kit_selection_sale_line', False)
 
208
            # selected qty
 
209
            qty = vals.get('qty_kit_selection_sale_line', 0.0)
 
210
            # selected uom
 
211
            uom_id = vals.get('uom_id_kit_selection_sale_line', False)
 
212
            # price unit
 
213
            price_unit = vals.get('price_unit_kit_selection_sale_line', 0.0)
 
214
            # gather default values
 
215
            data = self._call_sol_on_change(cr, uid, context['active_ids'],
 
216
                                            product_id, qty, uom_id, price_unit, type='product_id_change',
 
217
                                            context=context)
 
218
            
 
219
            # update price_unit value
 
220
            vals.update({'price_unit_kit_selection_sale_line': data['value']['price_unit']})
 
221
        return super(kit_selection_sale_line, self).create(cr, uid, vals, context=context)
 
222
    
 
223
    def _call_sol_on_change(self, cr, uid, ids, product_id, qty, uom_id, price_unit, type, context=None):
 
224
        '''
 
225
        core function from sale order line
 
226
        
 
227
        def product_id_change(self, cr, uid, ids, pricelist, product, qty=0,
 
228
            uom=False, qty_uos=0, uos=False, name='', partner_id=False,
 
229
            lang=False, update_tax=True, date_order=False, packaging=False, fiscal_position=False, flag=False):
 
230
        '''
 
231
        # quick integrity check
 
232
        assert context, 'No context defined, problem on method call'
 
233
        if isinstance(ids, (int, long)):
 
234
            ids = [ids]
 
235
            
 
236
        # objects
 
237
        sol_obj = self.pool.get('sale.order.line')
 
238
        prod_obj = self.pool.get('product.product')
 
239
        # id of corresponding purchase order line
 
240
        sol_id = context['sol_ids'][0]
 
241
        sol = sol_obj.browse(cr, uid, sol_id, context=context)
 
242
        # pricelist from purchase order
 
243
        pricelist_id = sol.order_id.pricelist_id.id
 
244
        # partner_id from purchase order
 
245
        partner_id = sol.order_id.partner_id.id
 
246
        # date_order from purchase order
 
247
        date_order = sol.order_id.date_order
 
248
        # fiscal_position from purchase order
 
249
        fiscal_position_id = sol.order_id.fiscal_position.id
 
250
        # date_planned from purchase order line
 
251
        date_planned = sol.date_planned
 
252
        # name
 
253
        name = False
 
254
        if product_id:
 
255
            name = prod_obj.read(cr, uid, product_id, ['name'], context=context)['name']
 
256
        # notes
 
257
        notes = sol.notes
 
258
        # state
 
259
        state = sol.order_id.state
 
260
        # gather default values
 
261
        if type == 'product_uom_change':
 
262
            data = getattr(sol_obj, type)(cr, uid, ids, pricelist=pricelist_id, product=product_id, qty=qty,
 
263
                                          uom=uom_id, qty_uos=qty, uos=uom_id, name=name, partner_id=partner_id,
 
264
                                          lang='lang' in context and context['lang'], update_tax=False, date_order=date_order)
 
265
        elif type == 'product_id_change':
 
266
            data = getattr(sol_obj, type)(cr, uid, ids, pricelist=pricelist_id, product=product_id, qty=qty,
 
267
                                             uom=uom_id, qty_uos=qty, uos=uom_id, name=name, partner_id=partner_id,
 
268
                                             lang='lang' in context and context['lang'], update_tax=True, date_order=date_order, packaging=False, fiscal_position=fiscal_position_id, flag=False)
 
269
        return data
 
270
    
 
271
    def on_product_id_change(self, cr, uid, ids, product_id, qty, uom_id, price_unit, context=None):
 
272
        '''
 
273
        core function from purchase order line
 
274
        
 
275
        def product_id_change(self, cr, uid, ids, pricelist, product, qty, uom,
 
276
            partner_id, date_order=False, fiscal_position=False, date_planned=False,
 
277
            name=False, price_unit=False, notes=False):
 
278
        '''
 
279
        # quick integrity check
 
280
        assert context, 'No context defined, problem on method call'
 
281
        if isinstance(ids, (int, long)):
 
282
            ids = [ids]
 
283
            
 
284
        # product change, we reset everything
 
285
        result = {'value': {'uom_id_kit_selection_sale_line': False,
 
286
                            'price_unit_kit_selection_sale_line': 0.0,
 
287
                            'qty_kit_selection_sale_line': 0.0}}
 
288
        # gather default values
 
289
#        data = self._call_sol_on_change(cr, uid, ids, product_id, qty, uom_id, price_unit, 'product_id_change', context=context)
 
290
        data = self._call_sol_on_change(cr, uid, ids, product_id, qty, uom_id, price_unit, type='product_id_change', context=context)
 
291
        
 
292
        # update result price_unit and default uom
 
293
        if 'price_unit' in data['value']:
 
294
            result['value'].update({'price_unit_kit_selection_sale_line': data['value']['price_unit']})
 
295
        if 'product_qty' in data['value']:
 
296
            result['value'].update({'qty_kit_selection_sale_line': data['value']['product_qty']})
 
297
        if 'product_uom' in data['value']:
 
298
            result['value'].update({'uom_id_kit_selection_sale_line': data['value']['product_uom']})
 
299
            
 
300
        return result
 
301
    
 
302
    def on_uom_id_change(self, cr, uid, ids, product_id, qty, uom_id, price_unit, context=None):
 
303
        '''
 
304
        core function from purchase order line
 
305
        
 
306
        def product_uom_change(self, cursor, user, ids, pricelist, product, qty=0,
 
307
            uom=False, qty_uos=0, uos=False, name='', partner_id=False,
 
308
            lang=False, update_tax=True, date_order=False):
 
309
        '''
 
310
        # quick integrity check
 
311
        assert context, 'No context defined, problem on method call'
 
312
        if isinstance(ids, (int, long)):
 
313
            ids = [ids]
 
314
            
 
315
        # uom change - we reset the qty and price
 
316
        result = {'value': {'qty_kit_selection_sale_line': 0.0,
 
317
                            'price_unit_kit_selection_sale_line': 0.0}}
 
318
        # gather default values
 
319
        data = self._call_sol_on_change(cr, uid, ids, product_id, qty, uom_id, price_unit, type='product_uom_change', context=context)
 
320
        # update result price_unit and default uom
 
321
        if 'price_unit' in data['value']:
 
322
            result['value'].update({'price_unit_kit_selection_sale_line': data['value']['price_unit']})
 
323
        if 'product_qty' in data['value']:
 
324
            result['value'].update({'qty_kit_selection_sale_line': data['value']['product_qty']})
 
325
        return result
 
326
    
 
327
    def on_qty_change(self, cr, uid, ids, product_id, qty, uom_id, price_unit, context=None):
 
328
        '''
 
329
        core function from purchase order line
 
330
        
 
331
        def product_id_change(self, cr, uid, ids, pricelist, product, qty, uom,
 
332
            partner_id, date_order=False, fiscal_position=False, date_planned=False,
 
333
            name=False, price_unit=False, notes=False):
 
334
        '''
 
335
        # quick integrity check
 
336
        assert context, 'No context defined, problem on method call'
 
337
        if isinstance(ids, (int, long)):
 
338
            ids = [ids]
 
339
            
 
340
        # qty changed - we do not reset anything 
 
341
        result = {'value': {}}
 
342
        # gather default values
 
343
        data = self._call_sol_on_change(cr, uid, ids, product_id, qty, uom_id, price_unit, type='product_id_change', context=context)
 
344
        # update result price_unit and default uom
 
345
        if 'price_unit' in data['value']:
 
346
            result['value'].update({'price_unit_kit_selection_sale_line': data['value']['price_unit']})
 
347
        if 'product_uom' in data['value'] and not uom_id:
 
348
            result['value'].update({'uom_id_kit_selection_sale_line': data['value']['product_uom']})
 
349
        return result
 
350
    
 
351
    _columns = {'integrity_status': fields.selection(string=' ', selection=INTEGRITY_STATUS_SELECTION, readonly=True),
 
352
                'order_line_id_kit_selection_sale_line': fields.many2one('sale.order.line', string='Sale Order Line', readonly=True, required=True),
 
353
                'wizard_id_kit_selection_sale_line': fields.many2one('kit.selection.sale', string='Kit Selection wizard'),
 
354
                # data
 
355
                'product_id_kit_selection_sale_line': fields.many2one('product.product', string='Product', required=True),
 
356
                'qty_kit_selection_sale_line': fields.float(string='Qty', digits_compute=dp.get_precision('Product UoM'), required=True),
 
357
                'uom_id_kit_selection_sale_line': fields.many2one('product.uom', string='UoM', required=True),
 
358
                'price_unit_kit_selection_sale_line': fields.float('Unit Price', required=True, digits_compute=dp.get_precision('Purchase Price')),
 
359
                }
 
360
    
 
361
    _defaults = {'integrity_status': 'empty',
 
362
                 }
 
363
    
 
364
kit_selection_sale_line()
 
365