2010-01-18 10:43:33 +00:00
# -*- encoding: utf-8 -*-
##############################################################################
#
2010-04-09 00:32:46 +00:00
# OpenERP, Open Source Management Solution
2010-08-05 11:23:26 +00:00
# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>). All Rights Reserved
2010-01-18 10:43:33 +00:00
# $Id$
#
# This program is free software: you can redistribute it and/or modify
2010-10-28 06:54:18 +00:00
# it under the terms of the GNU Affero General Public License as published by
2010-01-18 10:43:33 +00:00
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2010-10-28 06:54:18 +00:00
# GNU Affero General Public License for more details.
2010-01-18 10:43:33 +00:00
#
2010-10-28 06:54:18 +00:00
# You should have received a copy of the GNU Affero General Public License
2010-01-18 10:43:33 +00:00
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
2011-12-02 07:27:55 +00:00
from datetime import datetime
from dateutil . relativedelta import relativedelta
2010-06-23 11:53:20 +00:00
import time
2010-08-24 12:37:11 +00:00
import netsvc
2010-06-23 11:53:20 +00:00
2010-01-18 10:43:33 +00:00
from osv import fields , osv
2010-06-14 11:09:51 +00:00
from tools . translate import _
2011-10-31 11:38:30 +00:00
import decimal_precision as dp
2010-01-18 10:43:33 +00:00
2010-04-09 07:18:17 +00:00
class purchase_requisition ( osv . osv ) :
_name = " purchase.requisition "
2010-05-19 18:32:32 +00:00
_description = " Purchase Requisition "
2012-06-01 17:30:11 +00:00
_inherit = [ ' ir.needaction_mixin ' , ' mail.thread ' ]
2010-01-18 10:43:33 +00:00
_columns = {
2010-04-09 07:18:17 +00:00
' name ' : fields . char ( ' Requisition Reference ' , size = 32 , required = True ) ,
2012-05-04 12:32:02 +00:00
' origin ' : fields . char ( ' Source ' , size = 32 ) ,
2010-04-09 07:47:07 +00:00
' date_start ' : fields . datetime ( ' Requisition Date ' ) ,
' date_end ' : fields . datetime ( ' Requisition Deadline ' ) ,
2010-01-18 10:43:33 +00:00
' user_id ' : fields . many2one ( ' res.users ' , ' Responsible ' ) ,
2010-09-17 12:36:57 +00:00
' exclusive ' : fields . selection ( [ ( ' exclusive ' , ' Purchase Requisition (exclusive) ' ) , ( ' multiple ' , ' Multiple Requisitions ' ) ] , ' Requisition Type ' , required = True , help = " Purchase Requisition (exclusive): On the confirmation of a purchase order, it cancels the remaining purchase order. \n Purchase Requisition(Multiple): It allows to have multiple purchase orders.On confirmation of a purchase order it does not cancel the remaining orders " " " ) ,
2010-01-18 10:43:33 +00:00
' description ' : fields . text ( ' Description ' ) ,
2010-04-23 07:24:58 +00:00
' company_id ' : fields . many2one ( ' res.company ' , ' Company ' , required = True ) ,
2010-07-08 14:21:02 +00:00
' purchase_ids ' : fields . one2many ( ' purchase.order ' , ' requisition_id ' , ' Purchase Orders ' , states = { ' done ' : [ ( ' readonly ' , True ) ] } ) ,
2010-06-21 13:59:11 +00:00
' line_ids ' : fields . one2many ( ' purchase.requisition.line ' , ' requisition_id ' , ' Products to Purchase ' , states = { ' done ' : [ ( ' readonly ' , True ) ] } ) ,
2010-08-25 06:24:52 +00:00
' warehouse_id ' : fields . many2one ( ' stock.warehouse ' , ' Warehouse ' ) ,
2012-05-31 09:59:13 +00:00
' state ' : fields . selection ( [ ( ' draft ' , ' New ' ) , ( ' in_progress ' , ' Sent to Suppliers ' ) , ( ' cancel ' , ' Cancelled ' ) , ( ' done ' , ' Purchase Done ' ) ] , ' Status ' , required = True )
2010-01-18 10:43:33 +00:00
}
_defaults = {
2010-06-23 11:53:20 +00:00
' date_start ' : time . strftime ( ' % Y- % m- %d % H: % M: % S ' ) ,
' state ' : ' draft ' ,
' exclusive ' : ' multiple ' ,
2010-07-01 06:59:57 +00:00
' company_id ' : lambda self , cr , uid , c : self . pool . get ( ' res.company ' ) . _company_default_get ( cr , uid , ' purchase.requisition ' , context = c ) ,
' user_id ' : lambda self , cr , uid , c : self . pool . get ( ' res.users ' ) . browse ( cr , uid , uid , c ) . id ,
2010-04-09 07:18:17 +00:00
' name ' : lambda obj , cr , uid , context : obj . pool . get ( ' ir.sequence ' ) . get ( cr , uid , ' purchase.order.requisition ' ) ,
2010-01-18 10:43:33 +00:00
}
2012-06-01 12:30:48 +00:00
2010-11-22 10:37:53 +00:00
def copy ( self , cr , uid , id , default = None , context = None ) :
2010-08-25 11:39:56 +00:00
if not default :
default = { }
default . update ( {
' state ' : ' draft ' ,
' purchase_ids ' : [ ] ,
' name ' : self . pool . get ( ' ir.sequence ' ) . get ( cr , uid , ' purchase.order.requisition ' ) ,
} )
return super ( purchase_requisition , self ) . copy ( cr , uid , id , default , context )
2012-06-04 13:26:53 +00:00
2010-06-23 11:53:20 +00:00
def tender_cancel ( self , cr , uid , ids , context = None ) :
2010-04-12 06:58:25 +00:00
purchase_order_obj = self . pool . get ( ' purchase.order ' )
2010-11-22 10:37:53 +00:00
for purchase in self . browse ( cr , uid , ids , context = context ) :
2010-04-12 06:58:25 +00:00
for purchase_id in purchase . purchase_ids :
if str ( purchase_id . state ) in ( ' draft ' , ' wait ' ) :
2010-04-23 07:24:58 +00:00
purchase_order_obj . action_cancel ( cr , uid , [ purchase_id . id ] )
2010-04-09 13:57:28 +00:00
self . write ( cr , uid , ids , { ' state ' : ' cancel ' } )
2012-06-04 13:26:53 +00:00
self . cancel_send_note ( cr , uid , ids , context = context )
2010-04-23 07:24:58 +00:00
return True
2010-07-01 06:59:57 +00:00
2010-06-23 11:53:20 +00:00
def tender_in_progress ( self , cr , uid , ids , context = None ) :
2010-04-21 09:19:35 +00:00
self . write ( cr , uid , ids , { ' state ' : ' in_progress ' } , context = context )
2012-06-04 13:26:53 +00:00
self . in_progress_send_note ( cr , uid , ids , context = context )
2010-04-23 07:24:58 +00:00
return True
2010-07-01 06:59:57 +00:00
2010-06-23 11:53:20 +00:00
def tender_reset ( self , cr , uid , ids , context = None ) :
2010-04-09 13:57:28 +00:00
self . write ( cr , uid , ids , { ' state ' : ' draft ' } )
2012-06-04 13:26:53 +00:00
self . reset_send_note ( cr , uid , ids , context = context )
2010-04-23 07:24:58 +00:00
return True
2010-07-01 06:59:57 +00:00
2010-06-23 11:53:20 +00:00
def tender_done ( self , cr , uid , ids , context = None ) :
2010-04-21 09:19:35 +00:00
self . write ( cr , uid , ids , { ' state ' : ' done ' , ' date_end ' : time . strftime ( ' % Y- % m- %d % H: % M: % S ' ) } , context = context )
2012-06-04 13:26:53 +00:00
self . done_to_send_note ( cr , uid , ids , context = context )
2010-04-23 07:24:58 +00:00
return True
2012-06-04 13:26:53 +00:00
def in_progress_send_note ( self , cr , uid , ids , context = None ) :
for requisition in self . browse ( cr , uid , ids , context = context ) :
2012-06-08 08:33:05 +00:00
self . message_append_note ( cr , uid , [ requisition . id ] , body = _ ( " Draft Requisition has been <b>sent to suppliers</b>. " ) , context = context )
2012-06-04 13:26:53 +00:00
def reset_send_note ( self , cr , uid , ids , context = None ) :
2012-06-08 08:33:05 +00:00
self . message_append_note ( cr , uid , ids , body = _ ( """ Purchase Requisition has been set to <b>draft</b>. """ ) , context = context )
2012-06-04 13:26:53 +00:00
def done_to_send_note ( self , cr , uid , ids , context = None ) :
2012-06-08 08:29:19 +00:00
self . message_append_note ( cr , uid , ids , body = _ ( " Purchase Requisition has been set to <b>done</b>. " ) , context = context )
2012-06-04 13:26:53 +00:00
def draft_send_note ( self , cr , uid , ids , context = None ) :
return self . message_append_note ( cr , uid , ids , body = _ ( " Purchase Requisition has been set to <b>draft</b>. " ) , context = context )
def cancel_send_note ( self , cr , uid , ids , context = None ) :
for obj in self . browse ( cr , uid , ids , context = context ) :
self . message_append_note ( cr , uid , [ obj . id ] , body = _ ( " Purchase Requisition has been <b>cancelled</b>. " ) , context = context )
2011-12-02 07:27:55 +00:00
def _planned_date ( self , requisition , delay = 0.0 ) :
company = requisition . company_id
date_planned = False
if requisition . date_start :
date_planned = datetime . strptime ( requisition . date_start , ' % Y- % m- %d % H: % M: % S ' ) - relativedelta ( days = company . po_lead )
else :
date_planned = datetime . today ( ) - relativedelta ( days = company . po_lead )
if delay :
date_planned - = relativedelta ( days = delay )
return date_planned and date_planned . strftime ( ' % Y- % m- %d % H: % M: % S ' ) or False
def _seller_details ( self , cr , uid , requisition_line , supplier , context = None ) :
product_uom = self . pool . get ( ' product.uom ' )
pricelist = self . pool . get ( ' product.pricelist ' )
supplier_info = self . pool . get ( " product.supplierinfo " )
product = requisition_line . product_id
default_uom_po_id = product . uom_po_id . id
qty = product_uom . _compute_qty ( cr , uid , requisition_line . product_uom_id . id , requisition_line . product_qty , default_uom_po_id )
seller_delay = 0.0
seller_price = False
seller_qty = False
for product_supplier in product . seller_ids :
if supplier . id == product_supplier . name and qty > = product_supplier . qty :
seller_delay = product_supplier . delay
seller_qty = product_supplier . qty
supplier_pricelist = supplier . property_product_pricelist_purchase or False
seller_price = pricelist . price_get ( cr , uid , [ supplier_pricelist . id ] , product . id , qty , False , { ' uom ' : default_uom_po_id } ) [ supplier_pricelist . id ]
if seller_qty :
qty = max ( qty , seller_qty )
date_planned = self . _planned_date ( requisition_line . requisition_id , seller_delay )
return seller_price , qty , default_uom_po_id , date_planned
def make_purchase_order ( self , cr , uid , ids , partner_id , context = None ) :
"""
Create New RFQ for Supplier
"""
if context is None :
context = { }
assert partner_id , ' Supplier should be specified '
purchase_order = self . pool . get ( ' purchase.order ' )
purchase_order_line = self . pool . get ( ' purchase.order.line ' )
res_partner = self . pool . get ( ' res.partner ' )
fiscal_position = self . pool . get ( ' account.fiscal.position ' )
supplier = res_partner . browse ( cr , uid , partner_id , context = context )
supplier_pricelist = supplier . property_product_pricelist_purchase or False
res = { }
for requisition in self . browse ( cr , uid , ids , context = context ) :
if supplier . id in filter ( lambda x : x , [ rfq . state < > ' cancel ' and rfq . partner_id . id or None for rfq in requisition . purchase_ids ] ) :
2011-12-23 11:22:41 +00:00
raise osv . except_osv ( _ ( ' Warning ' ) , _ ( ' You have already one %s purchase order for this partner, you must cancel this purchase order to create a new quotation. ' ) % rfq . state )
2011-12-02 07:27:55 +00:00
location_id = requisition . warehouse_id . lot_input_id . id
purchase_id = purchase_order . create ( cr , uid , {
' origin ' : requisition . name ,
' partner_id ' : supplier . id ,
' pricelist_id ' : supplier_pricelist . id ,
' location_id ' : location_id ,
' company_id ' : requisition . company_id . id ,
' fiscal_position ' : supplier . property_account_position and supplier . property_account_position . id or False ,
' requisition_id ' : requisition . id ,
' notes ' : requisition . description ,
' warehouse_id ' : requisition . warehouse_id . id ,
} )
res [ requisition . id ] = purchase_id
for line in requisition . line_ids :
product = line . product_id
seller_price , qty , default_uom_po_id , date_planned = self . _seller_details ( cr , uid , line , supplier , context = context )
taxes_ids = product . supplier_taxes_id
taxes = fiscal_position . map_tax ( cr , uid , supplier . property_account_position , taxes_ids )
purchase_order_line . create ( cr , uid , {
' order_id ' : purchase_id ,
' name ' : product . partner_ref ,
' product_qty ' : qty ,
' product_id ' : product . id ,
' product_uom ' : default_uom_po_id ,
' price_unit ' : seller_price ,
' date_planned ' : date_planned ,
' notes ' : product . description_purchase ,
' taxes_id ' : [ ( 6 , 0 , taxes ) ] ,
} , context = context )
return res
2012-06-01 12:30:48 +00:00
def get_needaction_user_ids ( self , cr , uid , ids , context = None ) :
result = dict . fromkeys ( ids , [ ] )
for obj in self . browse ( cr , uid , ids , context = context ) :
2012-06-02 06:58:41 +00:00
if ( obj . state == ' draft ' ) and obj . user_id :
2012-06-01 12:30:48 +00:00
result [ obj . id ] = [ obj . user_id . id ]
return result
2012-06-04 13:26:53 +00:00
def create_send_note ( self , cr , uid , ids , context = None ) :
return self . message_append_note ( cr , uid , ids , body = _ ( " Purchase Requisition has been <b>created</b>. " ) , context = context )
def create ( self , cr , uid , vals , context = None ) :
requisition = super ( purchase_requisition , self ) . create ( cr , uid , vals , context = context )
if requisition :
self . create_send_note ( cr , uid , [ requisition ] , context = context )
return requisition
2011-12-02 07:27:55 +00:00
2010-04-09 07:18:17 +00:00
purchase_requisition ( )
2010-01-18 10:43:33 +00:00
2010-04-09 07:18:17 +00:00
class purchase_requisition_line ( osv . osv ) :
2010-07-01 06:59:57 +00:00
2010-04-09 07:18:17 +00:00
_name = " purchase.requisition.line "
_description = " Purchase Requisition Line "
2010-04-09 00:32:46 +00:00
_rec_name = ' product_id '
_columns = {
2010-05-04 12:54:03 +00:00
' product_id ' : fields . many2one ( ' product.product ' , ' Product ' ) ,
2012-04-25 12:09:08 +00:00
' product_uom_id ' : fields . many2one ( ' product.uom ' , ' Product Unit of Measure ' ) ,
' product_qty ' : fields . float ( ' Quantity ' , digits_compute = dp . get_precision ( ' Product Unit of Measure ' ) ) ,
2010-04-16 06:17:50 +00:00
' requisition_id ' : fields . many2one ( ' purchase.requisition ' , ' Purchase Requisition ' , ondelete = ' cascade ' ) ,
2011-12-02 07:27:55 +00:00
' company_id ' : fields . related ( ' requisition_id ' , ' company_id ' , type = ' many2one ' , relation = ' res.company ' , string = ' Company ' , store = True , readonly = True ) ,
2010-04-09 00:32:46 +00:00
}
2010-04-12 10:23:28 +00:00
2010-06-23 11:53:20 +00:00
def onchange_product_id ( self , cr , uid , ids , product_id , product_uom_id , context = None ) :
2010-04-09 13:57:28 +00:00
""" Changes UoM and name if product_id changes.
@param name : Name of the field
@param product_id : Changed product_id
@return : Dictionary of changed values
"""
2010-05-20 11:10:36 +00:00
value = { ' product_uom_id ' : ' ' }
2010-04-09 13:57:28 +00:00
if product_id :
2010-11-23 11:31:52 +00:00
prod = self . pool . get ( ' product.product ' ) . browse ( cr , uid , product_id , context = context )
2010-06-21 13:59:11 +00:00
value = { ' product_uom_id ' : prod . uom_id . id , ' product_qty ' : 1.0 }
2010-04-09 13:57:28 +00:00
return { ' value ' : value }
2010-05-05 12:48:59 +00:00
2010-04-23 07:24:58 +00:00
_defaults = {
2010-07-01 06:59:57 +00:00
' company_id ' : lambda self , cr , uid , c : self . pool . get ( ' res.company ' ) . _company_default_get ( cr , uid , ' purchase.requisition.line ' , context = c ) ,
2010-08-13 12:20:05 +00:00
}
2010-04-09 07:18:17 +00:00
purchase_requisition_line ( )
2010-04-09 00:32:46 +00:00
2010-01-18 10:43:33 +00:00
class purchase_order ( osv . osv ) :
_inherit = " purchase.order "
_columns = {
2010-04-09 07:18:17 +00:00
' requisition_id ' : fields . many2one ( ' purchase.requisition ' , ' Purchase Requisition ' )
2010-01-18 10:43:33 +00:00
}
2012-06-04 13:26:53 +00:00
2010-11-22 10:37:53 +00:00
def wkf_confirm_order ( self , cr , uid , ids , context = None ) :
res = super ( purchase_order , self ) . wkf_confirm_order ( cr , uid , ids , context = context )
2011-03-08 14:52:52 +00:00
proc_obj = self . pool . get ( ' procurement.order ' )
2010-11-22 10:37:53 +00:00
for po in self . browse ( cr , uid , ids , context = context ) :
2010-04-09 07:47:07 +00:00
if po . requisition_id and ( po . requisition_id . exclusive == ' exclusive ' ) :
2010-04-09 07:18:17 +00:00
for order in po . requisition_id . purchase_ids :
2010-01-18 10:43:33 +00:00
if order . id < > po . id :
2010-07-09 11:35:54 +00:00
proc_ids = proc_obj . search ( cr , uid , [ ( ' purchase_id ' , ' = ' , order . id ) ] )
if proc_ids and po . state == ' confirmed ' :
2011-02-25 11:40:03 +00:00
proc_obj . write ( cr , uid , proc_ids , { ' purchase_id ' : po . id } )
2010-01-18 10:43:33 +00:00
wf_service = netsvc . LocalService ( " workflow " )
wf_service . trg_validate ( uid , ' purchase.order ' , order . id , ' purchase_cancel ' , cr )
2011-12-02 05:15:06 +00:00
po . requisition_id . tender_done ( context = context )
2010-01-18 10:43:33 +00:00
return res
2010-04-09 00:32:46 +00:00
2010-04-23 07:24:58 +00:00
purchase_order ( )
2010-04-09 00:32:46 +00:00
class product_product ( osv . osv ) :
_inherit = ' product.product '
2010-07-01 06:59:57 +00:00
2010-04-09 00:32:46 +00:00
_columns = {
2012-05-31 09:59:13 +00:00
' purchase_requisition ' : fields . boolean ( ' Purchase Requisition ' , help = " Check this box so that procurement generates purchase requisition instead of only generating requests for quotation. " )
2010-04-09 00:32:46 +00:00
}
_defaults = {
2010-06-23 11:53:20 +00:00
' purchase_requisition ' : False
2010-04-09 00:32:46 +00:00
}
product_product ( )
2010-05-27 12:47:06 +00:00
class procurement_order ( osv . osv ) :
2010-07-01 06:59:57 +00:00
2010-05-27 12:47:06 +00:00
_inherit = ' procurement.order '
2010-07-09 09:26:48 +00:00
_columns = {
' requisition_id ' : fields . many2one ( ' purchase.requisition ' , ' Latest Requisition ' )
}
2010-07-01 06:59:57 +00:00
def make_po ( self , cr , uid , ids , context = None ) :
sequence_obj = self . pool . get ( ' ir.sequence ' )
res = super ( procurement_order , self ) . make_po ( cr , uid , ids , context = context )
for proc_id , po_id in res . items ( ) :
2010-11-22 10:37:53 +00:00
procurement = self . browse ( cr , uid , proc_id , context = context )
2010-07-09 09:26:48 +00:00
requisition_id = False
2010-04-09 07:18:17 +00:00
if procurement . product_id . purchase_requisition :
2010-07-09 09:26:48 +00:00
requisition_id = self . pool . get ( ' purchase.requisition ' ) . create ( cr , uid , {
2010-04-21 09:19:35 +00:00
' name ' : sequence_obj . get ( cr , uid , ' purchase.order.requisition ' ) ,
2011-10-13 13:07:31 +00:00
' origin ' : procurement . origin ,
2010-04-09 07:47:07 +00:00
' date_end ' : procurement . date_planned ,
2010-08-25 11:59:37 +00:00
' warehouse_id ' : procurement . purchase_id and procurement . purchase_id . warehouse_id . id ,
' company_id ' : procurement . company_id . id ,
2010-04-21 07:56:40 +00:00
' line_ids ' : [ ( 0 , 0 , {
2010-04-09 00:32:46 +00:00
' product_id ' : procurement . product_id . id ,
' product_uom_id ' : procurement . product_uom . id ,
' product_qty ' : procurement . product_qty
} ) ] ,
' purchase_ids ' : [ ( 6 , 0 , [ po_id ] ) ]
} )
2012-05-15 06:01:11 +00:00
self . write ( cr , uid , [ proc_id ] , { ' requisition_id ' : requisition_id } , context = context )
2010-04-09 07:47:07 +00:00
return res
2010-07-01 06:59:57 +00:00
2010-05-27 12:47:06 +00:00
procurement_order ( )
2010-06-23 11:53:20 +00:00
2010-08-24 12:37:11 +00:00
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: