~unifield-team/unifield-wm/us-671-homere

« back to all changes in this revision

Viewing changes to sale_override/sale.py

UF-370 [ADD] Added reason types_moves module
UF-370 [IMP] Moved sale feature from order_types to sale_override (with unit tests)
UF-370 [IMP] Moved purchase feature from order_types to purchase_override (with unit_tests)

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 MSF, TeMPO consulting
 
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
20
20
##############################################################################
21
21
 
22
22
from osv import osv, fields
23
 
from tools.translate import _
 
23
from order_types import ORDER_PRIORITY, ORDER_CATEGORY
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 _ 
27
29
 
28
30
class sale_order(osv.osv):
 
31
    _name = 'sale.order'
29
32
    _inherit = 'sale.order'
30
 
    _name = 'sale.order'
31
 
    
 
33
 
 
34
    def copy(self, cr, uid, id, default, context={}):
 
35
        '''
 
36
        Delete the loan_id field on the new sale.order
 
37
        '''
 
38
        return super(sale_order, self).copy(cr, uid, id, default={'loan_id': False}, context=context)
 
39
    
 
40
    #@@@override sale.sale_order._invoiced
 
41
    def _invoiced(self, cr, uid, ids, name, arg, context={}):
 
42
        '''
 
43
        Return True is the sale order is an uninvoiced order
 
44
        '''
 
45
        partner_obj = self.pool.get('res.partner')
 
46
        partner = False
 
47
        res = {}
 
48
        
 
49
        for sale in self.browse(cr, uid, ids):
 
50
            if sale.partner_id:
 
51
                partner = partner_obj.browse(cr, uid, [sale.partner_id.id])[0]
 
52
            if sale.state != 'draft' and (sale.order_type != 'regular' or (partner and partner.partner_type == 'internal')):
 
53
                res[sale.id] = True
 
54
            else:
 
55
                res[sale.id] = True
 
56
                for invoice in sale.invoice_ids:
 
57
                    if invoice.state != 'paid':
 
58
                        res[sale.id] = False
 
59
                        break
 
60
                if not sale.invoice_ids:
 
61
                    res[sale.id] = False
 
62
        return res
 
63
    #@@@end
 
64
    
 
65
    #@@@override sale.sale_order._invoiced_search
 
66
    def _invoiced_search(self, cursor, user, obj, name, args, context={}):
 
67
        if not len(args):
 
68
            return []
 
69
        clause = ''
 
70
        sale_clause = ''
 
71
        no_invoiced = False
 
72
        for arg in args:
 
73
            if arg[1] == '=':
 
74
                if arg[2]:
 
75
                    clause += 'AND inv.state = \'paid\' OR (sale.state != \'draft\' AND (sale.order_type != \'regular\' OR part.partner_type = \'internal\'))'
 
76
                else:
 
77
                    clause += 'AND inv.state != \'cancel\' AND sale.state != \'cancel\'  AND inv.state <> \'paid\' AND sale.order_type = \'regular\''
 
78
                    no_invoiced = True
 
79
 
 
80
        cursor.execute('SELECT rel.order_id ' \
 
81
                'FROM sale_order_invoice_rel AS rel, account_invoice AS inv, sale_order AS sale, res_partner AS part '+ sale_clause + \
 
82
                'WHERE rel.invoice_id = inv.id AND rel.order_id = sale.id AND sale.partner_id = part.id ' + clause)
 
83
        res = cursor.fetchall()
 
84
        if no_invoiced:
 
85
            cursor.execute('SELECT sale.id ' \
 
86
                    'FROM sale_order AS sale, res_partner AS part ' \
 
87
                    'WHERE sale.id NOT IN ' \
 
88
                        '(SELECT rel.order_id ' \
 
89
                        'FROM sale_order_invoice_rel AS rel) and sale.state != \'cancel\'' \
 
90
                        'AND sale.partner_id = part.id ' \
 
91
                        'AND sale.order_type = \'regular\' AND part.partner_type != \'internal\'')
 
