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

« back to all changes in this revision

Viewing changes to sale_override/sale.py

  • Committer: Matthieu Dietrich
  • Date: 2011-07-22 11:27:00 UTC
  • mto: This revision was merged to the branch mainline in revision 229.
  • Revision ID: matthieu.dietrich@geneva.msf.org-20110722112700-pxnmrctduwhie98g
UF-331: [IMP] Add manufacturers text fields

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 MSF, TeMPO consulting
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
20
20
##############################################################################
21
21
 
22
22
from osv import osv, fields
23
 
from order_types import ORDER_PRIORITY, ORDER_CATEGORY
 
23
from tools.translate import _
24
24
import netsvc
25
25
from datetime import datetime, timedelta
26
26
from dateutil.relativedelta import relativedelta
27
 
from mx.DateTime import *
28
 
from tools.translate import _ 
29
 
import logging
30
27
 
31
28
class sale_order(osv.osv):
 
29
    _inherit = 'sale.order'
32
30
    _name = 'sale.order'
33
 
    _inherit = 'sale.order'
34
 
 
35
 
    def copy(self, cr, uid, id, default, context={}):
36
 
        '''
37
 
        Delete the loan_id field on the new sale.order
38
 
        '''
39
 
        return super(sale_order, self).copy(cr, uid, id, default={'loan_id': False}, context=context)
40
 
    
41
 
    #@@@override sale.sale_order._invoiced
42
 
    def _invoiced(self, cr, uid, ids, name, arg, context={}):
43
 
        '''
44
 
        Return True is the sale order is an uninvoiced order
45
 
        '''
46
 
        partner_obj = self.pool.get('res.partner')
47
 
        partner = False
48
 
        res = {}
49
 
        
50
 
        for sale in self.browse(cr, uid, ids):
51
 
            if sale.partner_id:
52
 
                partner = partner_obj.browse(cr, uid, [sale.partner_id.id])[0]
53
 
            if sale.state != 'draft' and (sale.order_type != 'regular' or (partner and partner.partner_type == 'internal')):
54
 
                res[sale.id] = True
55
 
            else:
56
 
                res[sale.id] = True
57
 
                for invoice in sale.invoice_ids:
58
 
                    if invoice.state != 'paid':
59
 
                        res[sale.id] = False
60
 
                        break
61
 
                if not sale.invoice_ids:
62
 
                    res[sale.id] = False
63
 
        return res
64
 
    #@@@end
65
 
    
66
 
    #@@@override sale.sale_order._invoiced_search
67
 
    def _invoiced_search(self, cursor, user, obj, name, args, context={}):
68
 
        if not len(args):
69
 
            return []
70
 
        clause = ''
71
 
        sale_clause = ''
72
 
        no_invoiced = False
73
 
        for arg in args:
74
 
            if arg[1] == '=':
75
 
                if arg[2]:
76
 
                    clause += 'AND inv.state = \'paid\' OR (sale.state != \'draft\' AND (sale.order_type != \'regular\' OR part.partner_type = \'internal\'))'
77
 
                else:
78
 
                    clause += 'AND inv.state != \'cancel\' AND sale.state != \'cancel\'  AND inv.state <> \'paid\' AND sale.order_type = \'regular\''
79
 
                    no_invoiced = True
80
 
 
81
 
        cursor.execute('SELECT rel.order_id ' \
82
 
                'FROM sale_order_invoice_rel AS rel, account_invoice AS inv, sale_order AS sale, res_partner AS part '+ sale_clause + \
83
 
                'WHERE rel.invoice_id = inv.id AND rel.order_id = sale.id AND sale.partner_id = part.id ' + clause)
84
 
        res = cursor.fetchall()
85
 
        if no_invoiced:
86
 
            cursor.execute('SELECT sale.id ' \
87
 
                    'FROM sale_order AS sale, res_partner AS part ' \
88
 
                    'WHERE sale.id NOT IN ' \
89
 
                        '(SELECT rel.order_id ' \
90
 
                        'FROM sale_order_invoice_rel AS rel) and sale.state != \'cancel\'' \
91
 
                        'AND sale.partner_id = part.id ' \
92
 
                        'AND sale.order_type = \'regular\' AND part.partner_type != \'internal\'')
93
 
            res.extend(cursor.fetchall())
94
 
        if not res:
95
 
            return [('id', '=', 0)]
96
 
        return [('id', 'in', [x[0] for x in res])]
97
 
    #@@@end
98
 
    
99
 
    #@@@override sale.sale_order._invoiced_rate
100
 
    def _invoiced_rate(self, cursor, user, ids, name, arg, context=None):
101
 
        res = {}
102
 
        for sale in self.browse(cursor, user, ids, context=context):
103
 
            if sale.invoiced:
104
 
                res[sale.id] = 100.0
105
 
                continue
106
 
            tot = 0.0
107
 
            for invoice in sale.invoice_ids:
108
 
                if invoice.state not in ('draft', 'cancel'):
109
 
                    tot += invoice.amount_untaxed
110
 
            if tot:
111
 
                res[sale.id] = min(100.0, tot * 100.0 / (sale.amount_untaxed or 1.00))
112
 
            else:
113
 
                res[sale.id] = 0.0
114
 
        return res
115
 
    #@@@end
116
 
    
117
 
    def _get_noinvoice(self, cr, uid, ids, name, arg, context={}):
118
 
        res = {}
119
 
        for sale in self.browse(cr, uid, ids):
120
 
            res[sale.id] = sale.order_type != 'regular' or sale.partner_id.partner_type == 'internal'
121
 
        return res
122
 
    
123
 
    _columns = {
124
 
        'order_type': fields.selection([('regular', 'Regular'), ('donation_exp', 'Donation before expiry'),
125
 
                                        ('donation_st', 'Standard donation (for help)'), ('loan', 'Loan'),], 
126
 
                                        string='Order Type', required=True, readonly=True, states={'draft': [('readonly', False)]}),
127
 
        'loan_id': fields.many2one('purchase.order', string='Linked loan', readonly=True),
128
 
        'priority': fields.selection(ORDER_PRIORITY, string='Priority', readonly=True, states={'draft': [('readonly', False)]}),
129
 
        'categ': fields.selection(ORDER_CATEGORY, string='Order category', required=True, readonly=True, states={'draft': [('readonly', False)]}),
130
 
        'details': fields.char(size=30, string='Details', readonly=True, states={'draft': [('readonly', False)]}),
131
 
        'invoiced': fields.function(_invoiced, method=True, string='Paid',
132
 
            fnct_search=_invoiced_search, type='boolean', help="It indicates that an invoice has been paid."),
133
 
        'invoiced_rate': fields.function(_invoiced_rate, method=True, string='Invoiced', type='float'),
134
 
        'noinvoice': fields.function(_get_noinvoice, method=True, string="Don't create an invoice", type='boolean'),
135
 
        'loan_duration': fields.integer(string='Loan duration', help='Loan duration in months', readonly=True, states={'draft': [('readonly', False)]}),
136
 
        'from_yml_test': fields.boolean('Only used to pass addons unit test', readonly=True, help='Never set this field to true !'),
137
 
    }
138
 
    
139
 
    _defaults = {
140
 
        'order_type': lambda *a: 'regular',
141
 
        'priority': lambda *a: 'normal',
142
 
        'categ': lambda *a: 'mixed',
143
 
        'loan_duration': lambda *a: 2,
144
 
        'from_yml_test': lambda *a: False,
145
 
    }
146
 
    
147
 
    def create(self, cr, uid, vals, context={}):
148
 
        if not context:
149
 
            context = {}
150
 
        if context.get('update_mode') in ['init', 'update']:
151
 
            logging.getLogger('init').info('SO: set from yml test to True')
152
 
            vals['from_yml_test'] = True
153
 
        return super(sale_order, self).create(cr, uid, vals, context)
154
 
    
155
 
    def action_wait(self, cr, uid, ids, *args):
156
 
        '''
157
 
        Checks if the invoice should be create from the sale order
158
 
        or not
159
 
        '''
160
 
        line_obj = self.pool.get('sale.order.line')
161
 
        lines = []
162
 
        if isinstance(ids, (int, long)):
163
 
            ids = [ids]
164
 
            
165
 
        for order in self.browse(cr, uid, ids):
166
 
            if order.partner_id.partner_type == 'internal' and order.order_type == 'regular':
167
 
                self.write(cr, uid, [order.id], {'order_policy': 'manual'})
168
 
                for line in order.order_line:
169
 
                    lines.append(line.id)
170
 
            elif order.order_type in ['donation_exp', 'donation_st', 'loan']:
171
 
                self.write(cr, uid, [order.id], {'order_policy': 'manual'})
172
 
                for line in order.order_line:
173
 
                    lines.append(line.id)
174
 
            elif not order.from_yml_test:
175
 
                self.write(cr, uid, [order.id], {'order_policy': 'manual'})
176
 
    
177
 
        if lines:
178
 
            line_obj.write(cr, uid, lines, {'invoiced': 1})
179
 
            
180
 
        return super(sale_order, self).action_wait(cr, uid, ids, args)
181
 
 
182
 
    def action_purchase_order_create(self, cr, uid, ids, context={}):
183
 
        '''
184
 
        Create a purchase order as counterpart for the loan.
185
 
        '''
186
 
        if isinstance(ids, (int, long)):
187
 
            ids = [ids]
188
 
            
189
 
        purchase_obj = self.pool.get('purchase.order')
190
 
        purchase_line_obj = self.pool.get('purchase.order.line')
191
 
        partner_obj = self.pool.get('res.partner')
192
 
            
193
 
        for order in self.browse(cr, uid, ids):
194
 
            two_months = today() + RelativeDateTime(months=+2)
195
 
            order_id = purchase_obj.create(cr, uid, {'partner_id': order.partner_id.id,
196
 
                                                 'partner_address_id': partner_obj.address_get(cr, uid, [order.partner_id.id], ['contact'])['contact'],
197
 
                                                 'pricelist_id': order.partner_id.property_product_pricelist_purchase.id,
198
 
                                                 'loan_id': order.id,
199
 
                                                 'loan_duration': order.loan_duration,
200
 
                                                 'origin': order.name,
201
 
                                                 'order_type': 'loan',
202
 
                                                 'delivery_requested_date': (today() + RelativeDateTime(months=+order.loan_duration)).strftime('%Y-%m-%d'),
203
 
                                                 'categ': order.categ,
204
 
                                                 'location_id': order.shop_id.warehouse_id.lot_stock_id.id,
205
 
                                                 'priority': order.priority,})
206
 
            for line in order.order_line:
207
 
                purchase_line_obj.create(cr, uid, {'product_id': line.product_id and line.product_id.id or False,
208
 
                                                   'product_uom': line.product_uom.id,
209
 
                                                   'order_id': order_id,
210
 
                                                   'price_unit': line.price_unit,
211
 
                                                   'product_qty': line.product_uom_qty,
212
 
                                                   'date_planned': (today() + RelativeDateTime(months=+order.loan_duration)).strftime('%Y-%m-%d'),
213
 
                                                   'name': line.name,})
214
 
            self.write(cr, uid, [order.id], {'loan_id': order_id})
215
 
            
216
 
            purchase = purchase_obj.browse(cr, uid, order_id)
217
 
            
218
 
            message = _("Loan counterpart '%s' is created.") % (purchase.name,)
219
 
            
220
 
            purchase_obj.log(cr, uid, order_id, message)
221
 
        
222
 
        return order_id
223
 
    
224
 
    def has_stockable_products(self, cr, uid, ids, *args):
225
 
        '''
226
 
        Override the has_stockable_product to return False
227
 
        when the internal_type of the order is 'direct'
228
 
        '''
229
 
        for order in self.browse(cr, uid, ids):
230
 
            if order.order_type != 'direct':
231
 
                return super(sale_order, self).has_stockable_product(cr, uid, ids, args)
232
 
        
233
 
        return False
234
 
    
235
 
    #@@@override sale.sale_order.action_invoice_end
236
 
    def action_invoice_end(self, cr, uid, ids, context=None):
237
 
        ''' 
238
 
        Modified to set lines invoiced when order_type is not regular
239
 
        '''
240
 
        for order in self.browse(cr, uid, ids, context=context):
241
 
            #
242
 
            # Update the sale order lines state (and invoiced flag).
243
 
            #
244
 
            for line in order.order_line:
245
 
                vals = {}
246
 
                #
247
 
                # Check if the line is invoiced (has asociated invoice
248
 
                # lines from non-cancelled invoices).
249
 
                #
250
 
                invoiced = order.noinvoice
251
 
                if not invoiced:
252
 
                    for iline in line.invoice_lines:
253
 
                        if iline.invoice_id and iline.invoice_id.state != 'cancel':
254
 
                            invoiced = True
 
31
    
 
32
    def _hook_ship_create_stock_move(self, cr, uid, ids, move_data, order_line, *args, **kwargs):
 
33
        return move_data
 
34
 
 
35
    def _hook_ship_create_procurement_order(self, cr, uid, ids, procurement_data, order_line, *args, **kwargs):
 
36
        return procurement_data
 
37
 
 
38
    # @@@override@sale.sale.order.action_ship_create
 
39
    def action_ship_create(self, cr, uid, ids, *args, **kwargs):
 
40
        """
 
41
        Adds hooks
 
42
        """
 
43
        wf_service = netsvc.LocalService("workflow")
 
44
        picking_id = False
 
45
        move_obj = self.pool.get('stock.move')
 
46
        proc_obj = self.pool.get('procurement.order')
 
47
        company = self.pool.get('res.users').browse(cr, uid, uid).company_id
 
48
        for order in self.browse(cr, uid, ids, context={}):
 
49
            proc_ids = []
 
50
            output_id = order.shop_id.warehouse_id.lot_output_id.id
 
51
            picking_id = False
 
52
            for line in order.order_line:
 
53
                proc_id = False
 
54
                date_planned = datetime.now() + relativedelta(days=line.delay or 0.0)
 
55
                date_planned = (date_planned - timedelta(days=company.security_lead)).strftime('%Y-%m-%d %H:%M:%S')
 
56
 
 
57
                if line.state == 'done':
 
58
                    continue
 
59
                move_id = False
 
60
                if line.product_id and line.product_id.product_tmpl_id.type in ('product', 'consu') and not line.order_id.procurement_request:
 
61
                    location_id = order.shop_id.warehouse_id.lot_stock_id.id
 
62
                    if not picking_id:
 
63
                        pick_name = self.pool.get('ir.sequence').get(cr, uid, 'stock.picking.out')
 
64
                        picking_id = self.pool.get('stock.picking').create(cr, uid, {
 
65
                            'name': pick_name,
 
66
                            'origin': order.name,
 
67
                            'type': 'out',
 
68
                            'state': 'auto',
 
69
                            'move_type': order.picking_policy,
 
70
                            'sale_id': order.id,
 
71
                            'address_id': order.partner_shipping_id.id,
 
72
                            'note': order.note,
 
73
                            'invoice_state': (order.order_policy=='picking' and '2binvoiced') or 'none',
 
74
                            'company_id': order.company_id.id,
 
75
                        })
 
76
                    move_data =  {
 
77
                        'name': line.name[:64],
 
78
                        'picking_id': picking_id,
 
79
                        'product_id': line.product_id.id,
 
80
                        'date': date_planned,
 
81
                        'date_expected': date_planned,
 
82
                        'product_qty': line.product_uom_qty,
 
83
                        'product_uom': line.product_uom.id,
 
84
                        'product_uos_qty': line.product_uos_qty,
 
85
                        'product_uos': (line.product_uos and line.product_uos.id)\
 
86
                                or line.product_uom.id,
 
87
                        'product_packaging': line.product_packaging.id,
 
88
                        'address_id': line.address_allotment_id.id or order.partner_shipping_id.id,
 
89
                        'location_id': location_id,
 
90
                        'location_dest_id': output_id,
 
91
                        'sale_line_id': line.id,
 
92
                        'tracking_id': False,
 
93
                        'state': 'draft',
 
94
                        #'state': 'waiting',
 
95
                        'note': line.notes,
 
96
                        'company_id': order.company_id.id,
 
97
                    }
 
98
                    move_data = self._hook_ship_create_stock_move(cr, uid, ids, move_data, line, *args, **kwargs)
 
99
                    move_id = self.pool.get('stock.move').create(cr, uid, move_data)
 
100
 
 
101
                if line.product_id:
 
102
                    proc_data = {
 
103
                        'name': line.name,
 
104
                        'origin': order.name,
 
105
                        'date_planned': date_planned,
 
106
                        'product_id': line.product_id.id,
 
107
                        'product_qty': line.product_uom_qty,
 
108
                        'product_uom': line.product_uom.id,
 
109
                        'product_uos_qty': (line.product_uos and line.product_uos_qty)\
 
110
                                or line.product_uom_qty,
 
111
                        'product_uos': (line.product_uos and line.product_uos.id)\
 
112
                                or line.product_uom.id,
 
113
                        'location_id': order.shop_id.warehouse_id.lot_stock_id.id,
 
114
                        'procure_method': line.type,
 
115
                        'move_id': move_id,
 
116
                        'property_ids': [(6, 0, [x.id for x in line.property_ids])],
 
117
                        'company_id': order.company_id.id,
 
118
                    }
 
119
                    proc_data = self._hook_ship_create_procurement_order(cr, uid, ids, proc_data, line, *args, **kwargs)
 
120
                    proc_id = self.pool.get('procurement.order').create(cr, uid, proc_data)
 
121
                    proc_ids.append(proc_id)
 
122
                    self.pool.get('sale.order.line').write(cr, uid, [line.id], {'procurement_id': proc_id})
 
123
                    if order.state == 'shipping_except':
 
124
                        for pick in order.picking_ids:
 
125
                            for move in pick.move_lines:
 
126
                                if move.state == 'cancel':
 
127
                                    mov_ids = move_obj.search(cr, uid, [('state', '=', 'cancel'),('sale_line_id', '=', line.id),('picking_id', '=', pick.id)])
 
128
                                    if mov_ids:
 
129
                                        for mov in move_obj.browse(cr, uid, mov_ids):
 
130
                                            move_obj.write(cr, uid, [move_id], {'product_qty': mov.product_qty, 'product_uos_qty': mov.product_uos_qty})
 
131
                                            proc_obj.write(cr, uid, [proc_id], {'product_qty': mov.product_qty, 'product_uos_qty': mov.product_uos_qty})
 
132
 
 
133
            val = {}
 
134
 
 
135
            if picking_id:
 
136
                wf_service.trg_validate(uid, 'stock.picking', picking_id, 'button_confirm', cr)
 
137
 
 
138
            for proc_id in proc_ids:
 
139
                wf_service.trg_validate(uid, 'procurement.order', proc_id, 'button_confirm', cr)
 
140
                if order.state == 'proc_progress':
 
141
                    wf_service.trg_validate(uid, 'procurement.order', proc_id, 'button_check', cr)
 
142
 
 
143
            if order.state == 'shipping_except':
 
144
                val['state'] = 'progress'
 
145
                val['shipped'] = False
 
146
 
 
147
                if (order.order_policy == 'manual'):
 
148
                    for line in order.order_line:
 
149
                        if (not line.invoiced) and (line.state not in ('cancel', 'draft')):
 
150
                            val['state'] = 'manual'
255
151
                            break
256
 
                if line.invoiced != invoiced:
257
 
                    vals['invoiced'] = invoiced
258
 
                # If the line was in exception state, now it gets confirmed.
259
 
                if line.state == 'exception':
260
 
                    vals['state'] = 'confirmed'
261
 
                # Update the line (only when needed).
262
 
                if vals:
263
 
                    self.pool.get('sale.order.line').write(cr, uid, [line.id], vals, context=context)
264
 
            #
265
 
            # Update the sales order state.
266
 
            #
267
 
            if order.state == 'invoice_except':
268
 
                self.write(cr, uid, [order.id], {'state': 'progress'}, context=context)
269
 
        return True
270
 
        #@@@end
271
 
 
272
 
    def _get_reason_type(self, cr, uid, order, context={}):
273
 
        r_types = {
274
 
            'regular': 'reason_type_deliver_partner', 
275
 
            'loan': 'reason_type_loan',
276
 
            'donation_st': 'reason_type_donation',
277
 
            'donation_exp': 'reason_type_donation_expiry',
278
 
        }
279
 
 
280
 
        if order.order_type in r_types:
281
 
            return self.pool.get('ir.model.data').get_object_reference(cr, uid, 'reason_types_moves',r_types[order.order_type])[1]
282
 
 
283
 
        return False
284
 
    
285
 
    def _hook_ship_create_stock_picking(self, cr, uid, ids, context=None, *args, **kwargs):
286
 
        '''
287
 
        Please copy this to your module's method also.
288
 
        This hook belongs to the action_ship_create method from sale>sale.py
289
 
        
290
 
        - allow to modify the data for stock picking creation
291
 
        '''
292
 
        result = super(sale_order, self)._hook_ship_create_stock_picking(cr, uid, ids, context=context, *args, **kwargs)
293
 
        result['reason_type_id'] = self._get_reason_type(cr, uid, kwargs['order'], context)
294
 
        
295
 
        return result
296
 
    
297
 
    def _hook_ship_create_stock_move(self, cr, uid, ids, context=None, *args, **kwargs):
298
 
        '''
299
 
        Please copy this to your module's method also.
300
 
        This hook belongs to the action_ship_create method from sale>sale.py
301
 
        
302
 
        - allow to modify the data for stock move creation
303
 
        '''
304
 
        result = super(sale_order, self)._hook_ship_create_stock_move(cr, uid, ids, context=context, *args, **kwargs)
305
 
        result['reason_type_id'] = self._get_reason_type(cr, uid, kwargs['order'], context)
306
 
        
307
 
        return result
308
 
    
309
 
    def _hook_ship_create_execute_specific_code_01(self, cr, uid, ids, context=None, *args, **kwargs):
310
 
        '''
311
 
        Please copy this to your module's method also.
312
 
        This hook belongs to the action_ship_create method from sale>sale.py
313
 
        
314
 
        - allow to execute specific code at position 01
315
 
        '''
316
 
        super(sale_order, self)._hook_ship_create_execute_specific_code_01(cr, uid, ids, context=context, *args, **kwargs)
317
 
        wf_service = netsvc.LocalService("workflow")
318
 
        order = kwargs['order']
319
 
        proc_id = kwargs['proc_id']
320
 
        if order.procurement_request and order.state == 'progress':
321
 
            wf_service.trg_validate(uid, 'procurement.order', proc_id, 'button_check', cr)
322
 
        
323
 
        return True
324
 
    
325
 
    def _hook_ship_create_line_condition(self, cr, uid, ids, context=None, *args, **kwargs):
326
 
        '''
327
 
        Please copy this to your module's method also.
328
 
        This hook belongs to the action_ship_create method from sale>sale.py
329
 
        
330
 
        - allow to customize the execution condition
331
 
        '''
332
 
        line = kwargs['line']
333
 
        result = super(sale_order, self)._hook_ship_create_line_condition(cr, uid, ids, context=context, *args, **kwargs)
334
 
        result = result and not line.order_id.procurement_request
335
 
        return result
 
152
            self.write(cr, uid, [order.id], val)
 
153
        return True
 
154
        # @@@end
336
155
 
337
156
sale_order()
338
 
 
339
 
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: