~openerp-commiter/openobject-addons/extra-6.0

« back to all changes in this revision

Viewing changes to sale_delivery/sale_delivery.py

 new modules for EO2 :
* sale_delivery : Deliveries Planning at Sale Order Level
* sale_margin : Better margin control
* sale_margin_delivery : margin control on delivery planing
* Stock Planning: compute procurement based on sales previsions 

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# -*- encoding: utf-8 -*-
 
2
##############################################################################
 
3
#
 
4
# Copyright (c) 2004-2008 TINY SPRL. (http://tiny.be) All Rights Reserved.
 
5
#
 
6
# $Id$
 
7
#
 
8
# WARNING: This program as such is intended to be used by professional
 
9
# programmers who take the whole responsability of assessing all potential
 
10
# consequences resulting from its eventual inadequacies and bugs
 
11
# End users who are looking for a ready-to-use solution with commercial
 
12
# garantees and support are strongly adviced to contract a Free Software
 
13
# Service Company
 
14
#
 
15
# This program is Free Software; you can redistribute it and/or
 
16
# modify it under the terms of the GNU General Public License
 
17
# as published by the Free Software Foundation; either version 2
 
18
# of the License, or (at your option) any later version.
 
19
#
 
20
# This program is distributed in the hope that it will be useful,
 
21
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
22
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
23
# GNU General Public License for more details.
 
24
#
 
25
# You should have received a copy of the GNU General Public License
 
26
# along with this program; if not, write to the Free Software
 
27
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 
28
#
 
29
##############################################################################
 
30
 
 
31
import time
 
32
from osv import fields,osv
 
33
import netsvc
 
34
from mx import DateTime
 
35
 
 
36
class sale_delivery_line(osv.osv):
 
37
    _name = 'sale.delivery.line'
 
38
 
 
39
    _columns = {
 
40
        'product_id': fields.many2one('product.product', string='Product', required=True ),
 
41
        'product_qty': fields.float('Product Quantity', digits=(16,2), required=True),
 
42
        'product_uom' : fields.many2one('product.uom', 'Product UoM', required=True),
 
43
        'packaging_id' : fields.many2one('product.packaging', 'Packaging'),
 
44
        'date_planned': fields.datetime('Date Planned', select=True, required=True),
 
45
        'priority': fields.integer('Priority'),
 
46
        'note' : fields.text('Note'),
 
47
        'order_id': fields.many2one('sale.order', 'Order Ref', required=True, ondelete='cascade', select=True),
 
48
    }
 
49
    _order = 'priority,date_planned'
 
50
    _defaults = {
 
51
        'priority': lambda *a: 1,
 
52
    }
 
53
    
 
54
    def product_id_change(self, cr, uid, ids, product, qty=0, uom=False, packaging=False):
 
55
        warning={}
 
56
        product_uom_obj = self.pool.get('product.uom')
 
57
        product_obj = self.pool.get('product.product')
 
58
 
 
59
        if not product:
 
60
            return {'value': {'product_qty' : 0.0, 'product_uom': False,
 
61
                'packaging_id': False}, 'domain': {'product_uom': []}}
 
62
 
 
63
        result = {}
 
64
        product_obj = product_obj.browse(cr, uid, product)
 
65
        if packaging:
 
66
            default_uom = product_obj.uom_id and product_obj.uom_id.id
 
67
            pack = self.pool.get('product.packaging').browse(cr, uid, packaging)
 
68
            q = product_uom_obj._compute_qty(cr, uid, uom, pack.qty, default_uom)
 
69
            if not (qty % q) == 0 :
 
70
                ean = pack.ean
 
71
                qty_pack = pack.qty
 
72
                type_ul = pack.ul
 
73
                warn_msg = "You selected a quantity of %d Units.\nBut it's not compatible with the selected packaging.\nHere is a proposition of quantities according to the packaging: " % (qty)
 
74
                warn_msg = warn_msg + "\n\nEAN: " + str(ean) + " Quantiny: " + str(qty_pack) + " Type of ul: " + str(type_ul.name)
 
75
                warning={
 
76
                    'title':'Packing Information !',
 
77
                    'message': warn_msg
 
78
                    }
 
79
            result['product_qty'] = qty
 
80
 
 
81
        result .update({'type': product_obj.procure_method})
 
82
 
 
83
        domain = {}
 
84
        if not uom:
 
85
            result['product_uom'] = product_obj.uom_id.id
 
86
            domain = {'product_uom':
 
87
                        [('category_id', '=', product_obj.uom_id.category_id.id)],}
 
88
        
 
89
        return {'value': result, 'domain': domain,'warning':warning}
 
90
    
 
91
sale_delivery_line()
 
92
 
 
93
 
 
94
class sale_order(osv.osv):
 
95
    _inherit = 'sale.order'
 
96
    _columns = {
 
97
        'delivery_line': fields.one2many('sale.delivery.line', 'order_id', 'Delivery Lines', readonly=True, states={'draft':[('readonly',False)]}),
 
98
    }
 
99
    
 
100
    def action_ship_create(self, cr, uid, ids, *args):
 
101
        picking = {}
 
102
        company = self.pool.get('res.users').browse(cr, uid, uid).company_id
 
103
        for order in self.browse(cr, uid, ids, context={}):
 
104
            for delivery in order.delivery_line:
 
105
                cr.execute('select id from sale_order_line where order_id = %d and product_id = %d',(delivery.order_id,delivery.product_id))
 
106
                if not len(cr.fetchall()):
 
107
                    raise osv.except_osv(_('Error !'), _('You have selected a product %s for Delivery but it is not in supposed to be saled in this Sale Order') % (delivery.product_id.name))
 
108
 
 
109
            for delivery in order.delivery_line:
 
110
                cr.execute('select sum(product_uom_qty) from sale_order_line where order_id = %d and product_id = %d',(delivery.order_id,delivery.product_id))            
 
111
                sale_product_qty = cr.fetchall()[0][0]
 
112
                cr.execute('select sum(product_qty) from sale_delivery_line where order_id = %d and product_id = %d',(delivery.order_id,delivery.product_id))
 
113
                product_qty = cr.fetchall()[0][0]
 
114
                if  sale_product_qty < product_qty:
 
115
                    raise osv.except_osv(_('Error !'), _('The quanitties plannified in Deliveries (%d) for Product : %s must be equals to or less then the quantities in the Sale Order lines (%d)') % (product_qty,delivery.product_id.name,sale_product_qty))
 
116
                
 
117
            location_id = order.shop_id.warehouse_id.lot_stock_id.id
 
118
            output_id = order.shop_id.warehouse_id.lot_output_id.id
 
119
            if not order.delivery_line:
 
120
                return super(sale_order, self).action_ship_create(cr, uid, ids)
 
121
            picking_id = False
 
122
            for line in order.order_line:
 
123
                if line.product_id and line.product_id.product_tmpl_id.type in ('product', 'consu'):
 
124
                    product_qty = line.product_uom_qty-line.deliveries
 
125
                    if product_qty:
 
126
                        date_planned = DateTime.now() + DateTime.RelativeDateTime(days=line.delay or 0.0)
 
127
                        date_planned = (date_planned - DateTime.RelativeDateTime(days=company.security_lead)).strftime('%Y-%m-%d %H:%M:%S')
 
128
                        if not picking_id:
 
129
                            picking_id = self.pool.get('stock.picking').create(cr, uid, {
 
130
                                        'origin': order.name,
 
131
                                        'type': 'out',
 
132
                                        'state': 'confirmed',
 
133
                                        'move_type': order.picking_policy,
 
134
                                        'sale_id': order.id,
 
135
                                        'address_id': order.partner_shipping_id.id,
 
136
                                        'note': order.note,
 
137
                                        'invoice_state': (order.order_policy=='picking' and '2binvoiced') or 'none',
 
138
                                    })
 
139
                        move_id = self.pool.get('stock.move').create(cr, uid, {
 
140
                                'name': line.name[:64],
 
141
                                'picking_id': picking_id,
 
142
                                'product_id': line.product_id.id,
 
143
                                'date_planned': date_planned,
 
144
                                'product_qty': product_qty,
 
145
                                'product_uom': line.product_uom.id,
 
146
                                'product_uos_qty': product_qty,
 
147
                                'product_uos': (line.product_uos and line.product_uos.id)\
 
148
                                        or line.product_uom.id,
 
149
                                'product_packaging' : line.product_packaging.id,
 
150
                                'address_id' : line.address_allotment_id.id or order.partner_shipping_id.id,
 
151
                                'location_id': location_id,
 
152
                                'location_dest_id': output_id,
 
153
                                'sale_line_id': line.id,
 
154
                                'tracking_id': False,
 
155
                                'state': 'waiting',
 
156
                                'note': line.notes,
 
157
                            })
 
158
                        proc_id = self.pool.get('mrp.procurement').create(cr, uid, {
 
159
                            'name': order.name,
 
160
                            'origin': order.name,
 
161
                            'date_planned': date_planned,
 
162
                            'product_id': line.product_id.id,
 
163
                            'product_qty': product_qty,
 
164
                            'product_uom': line.product_uom.id,
 
165
                            'product_uos_qty': product_qty,
 
166
                            'product_uos': line.product_uom.id,
 
167
                            'location_id': order.shop_id.warehouse_id.lot_stock_id.id,
 
168
                            'procure_method': line.type,
 
169
                            'move_id': move_id,
 
170
                            'property_ids': [(6, 0, [x.id for x in line.property_ids])],
 
171
                        })
 
172
                    
 
173
                elif line.product_id and line.product_id.product_tmpl_id.type=='service':
 
174
                    proc_id = self.pool.get('mrp.procurement').create(cr, uid, {
 
175
                        'name': line.name,
 
176
                        'origin': order.name,
 
177
                        'date_planned': date_planned,
 
178
                        'product_id': line.product_id.id,
 
179
                        'product_qty': line.product_uom_qty,
 
180
                        'product_uom': line.product_uom.id,
 
181
                        'location_id': order.shop_id.warehouse_id.lot_stock_id.id,
 
182
                        'procure_method': line.type,
 
183
                        'property_ids': [(6, 0, [x.id for x in line.property_ids])],
 
184
                    })
 
185
                    wf_service = netsvc.LocalService("workflow")
 
186
                    wf_service.trg_validate(uid, 'mrp.procurement', proc_id, 'button_confirm', cr)
 
187
                    
 
188
            for line in order.delivery_line:
 
189
                cr.execute('select id from sale_order_line where order_id = %d and product_id = %d',(ids[0],line.product_id.id))
 
190
                sale_line_id = cr.fetchall()[0][0]
 
191
                sale_line = self.pool.get('sale.order.line').browse(cr, uid, sale_line_id)
 
192
                date_planned = line.date_planned
 
193
                if line.product_id and line.product_id.product_tmpl_id.type in ('product', 'consu'):
 
194
                    if not date_planned in picking:
 
195
                        loc_dest_id = order.partner_id.property_stock_customer.id
 
196
                        picking_id = self.pool.get('stock.picking').create(cr, uid, {
 
197
                            'origin': order.name,
 
198
                            'type': 'out',
 
199
                            'state': 'confirmed',
 
200
                            'move_type': order.picking_policy,
 
201
                            'sale_id': order.id,
 
202
                            'address_id': order.partner_shipping_id.id,
 
203
                            'note': order.note,
 
204
                            'invoice_state': (order.order_policy=='picking' and '2binvoiced') or 'none',
 
205
                        })
 
206
                        picking[date_planned] = picking_id
 
207
                    
 
208
                    else:
 
209
                        picking_id = picking[date_planned]
 
210
                        
 
211
                    move_id = self.pool.get('stock.move').create(cr, uid, {
 
212
                        'name': line.product_id.name[:64],
 
213
                        'picking_id': picking_id,
 
214
                        'product_id': line.product_id.id,
 
215
                        'date_planned': date_planned,
 
216
                        'product_qty': line.product_qty,
 
217
                        'product_uom': line.product_uom.id,
 
218
                        'product_uos_qty': line.product_qty,
 
219
                        'product_uos': line.product_uom.id,
 
220
                        'product_packaging' : line.packaging_id.id,
 
221
                        'address_id' : order.partner_shipping_id.id,
 
222
                        'location_id': location_id,
 
223
                        'location_dest_id': output_id,
 
224
                        'tracking_id': False,
 
225
                        'state': 'waiting',
 
226
                        'note': line.note,
 
227
                    })
 
228
                    proc_id = self.pool.get('mrp.procurement').create(cr, uid, {
 
229
                        'name': order.name,
 
230
                        'origin': order.name,
 
231
                        'date_planned': date_planned,
 
232
                        'product_id': line.product_id.id,
 
233
                        'product_qty': line.product_qty,
 
234
                        'product_uom': line.product_uom.id,
 
235
                        'product_uos_qty': line.product_qty,
 
236
                        'product_uos': line.product_uom.id,
 
237
                        'location_id': order.shop_id.warehouse_id.lot_stock_id.id,
 
238
                        'procure_method': sale_line.type,
 
239
                        'move_id': move_id,
 
240
                        'property_ids': [(6, 0, [x.id for x in sale_line.property_ids])],
 
241
                    })
 
242
                    wf_service = netsvc.LocalService("workflow")
 
243
                    wf_service.trg_validate(uid, 'mrp.procurement', proc_id, 'button_confirm', cr)
 
244
                    
 
245
                wf_service = netsvc.LocalService("workflow")
 
246
                wf_service.trg_validate(uid, 'stock.picking', picking[date_planned], 'button_confirm', cr)
 
247
 
 
248
            val = {}
 
249
                
 
250
            if order.state=='shipping_except':
 
251
                val['state'] = 'progress'
 
252
                if (order.order_policy == 'manual') and order.invoice_ids:
 
253
                    val['state'] = 'manual'
 
254
            self.write(cr, uid, [order.id], val)
 
255
 
 
256
        return True
 
257
sale_order()
 
258
 
 
259
class sale_order_line(osv.osv):
 
260
    _inherit = 'sale.order.line'
 
261
    
 
262
    def _get_planned_deliveries(self, cr, uid, ids, field_name, arg, context):
 
263
        res = {}
 
264
        for val in self.browse(cr, uid, ids):
 
265
            cr.execute('select sum(product_qty) from sale_delivery_line where order_id = %d and product_id = %d',(val.order_id,val.product_id))
 
266
            product_qty = cr.fetchall()[0][0]
 
267
            res[val.id] = product_qty
 
268
        return res
 
269
    
 
270
    _columns = {
 
271
         'deliveries': fields.function(_get_planned_deliveries, method=True, string='Planned Deliveries'),
 
272
    }
 
273
    
 
274
sale_order_line()
 
275
 
 
276
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: