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

« back to all changes in this revision

Viewing changes to procurement_request/procurement_request.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:
2
2
##############################################################################
3
3
#
4
4
#    OpenERP, Open Source Management Solution
5
 
#    Copyright (C) 2011 TeMPO Consulting, MSF
 
5
#    Copyright (C) 2011 TeMPO Consulting, MSF 
6
6
#
7
7
#    This program is free software: you can redistribute it and/or modify
8
8
#    it under the terms of the GNU Affero General Public License as
26
26
from sale_override import SALE_ORDER_STATE_SELECTION
27
27
from msf_order_date.order_dates import compute_rts
28
28
 
29
 
 
30
 
class procurement_request_sourcing_document(osv.osv):
31
 
    _name = 'procurement.request.sourcing.document'
32
 
    _rec_name = 'order_id'
33
 
 
34
 
    def _get_doc_name(self, cr, uid, ids, field_name, args, context=None):
35
 
        """
36
 
        Return for each record, the name of the sourcing document according to
37
 
        the model of the sourcing document and its ID.
38
 
        """
39
 
        if context is None:
40
 
            context = {}
41
 
 
42
 
        if isinstance(ids, (int, long)):
43
 
            ids = [ids]
44
 
 
45
 
        res = {}
46
 
        for doc in self.browse(cr, uid, ids, context=context):
47
 
            res[doc.id] = self.pool.get(doc.sourcing_document_model).browse(
48
 
                cr, uid, doc.sourcing_document_id, context=context).name
49
 
 
50
 
        return res
51
 
 
52
 
    _columns = {
53
 
        'order_id': fields.many2one(
54
 
            'sale.order',
55
 
            string='Internal request',
56
 
            required=True,
57
 
        ),
58
 
        'sourcing_document_id': fields.integer(
59
 
            string='Sourcing document ID',
60
 
            required=True,
61
 
        ),
62
 
        'sourcing_document_type': fields.selection(
63
 
            selection=[
64
 
                ('rfq', 'Request for Quotation'),
65
 
                ('tender', 'Tender'),
66
 
                ('po', 'Purchase order'),
67
 
                ('internal', 'Internal move'),
68
 
                ('out', 'Outgoing delivery'),
69
 
            ],
70
 
            string='Type',
71
 
            required=True,
72
 
        ),
73
 
        'sourcing_document_model': fields.selection(
74
 
            selection=[
75
 
                ('purchase.order', 'Purchase order'),
76
 
                ('tender', 'Tender'),
77
 
                ('stock.picking', 'Picking'),
78
 
            ],
79
 
            string='Model',
80
 
            required=True,
81
 
        ),
82
 
        'sourcing_document_name': fields.function(
83
 
            _get_doc_name,
84
 
            method=True,
85
 
            string='Document name',
86
 
            type='char',
87
 
            size=64,
88
 
            readonly=True,
89
 
            store=False,
90
 
        ),
91
 
    }
92
 
 
93
 
    def go_to_document(self, cr, uid, ids, context=None):
94
 
        """
95
 
        Open the sourcing document in the new tab
96
 
        """
97
 
        data_obj = self.pool.get('ir.model.data')
98
 
 
99
 
        if context is None:
100
 
            context = {}
101
 
 
102
 
        if isinstance(ids, (int, long)):
103
 
            ids = [ids]
104
 
 
105
 
        brw = self.browse(cr, uid, ids[0], context=context)
106
 
        doc = self.pool.get(brw.sourcing_document_model).browse(
107
 
            cr, uid, brw.sourcing_document_id, context=context)
108
 
 
109
 
        if brw.sourcing_document_type == 'rfq':
110
 
            context.update({
111
 
                'rfq_ok': True
112
 
            })
113
 
        elif brw.sourcing_document_type == 'out':
114
 
            pick_type = 'delivery'
115
 
            if doc.subtype == 'picking':
116
 
                pick_type = 'picking_ticket'
117
 
 
118
 
            context.update({
119
 
                'pick_type': pick_type,
120
 
            })
121
 
 
122
 
        res = {
123
 
            'type': 'ir.actions.act_window',
124
 
            'res_model': brw.sourcing_document_model,
125
 
            'view_type': 'form',
126
 
            'view_mode': 'form,tree',
127
 
            'res_id': brw.sourcing_document_id,
128
 
            'context': context,
129
 
        }
130
 
 
131
 
        if brw.sourcing_document_type == 'out':
132
 
            if doc.subtype == 'picking':
133
 
                view_id = data_obj.get_object_reference(cr, uid,
134
 
                    'msf_outgoing', 'view_picking_ticket_form')[1]
135
 
            elif doc.subtype == 'standard':
136
 
                view_id = data_obj.get_object_reference(cr, uid,
137
 
                    'stock', 'view_picking_out_form')[1]
138
 
 
139
 
            res['view_id'] = [view_id]
140
 
 
141
 
        return res
142
 
 
143
 
    def chk_create(self, cr, uid, vals, context=None):
144
 
        """
145
 
        Check if a same record already exist. If not, create a new record.
146
 
        """
147
 
        if context is None:
148
 
            context = {}
149
 
 
150
 
        chk_ids = self.search(cr, uid, [
151
 
            ('order_id', '=', vals.get('order_id')),
152
 
            ('sourcing_document_id', '=', vals.get('sourcing_document_id')),
153
 
            ('sourcing_document_model', '=', vals.get('sourcing_document_model')),
154
 
        ], context=context)
155
 
 
156
 
        if not chk_ids:
157
 
            self.create(cr, uid, {
158
 
                'order_id': vals.get('order_id'),
159
 
                'sourcing_document_id': vals.get('sourcing_document_id'),
160
 
                'sourcing_document_model': vals.get('sourcing_document_model'),
161
 
                'sourcing_document_type': vals.get('sourcing_document_type'),
162
 
            }, context=context)
163
 
 
164
 
        return True
165
 
 
166
 
procurement_request_sourcing_document()
167
 
 
168
 
 
169
29
class procurement_request(osv.osv):
170
30
    _name = 'sale.order'
171
31
    _inherit = 'sale.order'
172
 
 
173
 
    def _ir_amount_all(self, cr, uid, ids, field_name, arg, context=None):
174
 
        cur_obj = self.pool.get('res.currency')
175
 
        res = {}
176
 
        for ir in self.browse(cr, uid, ids, context=context):
177
 
            res[ir.id] = 0.0
178
 
            val = 0.0
179
 
            if ir.procurement_request:
180
 
                curr_browse = self.pool.get('res.users').browse(cr, uid, [uid], context)[0].company_id.currency_id
181
 
                for line in ir.order_line:
182
 
                    val += line.price_subtotal
183
 
                res[ir.id] = cur_obj.round(cr, uid, curr_browse.rounding, val)
184
 
        return res
185
 
 
 
32
    
186
33
    def _amount_all(self, cr, uid, ids, field_name, arg, context=None):
187
34
        '''
188
35
        Override the method to return 0.0 if the sale.order is a prcourement request
189
36
        '''
190
37
        res = {}
191
38
        new_ids = []
192
 
 
 
39
        
193
40
        for order in self.browse(cr, uid, ids, context=context):
194
41
            if order.procurement_request:
195
42
                res[order.id] = {}
198
45
                res[order.id]['amount_untaxed'] = 0.0
199
46
            else:
200
47
                new_ids.append(order.id)
201
 
 
 
48
                
202
49
        res.update(super(procurement_request, self)._amount_all(cr, uid, new_ids, field_name, arg, context=context))
203
 
 
204
 
        return res
205
 
 
206
 
    def _amount_by_type(self, cr, uid, ids, field_name, arg, context=None):
207
 
        '''
208
 
        Compute the amount of line by type of procurement
209
 
        '''
210
 
        line_obj = self.pool.get('sale.order.line')
211
 
 
212
 
        res = {}
213
 
 
214
 
        for id in ids:
215
 
            res[id] = {'purchase_amount': 0.00, 'stock_amount': 0.00, 'proc_amount': 0.00}
216
 
 
217
 
        line_ids = line_obj.search(cr, uid, [('order_id', 'in', ids)], context=context)
218
 
 
219
 
        for line_data in line_obj.read(cr, uid, line_ids, ['product_uom_qty', 'cost_price', 'order_id', 'type'], context=context):
220
 
            order_id = line_data['order_id'][0]
221
 
            line_amount = line_data['product_uom_qty'] * line_data['cost_price']
222
 
            res[order_id]['proc_amount'] += line_amount
223
 
            if line_data['type'] == 'make_to_stock':
224
 
                res[order_id]['stock_amount'] += line_amount
225
 
            else:
226
 
                res[order_id]['purchase_amount'] += line_amount
227
 
 
 
50
        
228
51
        return res
229
52
 
230
53
    def fields_view_get(self, cr, uid, view_id=None, view_type='form', context=None, toolbar=False, submenu=False):
233
56
        '''
234
57
        if not context:
235
58
            context = {}
236
 
        obj_data = self.pool.get('ir.model.data')
237
59
        if view_type == 'search' and context.get('procurement_request') and not view_id:
238
60
            view_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'procurement_request', 'procurement_request_search_view')[1]
239
61
 
240
 
        elif view_type == 'form' and context.get('procurement_request'):
241
 
            view_id = obj_data.get_object_reference(cr, uid, 'procurement_request', 'procurement_request_form_view')[1]
242
 
 
243
62
        return super(procurement_request, self).fields_view_get(cr, uid, view_id=view_id, view_type=view_type, context=context, toolbar=toolbar, submenu=submenu)
244
 
 
 
63
    
245
64
    #@@@ override sale.sale.order._get_order
246
65
    # Not modified method, but simply add here to fix an error on amount_total field
247
66
    def _get_order(self, cr, uid, ids, context=None):
250
69
            result[line.order_id.id] = True
251
70
        return result.keys()
252
71
    #@@@end override
253
 
 
 
72
    
254
73
    _columns = {
255
 
        'location_requestor_id': fields.many2one('stock.location', string='Location Requestor', ondelete="cascade",
256
 
        domain=[('location_category', '!=', 'transition'), '|', ('usage', '=', 'internal'), '&', ('usage', '=', 'customer'), ('location_category', '=', 'consumption_unit')], help='You can only select an internal location'),
257
74
        'requestor': fields.char(size=128, string='Requestor', states={'draft': [('readonly', False)]}, readonly=True),
258
75
        'procurement_request': fields.boolean(string='Internal Request', readonly=True),
259
76
        'warehouse_id': fields.many2one('stock.warehouse', string='Warehouse', states={'draft': [('readonly', False)]}, readonly=True),
260
 
        'origin': fields.char(size=512, string='Origin', states={'draft': [('readonly', False)]}, readonly=True),
 
77
        'origin': fields.char(size=64, string='Origin', states={'draft': [('readonly', False)]}, readonly=True),
261
78
        'notes': fields.text(string='Notes'),
262
 
        'order_ids': fields.one2many(
263
 
            'procurement.request.sourcing.document',
264
 
            'order_id',
265
 
            string='Sourcing document',
266
 
            readonly=True,
267
 
        ),
268
 
        'ir_total_amount': fields.function(_ir_amount_all, method=True, digits_compute=dp.get_precision('Sale Price'), string='Indicative Total Value'),
269
 
        'amount_untaxed': fields.function(_amount_all, method=True, digits_compute=dp.get_precision('Sale Price'), string='Untaxed Amount',
270
 
            store={
 
79
        'order_ids': fields.many2many('purchase.order', 'procurement_request_order_rel',
 
80
                                      'request_id', 'order_id', string='Orders', readonly=True),
 
81
        
 
82
        'amount_untaxed': fields.function(_amount_all, method=True, digits_compute= dp.get_precision('Sale Price'), string='Untaxed Amount',
 
83
            store = {
271
84
                'sale.order': (lambda self, cr, uid, ids, c=None: ids, ['order_line'], 10),
272
85
                'sale.order.line': (_get_order, ['price_unit', 'tax_id', 'discount', 'product_uom_qty'], 10),
273
86
            },
274
87
            multi='sums', help="The amount without tax."),
275
 
        'amount_tax': fields.function(_amount_all, method=True, digits_compute=dp.get_precision('Sale Price'), string='Taxes',
276
 
            store={
 
88
        'amount_tax': fields.function(_amount_all, method=True, digits_compute= dp.get_precision('Sale Price'), string='Taxes',
 
89
            store = {
277
90
                'sale.order': (lambda self, cr, uid, ids, c=None: ids, ['order_line'], 10),
278
91
                'sale.order.line': (_get_order, ['price_unit', 'tax_id', 'discount', 'product_uom_qty'], 10),
279
92
            },
280
93
            multi='sums', help="The tax amount."),
281
 
        'amount_total': fields.function(_amount_all, method=True, digits_compute=dp.get_precision('Sale Price'), string='Total',
282
 
            store={
 
94
        'amount_total': fields.function(_amount_all, method=True, digits_compute= dp.get_precision('Sale Price'), string='Total',
 
95
            store = {
283
96
                'sale.order': (lambda self, cr, uid, ids, c=None: ids, ['order_line'], 10),
284
97
                'sale.order.line': (_get_order, ['price_unit', 'tax_id', 'discount', 'product_uom_qty'], 10),
285
98
            },
286
99
            multi='sums', help="The total amount."),
287
 
        'purchase_amount': fields.function(_amount_by_type, method=True, digits_compute=dp.get_precision('Sale Price'), string='Purchase Total',
288
 
            store={
289
 
                'sale.order': (lambda self, cr, uid, ids, c=None: ids, ['order_line'], 10),
290
 
                'sale.order.line': (_get_order, ['price_unit', 'tax_id', 'discount', 'product_uom_qty', 'type'], 10),
291
 
            },
292
 
            multi='by_type', help="The amount of lines sourced on order"),
293
 
        'stock_amount': fields.function(_amount_by_type, method=True, digits_compute=dp.get_precision('Sale Price'), string='Stock Total',
294
 
            store={
295
 
                'sale.order': (lambda self, cr, uid, ids, c=None: ids, ['order_line'], 10),
296
 
                'sale.order.line': (_get_order, ['price_unit', 'tax_id', 'discount', 'product_uom_qty', 'type'], 10),
297
 
            },
298
 
            multi='by_type', help="The amount of lines sourced from stock"),
299
 
        'proc_amount': fields.function(_amount_by_type, method=True, digits_compute=dp.get_precision('Sale Price'), string='Stock Total',
300
 
            store={
301
 
                'sale.order': (lambda self, cr, uid, ids, c=None: ids, ['order_line'], 10),
302
 
                'sale.order.line': (_get_order, ['price_unit', 'tax_id', 'discount', 'product_uom_qty', 'type'], 10),
303
 
            },
304
 
            multi='by_type', help="The amount of lines sourced from stock"),
305
100
        'state': fields.selection(SALE_ORDER_STATE_SELECTION, 'Order State', readonly=True, help="Gives the state of the quotation or sales order. \nThe exception state is automatically set when a cancel operation occurs in the invoice validation (Invoice Exception) or in the picking list process (Shipping Exception). \nThe 'Waiting Schedule' state is set when the invoice is confirmed but waiting for the scheduler to run on the date 'Ordered Date'.", select=True),
306
 
        'name': fields.char('Order Reference', size=64, required=True, readonly=True, select=True),
307
 
        'is_ir_from_po_cancel': fields.boolean('Is IR from a PO cancelled', invisible=True),  # UFTP-82: flagging we are in an IR and its PO is cancelled
308
101
    }
309
 
 
 
102
    
310
103
    _defaults = {
311
 
        'name': lambda *a: False,
 
104
        'name': lambda obj, cr, uid, context: not context.get('procurement_request', False) and obj.pool.get('ir.sequence').get(cr, uid, 'sale.order') or obj.pool.get('ir.sequence').get(cr, uid, 'procurement.request'),
312
105
        'procurement_request': lambda obj, cr, uid, context: context.get('procurement_request', False),
313
106
        'state': 'draft',
314
107
        'warehouse_id': lambda obj, cr, uid, context: len(obj.pool.get('stock.warehouse').search(cr, uid, [])) and obj.pool.get('stock.warehouse').search(cr, uid, [])[0],
315
 
        'is_ir_from_po_cancel': False,  # UFTP-82
316
108
    }
317
109
 
318
110
    def create(self, cr, uid, vals, context=None):
337
129
            vals['pricelist_id'] = pl
338
130
            if 'delivery_requested_date' in vals:
339
131
                vals['ready_to_ship_date'] = compute_rts(self, cr, uid, vals['delivery_requested_date'], 0, 'so', context=context)
340
 
        elif not vals.get('name', False):
341
 
            vals.update({'name': self.pool.get('ir.sequence').get(cr, uid, 'sale.order')})
342
132
 
343
133
        return super(procurement_request, self).create(cr, uid, vals, context)
344
 
 
 
134
    
345
135
    def write(self, cr, uid, ids, vals, context=None):
346
136
        '''
347
137
        Update date_planned of lines
348
138
        '''
349
 
        res = True
350
139
        for req in self.browse(cr, uid, ids, context=context):
351
140
            # Only in case of Internal request
352
141
            if req.procurement_request and 'delivery_requested_date' in vals:
354
143
                vals['ready_to_ship_date'] = rts
355
144
                for line in req.order_line:
356
145
                    self.pool.get('sale.order.line').write(cr, uid, line.id, {'date_planned': vals['delivery_requested_date']}, context=context)
357
 
 
 
146
        
358
147
        return super(procurement_request, self).write(cr, uid, ids, vals, context=context)
359
148
 
360
149
    def unlink(self, cr, uid, ids, context=None):
361
150
        '''
362
151
        Changes the state of the order to allow the deletion
363
152
        '''
 
153
        line_obj = self.pool.get('sale.order.line')
 
154
        
364
155
        del_ids = []
365
156
        normal_ids = []
366
 
 
 
157
        
367
158
        for request in self.browse(cr, uid, ids, context=context):
368
159
            if request.procurement_request and request.state in ['draft', 'cancel']:
369
160
                del_ids.append(request.id)
371
162
                normal_ids.append(request.id)
372
163
            else:
373
164
                raise osv.except_osv(_('Invalid action !'), _('Cannot delete Internal Request(s) which are already validated !'))
374
 
 
 
165
                
375
166
        if del_ids:
376
167
            osv.osv.unlink(self, cr, uid, del_ids, context=context)
377
 
 
 
168
                
378
169
        return super(procurement_request, self).unlink(cr, uid, normal_ids, context=context)
379
 
 
 
170
    
380
171
    def search(self, cr, uid, args=None, offset=0, limit=None, order=None, context=None, count=False):
381
172
        '''
382
173
        Adds automatically a domain to search only True sale orders if no procurement_request in context
389
180
        for a in args:
390
181
            if a[0] == 'procurement_request':
391
182
                test = False
392
 
 
 
183
        
393
184
        if not context.get('procurement_request', False) and test:
394
185
            args.append(('procurement_request', '=', False))
395
 
 
396
 
        return super(procurement_request, self).search(cr, uid, args, offset,
397
 
                limit, order, context, count)
398
 
 
 
186
            
 
187
        return super(procurement_request, self).search(cr, uid, args, offset, limit, order, context, count)
 
188
   
399
189
    def _hook_copy_default(self, cr, uid, *args, **kwargs):
400
190
        id = kwargs['id']
401
191
        default = kwargs['default']
403
193
 
404
194
        if not default:
405
195
            default = {}
406
 
 
 
196
            
 
197
        seq_obj = self.pool.get('ir.sequence')
407
198
        order = self.browse(cr, uid, id)
 
199
 
408
200
        proc = order.procurement_request or context.get('procurement_request', False)
 
201
            
409
202
        default.update({
410
203
            'shipped': False,
411
204
            'invoice_ids': [],
413
206
            'date_confirm': False,
414
207
            'procurement_request': proc,
415
208
        })
416
 
        # UFTP-322: Remove the block of code to calculate 'name' as the creation could be blocked by the user right to make a wrong increase of sequence
417
 
        # moved this block of code to analytic_distribution_supply/sale.py method copy_data() 
 
209
 
 
210
        if not 'name' in default:
 
211
            name = (order.procurement_request or context.get('procurement_request', False)) and seq_obj.get(cr, uid, 'procurement.request') or seq_obj.get(cr, uid, 'sale.order')
 
212
            default.update({'name': name})
 
213
 
418
214
        return default
419
 
 
 
215
        
420
216
    def copy(self, cr, uid, id, default, context=None):
421
 
        if not default:
422
 
            default = {}
423
 
 
424
 
        if not default.get('order_ids'):
425
 
            default['order_ids'] = None
426
 
 
427
217
        # bypass name sequence
428
218
        new_id = super(procurement_request, self).copy(cr, uid, id, default, context=context)
429
219
        if new_id:
446
236
        self.write(cr, uid, ids, {'state': 'cancel'}, context=context)
447
237
        self.pool.get('sale.order.line').write(cr, uid, line_ids, {'state': 'cancel'}, context=context)
448
238
 
449
 
        for ir_id in ids:
450
 
            self.infolog(cr, uid, "The IR id:%s has been canceled" % ir_id)
451
 
 
452
239
        return True
453
240
 
454
241
    def validate_procurement(self, cr, uid, ids, context=None):
455
242
        '''
456
 
        Validate the request (which is a the same object as a SO)
457
 
        It is the action called on the activity of the workflow.
 
243
        Validate the request
458
244
        '''
459
 
        obj_data = self.pool.get('ir.model.data')
460
 
        line_obj = self.pool.get('sale.order.line')
461
 
        nomen_manda_0 = obj_data.get_object_reference(cr, uid, 'msf_doc_import', 'nomen_tbd0')[1]
462
 
        nomen_manda_1 = obj_data.get_object_reference(cr, uid, 'msf_doc_import', 'nomen_tbd1')[1]
463
 
        nomen_manda_2 = obj_data.get_object_reference(cr, uid, 'msf_doc_import', 'nomen_tbd2')[1]
464
 
        nomen_manda_3 = obj_data.get_object_reference(cr, uid, 'msf_doc_import', 'nomen_tbd3')[1]
465
 
        uom_tbd = obj_data.get_object_reference(cr, uid, 'msf_doc_import', 'uom_tbd')[1]
466
 
        nb_lines = 0
467
 
        line_ids = []
468
245
        for req in self.browse(cr, uid, ids, context=context):
469
246
            if len(req.order_line) <= 0:
470
247
                raise osv.except_osv(_('Error'), _('You cannot validate an Internal request with no lines !'))
471
 
            for line in req.order_line:
472
 
                line_ids.append(line.id)
473
 
                if line.nomen_manda_0.id == nomen_manda_0 \
474
 
                or line.nomen_manda_1.id == nomen_manda_1 \
475
 
                or line.nomen_manda_2.id == nomen_manda_2 \
476
 
                or line.nomen_manda_3.id == nomen_manda_3 \
477
 
                or line.product_uom.id == uom_tbd:
478
 
                    nb_lines += 1
479
 
                if line.product_uom_qty <= 0.00:
480
 
                    raise osv.except_osv(_('Error'), _('A line must a have a quantity larger than 0.00'))
481
 
            if nb_lines:
482
 
                raise osv.except_osv(_('Error'), _('Please check the lines : you cannot have "To Be confirmed" for Nomenclature Level". You have %s lines to correct !') % nb_lines)
483
 
            self.log(cr, uid, req.id, _("The internal request '%s' has been validated (nb lines: %s).") % (req.name, len(req.order_line)), context=context)
484
 
        line_obj.update_supplier_on_line(cr, uid, line_ids, context=context)
485
248
        self.write(cr, uid, ids, {'state': 'validated'}, context=context)
486
249
 
487
250
        return True
488
 
 
 
251
    
489
252
    def confirm_procurement(self, cr, uid, ids, context=None):
490
253
        '''
491
254
        Confirmed the request
498
261
        for request in self.browse(cr, uid, ids, context=context):
499
262
            if len(request.order_line) <= 0:
500
263
                raise osv.except_osv(_('Error'), _('You cannot confirm an Internal request with no lines !'))
501
 
            for line in request.order_line:
502
 
                # for FO
503
 
                if line.type == 'make_to_order' and not line.po_cft == 'cft':
504
 
                    if not line.supplier:
505
 
                        line_number = line.line_number
506
 
                        request_name = request.name
507
 
                        raise osv.except_osv(_('Error'), _('Please correct the line %s of the %s: the supplier is required for the procurement method "On Order" !') % (line_number, request_name))
508
 
                    # an Internal Request without product can only have Internal, Intersection or Intermission partners.
509
 
                    elif line.supplier and not line.product_id and line.order_id.procurement_request and line.supplier.partner_type not in ['internal', 'section', 'intermission']:
510
 
                        raise osv.except_osv(_('Warning'), _("""For an Internal Request with a procurement method 'On Order' and without product,
511
 
                        the supplier must be either in 'Internal', 'Inter-Section' or 'Intermission' type.
512
 
                        """))
513
 
            message = _("The internal request '%s' has been confirmed (nb lines: %s).") % (request.name, len(request.order_line))
 
264
            message = _("The internal request '%s' has been confirmed.") %(request.name,)
514
265
            proc_view = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'procurement_request', 'procurement_request_form_view')
515
266
            context.update({'view_id': proc_view and proc_view[1] or False})
516
267
            self.log(cr, uid, request.id, message, context=context)
517
 
 
 
268
        
518
269
        self.action_ship_create(cr, uid, ids, context=context)
519
 
 
520
 
        return True
521
 
 
522
 
    def test_state_done(self, cr, uid, ids, mode, *args):
523
 
        if not self.test_state(cr, uid, ids, mode, *args):
524
 
            return False
525
 
 
526
 
        for ir in self.browse(cr, uid, ids):
527
 
            is_out = ir.location_requestor_id.usage == 'customer'
528
 
            if not is_out:
529
 
                return True
530
 
 
531
 
            ir_lines = [x.id for x in ir.order_line]
532
 
            out_move_ids = self.pool.get('stock.move').search(cr, uid, [
533
 
                ('picking_id.type', '=', 'out'),
534
 
                ('sale_line_id', 'in', ir_lines),
535
 
                ('state', 'not in', ['done', 'cancel']),
536
 
            ])
537
 
            if out_move_ids:
538
 
                return False
539
 
 
540
 
        return True
541
 
 
 
270
        
 
271
        return True
 
272
    
542
273
    def procurement_done(self, cr, uid, ids, context=None):
543
274
        '''
544
275
        Creates all procurement orders according to lines
545
276
        '''
546
277
        self.write(cr, uid, ids, {'state': 'done'})
 
278
        
547
279
        return True
548
 
 
 
280
    
549
281
    def pricelist_id_change(self, cr, uid, ids, pricelist_id):
550
282
        '''
551
283
        Display a warning message on pricelist change
552
284
        '''
553
285
        res = {}
554
 
 
 
286
        
555
287
        if pricelist_id and ids:
556
288
            order = self.browse(cr, uid, ids[0])
557
289
            if pricelist_id != order.pricelist_id.id and order.order_line:
558
290
                res.update({'warning': {'title': 'Currency change',
559
291
                                        'message': 'You have changed the currency of the order. \
560
292
                                         Please note that all order lines in the old currency will be changed to the new currency without conversion !'}})
561
 
 
 
293
                
562
294
        return res
563
 
 
 
295
    
564
296
procurement_request()
565
297
 
566
298
class procurement_request_line(osv.osv):
567
299
    _name = 'sale.order.line'
568
 
    _inherit = 'sale.order.line'
569
 
 
 
300
    _inherit= 'sale.order.line'
 
301
    
570
302
    def _amount_line(self, cr, uid, ids, field_name, arg, context=None):
571
303
        '''
572
304
        Override the method to return 0.0 if the line is a procurement request line
573
305
        '''
574
306
        res = {}
575
 
        new_ids = []
576
 
        cur_obj = self.pool.get('res.currency')
577
 
        curr_browse = self.pool.get('res.users').browse(cr, uid, [uid], context)[0].company_id.currency_id
 
307
        new_ids= []
578
308
        for line in self.browse(cr, uid, ids):
579
309
            if line.order_id.procurement_request:
580
 
                subtotal = line.cost_price * line.product_uom_qty
581
 
                res[line.id] = cur_obj.round(cr, uid, curr_browse.rounding, subtotal)
 
310
                res[line.id] = 0.0
582
311
            else:
583
312
                new_ids.append(line.id)
584
 
 
 
313
                
585
314
        res.update(super(procurement_request_line, self)._amount_line(cr, uid, new_ids, field_name, arg, context=context))
586
 
 
 
315
        
587
316
        return res
588
 
 
 
317
    
589
318
    def create(self, cr, uid, vals, context=None):
590
319
        '''
591
 
        Adds the date_planned value.
592
 
        Check if product or comment exist and set the the fields required accordingly.
 
320
        Adds the date_planned value
593
321
        '''
594
322
        if context is None:
595
323
            context = {}
596
 
        if vals.get('product_id', False):
597
 
            vals.update({'comment_ok': True})
598
 
        if vals.get('comment', False):
599
 
            vals.update({'product_ok': True})
600
324
 
601
325
        if not 'date_planned' in vals and context.get('procurement_request'):
602
326
            if 'date_planned' in context:
604
328
            else:
605
329
                date_planned = self.pool.get('sale.order').browse(cr, uid, vals.get('order_id'), context=context).delivery_requested_date
606
330
                vals.update({'date_planned': date_planned})
607
 
 
608
 
        # Compute the rounding of the product qty
609
 
        if vals.get('product_uom') and vals.get('product_uom_qty'):
610
 
            vals['product_uom_qty'] = self.pool.get('product.uom')._compute_round_up_qty(cr, uid, vals.get('product_uom'), vals.get('product_uom_qty'), context=context)
611
 
 
 
331
                
612
332
        return super(procurement_request_line, self).create(cr, uid, vals, context=context)
613
 
 
614
 
    def write(self, cr, uid, ids, vals, context=None):
615
 
        '''
616
 
        Compute the UoM qty according to UoM rounding value
617
 
        '''
618
 
        res = True
619
 
 
620
 
        if 'product_uom_qty' in vals or 'product_uom' in vals:
621
 
            for req in self.read(cr, uid, ids, ['product_uom_qty', 'product_uom'], context=context):
622
 
                # Compute the rounding of the product qty
623
 
                uom_id = vals.get('product_uom', req['product_uom'][0])
624
 
                uom_qty = vals.get('product_uom_qty', req['product_uom_qty'])
625
 
                vals['product_uom_qty'] = self.pool.get('product.uom')._compute_round_up_qty(cr, uid, uom_id, uom_qty, context=context)
626
 
                res = res and super(procurement_request_line, self).write(cr, uid, [req['id']], vals, context=context)
627
 
        else:
628
 
            res = res and super(procurement_request_line, self).write(cr, uid, ids, vals, context=context)
629
 
 
630
 
        return res
631
 
 
 
333
    
632
334
    def _get_fake_state(self, cr, uid, ids, field_name, args, context=None):
633
335
        if isinstance(ids, (int, long)):
634
336
            ids = [ids]
636
338
        for pol in self.read(cr, uid, ids, ['state']):
637
339
            ret[pol['id']] = pol['state']
638
340
        return ret
639
 
 
640
 
    def _get_product_id_ok(self, cr, uid, ids, field_name, args, context=None):
641
 
        if isinstance(ids, (int, long)):
642
 
            ids = [ids]
643
 
        res = {}
644
 
        for pol in self.read(cr, uid, ids, ['product_id']):
645
 
            if pol['product_id']:
646
 
                res[pol['id']] = True
647
 
            else:
648
 
                res[pol['id']] = False
649
 
        return res
650
 
 
 
341
    
651
342
    _columns = {
652
 
        'cost_price': fields.float(string='Cost price'),
653
343
        'procurement_request': fields.boolean(string='Internal Request', readonly=True),
654
344
        'latest': fields.char(size=64, string='Latest documents', readonly=True),
655
 
        'price_subtotal': fields.function(_amount_line, method=True, string='Subtotal', digits_compute=dp.get_precision('Sale Price')),
656
 
        'my_company_id': fields.many2one('res.company', 'Company', select=1),
 
345
        'price_subtotal': fields.function(_amount_line, method=True, string='Subtotal', digits_compute= dp.get_precision('Sale Price')),
 
346
        'my_company_id': fields.many2one('res.company','Company',select=1),
657
347
        'supplier': fields.many2one('res.partner', 'Supplier', domain="[('id', '!=', my_company_id)]"),
658
348
        # openerp bug: eval invisible in p.o use the po line state and not the po state !
659
349
        'fake_state': fields.function(_get_fake_state, type='char', method=True, string='State', help='for internal use only'),
660
 
        'product_id_ok': fields.function(_get_product_id_ok, type="boolean", method=True, string='Product defined?', help='for if true the button "configurator" is hidden'),
661
 
        'product_ok': fields.boolean('Product selected'),
662
 
        'comment_ok': fields.boolean('Comment written'),
663
350
    }
664
 
 
 
351
    
665
352
    def _get_planned_date(self, cr, uid, c=None):
666
353
        if c is None:
667
354
            c = {}
674
361
        'procurement_request': lambda self, cr, uid, c: c.get('procurement_request', False),
675
362
        'date_planned': _get_planned_date,
676
363
        'my_company_id': lambda obj, cr, uid, context: obj.pool.get('res.users').browse(cr, uid, uid, context=context).company_id.id,
677
 
        'product_ok': False,
678
 
        'comment_ok': True,
679
364
    }
680
 
 
681
 
    def update_supplier_on_line(self, cr, uid, ids, context=None):
682
 
        return True
683
 
 
684
 
 
685
 
    def requested_product_id_change(self, cr, uid, ids, product_id, comment=False, context=None):
 
365
    
 
366
    def requested_product_id_change(self, cr, uid, ids, product_id, type, context=None):
686
367
        '''
687
 
        Fills automatically the product_uom_id field and the name on the line when the
688
 
        product is changed.
689
 
        Add a domain on the product_uom when a product is selected.
 
368
        Fills automatically the product_uom_id field on the line when the 
 
369
        product was changed.
690
370
        '''
691
371
        if context is None:
692
372
            context = {}
693
373
        product_obj = self.pool.get('product.product')
694
 
        uom_obj = self.pool.get('product.uom')
695
374
 
696
 
        value = {}
697
 
        domain = {}
 
375
        v = {}
698
376
        if not product_id:
699
 
            value = {'product_uom': False, 'supplier': False, 'name': '', 'type':'make_to_order', 'comment_ok': False, 'cost_price': False, 'price_subtotal': False, 'product_uom_qty': 0.00, 'product_uos_qty': 0.00}
700
 
            domain = {'product_uom':[], 'supplier': [('partner_type', 'in', ['internal', 'section', 'intermission'])]}
701
 
        elif product_id:
702
 
            product = product_obj.browse(cr, uid, product_id)
703
 
            # Test the compatibility of the product with a consumption report
704
 
            res, test = product_obj._on_change_restriction_error(cr, uid, product_id, field_name='product_id', values={'value': value}, vals={'constraints': 'consumption'}, context=context)
705
 
            if test:
706
 
                return res
707
 
            value = {'product_uom': product.uom_id.id, 'name': '[%s] %s' % (product.default_code, product.name),
708
 
                     'type': product.procure_method, 'comment_ok': True, 'cost_price': product.standard_price, }
709
 
            if value['type'] != 'make_to_stock':
710
 
                value.update({'supplier': product.seller_ids and product.seller_ids[0].name.id})
711
 
            uom_val = uom_obj.read(cr, uid, [product.uom_id.id], ['category_id'])
712
 
            domain = {'product_uom':[('category_id', '=', uom_val[0]['category_id'][0])]}
713
 
        return {'value': value, 'domain': domain}
714
 
 
715
 
 
716
 
    def requested_type_change(self, cr, uid, ids, product_id, type, context=None):
717
 
        """
718
 
        If there is a product, we check its type (procure_method) and update eventually the supplier.
719
 
        """
720
 
        if context is None:
721
 
            context = {}
722
 
        v = {}
723
 
        m = {}
724
 
        product_obj = self.pool.get('product.product')
725
 
        if product_id and type != 'make_to_stock':
726
 
            product = product_obj.browse(cr, uid, product_id, context=context)
727
 
            v.update({'supplier': product.seller_ids and product.seller_ids[0].name.id})
728
 
        elif product_id and type == 'make_to_stock':
729
 
            v.update({'supplier': False})
730
 
            product = product_obj.browse(cr, uid, product_id, context=context)
731
 
            if product.type in ('consu', 'service', 'service_recep'):
732
 
                v.update({'type': 'make_to_order'})
733
 
                m.update({'title': _('Warning'),
734
 
                          'message': _('You can\'t source a line \'from stock\' if line contains a non-stockable or service product.')})
735
 
        return {'value': v, 'warning': m}
736
 
 
737
 
    def comment_change(self, cr, uid, ids, comment, product_id, nomen_manda_0, context=None):
738
 
        '''