92
            res.extend(cursor.fetchall())
 
93
        if not res:
 
94
            return [('id', '=', 0)]
 
95
        return [('id', 'in', [x[0] for x in res])]
 
96
    #@@@end
 
97
    
 
98
    #@@@override sale.sale_order._invoiced_rate
 
99
    def _invoiced_rate(self, cursor, user, ids, name, arg, context=None):
 
100
        res = {}
 
101
        for sale in self.browse(cursor, user, ids, context=context):
 
102
            if sale.invoiced:
 
103
                res[sale.id] = 100.0
 
104
                continue
 
105
            tot = 0.0
 
106
            for invoice in sale.invoice_ids:
 
107
                if invoice.state not in ('draft', 'cancel'):
 
108
                    tot += invoice.amount_untaxed
 
109
            if tot:
 
110
                res[sale.id] = min(100.0, tot * 100.0 / (sale.amount_untaxed or 1.00))
 
111
            else:
 
112
                res[sale.id] = 0.0
 
113
        return res
 
114
    #@@@end
 
115
    
 
116
    def _get_noinvoice(self, cr, uid, ids, name, arg, context={}):
 
117
        res = {}
 
118
        for sale in self.browse(cr, uid, ids):
 
119
            res[sale.id] = sale.order_type != 'regular' or sale.partner_id.partner_type == 'internal'
 
120
        return res
 
121
    
 
122
    _columns = {
 
123
        'order_type': fields.selection([('regular', 'Regular'), ('donation_exp', 'Donation before expiry'),
 
124
                                        ('donation_st', 'Standard donation (for help)'), ('loan', 'Loan'),], 
 
125
                                        string='Order Type', required=True, readonly=True, states={'draft': [('readonly', False)]}),
 
126
        'loan_id': fields.many2one('purchase.order', string='Linked loan', readonly=True),
 
127
        'priority': fields.selection(ORDER_PRIORITY, string='Priority', readonly=True, states={'draft': [('readonly', False)]}),
 
128
        'categ': fields.selection(ORDER_CATEGORY, string='Order category', required=True, readonly=True, states={'draft': [('readonly', False)]}),
 
129
        'details': fields.char(size=30, string='Details', readonly=True, states={'draft': [('readonly', False)]}),
 
130
        'invoiced': fields.function(_invoiced, method=True, string='Paid',
 
131
            fnct_search=_invoiced_search, type='boolean', help="It indicates that an invoice has been paid."),
 
132
        'invoiced_rate': fields.function(_invoiced_rate, method=True, string='Invoiced', type='float'),
 
133
        'noinvoice': fields.function(_get_noinvoice, method=True, string="Don't create an invoice", type='boolean'),
 
134
        'loan_duration': fields.integer(string='Loan duration', help='Loan duration in months', readonly=True, states={'draft': [('readonly', False)]}),
 
135
    }
 
136
    
 
137
    _defaults = {
 
138
        'order_type': lambda *a: 'regular',
 
139
        'priority': lambda *a: 'normal',
 
140
        'categ': lambda *a: 'mixed',
 
141
        'loan_duration': lambda *a: 2,
 
142
    }
 
143
    
 
144
 
 
145
    
 
146
    def action_wait(self, cr, uid, ids, *args):
 
147
        '''
 
148
        Checks if the invoice should be create from the sale order
 
149
        or not
 
150
        '''
 
151
        line_obj = self.pool.get('sale.order.line')
 
152
        lines = []
 
153
        if isinstance(ids, (int, long)):
 
154
            ids = [ids]
 
155
            
 
156
        for order in self.browse(cr, uid, ids):
 
157
            if order.partner_id.partner_type == 'internal' and order.order_type == 'regular':
 
158
                self.write(cr, uid, [order.id], {'order_policy': 'manual'})
 
159
                for line in order.order_line:
 
160
                    lines.append(line.id)
 
161
            elif order.order_type in ['donation_exp', 'donation_st', 'loan']:
 
162
                self.write(cr, uid, [order.id], {'order_policy': 'manual'})
 
163
                for line in order.order_line:
 
164
                    lines.append(line.id)
 
165
            else:
 
166
                self.write(cr, uid, [order.id], {'order_policy': 'manual'})
 
167
                    
 
168
        line_obj.write(cr, uid, lines, {'invoiced': 1})
 
169
            
 
170
        return super(sale_order, self).action_wait(cr, uid, ids, args)
 
171
 
 
172
    def action_purchase_order_create(self, cr, uid, ids, context={}):
 
173
        '''
 
174
        Create a purchase order as counterpart for the loan.
 
175
        '''
 
176
        if isinstance(ids, (int, long)):
 
177
            ids = [ids]
 
178
            
 
179
        purchase_obj = self.pool.get('purchase.order')
 
180
        purchase_line_obj = self.pool.get('purchase.order.line')
 
181
        partner_obj = self.pool.get('res.partner')
 
182
            
 
183
        for order in self.browse(cr, uid, ids):
 
184
            two_months = today() + RelativeDateTime(months=+2)
 
185
            order_id = purchase_obj.create(cr, uid, {'partner_id': order.partner_id.id,
 
186
                                                 'partner_address_id': partner_obj.address_get(cr, uid, [order.partner_id.id], ['contact'])['contact'],
 
187
                                                 'pricelist_id': order.partner_id.property_product_pricelist_purchase.id,
 
188
                                                 'loan_id': order.id,
 
189
                                                 'loan_duration': order.loan_duration,
 
190
                                                 'origin': order.name,
 
191
                                                 'order_type': 'loan',
 
192
                                                 'delivery_requested_date': (today() + RelativeDateTime(months=+order.loan_duration)).strftime('%Y-%m-%d'),
 
193
                                                 'categ': order.categ,
 
194
                                                 'location_id': order.shop_id.warehouse_id.lot_stock_id.id,
 
195
                                                 'priority': order.priority,})
 
196
            for line in order.order_line:
 
197
                purchase_line_obj.create(cr, uid, {'product_id': line.product_id and line.product_id.id or False,
 
198
                                                   'product_uom': line.product_uom.id,
 
199
                                                   'order_id': order_id,
 
200
                                                   'price_unit': line.price_unit,
 
201
                                                   'product_qty': line.product_uom_qty,
 
202
                                                   'date_planned': (today() + RelativeDateTime(months=+order.loan_duration)).strftime('%Y-%m-%d'),
 
203
                                                   'name': line.name,})
 
204
            self.write(cr, uid, [order.id], {'loan_id': order_id})
 
205
            
 
206
            purchase = purchase_obj.browse(cr, uid, order_id)
 
207
            
 
208
            message = _("Loan counterpart '%s' is created.") % (purchase.name,)
 
209
            
 
210
            purchase_obj.log(cr, uid, order_id, message)
 
211
        
 
212
        return order_id
 
213
    
 
214
    def has_stockable_products(self, cr, uid, ids, *args):
 
215
        '''
 
216
        Override the has_stockable_product to return False
 
217
        when the internal_type of the order is 'direct'
 
218
        '''
 
219
        for order in self.browse(cr, uid, ids):
 
220
            if order.order_type != 'direct':
 
221
                return super(sale_order, self).has_stockable_product(cr, uid, ids, args)
 
222
        
 
223
        return False
 
224
    
 
225
    #@@@override sale.sale_order.action_invoice_end
 
226
    def action_invoice_end(self, cr, uid, ids, context=None):
 
227
        ''' 
 
228
        Modified to set lines invoiced when order_type is not regular
 
229
        '''
 
230
        for order in self.browse(cr, uid, ids, context=context):
 
231
            #
 
232
            # Update the sale order lines state (and invoiced flag).
 
233
            #
 
234
            for line in order.order_line:
 
235
                vals = {}
 
236
                #
 
237
                # Check if the line is invoiced (has asociated invoice
 
238
                # lines from non-cancelled invoices).
 
239
                #
 
240
                invoiced = order.noinvoice
 
241
                if not invoiced:
 
242
                    for iline in line.invoice_lines:
 
243
                        if iline.invoice_id and iline.invoice_id.state != 'cancel':
 
244
                            invoiced = True
 
245
                            break
 
246
                if line.invoiced != invoiced:
 
247
                    vals['invoiced'] = invoiced
 
248
                # If the line was in exception state, now it gets confirmed.
 
249
                if line.state == 'exception':
 
250
                    vals['state'] = 'confirmed'
 
251
                # Update the line (only when needed).
 
252
                if vals:
 
253
                    self.pool.get('sale.order.line').write(cr, uid, [line.id], vals, context=context)
 
254
            #
 
255
            # Update the sales order state.
 
256
            #
 
257
            if order.state == 'invoice_except':
 
258
                self.write(cr, uid, [order.id], {'state': 'progress'}, context=context)
 
259
        return True
 
260
        #@@@end
 
261
        
32
262
    def _hook_ship_create_stock_move(self, cr, uid, ids, move_data, order_line, *args, **kwargs):
33
263
        return move_data
34
264
 
47
277
        company = self.pool.get('res.users').browse(cr, uid, uid).company_id
48
278
        for order in self.browse(cr, uid, ids, context={}):
49
279
            proc_ids = []
 
280
            reason_type_id = False
50
281
            output_id = order.shop_id.warehouse_id.lot_output_id.id
51
282
            picking_id = False
52
283
            for line in order.order_line:
61
292
                    location_id = order.shop_id.warehouse_id.lot_stock_id.id
62
293
                    if not picking_id:
63
294
                        pick_name = self.pool.get('ir.sequence').get(cr, uid, 'stock.picking.out')
64
 
                        picking_id = self.pool.get('stock.picking').create(cr, uid, {
 
295
                        picking_values = {
65
296
                            'name': pick_name,
66
297
                            'origin': order.name,
67
298
                            'type': 'out',
72
303
                            'note': order.note,
73
304
                            'invoice_state': (order.order_policy=='picking' and '2binvoiced') or 'none',
74
305
                            'company_id': order.company_id.id,
75
 
                        })
 
306
                        }
 
307
                        
 
308
                        if order.order_type == 'regular':
 
309
                            reason_type_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'reason_types_moves', 'reason_type_deliver_partner')[1],
 
310
                        if order.order_type == 'loan':
 
311
                            reason_type_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'reason_types_moves', 'reason_type_loan')[1],
 
312
                        if order.order_type == 'donation_st':
 
313
                            reason_type_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'reason_types_moves', 'reason_type_donation')[1],
 
314
                        if order.order_type == 'donation_exp':
 
315
                            reason_type_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'reason_types_moves', 'reason_type_donation_expiry')[1]
 
316
                            
 
317
                        if reason_type_id:
 
318
                            picking_values.update({'reason_type_id': reason_type_id})
 
319
                            
 
320
                        picking_id = self.pool.get('stock.picking').create(cr, uid, picking_values)
76
321
                    move_data =  {
77
322
                        'name': line.name[:64],
78
323
                        'picking_id': picking_id,
95
340
                        'note': line.notes,
96
341
                        'company_id': order.company_id.id,
97
342
                    }
 
343
                    
 
344
                    if reason_type_id:
 
345
                        move_data.update({'reason_type_id': reason_type_id})
 
346
                        
98
347
                    move_data = self._hook_ship_create_stock_move(cr, uid, ids, move_data, line, *args, **kwargs)
99
348
                    move_id = self.pool.get('stock.move').create(cr, uid, move_data)
100
349
 
154
403
        # @@@end
155
404
 
156
405
sale_order()
 
406
 
 
407
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: