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
40 use_new_cursor = cr.dbname
41 self._procure_confirm(cr, uid, use_new_cursor=use_new_cursor, context=context)
42 self._procure_orderpoint_confirm(cr, uid, automatic=automatic,\
43 use_new_cursor=use_new_cursor, context=context)
45 def _procure_confirm(self, cr, uid, ids=None, use_new_cursor=False, context=None):
47 Call the scheduler to check the procurement order
49 @param self: The object pointer
50 @param cr: The current row, from the database cursor,
51 @param uid: The current user ID for security checks
52 @param ids: List of selected IDs
53 @param use_new_cursor: False or the dbname
54 @param context: A standard dictionary for contextual values
55 @return: Dictionary of values
61 cr = pooler.get_db(use_new_cursor).cursor()
62 wf_service = netsvc.LocalService("workflow")
64 procurement_obj = self.pool.get('procurement.order')
66 ids = procurement_obj.search(cr, uid, [('state', '=', 'exception')], order="date_planned")
68 wf_service.trg_validate(uid, 'procurement.order', id, 'button_restart', cr)
71 company = self.pool.get('res.users').browse(cr, uid, uid, context=context).company_id
72 maxdate = (datetime.today() + relativedelta(days=company.schedule_range)).strftime(tools.DEFAULT_SERVER_DATE_FORMAT)
73 start_date = fields.datetime.now()
80 ids = procurement_obj.search(cr, uid, [('state', '=', 'confirmed'), ('procure_method', '=', 'make_to_order')], offset=offset, limit=500, order='priority, date_planned', context=context)
81 for proc in procurement_obj.browse(cr, uid, ids, context=context):
82 if maxdate >= proc.date_planned:
83 wf_service.trg_validate(uid, 'procurement.order', proc.id, 'button_check', cr)
88 if proc.state == 'exception':
89 report.append(_('PROC %d: on order - %3.2f %-5s - %s') % \
90 (proc.id, proc.product_qty, proc.product_uom.name,
91 proc.product_id.name))
102 ids = procurement_obj.search(cr, uid, [('state', '=', 'confirmed'), ('procure_method', '=', 'make_to_stock')], offset=offset)
103 for proc in procurement_obj.browse(cr, uid, ids):
104 if maxdate >= proc.date_planned:
105 wf_service.trg_validate(uid, 'procurement.order', proc.id, 'button_check', cr)
106 report_ids.append(proc.id)
111 if proc.state == 'exception':
112 report.append(_('PROC %d: from stock - %3.2f %-5s - %s') % \
113 (proc.id, proc.product_qty, proc.product_uom.name,
114 proc.product_id.name,))
122 end_date = fields.datetime.now()
134 def _prepare_automatic_op_procurement(self, cr, uid, product, warehouse, location_id, context=None):
135 return {'name': _('Automatic OP: %s') % (product.name,),
136 'origin': _('SCHEDULER'),
137 'date_planned': datetime.today().strftime(DEFAULT_SERVER_DATETIME_FORMAT),
138 'product_id': product.id,
139 'product_qty': -product.virtual_available,
140 'product_uom': product.uom_id.id,
141 'location_id': location_id,
142 'company_id': warehouse.company_id.id,
143 'procure_method': 'make_to_order',}
145 def create_automatic_op(self, cr, uid, context=None):
147 Create procurement of virtual stock < 0
149 @param self: The object pointer
150 @param cr: The current row, from the database cursor,
151 @param uid: The current user ID for security checks
152 @param context: A standard dictionary for contextual values
153 @return: Dictionary of values
157 product_obj = self.pool.get('product.product')
158 proc_obj = self.pool.get('procurement.order')
159 warehouse_obj = self.pool.get('stock.warehouse')
160 wf_service = netsvc.LocalService("workflow")
162 warehouse_ids = warehouse_obj.search(cr, uid, [], context=context)
163 products_ids = product_obj.search(cr, uid, [], order='id', context=context)
165 for warehouse in warehouse_obj.browse(cr, uid, warehouse_ids, context=context):
166 context['warehouse'] = warehouse
167 # Here we check products availability.
168 # We use the method 'read' for performance reasons, because using the method 'browse' may crash the server.
169 for product_read in product_obj.read(cr, uid, products_ids, ['virtual_available'], context=context):
170 if product_read['virtual_available'] >= 0.0:
173 product = product_obj.browse(cr, uid, [product_read['id']], context=context)[0]
174 if product.supply_method == 'buy':
175 location_id = warehouse.lot_input_id.id
176 elif product.supply_method == 'produce':
177 location_id = warehouse.lot_stock_id.id
180 proc_id = proc_obj.create(cr, uid,
181 self._prepare_automatic_op_procurement(cr, uid, product, warehouse, location_id, context=context),
183 wf_service.trg_validate(uid, 'procurement.order', proc_id, 'button_confirm', cr)
184 wf_service.trg_validate(uid, 'procurement.order', proc_id, 'button_check', cr)
187 def _get_orderpoint_date_planned(self, cr, uid, orderpoint, start_date, context=None):
188 date_planned = start_date + \
189 relativedelta(days=orderpoint.product_id.seller_delay or 0.0)
190 return date_planned.strftime(DEFAULT_SERVER_DATE_FORMAT)
192 def _prepare_orderpoint_procurement(self, cr, uid, orderpoint, product_qty, context=None):
193 return {'name': orderpoint.name,
194 'date_planned': self._get_orderpoint_date_planned(cr, uid, orderpoint, datetime.today(), context=context),
195 'product_id': orderpoint.product_id.id,
196 'product_qty': product_qty,
197 'company_id': orderpoint.company_id.id,
198 'product_uom': orderpoint.product_uom.id,
199 'location_id': orderpoint.location_id.id,
200 'procure_method': 'make_to_order',
201 'origin': orderpoint.name}
203 def _product_virtual_get(self, cr, uid, order_point):
204 location_obj = self.pool.get('stock.location')
205 return location_obj._product_virtual_get(cr, uid,
206 order_point.location_id.id, [order_point.product_id.id],
207 {'uom': order_point.product_uom.id})[order_point.product_id.id]
209 def _procure_orderpoint_confirm(self, cr, uid, automatic=False,\
210 use_new_cursor=False, context=None, user_id=False):
212 Create procurement based on Orderpoint
213 use_new_cursor: False or the dbname
215 @param self: The object pointer
216 @param cr: The current row, from the database cursor,
217 @param user_id: The current user ID for security checks
218 @param context: A standard dictionary for contextual values
219 @param param: False or the dbname
220 @return: Dictionary of values
226 cr = pooler.get_db(use_new_cursor).cursor()
227 orderpoint_obj = self.pool.get('stock.warehouse.orderpoint')
229 procurement_obj = self.pool.get('procurement.order')
230 wf_service = netsvc.LocalService("workflow")
234 self.create_automatic_op(cr, uid, context=context)
236 ids = orderpoint_obj.search(cr, uid, [], offset=offset, limit=100)
237 for op in orderpoint_obj.browse(cr, uid, ids, context=context):
238 prods = self._product_virtual_get(cr, uid, op)
241 if prods < op.product_min_qty:
242 qty = max(op.product_min_qty, op.product_max_qty)-prods
244 reste = qty % op.qty_multiple
246 qty += op.qty_multiple - reste
250 if op.product_id.type not in ('consu'):
251 if op.procurement_draft_ids:
252 # Check draft procurement related to this order point
253 pro_ids = [x.id for x in op.procurement_draft_ids]
254 procure_datas = procurement_obj.read(
255 cr, uid, pro_ids, ['id', 'product_qty'], context=context)
257 for proc_data in procure_datas:
258 if to_generate >= proc_data['product_qty']:
259 wf_service.trg_validate(uid, 'procurement.order', proc_data['id'], 'button_confirm', cr)
260 procurement_obj.write(cr, uid, [proc_data['id']], {'origin': op.name}, context=context)
261 to_generate -= proc_data['product_qty']
267 proc_id = procurement_obj.create(cr, uid,
268 self._prepare_orderpoint_procurement(cr, uid, op, qty, context=context),
270 wf_service.trg_validate(uid, 'procurement.order', proc_id,
271 'button_confirm', cr)
272 wf_service.trg_validate(uid, 'procurement.order', proc_id,
274 orderpoint_obj.write(cr, uid, [op.id],
275 {'procurement_id': proc_id}, context=context)
286 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: