2009-10-13 05:58:37 +00:00
# -*- coding: utf-8 -*-
2006-12-07 13:41:40 +00:00
##############################################################################
2009-11-25 09:31:44 +00:00
#
2009-01-19 13:59:11 +00:00
# OpenERP, Open Source Management Solution
2010-01-12 09:18:39 +00:00
# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
2008-06-16 11:00:21 +00:00
#
2008-11-03 19:18:56 +00:00
# This program is free software: you can redistribute it and/or modify
2009-10-14 11:15:34 +00:00
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
2006-12-07 13:41:40 +00:00
#
2008-11-03 19:18:56 +00:00
# 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
2009-10-14 11:15:34 +00:00
# GNU Affero General Public License for more details.
2006-12-07 13:41:40 +00:00
#
2009-10-14 11:15:34 +00:00
# You should have received a copy of the GNU Affero General Public License
2009-11-25 09:31:44 +00:00
# along with this program. If not, see <http://www.gnu.org/licenses/>.
2006-12-07 13:41:40 +00:00
#
##############################################################################
import time
2010-03-01 05:13:20 +00:00
from datetime import datetime
from dateutil . relativedelta import relativedelta
2010-06-23 07:23:38 +00:00
from osv import osv , fields
import netsvc
2006-12-07 13:41:40 +00:00
import pooler
2008-07-08 08:13:12 +00:00
from tools . translate import _
2010-03-06 20:52:19 +00:00
import decimal_precision as dp
2010-03-25 08:25:11 +00:00
from osv . orm import browse_record , browse_null
2012-04-18 13:41:43 +00:00
from tools import DEFAULT_SERVER_DATE_FORMAT , DEFAULT_SERVER_DATETIME_FORMAT , DATETIME_FORMATS_MAP
2010-03-06 20:52:19 +00:00
2006-12-07 13:41:40 +00:00
class purchase_order ( osv . osv ) :
2010-10-27 05:55:22 +00:00
2010-06-17 07:10:42 +00:00
def _amount_all ( self , cr , uid , ids , field_name , arg , context = None ) :
2008-07-22 15:11:28 +00:00
res = { }
cur_obj = self . pool . get ( ' res.currency ' )
2010-11-19 13:48:01 +00:00
for order in self . browse ( cr , uid , ids , context = context ) :
2008-12-24 15:50:06 +00:00
res [ order . id ] = {
' amount_untaxed ' : 0.0 ,
' amount_tax ' : 0.0 ,
' amount_total ' : 0.0 ,
2010-08-13 12:20:05 +00:00
}
2008-12-24 15:50:06 +00:00
val = val1 = 0.0
2010-06-17 07:10:42 +00:00
cur = order . pricelist_id . currency_id
2008-07-22 15:11:28 +00:00
for line in order . order_line :
2010-06-17 07:10:42 +00:00
val1 + = line . price_subtotal
2012-03-29 12:41:38 +00:00
for c in self . pool . get ( ' account.tax ' ) . compute_all ( cr , uid , line . taxes_id , line . price_unit , line . product_qty , line . product_id . id , order . partner_id ) [ ' taxes ' ] :
2010-10-08 06:13:09 +00:00
val + = c . get ( ' amount ' , 0.0 )
2008-12-24 15:50:06 +00:00
res [ order . id ] [ ' amount_tax ' ] = cur_obj . round ( cr , uid , cur , val )
res [ order . id ] [ ' amount_untaxed ' ] = cur_obj . round ( cr , uid , cur , val1 )
res [ order . id ] [ ' amount_total ' ] = res [ order . id ] [ ' amount_untaxed ' ] + res [ order . id ] [ ' amount_tax ' ]
2008-07-22 15:11:28 +00:00
return res
2010-06-23 07:23:38 +00:00
def _set_minimum_planned_date ( self , cr , uid , ids , name , value , arg , context = None ) :
2008-09-17 07:55:45 +00:00
if not value : return False
if type ( ids ) != type ( [ ] ) :
ids = [ ids ]
2010-11-19 13:48:01 +00:00
for po in self . browse ( cr , uid , ids , context = context ) :
2011-01-18 12:35:08 +00:00
if po . order_line :
cr . execute ( """ update purchase_order_line set
date_planned = % s
where
order_id = % s and
( date_planned = % s or date_planned < % s ) """ , (value,po.id,po.minimum_planned_date,value))
cr . execute ( """ update purchase_order set
minimum_planned_date = % s where id = % s """ , (value, po.id))
2008-09-17 07:55:45 +00:00
return True
2010-06-23 07:23:38 +00:00
def _minimum_planned_date ( self , cr , uid , ids , field_name , arg , context = None ) :
2008-08-30 09:14:15 +00:00
res = { }
purchase_obj = self . browse ( cr , uid , ids , context = context )
for purchase in purchase_obj :
2011-01-18 12:35:08 +00:00
res [ purchase . id ] = False
2008-08-30 09:14:15 +00:00
if purchase . order_line :
min_date = purchase . order_line [ 0 ] . date_planned
for line in purchase . order_line :
if line . date_planned < min_date :
min_date = line . date_planned
res [ purchase . id ] = min_date
return res
2010-12-15 10:14:49 +00:00
2008-08-27 11:26:37 +00:00
def _invoiced_rate ( self , cursor , user , ids , name , arg , context = None ) :
res = { }
for purchase in self . browse ( cursor , user , ids , context = context ) :
tot = 0.0
2010-12-15 10:14:49 +00:00
for invoice in purchase . invoice_ids :
if invoice . state not in ( ' draft ' , ' cancel ' ) :
tot + = invoice . amount_untaxed
2008-08-28 23:48:43 +00:00
if purchase . amount_untaxed :
2008-08-27 11:26:37 +00:00
res [ purchase . id ] = tot * 100.0 / purchase . amount_untaxed
else :
res [ purchase . id ] = 0.0
return res
def _shipped_rate ( self , cr , uid , ids , name , arg , context = None ) :
if not ids : return { }
res = { }
for id in ids :
res [ id ] = [ 0.0 , 0.0 ]
cr . execute ( ''' SELECT
p . purchase_id , sum ( m . product_qty ) , m . state
FROM
stock_move m
LEFT JOIN
stock_picking p on ( p . id = m . picking_id )
WHERE
2010-06-10 13:34:19 +00:00
p . purchase_id IN % s GROUP BY m . state , p . purchase_id ''' ,(tuple(ids),))
2008-08-27 11:26:37 +00:00
for oid , nbr , state in cr . fetchall ( ) :
if state == ' cancel ' :
continue
if state == ' done ' :
res [ oid ] [ 0 ] + = nbr or 0.0
res [ oid ] [ 1 ] + = nbr or 0.0
else :
res [ oid ] [ 1 ] + = nbr or 0.0
for r in res :
if not res [ r ] [ 1 ] :
res [ r ] = 0.0
else :
res [ r ] = 100.0 * res [ r ] [ 0 ] / res [ r ] [ 1 ]
return res
2010-06-23 07:23:38 +00:00
def _get_order ( self , cr , uid , ids , context = None ) :
2008-12-24 15:50:06 +00:00
result = { }
for line in self . pool . get ( ' purchase.order.line ' ) . browse ( cr , uid , ids , context = context ) :
result [ line . order_id . id ] = True
return result . keys ( )
2009-05-05 11:00:23 +00:00
def _invoiced ( self , cursor , user , ids , name , arg , context = None ) :
res = { }
for purchase in self . browse ( cursor , user , ids , context = context ) :
2010-12-20 13:00:56 +00:00
invoiced = False
if purchase . invoiced_rate == 100.00 :
invoiced = True
2010-12-15 10:14:49 +00:00
res [ purchase . id ] = invoiced
2009-05-05 11:00:23 +00:00
return res
2012-10-25 10:05:15 +00:00
def _get_journal ( self , cr , uid , context = None ) :
if context is None :
context = { }
user = self . pool . get ( ' res.users ' ) . browse ( cr , uid , uid , context = context )
company_id = context . get ( ' company_id ' , user . company_id . id )
journal_obj = self . pool . get ( ' account.journal ' )
res = journal_obj . search ( cr , uid , [ ( ' type ' , ' = ' , ' purchase ' ) ,
( ' company_id ' , ' = ' , company_id ) ] ,
limit = 1 )
return res and res [ 0 ] or False
2009-05-05 11:00:23 +00:00
2010-09-03 09:38:12 +00:00
STATE_SELECTION = [
2012-02-27 13:15:57 +00:00
( ' draft ' , ' Draft PO ' ) ,
2012-05-25 14:35:49 +00:00
( ' sent ' , ' RFQ Sent ' ) ,
2011-01-12 15:28:42 +00:00
( ' confirmed ' , ' Waiting Approval ' ) ,
2012-02-27 13:15:57 +00:00
( ' approved ' , ' Purchase Order ' ) ,
2010-10-08 06:13:09 +00:00
( ' except_picking ' , ' Shipping Exception ' ) ,
2010-09-03 09:38:12 +00:00
( ' except_invoice ' , ' Invoice Exception ' ) ,
( ' done ' , ' Done ' ) ,
( ' cancel ' , ' Cancelled ' )
]
2010-10-08 06:13:09 +00:00
2008-07-22 15:11:28 +00:00
_columns = {
2012-07-13 10:17:51 +00:00
' name ' : fields . char ( ' Order Reference ' , size = 64 , required = True , select = True , help = " Unique number of the purchase order, computed automatically when the purchase order is created. " ) ,
2010-02-09 08:31:46 +00:00
' origin ' : fields . char ( ' Source Document ' , size = 64 ,
2012-10-25 08:36:44 +00:00
help = " Reference of the document that generated this purchase order request; a sale order or an internal procurement request. "
2008-09-22 08:14:07 +00:00
) ,
2012-10-25 08:36:44 +00:00
' partner_ref ' : fields . char ( ' Supplier Reference ' , states = { ' confirmed ' : [ ( ' readonly ' , True ) ] , ' approved ' : [ ( ' readonly ' , True ) ] , ' done ' : [ ( ' readonly ' , True ) ] } , size = 64 ,
help = " Reference of the sale order or quotation sent by your supplier. It ' s mainly used to do the matching when you receive the products as this reference is usually written on the delivery order sent by your supplier. " ) ,
2011-11-01 06:06:29 +00:00
' date_order ' : fields . date ( ' Order Date ' , required = True , states = { ' confirmed ' : [ ( ' readonly ' , True ) ] , ' approved ' : [ ( ' readonly ' , True ) ] } , select = True , help = " Date on which this document has been created. " ) ,
2011-01-17 06:22:31 +00:00
' date_approve ' : fields . date ( ' Date Approved ' , readonly = 1 , select = True , help = " Date on which purchase order has been approved " ) ,
2009-12-03 14:56:23 +00:00
' partner_id ' : fields . many2one ( ' res.partner ' , ' Supplier ' , required = True , states = { ' confirmed ' : [ ( ' readonly ' , True ) ] , ' approved ' : [ ( ' readonly ' , True ) ] , ' done ' : [ ( ' readonly ' , True ) ] } , change_default = True ) ,
2012-03-30 09:08:37 +00:00
' dest_address_id ' : fields . many2one ( ' res.partner ' , ' Customer Address (Direct Delivery) ' ,
2010-06-21 21:20:55 +00:00
states = { ' confirmed ' : [ ( ' readonly ' , True ) ] , ' approved ' : [ ( ' readonly ' , True ) ] , ' done ' : [ ( ' readonly ' , True ) ] } ,
2012-07-14 21:18:27 +00:00
help = " Put an address if you want to deliver directly from the supplier to the customer. " \
" Otherwise, keep empty to deliver to your own company. "
2008-09-22 08:14:07 +00:00
) ,
2012-07-16 12:13:10 +00:00
' warehouse_id ' : fields . many2one ( ' stock.warehouse ' , ' Destination Warehouse ' , states = { ' confirmed ' : [ ( ' readonly ' , True ) ] , ' approved ' : [ ( ' readonly ' , True ) ] , ' done ' : [ ( ' readonly ' , True ) ] } ) ,
2010-05-10 13:54:26 +00:00
' location_id ' : fields . many2one ( ' stock.location ' , ' Destination ' , required = True , domain = [ ( ' usage ' , ' <> ' , ' view ' ) ] ) ,
2009-12-03 14:56:23 +00:00
' pricelist_id ' : fields . many2one ( ' product.pricelist ' , ' Pricelist ' , required = True , states = { ' confirmed ' : [ ( ' readonly ' , True ) ] , ' approved ' : [ ( ' readonly ' , True ) ] , ' done ' : [ ( ' readonly ' , True ) ] } , help = " The pricelist sets the currency used for this purchase order. It also computes the supplier price for the selected products/quantities. " ) ,
2012-10-10 11:20:13 +00:00
' currency_id ' : fields . related ( ' pricelist_id ' , ' currency_id ' , type = " many2one " , relation = " res.currency " , readonly = True , required = True ) ,
2012-10-12 11:42:58 +00:00
' state ' : fields . selection ( STATE_SELECTION , ' Status ' , readonly = True , help = " The status of the purchase order or the quotation request. A quotation is a purchase order in a ' Draft ' status. Then the order has to be confirmed by the user, the status switch to ' Confirmed ' . Then the supplier must confirm the order to change the status to ' Approved ' . When the purchase order is paid and received, the status becomes ' Done ' . If a cancel action occurs in the invoice or in the reception of goods, the status becomes in exception. " , select = True ) ,
2009-12-03 14:56:23 +00:00
' order_line ' : fields . one2many ( ' purchase.order.line ' , ' order_id ' , ' Order Lines ' , states = { ' approved ' : [ ( ' readonly ' , True ) ] , ' done ' : [ ( ' readonly ' , True ) ] } ) ,
2008-07-22 15:11:28 +00:00
' validator ' : fields . many2one ( ' res.users ' , ' Validated by ' , readonly = True ) ,
2012-06-09 19:49:46 +00:00
' notes ' : fields . text ( ' Terms and Conditions ' ) ,
2010-12-21 14:17:59 +00:00
' invoice_ids ' : fields . many2many ( ' account.invoice ' , ' purchase_invoice_rel ' , ' purchase_id ' , ' invoice_id ' , ' Invoices ' , help = " Invoices generated for a purchase order " ) ,
2012-07-13 10:17:51 +00:00
' picking_ids ' : fields . one2many ( ' stock.picking.in ' , ' purchase_id ' , ' Picking List ' , readonly = True , help = " This is the list of incoming shipments that have been generated for this purchase order. " ) ,
2010-06-21 13:59:11 +00:00
' shipped ' : fields . boolean ( ' Received ' , readonly = True , select = True , help = " It indicates that a picking has been done " ) ,
2011-07-01 23:41:24 +00:00
' shipped_rate ' : fields . function ( _shipped_rate , string = ' Received ' , type = ' float ' ) ,
2012-03-05 05:11:17 +00:00
' invoiced ' : fields . function ( _invoiced , string = ' Invoice Received ' , type = ' boolean ' , help = " It indicates that an invoice has been paid " ) ,
2011-07-01 23:41:24 +00:00
' invoiced_rate ' : fields . function ( _invoiced_rate , string = ' Invoiced ' , type = ' float ' ) ,
2012-09-17 11:31:13 +00:00
' invoice_method ' : fields . selection ( [ ( ' manual ' , ' Based on Purchase Order lines ' ) , ( ' order ' , ' Based on generated draft invoice ' ) , ( ' picking ' , ' Based on incoming shipments ' ) ] , ' Invoicing Control ' , required = True ,
2011-12-21 11:59:30 +00:00
help = " Based on Purchase Order lines: place individual lines in ' Invoice Control > Based on P.O. lines ' from where you can selectively create an invoice. \n " \
2011-11-09 12:38:42 +00:00
" Based on generated invoice: create a draft invoice you can validate later. \n " \
2012-05-07 05:46:56 +00:00
" Bases on incoming shipments: let you create an invoice when receptions are validated. "
2008-09-22 08:14:07 +00:00
) ,
2011-07-01 23:41:24 +00:00
' minimum_planned_date ' : fields . function ( _minimum_planned_date , fnct_inv = _set_minimum_planned_date , string = ' Expected Date ' , type = ' date ' , select = True , help = " This is computed as the minimum scheduled date of all purchase order lines ' products. " ,
2011-01-18 12:35:08 +00:00
store = {
' purchase.order.line ' : ( _get_order , [ ' date_planned ' ] , 10 ) ,
}
) ,
2012-07-10 12:24:57 +00:00
' amount_untaxed ' : fields . function ( _amount_all , digits_compute = dp . get_precision ( ' Account ' ) , string = ' Untaxed Amount ' ,
2008-12-24 15:50:06 +00:00
store = {
' purchase.order.line ' : ( _get_order , None , 10 ) ,
2010-06-21 13:59:11 +00:00
} , multi = " sums " , help = " The amount without tax " ) ,
2012-07-10 12:24:57 +00:00
' amount_tax ' : fields . function ( _amount_all , digits_compute = dp . get_precision ( ' Account ' ) , string = ' Taxes ' ,
2008-12-24 15:50:06 +00:00
store = {
' purchase.order.line ' : ( _get_order , None , 10 ) ,
2010-06-21 13:59:11 +00:00
} , multi = " sums " , help = " The tax amount " ) ,
2012-07-10 12:24:57 +00:00
' amount_total ' : fields . function ( _amount_all , digits_compute = dp . get_precision ( ' Account ' ) , string = ' Total ' ,
2008-12-24 15:50:06 +00:00
store = {
' purchase.order.line ' : ( _get_order , None , 10 ) ,
2010-06-21 13:59:11 +00:00
} , multi = " sums " , help = " The total amount " ) ,
2009-10-30 12:22:28 +00:00
' fiscal_position ' : fields . many2one ( ' account.fiscal.position ' , ' Fiscal Position ' ) ,
' product_id ' : fields . related ( ' order_line ' , ' product_id ' , type = ' many2one ' , relation = ' product.product ' , string = ' Product ' ) ,
2009-11-25 09:31:44 +00:00
' create_uid ' : fields . many2one ( ' res.users ' , ' Responsible ' ) ,
2009-12-22 19:07:37 +00:00
' company_id ' : fields . many2one ( ' res.company ' , ' Company ' , required = True , select = 1 ) ,
2012-10-25 10:05:15 +00:00
' journal_id ' : fields . many2one ( ' account.journal ' , ' Journal ' ) ,
2008-07-22 15:11:28 +00:00
}
_defaults = {
2012-02-13 18:07:41 +00:00
' date_order ' : fields . date . context_today ,
2010-06-23 07:23:38 +00:00
' state ' : ' draft ' ,
2012-11-06 11:28:44 +00:00
' name ' : lambda obj , cr , uid , context : ' / ' ,
2010-06-23 07:23:38 +00:00
' shipped ' : 0 ,
' invoice_method ' : ' order ' ,
' invoiced ' : 0 ,
2008-07-22 15:11:28 +00:00
' pricelist_id ' : lambda self , cr , uid , context : context . get ( ' partner_id ' , False ) and self . pool . get ( ' res.partner ' ) . browse ( cr , uid , context [ ' partner_id ' ] ) . property_product_pricelist_purchase . id ,
2009-12-23 17:16:39 +00:00
' company_id ' : lambda self , cr , uid , c : self . pool . get ( ' res.company ' ) . _company_default_get ( cr , uid , ' purchase.order ' , context = c ) ,
2012-10-25 10:05:15 +00:00
' journal_id ' : _get_journal ,
2008-07-22 15:11:28 +00:00
}
2010-12-21 05:04:12 +00:00
_sql_constraints = [
2011-11-14 09:05:20 +00:00
( ' name_uniq ' , ' unique(name, company_id) ' , ' Order Reference must be unique per Company! ' ) ,
2010-12-21 05:04:12 +00:00
]
2008-07-22 15:11:28 +00:00
_name = " purchase.order "
2012-08-22 15:31:45 +00:00
_inherit = [ ' mail.thread ' , ' ir.needaction_mixin ' ]
2010-05-19 18:32:32 +00:00
_description = " Purchase Order "
2008-07-22 15:11:28 +00:00
_order = " name desc "
2009-01-19 13:59:11 +00:00
2012-03-13 10:07:26 +00:00
def create ( self , cr , uid , vals , context = None ) :
2012-11-06 11:28:44 +00:00
if ( ' name ' not in vals ) or ( vals . get ( ' name ' ) == ' / ' ) :
seq_obj_name = self . _name
vals [ ' name ' ] = self . pool . get ( ' ir.sequence ' ) . get ( cr , uid , seq_obj_name )
2012-03-13 10:07:26 +00:00
order = super ( purchase_order , self ) . create ( cr , uid , vals , context = context )
if order :
2012-03-16 14:57:04 +00:00
self . create_send_note ( cr , uid , [ order ] , context = context )
2012-03-13 10:07:26 +00:00
return order
2012-08-07 11:06:16 +00:00
2009-01-20 11:29:21 +00:00
def unlink ( self , cr , uid , ids , context = None ) :
2010-12-13 06:43:09 +00:00
purchase_orders = self . read ( cr , uid , ids , [ ' state ' ] , context = context )
2008-10-06 10:56:20 +00:00
unlink_ids = [ ]
for s in purchase_orders :
if s [ ' state ' ] in [ ' draft ' , ' cancel ' ] :
unlink_ids . append ( s [ ' id ' ] )
else :
2012-08-07 11:06:16 +00:00
raise osv . except_osv ( _ ( ' Invalid Action! ' ) , _ ( ' In order to delete a purchase order, you must cancel it first. ' ) )
2010-02-02 21:37:52 +00:00
# automatically sending subflow.delete upon deletion
wf_service = netsvc . LocalService ( " workflow " )
for id in unlink_ids :
wf_service . trg_validate ( uid , ' purchase.order ' , id , ' purchase_cancel ' , cr )
2009-01-20 11:29:21 +00:00
return super ( purchase_order , self ) . unlink ( cr , uid , unlink_ids , context = context )
2009-01-19 13:59:11 +00:00
2010-11-19 13:48:01 +00:00
def button_dummy ( self , cr , uid , ids , context = None ) :
2008-07-22 15:11:28 +00:00
return True
2012-10-10 11:20:13 +00:00
def onchange_pricelist ( self , cr , uid , ids , pricelist_id , context = None ) :
if not pricelist_id :
return { }
return { ' value ' : { ' currency_id ' : self . pool . get ( ' product.pricelist ' ) . browse ( cr , uid , pricelist_id , context = context ) . currency_id . id } }
2011-10-20 07:30:04 +00:00
def onchange_dest_address_id ( self , cr , uid , ids , address_id ) :
if not address_id :
2008-07-22 15:11:28 +00:00
return { }
2012-03-05 13:25:39 +00:00
address = self . pool . get ( ' res.partner ' )
2011-02-10 18:48:06 +00:00
values = { ' warehouse_id ' : False }
2012-03-05 13:25:39 +00:00
supplier = address . browse ( cr , uid , address_id )
2011-10-20 07:30:04 +00:00
if supplier :
location_id = supplier . property_stock_customer . id
values . update ( { ' location_id ' : location_id } )
2011-02-10 18:48:06 +00:00
return { ' value ' : values }
2008-07-22 15:11:28 +00:00
def onchange_warehouse_id ( self , cr , uid , ids , warehouse_id ) :
if not warehouse_id :
return { }
2011-10-20 07:30:04 +00:00
warehouse = self . pool . get ( ' stock.warehouse ' ) . browse ( cr , uid , warehouse_id )
return { ' value ' : { ' location_id ' : warehouse . lot_input_id . id , ' dest_address_id ' : False } }
2008-07-22 15:11:28 +00:00
2011-10-20 07:30:04 +00:00
def onchange_partner_id ( self , cr , uid , ids , partner_id ) :
partner = self . pool . get ( ' res.partner ' )
if not partner_id :
2012-03-05 13:25:39 +00:00
return { ' value ' : { ' fiscal_position ' : False } }
2011-10-20 07:30:04 +00:00
supplier_address = partner . address_get ( cr , uid , [ partner_id ] , [ ' default ' ] )
supplier = partner . browse ( cr , uid , partner_id )
pricelist = supplier . property_product_pricelist_purchase . id
fiscal_position = supplier . property_account_position and supplier . property_account_position . id or False
2012-03-05 13:25:39 +00:00
return { ' value ' : { ' pricelist_id ' : pricelist , ' fiscal_position ' : fiscal_position } }
2008-07-22 15:11:28 +00:00
2012-02-28 11:59:49 +00:00
def view_invoice ( self , cr , uid , ids , context = None ) :
2012-05-25 15:17:40 +00:00
'''
This function returns an action that display existing invoices of given sale order ids . It can either be a in a list or in a form view , if there is only one invoice to show .
'''
2012-02-28 11:59:49 +00:00
mod_obj = self . pool . get ( ' ir.model.data ' )
2012-03-05 10:48:56 +00:00
wizard_obj = self . pool . get ( ' purchase.order.line_invoice ' )
2012-05-25 15:17:40 +00:00
#compute the number of invoices to display
2012-02-28 11:59:49 +00:00
inv_ids = [ ]
for po in self . browse ( cr , uid , ids , context = context ) :
if po . invoice_method == ' manual ' :
if not po . invoice_ids :
2012-03-05 10:48:56 +00:00
context . update ( { ' active_ids ' : [ line . id for line in po . order_line ] } )
wizard_obj . makeInvoices ( cr , uid , [ ] , context = context )
2012-08-07 11:06:16 +00:00
2012-03-05 12:16:42 +00:00
for po in self . browse ( cr , uid , ids , context = context ) :
2012-02-28 11:59:49 +00:00
inv_ids + = [ invoice . id for invoice in po . invoice_ids ]
res = mod_obj . get_object_reference ( cr , uid , ' account ' , ' invoice_supplier_form ' )
res_id = res and res [ 1 ] or False
return {
' name ' : _ ( ' Supplier Invoices ' ) ,
' view_type ' : ' form ' ,
' view_mode ' : ' form ' ,
' view_id ' : [ res_id ] ,
' res_model ' : ' account.invoice ' ,
' context ' : " { ' type ' : ' in_invoice ' , ' journal_type ' : ' purchase ' } " ,
' type ' : ' ir.actions.act_window ' ,
' nodestroy ' : True ,
' target ' : ' current ' ,
' res_id ' : inv_ids and inv_ids [ 0 ] or False ,
}
def view_picking ( self , cr , uid , ids , context = None ) :
2012-05-25 15:17:40 +00:00
'''
This function returns an action that display existing pîcking orders of given purchase order ids .
'''
2012-02-28 11:59:49 +00:00
mod_obj = self . pool . get ( ' ir.model.data ' )
pick_ids = [ ]
for po in self . browse ( cr , uid , ids , context = context ) :
pick_ids + = [ picking . id for picking in po . picking_ids ]
2012-09-14 13:32:53 +00:00
action_model , action_id = tuple ( mod_obj . get_object_reference ( cr , uid , ' stock ' , ' action_picking_tree4 ' ) )
action = self . pool . get ( action_model ) . read ( cr , uid , action_id , context = context )
ctx = eval ( action [ ' context ' ] )
ctx . update ( {
' search_default_purchase_id ' : ids [ 0 ]
} )
if pick_ids and len ( pick_ids ) == 1 :
form_view_ids = [ view_id for view_id , view in action [ ' views ' ] if view == ' form ' ]
view_id = form_view_ids and form_view_ids [ 0 ] or False
action . update ( {
' views ' : [ ] ,
' view_mode ' : ' form ' ,
' view_id ' : view_id ,
' res_id ' : pick_ids [ 0 ]
} )
2012-10-02 10:29:15 +00:00
2012-09-14 13:32:53 +00:00
action . update ( {
' context ' : ctx ,
} )
return action
2008-07-22 15:11:28 +00:00
2010-11-19 13:48:01 +00:00
def wkf_approve_order ( self , cr , uid , ids , context = None ) :
2012-02-14 12:25:20 +00:00
self . write ( cr , uid , ids , { ' state ' : ' approved ' , ' date_approve ' : fields . date . context_today ( self , cr , uid , context = context ) } )
2008-07-22 15:11:28 +00:00
return True
2012-02-27 13:15:57 +00:00
def wkf_send_rfq ( self , cr , uid , ids , context = None ) :
2012-05-25 15:17:40 +00:00
'''
This function opens a window to compose an email , with the edi purchase template message loaded by default
'''
2012-02-27 13:15:57 +00:00
mod_obj = self . pool . get ( ' ir.model.data ' )
2012-05-25 14:35:49 +00:00
template = mod_obj . get_object_reference ( cr , uid , ' purchase ' , ' email_template_edi_purchase ' )
2012-04-02 12:36:51 +00:00
template_id = template and template [ 1 ] or False
2012-02-28 11:59:49 +00:00
res = mod_obj . get_object_reference ( cr , uid , ' mail ' , ' email_compose_message_wizard_form ' )
res_id = res and res [ 1 ] or False
2012-09-11 14:16:50 +00:00
ctx = dict ( context )
ctx . update ( {
' default_model ' : ' purchase.order ' ,
' default_res_id ' : ids [ 0 ] ,
' default_use_template ' : True ,
' default_template_id ' : template_id ,
} )
2012-02-27 13:15:57 +00:00
return {
' view_type ' : ' form ' ,
' view_mode ' : ' form ' ,
' res_model ' : ' mail.compose.message ' ,
2012-09-11 14:16:50 +00:00
' views ' : [ ( res_id , ' form ' ) ] ,
2012-02-28 11:59:49 +00:00
' view_id ' : res_id ,
2012-02-27 13:15:57 +00:00
' type ' : ' ir.actions.act_window ' ,
' target ' : ' new ' ,
2012-02-29 10:16:53 +00:00
' context ' : ctx ,
' nodestroy ' : True ,
2012-02-27 13:15:57 +00:00
}
2012-02-28 11:59:49 +00:00
2010-05-27 07:08:59 +00:00
#TODO: implement messages system
2010-11-19 13:48:01 +00:00
def wkf_confirm_order ( self , cr , uid , ids , context = None ) :
2010-04-13 10:28:22 +00:00
todo = [ ]
2010-11-19 13:48:01 +00:00
for po in self . browse ( cr , uid , ids , context = context ) :
2010-02-05 12:19:25 +00:00
if not po . order_line :
2012-08-07 11:31:37 +00:00
raise osv . except_osv ( _ ( ' Error! ' ) , _ ( ' You cannot confirm a purchase order without any purchase order line. ' ) )
2010-05-28 09:48:40 +00:00
for line in po . order_line :
2010-04-13 10:28:22 +00:00
if line . state == ' draft ' :
2010-05-28 09:48:40 +00:00
todo . append ( line . id )
2012-10-02 10:29:15 +00:00
2010-04-13 10:28:22 +00:00
self . pool . get ( ' purchase.order.line ' ) . action_confirm ( cr , uid , todo , context )
2008-07-22 15:11:28 +00:00
for id in ids :
self . write ( cr , uid , [ id ] , { ' state ' : ' confirmed ' , ' validator ' : uid } )
2012-03-16 14:57:04 +00:00
self . confirm_send_note ( cr , uid , ids , context )
2008-07-22 15:11:28 +00:00
return True
2011-11-15 07:02:27 +00:00
2011-11-15 15:32:01 +00:00
def _prepare_inv_line ( self , cr , uid , account_id , order_line , context = None ) :
2012-03-29 12:41:38 +00:00
""" Collects require data from purchase order line that is used to create invoice line
2011-11-15 07:14:36 +00:00
for that purchase order line
: param account_id : Expense account of the product of PO line if any .
: param browse_record order_line : Purchase order line browse record
: return : Value for fields of invoice lines .
: rtype : dict
"""
2011-11-15 07:02:27 +00:00
return {
' name ' : order_line . name ,
' account_id ' : account_id ,
' price_unit ' : order_line . price_unit or 0.0 ,
' quantity ' : order_line . product_qty ,
' product_id ' : order_line . product_id . id or False ,
' uos_id ' : order_line . product_uom . id or False ,
' invoice_line_tax_id ' : [ ( 6 , 0 , [ x . id for x in order_line . taxes_id ] ) ] ,
' account_analytic_id ' : order_line . account_analytic_id . id or False ,
}
2008-07-22 15:11:28 +00:00
2012-06-01 13:21:29 +00:00
def action_cancel_draft ( self , cr , uid , ids , context = None ) :
2008-10-13 13:08:08 +00:00
if not len ( ids ) :
return False
self . write ( cr , uid , ids , { ' state ' : ' draft ' , ' shipped ' : 0 } )
wf_service = netsvc . LocalService ( " workflow " )
for p_id in ids :
2010-02-05 11:35:25 +00:00
# Deleting the existing instance of workflow for PO
2010-05-28 09:48:40 +00:00
wf_service . trg_delete ( uid , ' purchase.order ' , p_id , cr )
2008-10-13 13:08:08 +00:00
wf_service . trg_create ( uid , ' purchase.order ' , p_id , cr )
2012-06-01 13:21:29 +00:00
self . draft_send_note ( cr , uid , ids , context = context )
2008-10-13 13:08:08 +00:00
return True
2011-11-15 06:43:53 +00:00
def action_invoice_create ( self , cr , uid , ids , context = None ) :
2011-11-15 07:14:36 +00:00
""" Generates invoice for given ids of purchase orders and links that invoice ID to purchase order.
: param ids : list of ids of purchase orders .
: return : ID of created invoice .
: rtype : int
"""
2008-07-22 15:11:28 +00:00
res = False
2010-05-28 09:48:40 +00:00
2009-01-23 08:18:01 +00:00
journal_obj = self . pool . get ( ' account.journal ' )
2011-11-15 06:43:53 +00:00
inv_obj = self . pool . get ( ' account.invoice ' )
2011-11-15 07:02:27 +00:00
inv_line_obj = self . pool . get ( ' account.invoice.line ' )
2011-11-15 06:43:53 +00:00
fiscal_obj = self . pool . get ( ' account.fiscal.position ' )
property_obj = self . pool . get ( ' ir.property ' )
for order in self . browse ( cr , uid , ids , context = context ) :
pay_acc_id = order . partner_id . property_account_payable . id
journal_ids = journal_obj . search ( cr , uid , [ ( ' type ' , ' = ' , ' purchase ' ) , ( ' company_id ' , ' = ' , order . company_id . id ) ] , limit = 1 )
2011-07-08 06:04:11 +00:00
if not journal_ids :
2012-08-07 11:31:37 +00:00
raise osv . except_osv ( _ ( ' Error! ' ) ,
2012-07-25 10:12:29 +00:00
_ ( ' Define purchase journal for this company: " %s " (id: %d ). ' ) % ( order . company_id . name , order . company_id . id ) )
2011-11-15 11:48:39 +00:00
2011-11-15 10:14:34 +00:00
# generate invoice line correspond to PO line and link that to created invoice (inv_id) and PO line
inv_lines = [ ]
for po_line in order . order_line :
if po_line . product_id :
acc_id = po_line . product_id . product_tmpl_id . property_account_expense . id
if not acc_id :
acc_id = po_line . product_id . categ_id . property_account_expense_categ . id
if not acc_id :
2012-08-07 11:31:37 +00:00
raise osv . except_osv ( _ ( ' Error! ' ) , _ ( ' Define expense account for this company: " %s " (id: %d ). ' ) % ( po_line . product_id . name , po_line . product_id . id , ) )
2011-11-15 10:14:34 +00:00
else :
acc_id = property_obj . get ( cr , uid , ' property_account_expense_categ ' , ' product.category ' ) . id
fpos = order . fiscal_position or False
acc_id = fiscal_obj . map_account ( cr , uid , fpos , acc_id )
2011-11-15 15:32:01 +00:00
inv_line_data = self . _prepare_inv_line ( cr , uid , acc_id , po_line , context = context )
2011-11-15 10:14:34 +00:00
inv_line_id = inv_line_obj . create ( cr , uid , inv_line_data , context = context )
inv_lines . append ( inv_line_id )
po_line . write ( { ' invoiced ' : True , ' invoice_lines ' : [ ( 4 , inv_line_id ) ] } , context = context )
2011-11-15 06:43:53 +00:00
2011-11-15 07:02:27 +00:00
# get invoice data and create invoice
2011-11-15 06:43:53 +00:00
inv_data = {
' name ' : order . partner_ref or order . name ,
' reference ' : order . partner_ref or order . name ,
' account_id ' : pay_acc_id ,
2011-07-08 06:04:11 +00:00
' type ' : ' in_invoice ' ,
2011-11-15 06:43:53 +00:00
' partner_id ' : order . partner_id . id ,
' currency_id ' : order . pricelist_id . currency_id . id ,
2011-07-08 06:04:11 +00:00
' journal_id ' : len ( journal_ids ) and journal_ids [ 0 ] or False ,
2012-03-29 12:41:38 +00:00
' invoice_line ' : [ ( 6 , 0 , inv_lines ) ] ,
2011-11-15 06:43:53 +00:00
' origin ' : order . name ,
' fiscal_position ' : order . fiscal_position . id or order . partner_id . property_account_position . id ,
' payment_term ' : order . partner_id . property_payment_term and order . partner_id . property_payment_term . id or False ,
' company_id ' : order . company_id . id ,
2011-07-08 06:04:11 +00:00
}
2011-11-15 06:43:53 +00:00
inv_id = inv_obj . create ( cr , uid , inv_data , context = context )
2011-11-15 07:02:27 +00:00
# compute the invoice
2011-11-15 06:43:53 +00:00
inv_obj . button_compute ( cr , uid , [ inv_id ] , context = context , set_total = True )
2011-11-15 07:02:27 +00:00
# Link this new invoice to related purchase order
order . write ( { ' invoice_ids ' : [ ( 4 , inv_id ) ] } , context = context )
2011-07-08 06:04:11 +00:00
res = inv_id
2012-03-13 10:07:26 +00:00
if res :
2012-03-16 14:57:04 +00:00
self . invoice_send_note ( cr , uid , ids , res , context )
2008-07-22 15:11:28 +00:00
return res
2012-03-13 10:07:26 +00:00
def invoice_done ( self , cr , uid , ids , context = None ) :
self . write ( cr , uid , ids , { ' state ' : ' approved ' } , context = context )
2012-04-03 11:34:04 +00:00
self . invoice_done_send_note ( cr , uid , ids , context = context )
2012-03-13 10:07:26 +00:00
return True
2012-08-07 11:06:16 +00:00
2012-03-05 18:40:03 +00:00
def has_stockable_product ( self , cr , uid , ids , * args ) :
2008-07-22 15:11:28 +00:00
for order in self . browse ( cr , uid , ids ) :
for order_line in order . order_line :
if order_line . product_id and order_line . product_id . product_tmpl_id . type in ( ' product ' , ' consu ' ) :
return True
return False
2010-11-19 13:48:01 +00:00
def action_cancel ( self , cr , uid , ids , context = None ) :
2011-11-09 11:49:08 +00:00
wf_service = netsvc . LocalService ( " workflow " )
2010-11-19 13:48:01 +00:00
for purchase in self . browse ( cr , uid , ids , context = context ) :
2008-10-13 13:08:08 +00:00
for pick in purchase . picking_ids :
if pick . state not in ( ' draft ' , ' cancel ' ) :
raise osv . except_osv (
2012-07-12 06:00:49 +00:00
_ ( ' Unable to cancel this purchase order. ' ) ,
2012-07-17 11:30:57 +00:00
_ ( ' First cancel all receptions related to this purchase order. ' ) )
2008-11-06 22:51:39 +00:00
for pick in purchase . picking_ids :
wf_service . trg_validate ( uid , ' stock.picking ' , pick . id , ' button_cancel ' , cr )
2010-12-15 10:14:49 +00:00
for inv in purchase . invoice_ids :
if inv and inv . state not in ( ' cancel ' , ' draft ' ) :
raise osv . except_osv (
2012-07-12 06:00:49 +00:00
_ ( ' Unable to cancel this purchase order. ' ) ,
2012-07-31 12:42:10 +00:00
_ ( ' You must first cancel all receptions related to this purchase order. ' ) )
2010-12-15 10:14:49 +00:00
if inv :
wf_service . trg_validate ( uid , ' account.invoice ' , inv . id , ' invoice_cancel ' , cr )
2008-10-13 13:08:08 +00:00
self . write ( cr , uid , ids , { ' state ' : ' cancel ' } )
2012-03-29 12:41:38 +00:00
2011-11-10 13:20:59 +00:00
for ( id , name ) in self . name_get ( cr , uid , ids ) :
2011-11-09 11:49:08 +00:00
wf_service . trg_validate ( uid , ' purchase.order ' , id , ' purchase_cancel ' , cr )
2012-05-08 12:59:11 +00:00
self . cancel_send_note ( cr , uid , ids , context )
2008-10-13 13:08:08 +00:00
return True
2011-11-21 22:31:37 +00:00
def _prepare_order_picking ( self , cr , uid , order , context = None ) :
2011-10-03 21:27:47 +00:00
return {
2011-11-10 11:21:39 +00:00
' name ' : self . pool . get ( ' ir.sequence ' ) . get ( cr , uid , ' stock.picking.in ' ) ,
' origin ' : order . name + ( ( order . origin and ( ' : ' + order . origin ) ) or ' ' ) ,
2011-11-09 18:12:56 +00:00
' date ' : order . date_order ,
2012-03-30 07:56:38 +00:00
' partner_id ' : order . dest_address_id . id or order . partner_id . id ,
2011-11-10 11:21:39 +00:00
' invoice_state ' : ' 2binvoiced ' if order . invoice_method == ' picking ' else ' none ' ,
2011-10-03 21:27:47 +00:00
' type ' : ' in ' ,
2012-03-30 07:56:38 +00:00
' partner_id ' : order . dest_address_id . id or order . partner_id . id ,
2011-11-10 11:21:39 +00:00
' invoice_state ' : ' 2binvoiced ' if order . invoice_method == ' picking ' else ' none ' ,
2011-10-03 21:27:47 +00:00
' purchase_id ' : order . id ,
' company_id ' : order . company_id . id ,
' move_lines ' : [ ] ,
}
2012-03-29 12:41:38 +00:00
2011-11-21 22:31:37 +00:00
def _prepare_order_line_move ( self , cr , uid , order , order_line , picking_id , context = None ) :
2011-10-03 21:27:47 +00:00
return {
2011-11-10 11:21:39 +00:00
' name ' : order . name + ' : ' + ( order_line . name or ' ' ) ,
2011-10-03 21:27:47 +00:00
' product_id ' : order_line . product_id . id ,
' product_qty ' : order_line . product_qty ,
' product_uos_qty ' : order_line . product_qty ,
' product_uom ' : order_line . product_uom . id ,
' product_uos ' : order_line . product_uom . id ,
' date ' : order_line . date_planned ,
' date_expected ' : order_line . date_planned ,
2011-10-16 01:31:25 +00:00
' location_id ' : order . partner_id . property_stock_supplier . id ,
' location_dest_id ' : order . location_id . id ,
2011-10-03 21:27:47 +00:00
' picking_id ' : picking_id ,
2012-03-30 07:56:38 +00:00
' partner_id ' : order . dest_address_id . id or order . partner_id . id ,
2011-10-03 21:27:47 +00:00
' move_dest_id ' : order_line . move_dest_id . id ,
' state ' : ' draft ' ,
2012-08-30 12:49:52 +00:00
' type ' : ' in ' ,
2011-10-03 21:27:47 +00:00
' purchase_line_id ' : order_line . id ,
2011-10-16 01:31:25 +00:00
' company_id ' : order . company_id . id ,
2011-10-03 21:27:47 +00:00
' price_unit ' : order_line . price_unit
}
2011-11-21 22:31:37 +00:00
def _create_pickings ( self , cr , uid , order , order_lines , picking_id = False , context = None ) :
2011-11-10 11:21:39 +00:00
""" Creates pickings and appropriate stock moves for given order lines, then
confirms the moves , makes them available , and confirms the picking .
2011-10-16 01:31:25 +00:00
If ` ` picking_id ` ` is provided , the stock moves will be added to it , otherwise
a standard outgoing picking will be created to wrap the stock moves , as returned
by : meth : ` ~ . _prepare_order_picking ` .
Modules that wish to customize the procurements or partition the stock moves over
multiple stock pickings may override this method and call ` ` super ( ) ` ` with
different subsets of ` ` order_lines ` ` and / or preset ` ` picking_id ` ` values .
: param browse_record order : purchase order to which the order lines belong
2011-11-10 11:21:39 +00:00
: param list ( browse_record ) order_lines : purchase order line records for which picking
and moves should be created .
2011-10-16 01:31:25 +00:00
: param int picking_id : optional ID of a stock picking to which the created stock moves
2011-11-10 11:21:39 +00:00
will be added . A new picking will be created if omitted .
: return : list of IDs of pickings used / created for the given order lines ( usually just one )
2011-10-03 21:27:47 +00:00
"""
2012-03-29 12:41:38 +00:00
if not picking_id :
2011-12-21 13:10:28 +00:00
picking_id = self . pool . get ( ' stock.picking ' ) . create ( cr , uid , self . _prepare_order_picking ( cr , uid , order , context = context ) )
2011-10-03 21:27:47 +00:00
todo_moves = [ ]
2011-11-10 11:21:39 +00:00
stock_move = self . pool . get ( ' stock.move ' )
wf_service = netsvc . LocalService ( " workflow " )
2011-10-03 21:27:47 +00:00
for order_line in order_lines :
if not order_line . product_id :
continue
2011-11-10 11:21:39 +00:00
if order_line . product_id . type in ( ' product ' , ' consu ' ) :
2011-12-21 13:10:28 +00:00
move = stock_move . create ( cr , uid , self . _prepare_order_line_move ( cr , uid , order , order_line , picking_id , context = context ) )
2011-10-03 21:27:47 +00:00
if order_line . move_dest_id :
2011-11-10 11:21:39 +00:00
order_line . move_dest_id . write ( { ' location_id ' : order . location_id . id } )
2011-10-03 21:27:47 +00:00
todo_moves . append ( move )
2011-11-10 11:21:39 +00:00
stock_move . action_confirm ( cr , uid , todo_moves )
stock_move . force_assign ( cr , uid , todo_moves )
wf_service . trg_validate ( uid , ' stock.picking ' , picking_id , ' button_confirm ' , cr )
return [ picking_id ]
2012-03-05 18:40:03 +00:00
def action_picking_create ( self , cr , uid , ids , context = None ) :
2011-11-10 11:21:39 +00:00
picking_ids = [ ]
2008-07-22 15:11:28 +00:00
for order in self . browse ( cr , uid , ids ) :
2011-12-21 13:10:28 +00:00
picking_ids . extend ( self . _create_pickings ( cr , uid , order , order . order_line , None , context = context ) )
2011-11-11 00:31:27 +00:00
# Must return one unique picking ID: the one to connect in the subflow of the purchase order.
# In case of multiple (split) pickings, we should return the ID of the critical one, i.e. the
# one that should trigger the advancement of the purchase workflow.
# By default we will consider the first one as most important, but this behavior can be overridden.
2012-03-13 10:07:26 +00:00
if picking_ids :
2012-03-16 14:57:04 +00:00
self . shipment_send_note ( cr , uid , ids , picking_ids [ 0 ] , context = context )
2011-11-11 00:31:27 +00:00
return picking_ids [ 0 ] if picking_ids else False
2009-12-01 14:54:18 +00:00
2012-03-13 10:07:26 +00:00
def picking_done ( self , cr , uid , ids , context = None ) :
self . write ( cr , uid , ids , { ' shipped ' : 1 , ' state ' : ' approved ' } , context = context )
2012-03-16 14:57:04 +00:00
self . shipment_done_send_note ( cr , uid , ids , context = context )
2012-03-13 10:07:26 +00:00
return True
2010-11-23 11:31:52 +00:00
def copy ( self , cr , uid , id , default = None , context = None ) :
2008-07-22 15:11:28 +00:00
if not default :
default = { }
default . update ( {
' state ' : ' draft ' ,
' shipped ' : False ,
' invoiced ' : False ,
2010-12-20 13:00:56 +00:00
' invoice_ids ' : [ ] ,
' picking_ids ' : [ ] ,
2008-07-22 15:11:28 +00:00
' name ' : self . pool . get ( ' ir.sequence ' ) . get ( cr , uid , ' purchase.order ' ) ,
} )
return super ( purchase_order , self ) . copy ( cr , uid , id , default , context )
2006-12-07 13:41:40 +00:00
2010-11-19 13:48:01 +00:00
def do_merge ( self , cr , uid , ids , context = None ) :
2010-05-28 09:48:40 +00:00
"""
To merge similar type of purchase orders .
Orders will only be merged if :
* Purchase Orders are in draft
* Purchase Orders belong to the same partner
2010-03-25 08:25:11 +00:00
* Purchase Orders are have same stock location , same pricelist
2010-05-28 09:48:40 +00:00
Lines will only be merged if :
* Order lines are exactly the same except for the quantity and unit
2010-03-25 08:25:11 +00:00
@param self : The object pointer .
@param cr : A database cursor
@param uid : ID of the user currently logged in
2010-05-28 09:48:40 +00:00
@param ids : the ID or list of IDs
@param context : A standard dictionary
2010-03-25 08:25:11 +00:00
@return : new purchase order id
2010-05-28 09:48:40 +00:00
"""
2011-10-20 07:30:04 +00:00
#TOFIX: merged order line should be unlink
2010-05-28 09:48:40 +00:00
wf_service = netsvc . LocalService ( " workflow " )
def make_key ( br , fields ) :
2010-03-25 08:25:11 +00:00
list_key = [ ]
for field in fields :
field_val = getattr ( br , field )
if field in ( ' product_id ' , ' move_dest_id ' , ' account_analytic_id ' ) :
if not field_val :
field_val = False
if isinstance ( field_val , browse_record ) :
field_val = field_val . id
elif isinstance ( field_val , browse_null ) :
field_val = False
elif isinstance ( field_val , list ) :
field_val = ( ( 6 , 0 , tuple ( [ v . id for v in field_val ] ) ) , )
list_key . append ( ( field , field_val ) )
list_key . sort ( )
return tuple ( list_key )
2012-10-02 10:29:15 +00:00
# Compute what the new orders should contain
2010-05-28 09:48:40 +00:00
2010-03-25 08:25:11 +00:00
new_orders = { }
2010-05-28 09:48:40 +00:00
2010-11-19 13:48:01 +00:00
for porder in [ order for order in self . browse ( cr , uid , ids , context = context ) if order . state == ' draft ' ] :
2010-03-25 08:25:11 +00:00
order_key = make_key ( porder , ( ' partner_id ' , ' location_id ' , ' pricelist_id ' ) )
new_order = new_orders . setdefault ( order_key , ( { } , [ ] ) )
new_order [ 1 ] . append ( porder . id )
order_infos = new_order [ 0 ]
if not order_infos :
order_infos . update ( {
2010-08-13 12:20:05 +00:00
' origin ' : porder . origin ,
2011-07-22 06:29:13 +00:00
' date_order ' : porder . date_order ,
2010-08-13 12:20:05 +00:00
' partner_id ' : porder . partner_id . id ,
' dest_address_id ' : porder . dest_address_id . id ,
' warehouse_id ' : porder . warehouse_id . id ,
' location_id ' : porder . location_id . id ,
' pricelist_id ' : porder . pricelist_id . id ,
' state ' : ' draft ' ,
' order_line ' : { } ,
' notes ' : ' %s ' % ( porder . notes or ' ' , ) ,
' fiscal_position ' : porder . fiscal_position and porder . fiscal_position . id or False ,
2010-03-25 08:25:11 +00:00
} )
else :
2011-07-22 06:29:13 +00:00
if porder . date_order < order_infos [ ' date_order ' ] :
order_infos [ ' date_order ' ] = porder . date_order
2010-03-25 08:25:11 +00:00
if porder . notes :
order_infos [ ' notes ' ] = ( order_infos [ ' notes ' ] or ' ' ) + ( ' \n %s ' % ( porder . notes , ) )
if porder . origin :
order_infos [ ' origin ' ] = ( order_infos [ ' origin ' ] or ' ' ) + ' ' + porder . origin
for order_line in porder . order_line :
2012-07-14 20:51:50 +00:00
line_key = make_key ( order_line , ( ' name ' , ' date_planned ' , ' taxes_id ' , ' price_unit ' , ' product_id ' , ' move_dest_id ' , ' account_analytic_id ' ) )
2010-03-25 08:25:11 +00:00
o_line = order_infos [ ' order_line ' ] . setdefault ( line_key , { } )
if o_line :
# merge the line with an existing line
o_line [ ' product_qty ' ] + = order_line . product_qty * order_line . product_uom . factor / o_line [ ' uom_factor ' ]
else :
# append a new "standalone" line
for field in ( ' product_qty ' , ' product_uom ' ) :
field_val = getattr ( order_line , field )
if isinstance ( field_val , browse_record ) :
field_val = field_val . id
o_line [ field ] = field_val
o_line [ ' uom_factor ' ] = order_line . product_uom and order_line . product_uom . factor or 1.0
2010-05-28 09:48:40 +00:00
2010-03-25 08:25:11 +00:00
allorders = [ ]
2010-12-09 11:56:59 +00:00
orders_info = { }
2010-03-25 08:25:11 +00:00
for order_key , ( order_data , old_ids ) in new_orders . iteritems ( ) :
# skip merges with only one order
if len ( old_ids ) < 2 :
allorders + = ( old_ids or [ ] )
continue
# cleanup order line data
for key , value in order_data [ ' order_line ' ] . iteritems ( ) :
del value [ ' uom_factor ' ]
value . update ( dict ( key ) )
order_data [ ' order_line ' ] = [ ( 0 , 0 , value ) for value in order_data [ ' order_line ' ] . itervalues ( ) ]
# create the new order
neworder_id = self . create ( cr , uid , order_data )
2010-12-09 11:56:59 +00:00
orders_info . update ( { neworder_id : old_ids } )
2010-03-25 08:25:11 +00:00
allorders . append ( neworder_id )
# make triggers pointing to the old orders point to the new order
for old_id in old_ids :
wf_service . trg_redirect ( uid , ' purchase.order ' , old_id , neworder_id , cr )
wf_service . trg_validate ( uid , ' purchase.order ' , old_id , ' purchase_cancel ' , cr )
2010-12-09 11:56:59 +00:00
return orders_info
2010-03-25 08:25:11 +00:00
2012-04-03 17:21:08 +00:00
# --------------------------------------
# OpenChatter methods and notifications
# --------------------------------------
2012-08-07 11:06:16 +00:00
2012-08-15 10:29:19 +00:00
def needaction_domain_get ( self , cr , uid , ids , context = None ) :
2012-09-04 09:28:20 +00:00
return [ ( ' state ' , ' = ' , ' draft ' ) ]
2012-08-07 11:06:16 +00:00
2012-03-16 14:57:04 +00:00
def create_send_note ( self , cr , uid , ids , context = None ) :
2012-08-17 10:03:02 +00:00
return self . message_post ( cr , uid , ids , body = _ ( " Request for quotation <b>created</b>. " ) , context = context )
2012-03-13 10:07:26 +00:00
2012-03-16 14:57:04 +00:00
def confirm_send_note ( self , cr , uid , ids , context = None ) :
2012-03-13 10:07:26 +00:00
for obj in self . browse ( cr , uid , ids , context = context ) :
2012-08-17 10:03:02 +00:00
self . message_post ( cr , uid , [ obj . id ] , body = _ ( " Quotation for <em> %s </em> <b>converted</b> to a Purchase Order of %s %s . " ) % ( obj . partner_id . name , obj . amount_total , obj . pricelist_id . currency_id . symbol ) , context = context )
2012-08-07 11:06:16 +00:00
2012-03-16 14:57:04 +00:00
def shipment_send_note ( self , cr , uid , ids , picking_id , context = None ) :
2012-03-13 10:07:26 +00:00
for order in self . browse ( cr , uid , ids , context = context ) :
for picking in ( pck for pck in order . picking_ids if pck . id == picking_id ) :
2012-04-18 12:31:50 +00:00
# convert datetime field to a datetime, using server format, then
# convert it to the user TZ and re-render it with %Z to add the timezone
2012-04-18 13:41:43 +00:00
picking_datetime = fields . DT . datetime . strptime ( picking . min_date , DEFAULT_SERVER_DATETIME_FORMAT )
2012-04-19 07:53:54 +00:00
picking_date_str = fields . datetime . context_timestamp ( cr , uid , picking_datetime , context = context ) . strftime ( DATETIME_FORMATS_MAP [ ' % + ' ] + " ( % Z) " )
2012-08-17 10:03:02 +00:00
self . message_post ( cr , uid , [ order . id ] , body = _ ( " Shipment <em> %s </em> <b>scheduled</b> for %s . " ) % ( picking . name , picking_date_str ) , context = context )
2012-08-07 11:06:16 +00:00
2012-03-16 14:57:04 +00:00
def invoice_send_note ( self , cr , uid , ids , invoice_id , context = None ) :
2012-03-13 10:07:26 +00:00
for order in self . browse ( cr , uid , ids , context = context ) :
for invoice in ( inv for inv in order . invoice_ids if inv . id == invoice_id ) :
2012-08-17 10:03:02 +00:00
self . message_post ( cr , uid , [ order . id ] , body = _ ( " Draft Invoice of %s %s is <b>waiting for validation</b>. " ) % ( invoice . amount_total , invoice . currency_id . symbol ) , context = context )
2012-08-07 11:06:16 +00:00
2012-03-16 14:57:04 +00:00
def shipment_done_send_note ( self , cr , uid , ids , context = None ) :
2012-08-17 10:03:02 +00:00
self . message_post ( cr , uid , ids , body = _ ( """ Shipment <b>received</b>. """ ) , context = context )
2012-08-07 11:06:16 +00:00
2012-04-03 11:34:04 +00:00
def invoice_done_send_note ( self , cr , uid , ids , context = None ) :
2012-08-17 10:03:02 +00:00
self . message_post ( cr , uid , ids , body = _ ( " Invoice <b>paid</b>. " ) , context = context )
2012-08-07 11:06:16 +00:00
2012-05-08 12:59:11 +00:00
def draft_send_note ( self , cr , uid , ids , context = None ) :
2012-08-17 10:03:02 +00:00
return self . message_post ( cr , uid , ids , body = _ ( " Purchase Order has been set to <b>draft</b>. " ) , context = context )
2012-08-07 11:06:16 +00:00
2012-03-16 14:57:04 +00:00
def cancel_send_note ( self , cr , uid , ids , context = None ) :
2012-03-13 10:07:26 +00:00
for obj in self . browse ( cr , uid , ids , context = context ) :
2012-08-17 10:03:02 +00:00
self . message_post ( cr , uid , [ obj . id ] , body = _ ( " Purchase Order for <em> %s </em> <b>cancelled</b>. " ) % ( obj . partner_id . name ) , context = context )
2010-03-25 08:25:11 +00:00
2006-12-07 13:41:40 +00:00
purchase_order ( )
class purchase_order_line ( osv . osv ) :
2010-11-22 10:37:53 +00:00
def _amount_line ( self , cr , uid , ids , prop , arg , context = None ) :
2008-07-22 15:11:28 +00:00
res = { }
cur_obj = self . pool . get ( ' res.currency ' )
2010-06-05 21:39:55 +00:00
tax_obj = self . pool . get ( ' account.tax ' )
for line in self . browse ( cr , uid , ids , context = context ) :
2010-06-05 22:45:14 +00:00
taxes = tax_obj . compute_all ( cr , uid , line . taxes_id , line . price_unit , line . product_qty )
2008-07-22 15:11:28 +00:00
cur = line . order_id . pricelist_id . currency_id
2010-06-05 21:39:55 +00:00
res [ line . id ] = cur_obj . round ( cr , uid , cur , taxes [ ' total ' ] )
2008-07-22 15:11:28 +00:00
return res
2008-08-27 11:26:37 +00:00
2008-07-22 15:11:28 +00:00
_columns = {
2012-07-14 21:18:27 +00:00
' name ' : fields . text ( ' Description ' , required = True ) ,
2012-04-25 12:09:08 +00:00
' product_qty ' : fields . float ( ' Quantity ' , digits_compute = dp . get_precision ( ' Product Unit of Measure ' ) , required = True ) ,
2011-01-17 06:22:31 +00:00
' date_planned ' : fields . date ( ' Scheduled Date ' , required = True , select = True ) ,
2008-07-22 15:11:28 +00:00
' taxes_id ' : fields . many2many ( ' account.tax ' , ' purchase_order_taxe ' , ' ord_id ' , ' tax_id ' , ' Taxes ' ) ,
2012-04-25 12:09:08 +00:00
' product_uom ' : fields . many2one ( ' product.uom ' , ' Product Unit of Measure ' , required = True ) ,
2008-07-22 15:11:28 +00:00
' product_id ' : fields . many2one ( ' product.product ' , ' Product ' , domain = [ ( ' purchase_ok ' , ' = ' , True ) ] , change_default = True ) ,
2009-07-08 14:02:12 +00:00
' move_ids ' : fields . one2many ( ' stock.move ' , ' purchase_line_id ' , ' Reservation ' , readonly = True , ondelete = ' set null ' ) ,
2008-07-22 15:11:28 +00:00
' move_dest_id ' : fields . many2one ( ' stock.move ' , ' Reservation Destination ' , ondelete = ' set null ' ) ,
2012-07-11 13:06:15 +00:00
' price_unit ' : fields . float ( ' Unit Price ' , required = True , digits_compute = dp . get_precision ( ' Product Price ' ) ) ,
2012-07-10 12:24:57 +00:00
' price_subtotal ' : fields . function ( _amount_line , string = ' Subtotal ' , digits_compute = dp . get_precision ( ' Account ' ) ) ,
2009-12-22 09:38:41 +00:00
' order_id ' : fields . many2one ( ' purchase.order ' , ' Order Reference ' , select = True , required = True , ondelete = ' cascade ' ) ,
2008-07-22 15:11:28 +00:00
' account_analytic_id ' : fields . many2one ( ' account.analytic.account ' , ' Analytic Account ' , ) ,
2011-01-06 11:32:21 +00:00
' company_id ' : fields . related ( ' order_id ' , ' company_id ' , type = ' many2one ' , relation = ' res.company ' , string = ' Company ' , store = True , readonly = True ) ,
2012-05-04 11:57:48 +00:00
' state ' : fields . selection ( [ ( ' draft ' , ' Draft ' ) , ( ' confirmed ' , ' Confirmed ' ) , ( ' done ' , ' Done ' ) , ( ' cancel ' , ' Cancelled ' ) ] , ' Status ' , required = True , readonly = True ,
2012-10-12 11:42:58 +00:00
help = ' * The \' Draft \' status is set automatically when purchase order in draft status. \
\n * The \' Confirmed \' status is set automatically as confirm when purchase order in confirm status. \
\n * The \' Done \' status is set automatically when purchase order is set as done. \
\n * The \' Cancelled \' status is set automatically when user cancel purchase order. ' ) ,
2010-04-13 10:28:22 +00:00
' invoice_lines ' : fields . many2many ( ' account.invoice.line ' , ' purchase_order_line_invoice_rel ' , ' order_line_id ' , ' invoice_id ' , ' Invoice Lines ' , readonly = True ) ,
' invoiced ' : fields . boolean ( ' Invoiced ' , readonly = True ) ,
2010-10-19 09:11:03 +00:00
' partner_id ' : fields . related ( ' order_id ' , ' partner_id ' , string = ' Partner ' , readonly = True , type = " many2one " , relation = " res.partner " , store = True ) ,
2010-04-13 10:28:22 +00:00
' date_order ' : fields . related ( ' order_id ' , ' date_order ' , string = ' Order Date ' , readonly = True , type = " date " )
2010-05-28 09:48:40 +00:00
2008-07-22 15:11:28 +00:00
}
_defaults = {
2010-04-13 10:28:22 +00:00
' product_qty ' : lambda * a : 1.0 ,
' state ' : lambda * args : ' draft ' ,
' invoiced ' : lambda * a : 0 ,
2008-07-22 15:11:28 +00:00
}
_table = ' purchase_order_line '
_name = ' purchase.order.line '
2010-05-19 18:32:32 +00:00
_description = ' Purchase Order Line '
2010-06-23 07:23:38 +00:00
2010-11-22 10:37:53 +00:00
def copy_data ( self , cr , uid , id , default = None , context = None ) :
2008-07-22 15:11:28 +00:00
if not default :
default = { }
2010-04-13 10:28:22 +00:00
default . update ( { ' state ' : ' draft ' , ' move_ids ' : [ ] , ' invoiced ' : 0 , ' invoice_lines ' : [ ] } )
2009-02-04 13:12:10 +00:00
return super ( purchase_order_line , self ) . copy_data ( cr , uid , id , default , context )
2008-07-22 15:11:28 +00:00
2012-01-12 07:02:40 +00:00
def onchange_product_uom ( self , cr , uid , ids , pricelist_id , product_id , qty , uom_id ,
2012-01-06 12:16:12 +00:00
partner_id , date_order = False , fiscal_position_id = False , date_planned = False ,
2012-07-14 20:51:50 +00:00
name = False , price_unit = False , context = None ) :
2012-01-12 07:02:40 +00:00
"""
onchange handler of product_uom .
"""
if not uom_id :
2012-07-14 20:51:50 +00:00
return { ' value ' : { ' price_unit ' : price_unit or 0.0 , ' name ' : name or ' ' , ' product_uom ' : uom_id or False } }
2012-01-12 07:02:40 +00:00
return self . onchange_product_id ( cr , uid , ids , pricelist_id , product_id , qty , uom_id ,
partner_id , date_order = date_order , fiscal_position_id = fiscal_position_id , date_planned = date_planned ,
2012-07-14 20:51:50 +00:00
name = name , price_unit = price_unit , context = context )
2012-01-12 07:02:40 +00:00
2012-02-16 11:55:15 +00:00
def _get_date_planned ( self , cr , uid , supplier_info , date_order_str , context = None ) :
""" Return the datetime value to use as Schedule Date (``date_planned``) for
PO Lines that correspond to the given product . supplierinfo ,
when ordered at ` date_order_str ` .
: param browse_record | False supplier_info : product . supplierinfo , used to
determine delivery delay ( if False , default delay = 0 )
: param str date_order_str : date of order , as a string in
DEFAULT_SERVER_DATE_FORMAT
2012-02-15 22:47:43 +00:00
: rtype : datetime
2012-02-16 11:55:15 +00:00
: return : desired Schedule Date for the PO line
"""
supplier_delay = int ( supplier_info . delay ) if supplier_info else 0
return datetime . strptime ( date_order_str , DEFAULT_SERVER_DATE_FORMAT ) + relativedelta ( days = supplier_delay )
2012-02-15 22:47:43 +00:00
2012-09-17 08:45:34 +00:00
def _check_product_uom_group ( self , cr , uid , context = None ) :
group_uom = self . pool . get ( ' ir.model.data ' ) . get_object ( cr , uid , ' product ' , ' group_uom ' )
res = [ user for user in group_uom . users if user . id == uid ]
return len ( res ) and True or False
2012-01-12 07:02:40 +00:00
def onchange_product_id ( self , cr , uid , ids , pricelist_id , product_id , qty , uom_id ,
partner_id , date_order = False , fiscal_position_id = False , date_planned = False ,
2012-07-14 20:51:50 +00:00
name = False , price_unit = False , context = None ) :
2012-01-12 07:02:40 +00:00
"""
onchange handler of product_id .
"""
2011-12-05 10:44:59 +00:00
if context is None :
context = { }
2012-03-29 12:41:38 +00:00
2012-07-14 20:51:50 +00:00
res = { ' value ' : { ' price_unit ' : price_unit or 0.0 , ' name ' : name or ' ' , ' product_uom ' : uom_id or False } }
2012-01-06 12:16:12 +00:00
if not product_id :
2012-01-24 12:31:04 +00:00
return res
2012-01-06 12:16:12 +00:00
product_product = self . pool . get ( ' product.product ' )
2012-01-12 07:02:40 +00:00
product_uom = self . pool . get ( ' product.uom ' )
res_partner = self . pool . get ( ' res.partner ' )
product_supplierinfo = self . pool . get ( ' product.supplierinfo ' )
product_pricelist = self . pool . get ( ' product.pricelist ' )
2012-01-06 12:16:12 +00:00
account_fiscal_position = self . pool . get ( ' account.fiscal.position ' )
account_tax = self . pool . get ( ' account.tax ' )
2012-01-12 07:02:40 +00:00
# - check for the presence of partner_id and pricelist_id
2012-11-02 09:05:58 +00:00
#if not partner_id:
# raise osv.except_osv(_('No Partner!'), _('Select a partner in purchase order to choose a product.'))
#if not pricelist_id:
# raise osv.except_osv(_('No Pricelist !'), _('Select a price list in the purchase order form before choosing a product.'))
2012-01-12 07:02:40 +00:00
# - determine name and notes based on product in partner lang.
2012-11-02 09:05:58 +00:00
context_partner = context . copy ( )
if partner_id :
lang = res_partner . browse ( cr , uid , partner_id ) . lang
context_partner . update ( { ' lang ' : lang , ' partner_id ' : partner_id } )
2012-01-12 07:09:27 +00:00
product = product_product . browse ( cr , uid , product_id , context = context_partner )
2012-07-14 20:51:50 +00:00
name = product . name
if product . description_purchase :
name + = ' \n ' + product . description_purchase
res [ ' value ' ] . update ( { ' name ' : name } )
2012-03-29 12:41:38 +00:00
2012-01-12 07:02:40 +00:00
# - set a domain on product_uom
2012-01-24 12:31:04 +00:00
res [ ' domain ' ] = { ' product_uom ' : [ ( ' category_id ' , ' = ' , product . uom_id . category_id . id ) ] }
2011-12-05 10:44:59 +00:00
2012-01-12 07:02:40 +00:00
# - check that uom and product uom belong to the same category
2012-01-06 12:16:12 +00:00
product_uom_po_id = product . uom_po_id . id
if not uom_id :
uom_id = product_uom_po_id
2012-03-29 12:41:38 +00:00
2012-01-12 07:02:40 +00:00
if product . uom_id . category_id . id != product_uom . browse ( cr , uid , uom_id , context = context ) . category_id . id :
2012-09-17 08:45:34 +00:00
if self . _check_product_uom_group ( cr , uid , context = context ) :
res [ ' warning ' ] = { ' title ' : _ ( ' Warning! ' ) , ' message ' : _ ( ' Selected Unit of Measure does not belong to the same category as the product Unit of Measure. ' ) }
2012-01-06 12:16:12 +00:00
uom_id = product_uom_po_id
2012-01-12 07:02:40 +00:00
2012-01-24 12:31:04 +00:00
res [ ' value ' ] . update ( { ' product_uom ' : uom_id } )
2012-01-12 07:02:40 +00:00
# - determine product_qty and date_planned based on seller info
2008-07-22 15:11:28 +00:00
if not date_order :
2012-06-12 11:57:13 +00:00
date_order = fields . date . context_today ( self , cr , uid , context = context )
2012-01-06 12:16:12 +00:00
2009-03-10 07:12:13 +00:00
qty = qty or 1.0
2012-02-16 12:34:08 +00:00
supplierinfo = False
2012-07-10 23:49:48 +00:00
for supplier in product . seller_ids :
2012-11-02 09:05:58 +00:00
if partner_id and ( supplier . name . id == partner_id ) :
2012-07-10 23:49:48 +00:00
supplierinfo = supplier
if supplierinfo . product_uom . id != uom_id :
2012-08-07 11:34:14 +00:00
res [ ' warning ' ] = { ' title ' : _ ( ' Warning! ' ) , ' message ' : _ ( ' The selected supplier only sells this product by %s ' ) % supplierinfo . product_uom . name }
2012-07-10 23:49:48 +00:00
min_qty = product_uom . _compute_qty ( cr , uid , supplierinfo . product_uom . id , supplierinfo . min_qty , to_uom_id = uom_id )
if qty < min_qty : # If the supplier quantity is greater than entered from user, set minimal.
2012-08-07 11:34:14 +00:00
res [ ' warning ' ] = { ' title ' : _ ( ' Warning! ' ) , ' message ' : _ ( ' The selected supplier has a minimal quantity set to %s %s , you should not purchase less. ' ) % ( supplierinfo . min_qty , supplierinfo . product_uom . name ) }
2012-07-10 23:49:48 +00:00
qty = min_qty
2012-01-12 07:02:40 +00:00
2012-02-16 12:34:08 +00:00
dt = self . _get_date_planned ( cr , uid , supplierinfo , date_order , context = context ) . strftime ( DEFAULT_SERVER_DATETIME_FORMAT )
2012-02-15 22:47:43 +00:00
2012-01-24 12:31:04 +00:00
res [ ' value ' ] . update ( { ' date_planned ' : date_planned or dt , ' product_qty ' : qty } )
2012-01-12 07:02:40 +00:00
# - determine price_unit and taxes_id
2012-11-02 09:05:58 +00:00
if pricelist_id :
price = product_pricelist . price_get ( cr , uid , [ pricelist_id ] ,
product . id , qty or 1.0 , partner_id or False , { ' uom ' : uom_id , ' date ' : date_order } ) [ pricelist_id ]
else :
price = product . standard_price
2012-03-29 12:41:38 +00:00
2012-01-06 12:16:12 +00:00
taxes = account_tax . browse ( cr , uid , map ( lambda x : x . id , product . supplier_taxes_id ) )
fpos = fiscal_position_id and account_fiscal_position . browse ( cr , uid , fiscal_position_id , context = context ) or False
2012-01-12 07:02:40 +00:00
taxes_ids = account_fiscal_position . map_tax ( cr , uid , fpos , taxes )
2012-01-24 12:31:04 +00:00
res [ ' value ' ] . update ( { ' price_unit ' : price , ' taxes_id ' : taxes_ids } )
2012-01-12 07:02:40 +00:00
2008-07-22 15:11:28 +00:00
return res
2012-01-12 07:02:40 +00:00
product_id_change = onchange_product_id
product_uom_change = onchange_product_uom
2010-06-23 07:23:38 +00:00
2010-11-19 13:48:01 +00:00
def action_confirm ( self , cr , uid , ids , context = None ) :
self . write ( cr , uid , ids , { ' state ' : ' confirmed ' } , context = context )
2010-05-28 09:48:40 +00:00
return True
2010-06-23 07:23:38 +00:00
2006-12-07 13:41:40 +00:00
purchase_order_line ( )
2008-07-23 14:41:47 +00:00
2010-05-27 12:47:06 +00:00
class procurement_order ( osv . osv ) :
_inherit = ' procurement.order '
2010-04-29 13:30:07 +00:00
_columns = {
2010-07-09 09:26:48 +00:00
' purchase_id ' : fields . many2one ( ' purchase.order ' , ' Purchase Order ' ) ,
2010-04-29 13:30:07 +00:00
}
2010-05-28 09:48:40 +00:00
2010-11-19 13:48:01 +00:00
def action_po_assign ( self , cr , uid , ids , context = None ) :
2010-04-29 13:30:07 +00:00
""" This is action which call from workflow to assign purchase order to procurements
@return : True
"""
res = self . make_po ( cr , uid , ids , context = context )
res = res . values ( )
return len ( res ) and res [ 0 ] or 0 #TO CHECK: why workflow is generated error if return not integer value
2011-08-26 14:28:36 +00:00
def create_procurement_purchase_order ( self , cr , uid , procurement , po_vals , line_vals , context = None ) :
""" Create the purchase order from the procurement, using
the provided field values , after adding the given purchase
order line in the purchase order .
: params procurement : the procurement object generating the purchase order
: params dict po_vals : field values for the new purchase order ( the
` ` order_line ` ` field will be overwritten with one
single line , as passed in ` ` line_vals ` ` ) .
: params dict line_vals : field values of the single purchase order line that
the purchase order will contain .
: return : id of the newly created purchase order
: rtype : int
2011-08-24 22:18:06 +00:00
"""
2011-08-26 14:28:36 +00:00
po_vals . update ( { ' order_line ' : [ ( 0 , 0 , line_vals ) ] } )
2011-07-06 00:26:24 +00:00
return self . pool . get ( ' purchase.order ' ) . create ( cr , uid , po_vals , context = context )
2011-12-23 13:36:16 +00:00
def _get_purchase_schedule_date ( self , cr , uid , procurement , company , context = None ) :
""" Return the datetime value to use as Schedule Date (``date_planned``) for the
Purchase Order Lines created to satisfy the given procurement .
: param browse_record procurement : the procurement for which a PO will be created .
: param browse_report company : the company to which the new PO will belong to .
: rtype : datetime
: return : the desired Schedule Date for the PO lines
"""
procurement_date_planned = datetime . strptime ( procurement . date_planned , DEFAULT_SERVER_DATETIME_FORMAT )
2011-12-22 08:31:44 +00:00
schedule_date = ( procurement_date_planned - relativedelta ( days = company . po_lead ) )
return schedule_date
2011-12-23 13:36:16 +00:00
def _get_purchase_order_date ( self , cr , uid , procurement , company , schedule_date , context = None ) :
""" Return the datetime value to use as Order Date (``date_order``) for the
Purchase Order created to satisfy the given procurement .
: param browse_record procurement : the procurement for which a PO will be created .
: param browse_report company : the company to which the new PO will belong to .
: param datetime schedule_date : desired Scheduled Date for the Purchase Order lines .
: rtype : datetime
: return : the desired Order Date for the PO
"""
seller_delay = int ( procurement . product_id . seller_delay )
return schedule_date - relativedelta ( days = seller_delay )
2011-12-22 08:31:44 +00:00
2010-11-19 13:48:01 +00:00
def make_po ( self , cr , uid , ids , context = None ) :
2010-04-29 13:30:07 +00:00
""" Make purchase order from procurement
@return : New created Purchase Orders procurement wise
"""
res = { }
2010-11-23 07:05:05 +00:00
if context is None :
2010-11-22 10:37:53 +00:00
context = { }
2010-11-19 13:48:01 +00:00
company = self . pool . get ( ' res.users ' ) . browse ( cr , uid , uid , context = context ) . company_id
2010-04-29 13:30:07 +00:00
partner_obj = self . pool . get ( ' res.partner ' )
uom_obj = self . pool . get ( ' product.uom ' )
pricelist_obj = self . pool . get ( ' product.pricelist ' )
prod_obj = self . pool . get ( ' product.product ' )
acc_pos_obj = self . pool . get ( ' account.fiscal.position ' )
2011-09-15 08:33:33 +00:00
seq_obj = self . pool . get ( ' ir.sequence ' )
2011-09-20 11:27:26 +00:00
warehouse_obj = self . pool . get ( ' stock.warehouse ' )
2010-11-22 10:37:53 +00:00
for procurement in self . browse ( cr , uid , ids , context = context ) :
2010-04-29 13:30:07 +00:00
res_id = procurement . move_id . id
2010-07-23 13:17:30 +00:00
partner = procurement . product_id . seller_id # Taken Main Supplier of Product of Procurement.
seller_qty = procurement . product_id . seller_qty
2010-06-13 21:16:36 +00:00
partner_id = partner . id
2010-04-29 13:30:07 +00:00
address_id = partner_obj . address_get ( cr , uid , [ partner_id ] , [ ' delivery ' ] ) [ ' delivery ' ]
pricelist_id = partner . property_product_pricelist_purchase . id
2011-10-18 08:42:34 +00:00
warehouse_id = warehouse_obj . search ( cr , uid , [ ( ' company_id ' , ' = ' , procurement . company_id . id or company . id ) ] , context = context )
2010-04-29 13:30:07 +00:00
uom_id = procurement . product_id . uom_po_id . id
qty = uom_obj . _compute_qty ( cr , uid , procurement . product_uom . id , procurement . product_qty , uom_id )
2010-07-23 13:17:30 +00:00
if seller_qty :
qty = max ( qty , seller_qty )
2010-04-29 13:30:07 +00:00
2011-02-21 07:25:45 +00:00
price = pricelist_obj . price_get ( cr , uid , [ pricelist_id ] , procurement . product_id . id , qty , partner_id , { ' uom ' : uom_id } ) [ pricelist_id ]
2010-04-29 13:30:07 +00:00
2011-12-23 13:36:16 +00:00
schedule_date = self . _get_purchase_schedule_date ( cr , uid , procurement , company , context = context )
purchase_date = self . _get_purchase_order_date ( cr , uid , procurement , company , schedule_date , context = context )
2010-04-29 13:30:07 +00:00
#Passing partner_id to context for purchase order line integrity of Line name
2012-02-22 13:18:49 +00:00
new_context = context . copy ( )
new_context . update ( { ' lang ' : partner . lang , ' partner_id ' : partner_id } )
2010-04-29 13:30:07 +00:00
2012-02-22 13:18:49 +00:00
product = prod_obj . browse ( cr , uid , procurement . product_id . id , context = new_context )
2011-07-06 00:26:24 +00:00
taxes_ids = procurement . product_id . product_tmpl_id . supplier_taxes_id
taxes = acc_pos_obj . map_tax ( cr , uid , partner . property_account_position , taxes_ids )
2010-04-29 13:30:07 +00:00
2012-07-14 20:51:50 +00:00
name = product . partner_ref
if product . description_purchase :
name + = ' \n ' + product . description_purchase
2011-08-26 14:28:36 +00:00
line_vals = {
2012-07-14 20:51:50 +00:00
' name ' : name ,
2010-04-29 13:30:07 +00:00
' product_qty ' : qty ,
' product_id ' : procurement . product_id . id ,
' product_uom ' : uom_id ,
2011-09-25 22:44:29 +00:00
' price_unit ' : price or 0.0 ,
2011-12-23 13:36:16 +00:00
' date_planned ' : schedule_date . strftime ( DEFAULT_SERVER_DATETIME_FORMAT ) ,
2010-04-29 13:30:07 +00:00
' move_dest_id ' : res_id ,
2011-07-06 00:26:24 +00:00
' taxes_id ' : [ ( 6 , 0 , taxes ) ] ,
2010-04-29 13:30:07 +00:00
}
2011-09-19 13:21:07 +00:00
name = seq_obj . get ( cr , uid , ' purchase.order ' ) or _ ( ' PO: %s ' ) % procurement . name
2011-07-06 00:26:24 +00:00
po_vals = {
2011-09-15 08:33:33 +00:00
' name ' : name ,
2010-04-29 13:30:07 +00:00
' origin ' : procurement . origin ,
' partner_id ' : partner_id ,
' location_id ' : procurement . location_id . id ,
2011-09-15 08:33:33 +00:00
' warehouse_id ' : warehouse_id and warehouse_id [ 0 ] or False ,
2010-04-29 13:30:07 +00:00
' pricelist_id ' : pricelist_id ,
2011-12-23 13:36:16 +00:00
' date_order ' : purchase_date . strftime ( DEFAULT_SERVER_DATETIME_FORMAT ) ,
2010-04-29 13:30:07 +00:00
' company_id ' : procurement . company_id . id ,
' fiscal_position ' : partner . property_account_position and partner . property_account_position . id or False
2011-07-06 00:26:24 +00:00
}
2012-02-22 13:18:49 +00:00
res [ procurement . id ] = self . create_procurement_purchase_order ( cr , uid , procurement , po_vals , line_vals , context = new_context )
2011-07-06 00:26:24 +00:00
self . write ( cr , uid , [ procurement . id ] , { ' state ' : ' running ' , ' purchase_id ' : res [ procurement . id ] } )
2012-10-03 09:43:41 +00:00
self . purchase_order_create_note ( cr , uid , ids , context = context )
2010-04-29 13:30:07 +00:00
return res
2012-10-03 07:57:30 +00:00
def purchase_order_create_note ( self , cr , uid , ids , context = None ) :
for procurement in self . browse ( cr , uid , ids , context = context ) :
2012-10-10 19:04:38 +00:00
body = _ ( " Draft Purchase Order created " )
2012-10-03 11:38:10 +00:00
self . message_post ( cr , uid , [ procurement . id ] , body = body , context = context )
2012-10-03 07:57:30 +00:00
2010-06-23 07:23:38 +00:00
procurement_order ( )
2012-03-27 11:44:44 +00:00
2012-09-19 13:04:12 +00:00
class mail_mail ( osv . osv ) :
_name = ' mail.mail '
_inherit = ' mail.mail '
2012-08-07 11:06:16 +00:00
2012-09-19 13:04:12 +00:00
def _postprocess_sent_message ( self , cr , uid , mail , context = None ) :
if mail . model == ' purchase.order ' :
2012-03-27 11:44:44 +00:00
wf_service = netsvc . LocalService ( " workflow " )
2012-09-19 13:04:12 +00:00
wf_service . trg_validate ( uid , ' purchase.order ' , mail . res_id , ' send_rfq ' , cr )
return super ( mail_mail , self ) . _postprocess_sent_message ( cr , uid , mail = mail , context = context )
2012-03-27 11:44:44 +00:00
2012-09-19 13:04:12 +00:00
mail_mail ( )
2012-10-02 10:29:15 +00:00
2012-10-03 06:48:40 +00:00
class product_template ( osv . osv ) :
_name = ' product.template '
_inherit = ' product.template '
_columns = {
2012-10-23 13:02:42 +00:00
' purchase_ok ' : fields . boolean ( ' Can be Purchased ' , help = " Specify if the product can be selected in a purchase order line. " ) ,
2012-10-10 20:38:18 +00:00
}
2012-10-03 06:48:40 +00:00
_defaults = {
2012-10-10 20:38:18 +00:00
' purchase_ok ' : 1 ,
}
2012-10-03 06:48:40 +00:00
product_template ( )
2012-10-03 10:10:55 +00:00
2010-07-09 14:31:07 +00:00
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: