1 # -*- coding: utf-8 -*-
2 ##############################################################################
4 # OpenERP, Open Source Management Solution
5 # Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
7 # This program is free software: you can redistribute it and/or modify
8 # it under the terms of the GNU Affero General Public License as
9 # published by the Free Software Foundation, either version 3 of the
10 # License, or (at your option) any later version.
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 Affero General Public License for more details.
17 # You should have received a copy of the GNU Affero General Public License
18 # along with this program. If not, see <http://www.gnu.org/licenses/>.
20 ##############################################################################
22 from datetime import datetime
23 from dateutil.relativedelta import relativedelta
24 from openerp import netsvc
25 from openerp import pooler
26 from openerp.osv import osv
27 from openerp.osv import fields
28 from openerp.tools.translate import _
29 from openerp.tools import DEFAULT_SERVER_DATE_FORMAT, DEFAULT_SERVER_DATETIME_FORMAT
30 from openerp import tools
32 class procurement_order(osv.osv):
33 _inherit = 'procurement.order'
35 def run_scheduler(self, cr, uid, automatic=False, use_new_cursor=False, context=None):
36 ''' Runs through scheduler.
37 @param use_new_cursor: False or the dbname
39 self._procure_confirm(cr, uid, use_new_cursor=use_new_cursor, context=context)
40 self._procure_orderpoint_confirm(cr, uid, automatic=automatic,\
41 use_new_cursor=use_new_cursor, context=context)
43 def _procure_confirm(self, cr, uid, ids=None, use_new_cursor=False, context=None):
45 Call the scheduler to check the procurement order
47 @param self: The object pointer
48 @param cr: The current row, from the database cursor,
49 @param uid: The current user ID for security checks
50 @param ids: List of selected IDs
51 @param use_new_cursor: False or the dbname
52 @param context: A standard dictionary for contextual values
53 @return: Dictionary of values
59 cr = pooler.get_db(use_new_cursor).cursor()
61 procurement_obj = self.pool.get('procurement.order')
63 ids = procurement_obj.search(cr, uid, [('state', '=', 'exception')], order="date_planned")
64 self.signal_button_restart(cr, uid, ids)
67 company = self.pool.get('res.users').browse(cr, uid, uid, context=context).company_id
68 maxdate = (datetime.today() + relativedelta(days=company.schedule_range)).strftime(tools.DEFAULT_SERVER_DATE_FORMAT)
69 start_date = fields.datetime.now()
76 ids = procurement_obj.search(cr, uid, [('state', '=', 'confirmed'), ('procure_method', '=', 'make_to_order')], offset=offset, limit=500, order='priority, date_planned', context=context)
77 for proc in procurement_obj.browse(cr, uid, ids, context=context):
78 if maxdate >= proc.date_planned:
79 self.signal_button_check(cr, uid, [proc.id])
84 if proc.state == 'exception':
85 report.append(_('PROC %d: on order - %3.2f %-5s - %s') % \
86 (proc.id, proc.product_qty, proc.product_uom.name,
87 proc.product_id.name))
98 ids = procurement_obj.search(cr, uid, [('state', '=', 'confirmed'), ('procure_method', '=', 'make_to_stock')], offset=offset)
99 for proc in procurement_obj.browse(cr, uid, ids):
100 if maxdate >= proc.date_planned:
101 self.signal_button_check(cr, uid, [proc.id])
102 report_ids.append(proc.id)
107 if proc.state == 'exception':
108 report.append(_('PROC %d: from stock - %3.2f %-5s - %s') % \
109 (proc.id, proc.product_qty, proc.product_uom.name,
110 proc.product_id.name,))
118 end_date = fields.datetime.now()
130 def _prepare_automatic_op_procurement(self, cr, uid, product, warehouse, location_id, context=None):
131 return {'name': _('Automatic OP: %s') % (product.name,),
132 'origin': _('SCHEDULER'),
133 'date_planned': datetime.today().strftime(DEFAULT_SERVER_DATETIME_FORMAT),
134 'product_id': product.id,
135 'product_qty': -product.virtual_available,
136 'product_uom': product.uom_id.id,
137 'location_id': location_id,
138 'company_id': warehouse.company_id.id,
139 'procure_method': 'make_to_order',}
141 def create_automatic_op(self, cr, uid, context=None):
143 Create procurement of virtual stock < 0
145 @param self: The object pointer
146 @param cr: The current row, from the database cursor,
147 @param uid: The current user ID for security checks
148 @param context: A standard dictionary for contextual values
149 @return: Dictionary of values
153 product_obj = self.pool.get('product.product')
154 proc_obj = self.pool.get('procurement.order')
155 warehouse_obj = self.pool.get('stock.warehouse')
157 warehouse_ids = warehouse_obj.search(cr, uid, [], context=context)
158 products_ids = product_obj.search(cr, uid, [('purchase_ok', '=', True)], order='id', context=context)
160 for warehouse in warehouse_obj.browse(cr, uid, warehouse_ids, context=context):
161 context['warehouse'] = warehouse
162 # Here we check products availability.
163 # We use the method 'read' for performance reasons, because using the method 'browse' may crash the server.
164 for product_read in product_obj.read(cr, uid, products_ids, ['virtual_available'], context=context):
165 if product_read['virtual_available'] >= 0.0:
168 product = product_obj.browse(cr, uid, [product_read['id']], context=context)[0]
169 if product.supply_method == 'buy':
170 location_id = warehouse.lot_input_id.id
171 elif product.supply_method == 'produce':
172 location_id = warehouse.lot_stock_id.id
175 proc_id = proc_obj.create(cr, uid,
176 self._prepare_automatic_op_procurement(cr, uid, product, warehouse, location_id, context=context),
178 self.signal_button_confirm(cr, uid, [proc_id])
179 self.signal_button_check(cr, uid, [proc_id])
182 def _get_orderpoint_date_planned(self, cr, uid, orderpoint, start_date, context=None):
183 date_planned = start_date + \
184 relativedelta(days=orderpoint.product_id.seller_delay or 0.0)
185 return date_planned.strftime(DEFAULT_SERVER_DATE_FORMAT)
187 def _prepare_orderpoint_procurement(self, cr, uid, orderpoint, product_qty, context=None):
188 return {'name': orderpoint.name,
189 'date_planned': self._get_orderpoint_date_planned(cr, uid, orderpoint, datetime.today(), context=context),
190 'product_id': orderpoint.product_id.id,
191 'product_qty': product_qty,
192 'company_id': orderpoint.company_id.id,
193 'product_uom': orderpoint.product_uom.id,
194 'location_id': orderpoint.location_id.id,
195 'procure_method': 'make_to_order',
196 'origin': orderpoint.name}
198 def _product_virtual_get(self, cr, uid, order_point):
199 location_obj = self.pool.get('stock.location')
200 return location_obj._product_virtual_get(cr, uid,
201 order_point.location_id.id, [order_point.product_id.id],
202 {'uom': order_point.product_uom.id})[order_point.product_id.id]
204 def _procure_orderpoint_confirm(self, cr, uid, automatic=False,\
205 use_new_cursor=False, context=None, user_id=False):
207 Create procurement based on Orderpoint
208 use_new_cursor: False or the dbname
210 @param self: The object pointer
211 @param cr: The current row, from the database cursor,
212 @param user_id: The current user ID for security checks
213 @param context: A standard dictionary for contextual values
214 @param param: False or the dbname
215 @return: Dictionary of values
221 cr = pooler.get_db(use_new_cursor).cursor()
222 orderpoint_obj = self.pool.get('stock.warehouse.orderpoint')
224 procurement_obj = self.pool.get('procurement.order')
228 self.create_automatic_op(cr, uid, context=context)
230 ids = orderpoint_obj.search(cr, uid, [], offset=offset, limit=100)
231 for op in orderpoint_obj.browse(cr, uid, ids, context=context):
232 prods = self._product_virtual_get(cr, uid, op)
235 if prods < op.product_min_qty:
236 qty = max(op.product_min_qty, op.product_max_qty)-prods
238 reste = qty % op.qty_multiple
240 qty += op.qty_multiple - reste
244 if op.product_id.type not in ('consu'):
245 if op.procurement_draft_ids:
246 # Check draft procurement related to this order point
247 pro_ids = [x.id for x in op.procurement_draft_ids]
248 procure_datas = procurement_obj.read(
249 cr, uid, pro_ids, ['id', 'product_qty'], context=context)
251 for proc_data in procure_datas:
252 if to_generate >= proc_data['product_qty']:
253 self.signal_button_confirm(cr, uid, [proc_data['id']])
254 procurement_obj.write(cr, uid, [proc_data['id']], {'origin': op.name}, context=context)
255 to_generate -= proc_data['product_qty']
261 proc_id = procurement_obj.create(cr, uid,
262 self._prepare_orderpoint_procurement(cr, uid, op, qty, context=context),
264 self.signal_button_confirm(cr, uid, [proc_id])
265 self.signal_button_check(cr, uid, [proc_id])
266 orderpoint_obj.write(cr, uid, [op.id],
267 {'procurement_id': proc_id}, context=context)
276 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: