~zaber/openobject-addons/stable_5.0-extra-addons

« back to all changes in this revision

Viewing changes to stock_planning/stock_planning.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-2006 TINY SPRL. (http://tiny.be) All Rights Reserved.
 
5
#
 
6
# $Id: partner.py 1007 2005-07-25 13:18:09Z kayhman $
 
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
from osv import fields,osv
 
32
import pooler
 
33
from tools import config
 
34
import time
 
35
import netsvc
 
36
import mx.DateTime
 
37
from mx.DateTime import RelativeDateTime, now, DateTime, localtime
 
38
 
 
39
class stock_planning_period(osv.osv):
 
40
    _name = "stock.planning.period"
 
41
    _columns = {
 
42
        'name': fields.char('Period Name', size=64),
 
43
        'date_start': fields.date('Start Date', required=True),
 
44
        'date_stop': fields.date('End Date', required=True),
 
45
        'period_ids': fields.one2many('stock.period', 'planning_id', 'Periods'),
 
46
    }
 
47
    
 
48
    def create_period_weekly(self,cr, uid, ids, context={}):
 
49
        return self.create_period(cr, uid, ids, context, 7)
 
50
    
 
51
    def create_period_monthly(self,cr, uid, ids, context={}):
 
52
        return self.create_period(cr, uid, ids, context, 30)
 
53
 
 
54
    def create_period(self,cr, uid, ids, context={}, interval=1):
 
55
        for p in self.browse(cr, uid, ids, context):
 
56
            dt = p.date_start
 
57
            ds = mx.DateTime.strptime(p.date_start, '%Y-%m-%d')
 
58
            while ds.strftime('%Y-%m-%d')<p.date_stop:
 
59
                de = ds + RelativeDateTime(days=interval)
 
60
                self.pool.get('stock.period').create(cr, uid, {
 
61
                    'name': ds.strftime('%d/%m'),
 
62
                    'date_start': ds.strftime('%Y-%m-%d'),
 
63
                    'date_stop': de.strftime('%Y-%m-%d'),
 
64
                    'planning_id': p.id,
 
65
                })
 
66
                ds = ds + RelativeDateTime(days=interval)
 
67
        return True
 
68
stock_planning_period()
 
69
 
 
70
 
 
71
class stock_period(osv.osv):
 
72
    _name = "stock.period"
 
73
 
 
74
    _columns = {
 
75
        'name': fields.char('Period Name', size=64),
 
76
        'date_start': fields.date('Start Date', required=True),
 
77
        'date_stop': fields.date('End Date', required=True),
 
78
        'planning_id': fields.many2one('stock.planning.period', 'Period', required=True, select=True),
 
79
    }
 
80
    
 
81
stock_period()
 
82
 
 
83
 
 
84
class stock_planning_sale_prevision(osv.osv):
 
85
    _name = "stock.planning.sale.prevision"
 
86
    
 
87
    _columns = {
 
88
        'name' : fields.char('Name', size=64),
 
89
        'user_id': fields.many2one('res.users' , 'Salesman'),
 
90
        'period_id': fields.many2one('stock.period' , 'Period', required=True),
 
91
        'product_id': fields.many2one('product.product' , 'Product', required=True),
 
92
        'product_qty' : fields.float('Product Quantity', required=True),
 
93
        'product_uom' : fields.many2one('product.uom', 'Product UoM', required=True),
 
94
    }
 
95
    
 
96
    
 
97
    def product_id_change(self, cr, uid, ids, product, uom=False):
 
98
        if not product:
 
99
            return {'value': {'product_qty' : 0.0, 'product_uom': False},'domain': {'product_uom': []}}
 
100
 
 
101
        product_obj =  self.pool.get('product.product').browse(cr, uid, product)
 
102
        result = {}
 
103
        if not uom:
 
104
            result['product_uom'] = product_obj.uom_id.id
 
105
            domain = {'product_uom':
 
106
                        [('category_id', '=', product_obj.uom_id.category_id.id)],}
 
107
        return {'value': result}
 
108
    
 
109
stock_planning_sale_prevision()
 
110
 
 
111
class stock_planning(osv.osv):
 
112
    _name = "stock.planning"
 
113
    
 
114
    def _get_product_qty(self, cr, uid, ids, field_names, arg, context):
 
115
        res = {}
 
116
        if not context:
 
117
            context = {}
 
118
        mapping = {
 
119
            'incoming': "incoming_qty",
 
120
            'outgoing': "outgoing_qty",
 
121
        }
 
122
        for val in self.browse(cr, uid, ids):
 
123
            res[val.id] = {}
 
124
            context['from_date'] = val.period_id.date_start
 
125
            context['to_date'] = val.period_id.date_stop
 
126
            context['warehouse'] = val.warehouse_id.id or False
 
127
            product_obj =  self.pool.get('product.product').read(cr, uid,val.product_id.id,[], context)
 
128
            product_qty =product_obj[' , '.join(map(lambda x: mapping[x], field_names))]# 0.0
 
129
            res[val.id][field_names[0]] = product_qty
 
130
        return res
 
131
    
 
132
    def _get_planned_sale(self, cr, uid, ids, field_name, arg, context):
 
133
        res = {}
 
134
        for val in self.browse(cr, uid, ids):
 
135
            cr.execute('select sum(product_qty) from stock_planning_sale_prevision where product_id = %d and period_id = %d',(val.product_id.id,val.period_id.id))
 
136
            product_qty = cr.fetchall()[0][0]
 
137
            res[val.id] = product_qty
 
138
        return res
 
139
    
 
140
    def _get_stock_start(self, cr, uid, ids, field_name, arg, context):
 
141
        res = {}
 
142
        for val in self.browse(cr, uid, ids):
 
143
            res[val.id] = 0.0
 
144
            context['from_date'] = val.period_id.date_start
 
145
            context['to_date'] = val.period_id.date_stop
 
146
            context['warehouse'] = val.warehouse_id.id or False
 
147
            current_date =  time.strftime('%Y-%m-%d')
 
148
            product_obj =  self.pool.get('product.product').browse(cr, uid,val.product_id.id,[], context)
 
149
            if current_date > val.period_id.date_stop:
 
150
                pass
 
151
            elif current_date > val.period_id.date_start and current_date < val.period_id.date_stop:
 
152
                res[val.id] = product_obj.qty_available
 
153
            else:
 
154
                res[val.id] = product_obj.qty_available + (val.to_procure - val.planned_outgoing)
 
155
        return res
 
156
    
 
157
    def _get_value_left(self, cr, uid, ids, field_names, arg, context):
 
158
        res = {}
 
159
        for val in self.browse(cr, uid, ids):
 
160
            res[val.id] = {}
 
161
            if field_names[0] == 'stock_incoming_left':
 
162
                ret = val.to_procure-val.incoming
 
163
            if  field_names[0] == 'outgoing_left':
 
164
                ret = val.planned_outgoing-val.outgoing
 
165
            res[val.id][field_names[0]] = ret
 
166
        return res
 
167
    
 
168
    _columns = {
 
169
        'name' : fields.char('Name', size=64),
 
170
        'period_id': fields.many2one('stock.period' , 'Period', required=True),
 
171
        'product_id': fields.many2one('product.product' , 'Product', required=True),
 
172
        'product_uom' : fields.many2one('product.uom', 'Product UoM', required=True),
 
173
        'planned_outgoing' : fields.float('Planned Outgoing', required=True),
 
174
        'planned_sale': fields.function(_get_planned_sale, method=True, string='Planned Sales'),
 
175
        'stock_start': fields.function(_get_stock_start, method=True, string='Stock Start'),
 
176
        'incoming': fields.function(_get_product_qty, method=True, type='float', string='Incomming', multi='incoming'),
 
177
        'outgoing': fields.function(_get_product_qty, method=True, type='float', string='Outgoing', multi='outgoing'),
 
178
        'stock_incoming_left': fields.function(_get_value_left, method=True, string='To Procure Left', multi="stock_incoming_left"),
 
179
        'outgoing_left': fields.function(_get_value_left, method=True, string='Outgoing Left', multi="outgoing_left"),
 
180
        'to_procure': fields.float(string='To Procure'),
 
181
        'warehouse_id' : fields.many2one('stock.warehouse','Warehouse'),
 
182
    }
 
183
    
 
184
    def procure_incomming_left(self, cr, uid, ids, context, *args):
 
185
        result = {}
 
186
        for obj in self.browse(cr, uid, ids):       
 
187
            if not obj.warehouse_id:
 
188
                raise osv.except_osv('Error', "select warehouse")
 
189
            location_id = obj.warehouse_id and obj.warehouse_id.lot_stock_id.id or False
 
190
            output_id = obj.warehouse_id and obj.warehouse_id.lot_output_id.id or False
 
191
            move_id = self.pool.get('stock.move').create(cr, uid, {
 
192
                            'name': obj.product_id.name[:64],
 
193
                            'product_id': obj.product_id.id,
 
194
                            'date_planned': obj.period_id.date_start,
 
195
                            'product_qty': obj.stock_incoming_left,
 
196
                            'product_uom': obj.product_uom.id,
 
197
                            'product_uos_qty': obj.stock_incoming_left,
 
198
                            'product_uos': obj.product_uom.id,
 
199
                            'location_id': location_id,
 
200
                            'location_dest_id': output_id,
 
201
                            'state': 'waiting',
 
202
                        })
 
203
            proc_id = self.pool.get('mrp.procurement').create(cr, uid, {
 
204
                            'name': 'Procure left From Planning',
 
205
                            'origin': 'Stock Planning',
 
206
                            'date_planned': obj.period_id.date_start,
 
207
                            'product_id': obj.product_id.id,
 
208
                            'product_qty': obj.stock_incoming_left,
 
209
                            'product_uom': obj.product_uom.id,
 
210
                            'product_uos_qty': obj.stock_incoming_left,
 
211
                            'product_uos': obj.product_uom.id,
 
212
                            'location_id': location_id,
 
213
                            'procure_method': obj.product_id.product_tmpl_id.procure_method,
 
214
                            'move_id': move_id,
 
215
                        })
 
216
            wf_service = netsvc.LocalService("workflow")
 
217
            wf_service.trg_validate(uid, 'mrp.procurement', proc_id, 'button_confirm', cr)
 
218
                    
 
219
        return True
 
220
    
 
221
    
 
222
    def product_id_change(self, cr, uid, ids, product, uom=False):
 
223
        if not product:
 
224
            return {'value': { 'product_uom': False},'domain': {'product_uom': []}}
 
225
        product_obj =  self.pool.get('product.product').browse(cr, uid, product)
 
226
        result = {}
 
227
        if not uom:
 
228
            result['product_uom'] = product_obj.uom_id.id
 
229
            domain = {'product_uom':
 
230
                        [('category_id', '=', product_obj.uom_id.category_id.id)],}
 
231
        return {'value': result}
 
232
stock_planning()
 
233
 
 
234
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: