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
2010-03-06 20:52:19 +00:00
2006-12-07 13:41:40 +00:00
#
# Model definition
#
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
2010-06-14 11:09:51 +00:00
for c in self . pool . get ( ' account.tax ' ) . compute_all ( cr , uid , line . taxes_id , line . price_unit , line . product_qty , order . partner_address_id . id , 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
2010-09-03 09:38:12 +00:00
STATE_SELECTION = [
( ' draft ' , ' Request for Quotation ' ) ,
( ' wait ' , ' Waiting ' ) ,
2011-01-12 15:28:42 +00:00
( ' confirmed ' , ' Waiting Approval ' ) ,
2010-09-03 09:38:12 +00:00
( ' approved ' , ' Approved ' ) ,
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 = {
2010-06-21 13:59:11 +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 ,
2008-09-22 08:14:07 +00:00
help = " Reference of the document that generated this purchase order request. "
) ,
2010-06-21 21:20:55 +00:00
' partner_ref ' : fields . char ( ' Supplier Reference ' , states = { ' confirmed ' : [ ( ' readonly ' , True ) ] , ' approved ' : [ ( ' readonly ' , True ) ] , ' done ' : [ ( ' readonly ' , True ) ] } , size = 64 ) ,
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 ) ,
2010-06-21 21:20:55 +00:00
' partner_address_id ' : fields . many2one ( ' res.partner.address ' , ' Address ' , required = True ,
2010-07-01 05:27:08 +00:00
states = { ' confirmed ' : [ ( ' readonly ' , True ) ] , ' approved ' : [ ( ' readonly ' , True ) ] , ' done ' : [ ( ' readonly ' , True ) ] } , domain = " [( ' partner_id ' , ' = ' , partner_id)] " ) ,
2011-02-03 13:15:11 +00:00
' dest_address_id ' : fields . many2one ( ' res.partner.address ' , ' Destination Address ' , domain = " [( ' partner_id ' , ' != ' , False)] " ,
2010-06-21 21:20:55 +00:00
states = { ' confirmed ' : [ ( ' readonly ' , True ) ] , ' approved ' : [ ( ' readonly ' , True ) ] , ' done ' : [ ( ' readonly ' , True ) ] } ,
2008-09-22 08:14:07 +00:00
help = " Put an address if you want to deliver directly from the supplier to the customer. " \
" In this case, it will remove the warehouse link and set the customer location. "
) ,
2010-06-21 21:20:55 +00:00
' warehouse_id ' : fields . many2one ( ' stock.warehouse ' , ' 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. " ) ,
2010-09-03 09:38:12 +00:00
' state ' : fields . selection ( STATE_SELECTION , ' State ' , readonly = True , help = " The state of the purchase order or the quotation request. A quotation is a purchase order in a ' Draft ' state. Then the order has to be confirmed by the user, the state switch to ' Confirmed ' . Then the supplier must confirm the order to change the state to ' Approved ' . When the purchase order is paid and received, the state becomes ' Done ' . If a cancel action occurs in the invoice or in the reception of goods, the state 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 ) ,
2010-06-12 18:32:16 +00:00
' notes ' : fields . text ( ' Notes ' ) ,
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 " ) ,
2008-07-22 15:11:28 +00:00
' picking_ids ' : fields . one2many ( ' stock.picking ' , ' purchase_id ' , ' Picking List ' , readonly = True , help = " This is the list of picking list that have been generated for this purchase " ) ,
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 ' ) ,
' invoiced ' : fields . function ( _invoiced , string = ' Invoiced & Paid ' , type = ' boolean ' , help = " It indicates that an invoice has been paid " ) ,
' invoiced_rate ' : fields . function ( _invoiced_rate , string = ' Invoiced ' , type = ' float ' ) ,
2011-11-14 11:18:10 +00:00
' invoice_method ' : fields . selection ( [ ( ' manual ' , ' Based on Purchase Order lines ' ) , ( ' order ' , ' Based on generated draft invoice ' ) , ( ' picking ' , ' Based on receptions ' ) ] , ' 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 " \
" Based on receptions: 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 ) ,
}
) ,
2011-07-01 23:41:24 +00:00
' amount_untaxed ' : fields . function ( _amount_all , digits_compute = dp . get_precision ( ' Purchase Price ' ) , 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 " ) ,
2011-07-01 23:41:24 +00:00
' amount_tax ' : fields . function ( _amount_all , digits_compute = dp . get_precision ( ' Purchase Price ' ) , 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 " ) ,
2011-07-01 23:41:24 +00:00
' amount_total ' : fields . function ( _amount_all , digits_compute = dp . get_precision ( ' Purchase Price ' ) , 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 ) ,
2008-07-22 15:11:28 +00:00
}
_defaults = {
2011-01-19 06:10:51 +00:00
' date_order ' : lambda * a : time . strftime ( ' % Y- % m- %d ' ) ,
2010-06-23 07:23:38 +00:00
' state ' : ' draft ' ,
2008-07-22 15:11:28 +00:00
' name ' : lambda obj , cr , uid , context : obj . pool . get ( ' ir.sequence ' ) . get ( cr , uid , ' purchase.order ' ) ,
2010-06-23 07:23:38 +00:00
' shipped ' : 0 ,
' invoice_method ' : ' order ' ,
' invoiced ' : 0 ,
2008-07-22 15:11:28 +00:00
' partner_address_id ' : lambda self , cr , uid , context : context . get ( ' partner_id ' , False ) and self . pool . get ( ' res.partner ' ) . address_get ( cr , uid , [ context [ ' partner_id ' ] ] , [ ' default ' ] ) [ ' default ' ] ,
' 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 ) ,
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 "
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
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 :
2011-09-18 14:01:59 +00:00
raise osv . except_osv ( _ ( ' Invalid action ! ' ) , _ ( ' In order to delete a purchase order, it must be cancelled first! ' ) )
2010-02-02 21:37:52 +00:00
2010-05-28 09:48:40 +00:00
# TODO: temporary fix in 5.0, to remove in 5.2 when subflows support
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
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 { }
2011-10-20 07:30:04 +00:00
address = self . pool . get ( ' res.partner.address ' )
2011-02-10 18:48:06 +00:00
values = { ' warehouse_id ' : False }
2011-10-20 07:30:04 +00:00
supplier = address . browse ( cr , uid , address_id ) . partner_id
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 :
2009-01-19 13:59:11 +00:00
return { ' value ' : { ' partner_address_id ' : False , ' 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
return { ' value ' : { ' partner_address_id ' : supplier_address [ ' default ' ] , ' pricelist_id ' : pricelist , ' fiscal_position ' : fiscal_position } }
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 ) :
2008-07-22 15:11:28 +00:00
self . write ( cr , uid , ids , { ' state ' : ' approved ' , ' date_approve ' : time . strftime ( ' % Y- % m- %d ' ) } )
return True
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 :
2011-09-18 13:53:10 +00:00
raise osv . except_osv ( _ ( ' Error ! ' ) , _ ( ' You cannot confirm a purchase order without any lines. ' ) )
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 )
2010-10-16 14:02:41 +00:00
message = _ ( " Purchase order ' %s ' is confirmed. " ) % ( po . name , )
self . log ( cr , uid , po . id , message )
2010-10-27 15:29:39 +00:00
# current_name = self.name_get(cr, uid, ids)[0][1]
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 } )
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 ) :
2011-11-15 07:14:36 +00:00
""" Collects require data from purchase order line that is used to create invoice line
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
2008-10-13 13:08:08 +00:00
def action_cancel_draft ( self , cr , uid , ids , * args ) :
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 )
2010-06-15 04:28:11 +00:00
for ( id , name ) in self . name_get ( cr , uid , ids ) :
2010-10-17 17:30:00 +00:00
message = _ ( " Purchase order ' %s ' has been set in draft state. " ) % name
2010-10-16 14:02:41 +00:00
self . log ( cr , uid , id , message )
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 :
raise osv . except_osv ( _ ( ' Error ! ' ) ,
2011-11-15 06:43:53 +00:00
_ ( ' There is no purchase journal defined 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 :
raise osv . except_osv ( _ ( ' Error ! ' ) , _ ( ' There is no expense account defined for this product: " %s " (id: %d ) ' ) % ( po_line . product_id . name , po_line . product_id . id , ) )
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 ,
' address_invoice_id ' : order . partner_address_id . id ,
' address_contact_id ' : order . partner_address_id . id ,
2011-07-08 06:04:11 +00:00
' journal_id ' : len ( journal_ids ) and journal_ids [ 0 ] or False ,
2011-11-15 10:14:34 +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
2008-07-22 15:11:28 +00:00
return res
def has_stockable_product ( self , cr , uid , ids , * args ) :
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 (
2011-09-18 13:41:16 +00:00
_ ( ' Unable to cancel this purchase order! ' ) ,
_ ( ' You must 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 (
2011-09-18 13:41:16 +00:00
_ ( ' Unable to cancel this purchase order! ' ) ,
_ ( ' You must first cancel all invoices 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 ' } )
2011-11-09 11:49:08 +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 )
2010-10-17 17:30:00 +00:00
message = _ ( " Purchase order ' %s ' is cancelled. " ) % name
2010-10-14 10:03:23 +00:00
self . log ( cr , uid , id , message )
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 ,
2011-10-03 21:27:47 +00:00
' type ' : ' in ' ,
' address_id ' : order . dest_address_id . id or order . partner_address_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 ' : [ ] ,
}
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 ,
2011-10-16 01:31:25 +00:00
' address_id ' : order . dest_address_id . id or order . partner_address_id . id ,
2011-10-03 21:27:47 +00:00
' move_dest_id ' : order_line . move_dest_id . id ,
' state ' : ' draft ' ,
' 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
"""
2011-10-27 21:14:59 +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 ]
2011-12-19 16:54:40 +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.
return picking_ids [ 0 ] if picking_ids else False
2009-12-01 14:54:18 +00:00
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 )
# 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 ,
' partner_address_id ' : porder . partner_address_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 :
line_key = make_key ( order_line , ( ' name ' , ' date_planned ' , ' taxes_id ' , ' price_unit ' , ' notes ' , ' product_id ' , ' move_dest_id ' , ' account_analytic_id ' ) )
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
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
2011-09-15 08:54:54 +00:00
def _get_uom_id ( self , cr , uid , context = None ) :
try :
proxy = self . pool . get ( ' ir.model.data ' )
result = proxy . get_object_reference ( cr , uid , ' product ' , ' product_uom_unit ' )
return result [ 1 ]
except Exception , ex :
return False
2011-11-01 05:28:59 +00:00
2008-07-22 15:11:28 +00:00
_columns = {
2009-07-02 15:11:59 +00:00
' name ' : fields . char ( ' Description ' , size = 256 , required = True ) ,
2011-10-31 11:38:30 +00:00
' product_qty ' : fields . float ( ' Quantity ' , digits_compute = dp . get_precision ( ' Product UoM ' ) , 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 ' ) ,
' product_uom ' : fields . many2one ( ' product.uom ' , ' Product UOM ' , required = True ) ,
' 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 ' ) ,
2010-03-06 20:52:19 +00:00
' price_unit ' : fields . float ( ' Unit Price ' , required = True , digits_compute = dp . get_precision ( ' Purchase Price ' ) ) ,
2011-07-01 23:41:24 +00:00
' price_subtotal ' : fields . function ( _amount_line , string = ' Subtotal ' , digits_compute = dp . get_precision ( ' Purchase Price ' ) ) ,
2010-06-12 18:32:16 +00:00
' notes ' : fields . text ( ' Notes ' ) ,
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 ) ,
2010-04-13 10:28:22 +00:00
' state ' : fields . selection ( [ ( ' draft ' , ' Draft ' ) , ( ' confirmed ' , ' Confirmed ' ) , ( ' done ' , ' Done ' ) , ( ' cancel ' , ' Cancelled ' ) ] , ' State ' , required = True , readonly = True ,
help = ' * The \' Draft \' state is set automatically when purchase order in draft state. \
\n * The \' Confirmed \' state is set automatically as confirm when purchase order in confirm state. \
\n * The \' Done \' state is set automatically when purchase order is set as done. \
\n * The \' Cancelled \' state is set automatically when user cancel purchase order. ' ) ,
' 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 = {
2011-09-07 05:07:01 +00:00
' product_uom ' : _get_uom_id ,
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
2011-10-20 07:30:04 +00:00
#TOFIX:
# - name of method should "onchange_product_id"
# - docstring
# - merge 'product_uom_change' method
# - split into small internal methods for clearity
2008-07-22 15:11:28 +00:00
def product_id_change ( self , cr , uid , ids , pricelist , product , qty , uom ,
2010-02-09 08:31:46 +00:00
partner_id , date_order = False , fiscal_position = False , date_planned = False ,
2011-09-24 14:07:44 +00:00
name = False , price_unit = False , notes = False , context = { } ) :
2008-07-22 15:11:28 +00:00
if not pricelist :
2010-05-28 09:48:40 +00:00
raise osv . except_osv ( _ ( ' No Pricelist ! ' ) , _ ( ' You have to select a pricelist or a supplier in the purchase form ! \n Please set one before choosing a product. ' ) )
2009-01-23 13:45:30 +00:00
if not partner_id :
raise osv . except_osv ( _ ( ' No Partner! ' ) , _ ( ' You have to select a partner in the purchase form ! \n Please set one partner before choosing a product. ' ) )
2008-07-22 15:11:28 +00:00
if not product :
2009-11-26 13:54:00 +00:00
return { ' value ' : { ' price_unit ' : price_unit or 0.0 , ' name ' : name or ' ' ,
' notes ' : notes or ' ' , ' product_uom ' : uom or False } , ' domain ' : { ' product_uom ' : [ ] } }
2010-12-21 12:56:38 +00:00
res = { }
2010-06-16 11:51:39 +00:00
prod = self . pool . get ( ' product.product ' ) . browse ( cr , uid , product )
2010-12-08 12:33:38 +00:00
product_uom_pool = self . pool . get ( ' product.uom ' )
2008-07-22 15:11:28 +00:00
lang = False
if partner_id :
2010-06-11 14:26:31 +00:00
lang = self . pool . get ( ' res.partner ' ) . read ( cr , uid , partner_id , [ ' lang ' ] ) [ ' lang ' ]
2008-07-22 15:11:28 +00:00
context = { ' lang ' : lang }
2008-12-16 19:35:32 +00:00
context [ ' partner_id ' ] = partner_id
2008-07-22 15:11:28 +00:00
2008-12-16 19:35:32 +00:00
prod = self . pool . get ( ' product.product ' ) . browse ( cr , uid , product , context = context )
prod_uom_po = prod . uom_po_id . id
2008-07-22 15:11:28 +00:00
if not uom :
uom = prod_uom_po
if not date_order :
date_order = time . strftime ( ' % Y- % m- %d ' )
2009-03-10 07:12:13 +00:00
qty = qty or 1.0
2008-12-16 19:35:32 +00:00
seller_delay = 0
2011-02-11 12:59:20 +00:00
if uom :
uom1_cat = prod . uom_id . category_id . id
uom2_cat = product_uom_pool . browse ( cr , uid , uom ) . category_id . id
if uom1_cat != uom2_cat :
uom = False
2010-11-23 12:05:08 +00:00
2010-11-24 09:20:15 +00:00
prod_name = self . pool . get ( ' product.product ' ) . name_get ( cr , uid , [ prod . id ] , context = context ) [ 0 ] [ 1 ]
2011-01-13 04:44:49 +00:00
res = { }
2008-12-16 19:35:32 +00:00
for s in prod . seller_ids :
if s . name . id == partner_id :
seller_delay = s . delay
2010-12-08 12:33:38 +00:00
if s . product_uom :
2010-12-21 12:56:38 +00:00
temp_qty = product_uom_pool . _compute_qty ( cr , uid , s . product_uom . id , s . min_qty , to_uom_id = prod . uom_id . id )
uom = s . product_uom . id #prod_uom_po
temp_qty = s . min_qty # supplier _qty assigned to temp
2009-03-17 14:02:37 +00:00
if qty < temp_qty : # If the supplier quantity is greater than entered from user, set minimal.
qty = temp_qty
2011-09-18 13:41:16 +00:00
res . update ( { ' warning ' : { ' title ' : _ ( ' Warning ' ) , ' message ' : _ ( ' The selected supplier has a minimal quantity set to %s , you should not purchase less. ' ) % qty } } )
2010-12-21 12:56:38 +00:00
qty_in_product_uom = product_uom_pool . _compute_qty ( cr , uid , uom , qty , to_uom_id = prod . uom_id . id )
price = self . pool . get ( ' product.pricelist ' ) . price_get ( cr , uid , [ pricelist ] ,
product , qty_in_product_uom or 1.0 , partner_id , {
2010-01-21 15:13:50 +00:00
' uom ' : uom ,
' date ' : date_order ,
} ) [ pricelist ]
2010-03-19 08:46:15 +00:00
dt = ( datetime . now ( ) + relativedelta ( days = int ( seller_delay ) or 0.0 ) ) . strftime ( ' % Y- % m- %d % H: % M: % S ' )
2008-12-16 19:35:32 +00:00
2011-01-18 13:33:35 +00:00
res . update ( { ' value ' : { ' price_unit ' : price , ' name ' : prod_name ,
2009-11-26 13:54:00 +00:00
' taxes_id ' : map ( lambda x : x . id , prod . supplier_taxes_id ) ,
' date_planned ' : date_planned or dt , ' notes ' : notes or prod . description_purchase ,
2008-12-16 19:35:32 +00:00
' product_qty ' : qty ,
2011-02-11 12:59:20 +00:00
' product_uom ' : prod . uom_id . id } } )
2008-07-22 15:11:28 +00:00
domain = { }
2008-12-16 19:35:32 +00:00
taxes = self . pool . get ( ' account.tax ' ) . browse ( cr , uid , map ( lambda x : x . id , prod . supplier_taxes_id ) )
2009-01-20 06:04:07 +00:00
fpos = fiscal_position and self . pool . get ( ' account.fiscal.position ' ) . browse ( cr , uid , fiscal_position ) or False
res [ ' value ' ] [ ' taxes_id ' ] = self . pool . get ( ' account.fiscal.position ' ) . map_tax ( cr , uid , fpos , taxes )
2011-02-11 12:59:20 +00:00
res2 = self . pool . get ( ' product.uom ' ) . read ( cr , uid , [ prod . uom_id . id ] , [ ' category_id ' ] )
2008-12-16 19:35:32 +00:00
res3 = prod . uom_id . category_id . id
2008-07-22 15:11:28 +00:00
domain = { ' product_uom ' : [ ( ' category_id ' , ' = ' , res2 [ 0 ] [ ' category_id ' ] [ 0 ] ) ] }
2008-12-16 19:35:32 +00:00
if res2 [ 0 ] [ ' category_id ' ] [ 0 ] != res3 :
2008-07-22 15:11:28 +00:00
raise osv . except_osv ( _ ( ' Wrong Product UOM ! ' ) , _ ( ' You have to select a product UOM in the same category than the purchase UOM of the product ' ) )
res [ ' domain ' ] = domain
return res
2011-10-20 07:30:04 +00:00
#TOFIX:
# - merge into 'product_id_change' method
2008-07-22 15:11:28 +00:00
def product_uom_change ( self , cr , uid , ids , pricelist , product , qty , uom ,
2011-01-18 13:33:35 +00:00
partner_id , date_order = False , fiscal_position = False , date_planned = False ,
2011-09-24 14:07:44 +00:00
name = False , price_unit = False , notes = False , context = { } ) :
2008-07-22 15:11:28 +00:00
res = self . product_id_change ( cr , uid , ids , pricelist , product , qty , uom ,
2011-01-18 13:33:35 +00:00
partner_id , date_order = date_order , fiscal_position = fiscal_position , date_planned = date_planned ,
2011-09-24 14:07:44 +00:00
name = name , price_unit = price_unit , notes = notes , context = context )
2008-07-22 15:11:28 +00:00
if ' product_uom ' in res [ ' value ' ] :
2011-01-18 11:09:05 +00:00
if uom and ( uom != res [ ' value ' ] [ ' product_uom ' ] ) and res [ ' value ' ] [ ' product_uom ' ] :
2010-12-21 12:56:38 +00:00
seller_uom_name = self . pool . get ( ' product.uom ' ) . read ( cr , uid , [ res [ ' value ' ] [ ' product_uom ' ] ] , [ ' name ' ] ) [ 0 ] [ ' name ' ]
res . update ( { ' warning ' : { ' title ' : _ ( ' Warning ' ) , ' message ' : _ ( ' The selected supplier only sells this product by %s ' ) % seller_uom_name } } )
2008-07-22 15:11:28 +00:00
del res [ ' value ' ] [ ' product_uom ' ]
if not uom :
res [ ' value ' ] [ ' price_unit ' ] = 0.0
return res
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-22 08:31:44 +00:00
def _get_schedule_date ( self , cr , uid , procurement_date_planned , company , context = None ) :
schedule_date = ( procurement_date_planned - relativedelta ( days = company . po_lead ) )
return schedule_date
def _get_order_dates ( self , cr , uid , schedule_date , seller_delay , context = None ) :
order_dates = schedule_date - relativedelta ( days = seller_delay )
return order_dates
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
seller_delay = int ( procurement . product_id . seller_delay )
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-22 08:31:44 +00:00
procurement_date_planned = datetime . strptime ( procurement . date_planned , ' % Y- % m- %d % H: % M: % S ' )
schedule_date = self . _get_schedule_date ( cr , uid , procurement_date_planned , company , context = context )
order_dates = self . _get_order_dates ( cr , uid , schedule_date , seller_delay , context = context )
2010-04-29 13:30:07 +00:00
#Passing partner_id to context for purchase order line integrity of Line name
context . update ( { ' lang ' : partner . lang , ' partner_id ' : partner_id } )
product = prod_obj . browse ( cr , uid , procurement . product_id . id , context = 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
2011-08-26 14:28:36 +00:00
line_vals = {
2010-04-29 13:30:07 +00:00
' name ' : product . partner_ref ,
' 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-07-20 09:46:39 +00:00
' date_planned ' : schedule_date . strftime ( ' % Y- % m- %d % H: % M: % S ' ) ,
2010-04-29 13:30:07 +00:00
' move_dest_id ' : res_id ,
' notes ' : product . description_purchase ,
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 ,
' partner_address_id ' : address_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-07-21 05:59:40 +00:00
' date_order ' : order_dates . strftime ( ' % Y- % m- %d % H: % M: % S ' ) ,
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
}
2011-08-26 14:28:36 +00:00
res [ procurement . id ] = self . create_procurement_purchase_order ( cr , uid , procurement , po_vals , line_vals , context = context )
2011-07-06 00:26:24 +00:00
self . write ( cr , uid , [ procurement . id ] , { ' state ' : ' running ' , ' purchase_id ' : res [ procurement . id ] } )
2010-04-29 13:30:07 +00:00
return res
2010-06-23 07:23:38 +00:00
procurement_order ( )
2010-07-09 14:31:07 +00:00
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: