~numerigraphe-team/stock-logistic-warehouse/7.0-inventory-location

« back to all changes in this revision

Viewing changes to stock_move_on_hold/stock.py

  • Committer: Mathieu Vatel
  • Date: 2012-03-07 12:56:37 UTC
  • Revision ID: mathieu@julius.fr-20120307125637-oz14go5k32kyqn2f
Extra modules Julius V1.0

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) 2011 Julius Network Solutions SARL <contact@julius.fr>
 
6
#
 
7
#    This program is free software: you can redistribute it and/or modify
 
8
#    it under the terms of the GNU General Public License as published by
 
9
#    the Free Software Foundation, either version 3 of the License, or
 
10
#    (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 General Public License for more details.
 
16
#
 
17
#    You should have received a copy of the GNU General Public License
 
18
#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
19
#
 
20
#################################################################################
 
21
 
 
22
from datetime import datetime
 
23
from osv import fields, osv
 
24
from tools.translate import _
 
25
import netsvc
 
26
 
 
27
# ----------------------------------------------------
 
28
# Move
 
29
# ----------------------------------------------------
 
30
 
 
31
#
 
32
# Fields:
 
33
#   location_dest_id is only used for predicting future stocks
 
34
#
 
35
class stock_move(osv.osv):
 
36
    
 
37
    _inherit = 'stock.move'
 
38
 
 
39
    _columns = {
 
40
        'state': fields.selection([
 
41
            ('draft', 'Draft'),
 
42
            ('waiting', 'Waiting'),
 
43
            ('on_hold', 'Waiting for prepayment'),
 
44
            ('on_hold_billing', 'Waiting for billing'),
 
45
            ('on_hold_paym', 'Waiting for payment'),
 
46
            ('confirmed', 'Not Available'),
 
47
            ('assigned', 'Available'),
 
48
            ('done', 'Done'),
 
49
            ('cancel', 'Cancelled')
 
50
            ], 'State', readonly=True, select=True,
 
51
            help="* When the stock move is created it is in the \'Draft\' state.\n"\
 
52
                 "* After that, it is set to \'Not Available\' state if the scheduler did not find the products.\n"\
 
53
                 "* When products are reserved it is set to \'Available\'.\n"\
 
54
                 "* When the picking is done the state is \'Done\'.\n"\
 
55
                 "* The state is \'Waiting\' if the move is waiting for another one.\n"\
 
56
                 "* The state is \'Waiting for prepayment\' if it's waiting for a prepayment of the sale order\n"\
 
57
                 "* The state is \'Waiting for billing\' if it's waiting for billing of the move\n"\
 
58
                 "* The state is \'Waiting for payment\' if it's waiting for the payment of the move"),
 
59
    }
 
60
    
 
61
    def action_on_hold(self, cr, uid, ids, context=None):
 
62
        """ Holds stock move.
 
63
        @return: List of ids.
 
64
        """
 
65
        moves = self.browse(cr, uid, ids, context=context)
 
66
        self.write(cr, uid, ids, {'state': 'on_hold'})
 
67
        
 
68
        if moves:
 
69
            picking = moves[0].picking_id
 
70
            if picking:
 
71
                moves = picking.move_lines
 
72
                state = 'on_hold'
 
73
                for move in moves:
 
74
                    if move.state not in ['on_hold','done','cancel']:
 
75
                        state = False
 
76
                        break
 
77
                if state:
 
78
                    self.pool.get('stock.picking').write(cr, uid, picking.id, {'state': state}, context)
 
79
        return []
 
80
    
 
81
    def action_hold_to_confirm(self, cr, uid, ids, context=None):
 
82
        """ Makes hold stock moves to the confirmed state.
 
83
        @return: List of ids.
 
84
        """
 
85
        moves = self.browse(cr, uid, ids, context=context)
 
86
        self.write(cr, uid, ids, {'state': 'confirmed'})
 
87
        
 
88
        if moves:
 
89
            picking = moves[0].picking_id
 
90
            if picking:
 
91
                moves = picking.move_lines
 
92
                state = 'confirmed'
 
93
                for move in moves:
 
94
                    if move.state in ['on_hold','draft']:
 
95
                        state = False
 
96
                        break
 
97
                if state:
 
98
                    self.pool.get('stock.picking').write(cr, uid, picking.id, {'state': state}, context)
 
99
        return []
 
100
 
 
101
    def action_confirm_waiting_bill(self, cr, uid, ids, context=None):
 
102
        """ Makes hold stock moves to the wait for billing state.
 
103
        @return: List of ids.
 
104
        """
 
105
        moves = self.browse(cr, uid, ids, context=context)
 
106
        self.write(cr, uid, ids, {'state': 'on_hold_billing'})
 
107
        
 
108
        if moves:
 
109
            picking = moves[0].picking_id
 
110
            if picking:
 
111
                moves = picking.move_lines
 
112
                state = 'on_hold_billing'
 
113
                for move in moves:
 
114
                    if move.state in ['on_hold','draft']:
 
115
                        state = False
 
116
                        break
 
117
                if state:
 
118
                    self.pool.get('stock.picking').write(cr, uid, picking.id, {'state': state}, context)
 
119
        return []
 
120
 
 
121
    def action_waiting_bill_to_unpaid(self, cr, uid, ids, context=None):
 
122
        """ Makes hold stock moves to the wait for payment state.
 
123
        @return: List of ids.
 
124
        """
 
125
        moves = self.browse(cr, uid, ids, context=context)
 
126
        
 
127
        if moves:
 
128
            picking = moves[0].picking_id
 
129
            if picking:
 
130
                moves = picking.move_lines
 
131
                state = 'on_hold_paym'
 
132
                for move in moves:
 
133
                    if move.state in ['on_hold','draft']:
 
134
                        state = False
 
135
                        break
 
136
                if state:
 
137
                    self.pool.get('stock.picking').write(cr, uid, picking.id, {'state': state}, context)
 
138
        return []
 
139
    
 
140
    """ This part is replacing the usual one to be able to taking in account the new states. """
 
141
    def action_assign(self, cr, uid, ids, *args):
 
142
        """ Changes state to confirmed or waiting.
 
143
        @return: List of values
 
144
        """
 
145
        todo = []
 
146
        for move in self.browse(cr, uid, ids):
 
147
            if move.state in ('confirmed', 'waiting', 'on_hold_billing', 'on_hold_paym', 'assigned'):
 
148
                todo.append(move.id)
 
149
        res = self.check_assign(cr, uid, todo)
 
150
        return res
 
151
    
 
152
    """ This part is replacing the usual one to be able to taking in account the new states. """
 
153
    def check_assign(self, cr, uid, ids, context=None):
 
154
        """ Checks the product type and accordingly writes the state.
 
155
        @return: No. of moves done
 
156
        """
 
157
        done = []
 
158
        count = 0
 
159
        waiting = 0
 
160
        pickings = {}
 
161
        if context is None:
 
162
            context = {}
 
163
        for move in self.browse(cr, uid, ids, context=context):
 
164
            if move.product_id.type == 'consu' or move.location_id.usage == 'supplier':
 
165
                if move.state in ('confirmed', 'waiting'):
 
166
                    done.append(move.id)
 
167
                pickings[move.picking_id.id] = 1
 
168
                continue
 
169
            if move.state in ('confirmed', 'waiting', 'on_hold_billing', 'on_hold_paym'):
 
170
                if move.state in ('on_hold_billing', 'on_hold_paym'):
 
171
                    res = True
 
172
                else:
 
173
                    # Important: we must pass lock=True to _product_reserve() to avoid race conditions and double reservations
 
174
                    res = self.pool.get('stock.location')._product_reserve(cr, uid, [move.location_id.id], move.product_id.id, move.product_qty, {'uom': move.product_uom.id}, lock=True)
 
175
                if res:
 
176
                    if move.sale_line_id and move.sale_line_id.order_id.order_policy in ['ship_prepayment','wait_prepayment']:
 
177
                        if not move.sale_line_id.invoice_lines:
 
178
                            self.write(cr, uid, [move.id], {'state':'on_hold_billing'})
 
179
                            pickings[move.picking_id.id] = 1
 
180
                            waiting += 1
 
181
                        else:
 
182
                            for line in move.sale_line_id.invoice_lines:
 
183
                                if line.invoice_id and line.invoice_id.state == 'cancel':
 
184
                                    self.write(cr, uid, [move.id], {'state':'on_hold_billing'})
 
185
                                    pickings[move.picking_id.id] = 1
 
186
                                    waiting += 1
 
187
                                elif line.invoice_id and line.invoice_id.state != 'paid':
 
188
                                    self.write(cr, uid, [move.id], {'state':'on_hold_paym'})
 
189
                                    pickings[move.picking_id.id] = 1
 
190
                                    waiting += 1
 
191
                                elif line.invoice_id and line.invoice_id.state == 'paid':
 
192
                                    self.write(cr, uid, [move.id], {'state':'assigned'})
 
193
                                    pickings[move.picking_id.id] = 1
 
194
                                    waiting += 1
 
195
                    #_product_available_test depends on the next status for correct functioning
 
196
                    #the test does not work correctly if the same product occurs multiple times
 
197
                    #in the same order. This is e.g. the case when using the button 'split in two' of
 
198
                    #the stock outgoing form
 
199
                    elif res != True:
 
200
                        self.write(cr, uid, [move.id], {'state':'assigned'})
 
201
                        done.append(move.id)
 
202
                        pickings[move.picking_id.id] = 1
 
203
                        r = res.pop(0)
 
204
                        cr.execute('update stock_move set location_id=%s, product_qty=%s where id=%s', (r[1], r[0], move.id))
 
205
    
 
206
                        while res:
 
207
                            r = res.pop(0)
 
208
                            move_id = self.copy(cr, uid, move.id, {'product_qty': r[0], 'location_id': r[1]})
 
209
                            done.append(move_id)
 
210
        if done:
 
211
            count += len(done)
 
212
            self.write(cr, uid, done, {'state': 'assigned'})
 
213
 
 
214
        if count or waiting:
 
215
            for pick_id in pickings:
 
216
                wf_service = netsvc.LocalService("workflow")
 
217
                wf_service.trg_write(uid, 'stock.picking', pick_id, cr)
 
218
        return count
 
219
    
 
220
stock_move()
 
221
 
 
222
class stock_picking(osv.osv):
 
223
    
 
224
    _inherit = 'stock.picking'
 
225
    
 
226
    _columns = {
 
227
        'state': fields.selection([
 
228
            ('draft', 'Draft'),
 
229
            ('auto', 'Waiting'),
 
230
            ('on_hold', 'Waiting for prepayment'),
 
231
            ('on_hold_billing', 'Waiting for billing'),
 
232
            ('on_hold_paym', 'Waiting for payment'),
 
233
            ('confirmed', 'Confirmed'),
 
234
            ('assigned', 'Available'),
 
235
            ('done', 'Done'),
 
236
            ('cancel', 'Cancelled'),
 
237
            ], 'State', readonly=True, select=True,
 
238
            help="* Draft: not confirmed yet and will not be scheduled until confirmed\n"\
 
239
                 "* Confirmed: still waiting for the availability of products\n"\
 
240
                 "* Waiting: waiting for another move to proceed before it becomes automatically available (e.g. in Make-To-Order flows)\n"\
 
241
                 "* On Hold: waiting for a payment, payment or billing\n"\
 
242
                 "* Waiting for billing: waiting for billing of the picking\n"\
 
243
                 "* Waiting for payment: waiting for the payment of the picking\n"\
 
244
                 "* Available: products reserved, simply waiting for confirmation.\n"\
 
245
                 "* Done: has been processed, can't be modified or cancelled anymore\n"\
 
246
                 "* Cancelled: has been cancelled, can't be confirmed anymore"),
 
247
    }
 
248
    
 
249
    def action_on_hold(self, cr, uid, ids, context=None):
 
250
        """ Holds picking.
 
251
        @return: True
 
252
        """
 
253
        self.write(cr, uid, ids, {'state': 'on_hold'})
 
254
        todo = []
 
255
        for picking in self.browse(cr, uid, ids, context=context):
 
256
            for r in picking.move_lines:
 
257
                if r.state in ['draft','confirmed','waiting','assigned','on_hold_billing','on_hold_paym']:
 
258
                    todo.append(r.id)
 
259
 
 
260
        self.log_picking(cr, uid, ids, context=context)
 
261
 
 
262
        todo = self.action_explode(cr, uid, todo, context)
 
263
        if len(todo):
 
264
            self.pool.get('stock.move').action_on_hold(cr, uid, todo, context=context)
 
265
        return True
 
266
    
 
267
    def action_hold_to_confirm(self, cr, uid, ids, context=None):
 
268
        """ Makes hold pickings to the confirmed state.
 
269
        @return: True
 
270
        """
 
271
        self.write(cr, uid, ids, {'state': 'confirmed'})
 
272
        todo = []
 
273
        for picking in self.browse(cr, uid, ids, context=context):
 
274
            for r in picking.move_lines:
 
275
                if r.state in ['on_hold']:
 
276
                    todo.append(r.id)
 
277
 
 
278
        self.log_picking(cr, uid, ids, context=context)
 
279
 
 
280
        todo = self.action_explode(cr, uid, todo, context)
 
281
        if len(todo):
 
282
            self.pool.get('stock.move').action_hold_to_confirm(cr, uid, todo, context=context)
 
283
        return True
 
284
 
 
285
    def action_confirm_waiting_bill(self, cr, uid, ids, context=None):
 
286
        """ Makes hold pickings to the wait for billing state.
 
287
        @return: True
 
288
        """
 
289
        self.write(cr, uid, ids, {'state': 'on_hold_billing', 'invoice_state': '2binvoiced'})
 
290
        todo = []
 
291
        for picking in self.browse(cr, uid, ids, context=context):
 
292
            for r in picking.move_lines:
 
293
                if r.state in ['on_hold_billing']:
 
294
                    todo.append(r.id)
 
295
 
 
296
        self.log_picking(cr, uid, ids, context=context)
 
297
 
 
298
        todo = self.action_explode(cr, uid, todo, context)
 
299
        if len(todo):
 
300
            self.pool.get('stock.move').action_confirm_waiting_bill(cr, uid, todo, context=context)
 
301
        return True
 
302
    
 
303
    def action_waiting_bill_to_unpaid(self, cr, uid, ids, context=None):
 
304
        """ Makes hold pickings to the wait for payment state.
 
305
        @return: True
 
306
        """
 
307
        self.write(cr, uid, ids, {'state': 'on_hold_paym'})
 
308
        todo = []
 
309
        for picking in self.browse(cr, uid, ids, context=context):
 
310
            for r in picking.move_lines:
 
311
                if r.state in ['on_hold_paym']:
 
312
                    todo.append(r.id)
 
313
 
 
314
        self.log_picking(cr, uid, ids, context=context)
 
315
 
 
316
        todo = self.action_explode(cr, uid, todo, context)
 
317
        if len(todo):
 
318
            self.pool.get('stock.move').action_waiting_bill_to_unpaid(cr, uid, todo, context=context)
 
319
        return True
 
320
 
 
321
    """ This part is replacing the usual one to be able to taking in account the new states. """
 
322
    def action_assign(self, cr, uid, ids, *args):
 
323
        """ Changes state of picking to available if all moves are confirmed.
 
324
        @return: True
 
325
        """
 
326
        for pick in self.browse(cr, uid, ids):
 
327
            move_ids = [x.id for x in pick.move_lines if x.state in ['confirmed','on_hold_billing','on_hold_paym']]
 
328
            if not move_ids:
 
329
                raise osv.except_osv(_('Warning !'),_('Not enough stock, unable to reserve the products.'))
 
330
            self.pool.get('stock.move').action_assign(cr, uid, move_ids)
 
331
        return True
 
332
    
 
333
    def test_billed(self, cr, uid, ids):
 
334
        """ Tests whether the move has been billed or not.
 
335
        @return: True or False
 
336
        """
 
337
        ok = True
 
338
        for pick in self.browse(cr, uid, ids):
 
339
            mt = pick.move_type
 
340
            for move in pick.move_lines:
 
341
                if (move.state in ('confirmed', 'draft')) and (mt == 'one'):
 
342
                    return False
 
343
                if (mt == 'direct') and (move.state == 'on_hold_billing') and (move.product_qty):
 
344
                    return True
 
345
                ok = ok and (move.state in ('cancel', 'done', 'assigned', 'on_hold_paym'))
 
346
        return ok
 
347
    
 
348
    def test_bill_and_paid(self, cr, uid, ids):
 
349
        """ Tests whether the move has been billed and paid or not.
 
350
        @return: True or False
 
351
        """
 
352
        ok = True
 
353
        for pick in self.browse(cr, uid, ids):
 
354
            mt = pick.move_type
 
355
            for move in pick.move_lines:
 
356
                if (move.state in ('confirmed', 'draft', 'on_hold', 'on_hold_billing')) and (mt == 'one'):
 
357
                    return False
 
358
                if (mt == 'direct') and (move.state == 'on_hold_paym') and (move.product_qty):
 
359
                    return True
 
360
                ok = ok and (move.state in ('cancel', 'done', 'assigned'))
 
361
        return ok
 
362
    
 
363
    def test_paid(self, cr, uid, ids):
 
364
        """ Tests whether the move has been paid.
 
365
        @return: True or False
 
366
        """
 
367
        ok = True
 
368
        for pick in self.browse(cr, uid, ids):
 
369
            mt = pick.move_type
 
370
            for move in pick.move_lines:
 
371
                if (move.state in ('confirmed', 'draft', 'on_hold', 'on_hold_billing', 'on_hold_paym')) and (mt == 'one'):
 
372
                    return False
 
373
                if (mt == 'direct') and (move.state == 'assigned') and (move.product_qty):
 
374
                    return True
 
375
                ok = ok and (move.state in ('cancel', 'done', 'assigned'))
 
376
        return ok
 
377
 
 
378
    """ This part is replacing the usual one to be able to taking in account the new states. """
 
379
    def log_picking(self, cr, uid, ids, context=None):
 
380
        """ This function will create log messages for picking.
 
381
        @param cr: the database cursor
 
382
        @param uid: the current user's ID for security checks,
 
383
        @param ids: List of Picking Ids
 
384
        @param context: A standard dictionary for contextual values
 
385
        """
 
386
        if context is None:
 
387
            context = {}
 
388
        data_obj = self.pool.get('ir.model.data')
 
389
        for pick in self.browse(cr, uid, ids, context=context):
 
390
            msg=''
 
391
            if pick.auto_picking:
 
392
                continue
 
393
            type_list = {
 
394
                'out':_("Delivery Order"),
 
395
                'in':_('Reception'),
 
396
                'internal': _('Internal picking'),
 
397
            }
 
398
            view_list = {
 
399
                'out': 'view_picking_out_form',
 
400
                'in': 'view_picking_in_form',
 
401
                'internal': 'view_picking_form',
 
402
            }
 
403
            message = type_list.get(pick.type, _('Document')) + " '" + (pick.name or '?') + "' "
 
404
            if pick.min_date:
 
405
                msg= _(' for the ')+ datetime.strptime(pick.min_date, '%Y-%m-%d %H:%M:%S').strftime('%m/%d/%Y')
 
406
            state_list = {
 
407
                'confirmed': _("is scheduled") + msg +'.',
 
408
                'assigned': _('is ready to process.'),
 
409
                'cancel': _('is cancelled.'),
 
410
                'done': _('is done.'),
 
411
                'draft': _('is in draft state.'),
 
412
                'auto': _('is waiting.'),
 
413
                'on_hold': _('is hold, waiting for prepayment.'),
 
414
                'on_hold_billing': _('is ready to process but waiting for a billing.'),
 
415
                'on_hold_paym': _('is ready to process but waiting for the payment.'),
 
416
            }
 
417
            res = data_obj.get_object_reference(cr, uid, 'stock', view_list.get(pick.type, 'view_picking_form'))
 
418
            context.update({'view_id': res and res[1] or False})
 
419
            message += state_list[pick.state]
 
420
            self.log(cr, uid, pick.id, message, context=context)
 
421
        return True
 
422
    
 
423
    def already_invoiced(self, cr, uid, ids, pick_invoice, context=None):
 
424
        """ Looking for what have already been invoice to deduce it on the new invoice """
 
425
        invoice_obj = self.pool.get('account.invoice')
 
426
        sale_obj = self.pool.get('sale.order')
 
427
        picking_ids = self.browse(cr, uid, ids, context)
 
428
        res = {}
 
429
        for pick in picking_ids:
 
430
            sale_id = pick.sale_id and pick.sale_id.id
 
431
            if sale_id:
 
432
                amount_prepaid = 0.00
 
433
                amount_total = 0.00
 
434
                cr.execute("""SELECT invoice_id FROM sale_order_invoice_rel WHERE order_id = %s """, (sale_id,))
 
435
                invoice_ids = map(lambda x: x[0], cr.fetchall())
 
436
                invoice_open_paid = invoice_obj.search(cr, uid, [('id', 'in', invoice_ids),('state', 'in', ['open','paid'])])
 
437
                invoice_id = pick_invoice[pick.id]
 
438
                res[invoice_id] = invoice_open_paid
 
439
                for invoice in invoice_obj.browse(cr, uid, invoice_open_paid):
 
440
                    if invoice.is_advance:
 
441
                        amount_prepaid += invoice.amount_untaxed or 0.00
 
442
                    else:
 
443
                        for line in invoice.invoice_line:
 
444
                            if not line.is_advance:
 
445
                                amount_total += line.price_subtotal or 0.00
 
446
                sale_obj.write(cr, uid, [sale_id], {'amount_prepaid': amount_prepaid, 'amount_shipped': amount_total})
 
447
        return res
 
448
    
 
449
    def create_advance_line(self, cr, uid, pick_invoice, invoice_done, context=None):
 
450
        invoice_obj = self.pool.get('account.invoice')
 
451
        account_line_obj = self.pool.get('account.invoice.line')
 
452
        for pick in pick_invoice:
 
453
            picking_id = self.browse(cr, uid, pick, context)
 
454
            sale_id = picking_id.sale_id
 
455
            total_amount = sale_id.amount_untaxed or 0.00
 
456
            prepaid_amount = sale_id.amount_prepaid or 0.00
 
457
            amount_shipped = sale_id.amount_shipped or 0.00
 
458
            invoice_id = pick_invoice[pick]
 
459
            invoice_id = invoice_obj.browse(cr, uid, invoice_id)
 
460
            invoice_amount = invoice_id.amount_untaxed
 
461
            if prepaid_amount >= amount_shipped:
 
462
                if (invoice_amount + amount_shipped) > prepaid_amount:
 
463
                    line_amount = - (prepaid_amount - amount_shipped)
 
464
                else:
 
465
                    line_amount = - invoice_amount
 
466
                res = account_line_obj.product_id_change(cr, uid, [], 7624, False, 1, partner_id=invoice_id.partner_id.id, fposition_id=invoice_id.fiscal_position.id, price_unit=line_amount, address_invoice_id=invoice_id.address_invoice_id.id, currency_id=invoice_id.currency_id.id, context=context)
 
467
                account_line_obj = self.pool.get('account.invoice.line')
 
468
                vals = res['value']
 
469
                vals.update({'invoice_id': invoice_id.id, 'invoice_line_tax_id': [(6, 0, vals['invoice_line_tax_id'])], 'note':'', 'price_unit': line_amount, 'is_advance': True})
 
470
                if vals['price_unit'] <> 0:
 
471
                    account_line_obj.create(cr, uid, vals)
 
472
            invoice_amount = invoice_id.amount_untaxed
 
473
            if sale_id:
 
474
                self.pool.get('sale.order').write(cr, uid, [sale_id.id], {'amount_shipped': amount_shipped + invoice_amount})
 
475
            invoice_obj.button_reset_taxes(cr, uid, [invoice_id.id], context=context)
 
476
        return
 
477
    
 
478
    def associate_lines(self, cr, uid, ids, pick_invoice, context):
 
479
        inv_line_obj = self.pool.get('account.invoice.line')
 
480
        move_line_obj = self.pool.get('stock.move')
 
481
        for pick in pick_invoice:
 
482
            picking_id = self.browse(cr, uid, pick, context)
 
483
            invoice_id = pick_invoice[pick]
 
484
            move_lines = picking_id.move_lines
 
485
            for line in move_lines:
 
486
                invoice_line_ids = inv_line_obj(cr, uid, [
 
487
                                                          ('invoice_id', '=', invoice_id),
 
488
                                                          ('product_id', '=', line.product_id.id),
 
489
                                                          ('quantity', '=', line.product_qty),
 
490
                                                          ])
 
491
                if invoice_line_ids:
 
492
                    move_line_obj.write(cr, uid, [line.id], {'invoice_line_id': invoice_line_ids[0]})
 
493
 
 
494
    def action_invoice_create(self, cr, uid, ids, journal_id=False,
 
495
            group=False, type='out_invoice', context=None):
 
496
        if context is None:
 
497
            context = {}
 
498
        if context.get('before_shipping', False):
 
499
            picking_ids = self.browse(cr, uid, ids)
 
500
            todo = []
 
501
            sequence_obj = self.pool.get('ir.sequence')
 
502
            for pick in picking_ids:
 
503
                line_ids = [x.id for x in pick.move_lines if x.state == 'on_hold_billing']
 
504
                old_lines_ids = [x.id for x in pick.move_lines if x.id not in line_ids]
 
505
                if line_ids:
 
506
                    wf_service = netsvc.LocalService("workflow")
 
507
                    if old_lines_ids:
 
508
                        new_picking = self.copy(cr, uid, pick.id,
 
509
                                {
 
510
                                 'name': sequence_obj.get(cr, uid, 'stock.picking.%s'%(pick.type)),
 
511
                                 'move_lines' : [],
 
512
                                 'state':'confirmed',
 
513
                                 'backorder_id': pick.id,
 
514
                                })
 
515
                        self.pool.get('stock.move').write(cr, uid, old_lines_ids, {'picking_id': new_picking}),
 
516
                        wf_service.trg_validate(uid, 'stock.picking', new_picking, 'button_confirm', cr)
 
517
                    self.pool.get('stock.move').write(cr, uid, line_ids, {'state': 'on_hold_paym'}),
 
518
                    todo.append(pick.id)
 
519
            ids = todo
 
520
        res = super(stock_picking, self).action_invoice_create(cr, uid, ids, journal_id, group, type, context)
 
521
        invoice_done = self.already_invoiced(cr, uid, ids, res, context)
 
522
        self.create_advance_line(cr, uid, res, invoice_done, context)
 
523
        return res
 
524
    
 
525
stock_picking()
 
526
 
 
527
class stock_location(osv.osv):
 
528
    
 
529
    _inherit = "stock.location"
 
530
 
 
531
    def _product_reserve(self, cr, uid, ids, product_id, product_qty, context=None, lock=False):
 
532
        result = super(stock_location, self)._product_reserve(cr, uid, ids, product_id, product_qty, context, lock)
 
533
        if context is None:
 
534
            context = {}
 
535
        result = []
 
536
        for id in self.search(cr, uid, [('location_id', 'child_of', ids)]):
 
537
            cr.execute("""SELECT product_uom, sum(product_qty) AS product_qty
 
538
                          FROM stock_move
 
539
                          WHERE location_dest_id=%s AND
 
540
                                location_id<>%s AND
 
541
                                product_id=%s AND
 
542
                                state='done'
 
543
                          GROUP BY product_uom
 
544
                       """,
 
545
                       (id, id, product_id))
 
546
            results = cr.dictfetchall()
 
547
            cr.execute("""SELECT product_uom,-sum(product_qty) AS product_qty
 
548
                          FROM stock_move
 
549
                          WHERE location_id=%s AND
 
550
                                location_dest_id<>%s AND
 
551
                                product_id=%s AND
 
552
                                state in ('done', 'assigned', 'on_hold_billing', 'on_hold_paym')
 
553
                          GROUP BY product_uom
 
554
                       """,
 
555
                       (id, id, product_id))
 
556
            results += cr.dictfetchall()
 
557
            total = 0.0
 
558
            results2 = 0.0
 
559
            for r in results:
 
560
                amount = self.pool.get('product.uom')._compute_qty(cr, uid, r['product_uom'], r['product_qty'], context.get('uom', False))
 
561
                results2 += amount
 
562
                total += amount
 
563
 
 
564
            if total <= 0.0:
 
565
                continue
 
566
 
 
567
            amount = results2
 
568
            if amount > 0:
 
569
                if amount > min(total, product_qty):
 
570
                    amount = min(product_qty, total)
 
571
                result.append((amount, id))
 
572
                product_qty -= amount
 
573
                total -= amount
 
574
                if product_qty <= 0.0:
 
575
                    return result
 
576
                if total <= 0.0:
 
577
                    continue
 
578
        return False
 
579
        
 
580
stock_location()
 
581
 
 
582
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: