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()
60 wf_service = netsvc.LocalService("workflow")
62 procurement_obj = self.pool.get('procurement.order')
64 ids = procurement_obj.search(cr, uid, [('state', '=', 'exception')], order="date_planned")
66 wf_service.trg_validate(uid, 'procurement.order', id, 'button_restart', cr)
69 company = self.pool.get('res.users').browse(cr, uid, uid, context=context).company_id
70 maxdate = (datetime.today() + relativedelta(days=company.schedule_range)).strftime(tools.DEFAULT_SERVER_DATE_FORMAT)
71 start_date = fields.datetime.now()
78 ids = procurement_obj.search(cr, uid, [('state', '=', 'confirmed'), ('procure_method', '=', 'make_to_order')], offset=offset, limit=500, order='priority, date_planned', context=context)
79 for proc in procurement_obj.browse(cr, uid, ids, context=context):
80 if maxdate >= proc.date_planned:
81 wf_service.trg_validate(uid, 'procurement.order', proc.id, 'button_check', cr)
86 if proc.state == 'exception':
87 report.append(_('PROC %d: on order - %3.2f %-5s - %s') % \
88 (proc.id, proc.product_qty, proc.product_uom.name,
89 proc.product_id.name))
100 ids = procurement_obj.search(cr, uid, [('state', '=', 'confirmed'), ('procure_method', '=', 'make_to_stock')], offset=offset)
101 for proc in procurement_obj.browse(cr, uid, ids):
102 if maxdate >= proc.date_planned:
103 wf_service.trg_validate(uid, 'procurement.order', proc.id, 'button_check', cr)
104 report_ids.append(proc.id)
109 if proc.state == 'exception':
110 report.append(_('PROC %d: from stock - %3.2f %-5s - %s') % \
111 (proc.id, proc.product_qty, proc.product_uom.name,
112 proc.product_id.name,))
120 end_date = fields.datetime.now()
132 def _prepare_automatic_op_procurement(self, cr, uid, product, warehouse, location_id, context=None):
133 return {'name': _('Automatic OP: %s') % (product.name,),
134 'origin': _('SCHEDULER'),
135 'date_planned': datetime.today().strftime(DEFAULT_SERVER_DATETIME_FORMAT),
136 'product_id': product.id,
137 'product_qty': -product.virtual_available,
138 'product_uom': product.uom_id.id,
139 'location_id': location_id,
140 'company_id': warehouse.company_id.id,
141 'procure_method': 'make_to_order',}
143 def create_automatic_op(self, cr, uid, context=None):
145 Create procurement of virtual stock < 0
147 @param self: The object pointer
148 @param cr: The current row, from the database cursor,
149 @param uid: The current user ID for security checks
150 @param context: A standard dictionary for contextual values
151 @return: Dictionary of values
155 product_obj = self.pool.get('product.product')
156 proc_obj = self.pool.get('procurement.order')
157 warehouse_obj = self.pool.get('stock.warehouse')
158 wf_service = netsvc.LocalService("workflow")
160 warehouse_ids = warehouse_obj.search(cr, uid, [], context=context)
161 products_ids = product_obj.search(cr, uid, [('purchase_ok', '=', True)], order='id', context=context)
163 for warehouse in warehouse_obj.browse(cr, uid, warehouse_ids, context=context):
164 context['warehouse'] = warehouse
165 # Here we check products availability.
166 # We use the method 'read' for performance reasons, because using the method 'browse' may crash the server.
167 for product_read in product_obj.read(cr, uid, products_ids, ['virtual_available'], context=context):
168 if product_read['virtual_available'] >= 0.0:
171 product = product_obj.browse(cr, uid, [product_read['id']], context=context)[0]
172 if product.supply_method == 'buy':
173 location_id = warehouse.lot_input_id.id
174 elif product.supply_method == 'produce':
175 location_id = warehouse.lot_stock_id.id
178 proc_id = proc_obj.create(cr, uid,
179 self._prepare_automatic_op_procurement(cr, uid, product, warehouse, location_id, context=context),
181 wf_service.trg_validate(uid, 'procurement.order', proc_id, 'button_confirm', cr)
182 wf_service.trg_validate(uid, 'procurement.order', proc_id, 'button_check', cr)
185 def _get_orderpoint_date_planned(self, cr, uid, orderpoint, start_date, context=None):
186 date_planned = start_date + \
187 relativedelta(days=orderpoint.product_id.seller_delay or 0.0)
188 return date_planned.strftime(DEFAULT_SERVER_DATE_FORMAT)
190 def _prepare_orderpoint_procurement(self, cr, uid, orderpoint, product_qty, context=None):
191 return {'name': orderpoint.name,
192 'date_planned': self._get_orderpoint_date_planned(cr, uid, orderpoint, datetime.today(), context=context),
193 'product_id': orderpoint.product_id.id,
194 'product_qty': product_qty,
195 'company_id': orderpoint.company_id.id,
196 'product_uom': orderpoint.product_uom.id,
197 'location_id': orderpoint.location_id.id,
198 'procure_method': 'make_to_order',
199 'origin': orderpoint.name}
201 def _product_virtual_get(self, cr, uid, order_point):
202 location_obj = self.pool.get('stock.location')
203 return location_obj._product_virtual_get(cr, uid,
204 order_point.location_id.id, [order_point.product_id.id],
205 {'uom': order_point.product_uom.id})[order_point.product_id.id]
207 def _procure_orderpoint_confirm(self, cr, uid, automatic=False,\
208 use_new_cursor=False, context=None, user_id=False):
210 Create procurement based on Orderpoint
211 use_new_cursor: False or the dbname
213 @param self: The object pointer
214 @param cr: The current row, from the database cursor,
215 @param user_id: The current user ID for security checks
216 @param context: A standard dictionary for contextual values
217 @param param: False or the dbname
218 @return: Dictionary of values
224 cr = pooler.get_db(use_new_cursor).cursor()
225 orderpoint_obj = self.pool.get('stock.warehouse.orderpoint')
227 procurement_obj = self.pool.get('procurement.order')
228 wf_service = netsvc.LocalService("workflow")
232 self.create_automatic_op(cr, uid, context=context)
234 ids = orderpoint_obj.search(cr, uid, [], offset=offset, limit=100)
235 for op in orderpoint_obj.browse(cr, uid, ids, context=context):
236 prods = self._product_virtual_get(cr, uid, op)
239 if prods < op.product_min_qty:
240 qty = max(op.product_min_qty, op.product_max_qty)-prods
242 reste = qty % op.qty_multiple
244 qty += op.qty_multiple - reste
248 if op.product_id.type not in ('consu'):
249 if op.procurement_draft_ids:
250 # Check draft procurement related to this order point
251 pro_ids = [x.id for x in op.procurement_draft_ids]
252 procure_datas = procurement_obj.read(
253 cr, uid, pro_ids, ['id', 'product_qty'], context=context)
255 for proc_data in procure_datas:
256 if to_generate >= proc_data['product_qty']:
257 wf_service.trg_validate(uid, 'procurement.order', proc_data['id'], 'button_confirm', cr)
258 procurement_obj.write(cr, uid, [proc_data['id']], {'origin': op.name}, context=context)
259 to_generate -= proc_data['product_qty']
265 proc_id = procurement_obj.create(cr, uid,
266 self._prepare_orderpoint_procurement(cr, uid, op, qty, context=context),
268 wf_service.trg_validate(uid, 'procurement.order', proc_id,
269 'button_confirm', cr)
270 wf_service.trg_validate(uid, 'procurement.order', proc_id,
272 orderpoint_obj.write(cr, uid, [op.id],
273 {'procurement_id': proc_id}, context=context)
284 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: