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

« back to all changes in this revision

Viewing changes to kit/wizard/kit_selection_sale.py

  • Committer: chloups208
  • Date: 2011-09-07 12:58:12 UTC
  • mto: (307.2.1 unifield-wm)
  • mto: This revision was merged to the branch mainline in revision 311.
  • Revision ID: chloups208@chloups208-laptop-20110907125812-dsb1jt93ae6bgfe2
[UF-390]pro forma invoice + shipment object refactoring + pack family object refactoring

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
 
 
350
 
        if qty:
351
 
            uom_id = result.get('value', {}).get('uom_id_kit_selection_sale_line', uom_id)
352
 
            result = self.pool.get('product.uom')._change_round_up_qty(cr, uid, uom_id, qty, 'qty_kit_selection_sale_line', result=result)
353
 
 
354
 
        return result
355
 
    
356
 
    _columns = {'integrity_status': fields.selection(string=' ', selection=INTEGRITY_STATUS_SELECTION, readonly=True),
357
 
                'order_line_id_kit_selection_sale_line': fields.many2one('sale.order.line', string='Sale Order Line', readonly=True, required=True),
358
 
                'wizard_id_kit_selection_sale_line': fields.many2one('kit.selection.sale', string='Kit Selection wizard'),
359
 
                # data
360
 
                'product_id_kit_selection_sale_line': fields.many2one('product.product', string='Product', required=True),
361
 
                'qty_kit_selection_sale_line': fields.float(string='Qty', digits_compute=dp.get_precision('Product UoM'), required=True),
362
 
                'uom_id_kit_selection_sale_line': fields.many2one('product.uom', string='UoM', required=True),
363
 
                'price_unit_kit_selection_sale_line': fields.float('Unit Price', required=True, digits_compute=dp.get_precision('Purchase Price')),
364
 
                }
365
 
    
366
 
    _defaults = {'integrity_status': 'empty',
367
 
                 }
368
 
    
369
 
kit_selection_sale_line()
370