739
 
        Fill the level of nomenclatures with tag "to be defined" if you have only comment
740
 
        '''
741
 
        if context is None:
742
 
            context = {}
743
 
        value = {'comment': comment}
744
 
        domain = {}
745
 
        obj_data = self.pool.get('ir.model.data')
746
 
        tbd_0 = obj_data.get_object_reference(cr, uid, 'msf_doc_import', 'nomen_tbd0')[1]
747
 
        tbd_1 = obj_data.get_object_reference(cr, uid, 'msf_doc_import', 'nomen_tbd1')[1]
748
 
        tbd_2 = obj_data.get_object_reference(cr, uid, 'msf_doc_import', 'nomen_tbd2')[1]
749
 
 
750
 
        if comment and not product_id:
751
 
            value.update({'name': 'To be defined',
752
 
                          'supplier': False,
753
 
                          'product_ok': True})
754
 
            # it bugs with the To Be Defined => needs to be removed
755
 
#            if not nomen_manda_0:
756
 
#                value.update({'nomen_manda_0': tbd_0,
757
 
#                              'nomen_manda_1': tbd_1,
758
 
#                              'nomen_manda_2': tbd_2,})
759
 
            domain = {'product_uom':[], 'supplier': [('partner_type', 'in', ['internal', 'section', 'intermission'])]}
760
 
        if not comment:
761
 
            value.update({'product_ok': True})
762
 
            domain = {'product_uom':[], 'supplier': []}
763
 
        return {'value': value, 'domain': domain}
764
 
 
 
377
            v.update({'product_uom': False, 'supplier': False, 'name': ''})
 
378
        else:
 
379
            product = product_obj.browse(cr, uid, product_id, context=context)
 
380
            v.update({'product_uom': product.uom_id.id, 'name': '[%s] %s'%(product.default_code, product.name)})
 
381
            if type != 'make_to_stock':
 
382
                v.update({'supplier': product.seller_ids and product.seller_ids[0].name.id})
 
383
 
 
384
        return {'value': v}
 
385
    
765
386
procurement_request_line()
766
387
 
767
388
class purchase_order(osv.osv):
768
389
    _name = 'purchase.order'
769
390
    _inherit = 'purchase.order'
770
 
 
 
391
    
771
392
    def _hook_action_picking_create_modify_out_source_loc_check(self, cr, uid, ids, context=None, *args, **kwargs):
772
393
        '''
773
394
        Please copy this to your module's method also.
774
395
        This hook belongs to the action_picking_create method from purchase>purchase.py>purchase_order class
775
 
 
 
396
        
776
397
        - allow to choose whether or not the source location of the corresponding outgoing stock move should
777
398
        match the destination location of incoming stock move
778
399
        '''
781
402
        proc_obj = self.pool.get('procurement.order')
782
403
        move_obj = self.pool.get('stock.move')
783
404
        sale_line_obj = self.pool.get('sale.order.line')
784
 
        po_line_obj = self.pool.get('purchase.order.line')
785
 
        # If the line comes from an ISR and it's not splitted line,
786
 
        # change the move_dest_id of this line (and their children)
787
 
        # to match with the procurement ordre move destination
788
 
        if order_line.move_dest_id and not order_line.is_line_split:  # UTP-972: Use the boolean for split line
 
405
        if order_line.move_dest_id:
789
406
            proc_ids = proc_obj.search(cr, uid, [('move_id', '=', order_line.move_dest_id.id)], context=context)
790
407
            so_line_ids = sale_line_obj.search(cr, uid, [('procurement_id', 'in', proc_ids)], context=context)
791
 
            po_line_ids = po_line_obj.search(cr, uid, [('move_dest_id', '=', order_line.move_dest_id.id)], context=context)
792
 
            if so_line_ids and all(not line.order_id or (line.order_id.procurement_request and line.order_id.location_requestor_id.usage != 'customer') for line in sale_line_obj.browse(cr, uid, so_line_ids, context=context)):
 
408
            if all(not line.order_id or line.order_id.procurement_request for line in sale_line_obj.browse(cr, uid, so_line_ids, context=context)):
793
409
                for proc in proc_obj.browse(cr, uid, proc_ids, context=context):
794
410
                    if proc.move_id:
795
 
                        move_obj.write(cr, uid, [proc.move_id.id], {'state': 'draft'}, context=context)
796
 
                        move_obj.unlink(cr, uid, [proc.move_id.id], context=context)
 
411
                        move_obj.write(cr, uid, [proc.move_id.id], {'state': 'draft'}, context=context)
 
412
                        move_obj.unlink(cr, uid, [proc.move_id.id], context=context)
797
413
                    proc_obj.write(cr, uid, [proc.id], {'move_id': move_id}, context=context)
798
 
                    # Update the move_dest_id of all children to avoid the system to deal with a deleted stock move
799
 
                    po_line_obj.write(cr, uid, po_line_ids, {'move_dest_id': move_id}, context=context)
800
 
 
 
414
                    
801
415
        return super(purchase_order, self)._hook_action_picking_create_modify_out_source_loc_check(cr, uid, ids, context, *args, **kwargs)
802
 
 
 
416
    
803
417
purchase_order()
804
418
 
805
419
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: