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
#
##############################################################################
2013-03-26 12:14:37 +00:00
import pytz
2014-01-14 17:36:53 +00:00
from openerp import SUPERUSER_ID , workflow
2010-03-01 05:13:20 +00:00
from datetime import datetime
from dateutil . relativedelta import relativedelta
2013-01-25 16:53:55 +00:00
from operator import attrgetter
2013-08-06 07:22:25 +00:00
from openerp . tools . safe_eval import safe_eval as eval
2012-12-06 14:56:32 +00:00
from openerp . osv import fields , osv
from openerp . tools . translate import _
2012-12-17 15:23:03 +00:00
import openerp . addons . decimal_precision as dp
2014-07-06 14:44:26 +00:00
from openerp . osv . orm import browse_record_list , browse_record , browse_null
2012-12-06 14:56:32 +00:00
from openerp . tools import DEFAULT_SERVER_DATE_FORMAT , DEFAULT_SERVER_DATETIME_FORMAT , DATETIME_FORMATS_MAP
2014-07-16 13:45:56 +00:00
from openerp . tools . float_utils import float_compare
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-10-31 10:52:24 +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 , 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))
2014-07-06 14:44:26 +00:00
self . invalidate_cache ( cr , uid , context = context )
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
2013-10-21 11:25:18 +00:00
p . order_id , sum ( m . product_qty ) , m . state
2008-08-27 11:26:37 +00:00
FROM
stock_move m
LEFT JOIN
2013-10-21 11:25:18 +00:00
purchase_order_line p on ( p . id = m . purchase_line_id )
2008-08-27 11:26:37 +00:00
WHERE
2013-10-21 11:25:18 +00:00
p . order_id IN % s GROUP BY m . state , p . order_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 ) :
2014-04-15 08:12:11 +00:00
res [ purchase . id ] = all ( line . invoiced for line in purchase . order_line )
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
2013-08-29 07:35:16 +00:00
def _get_picking_in ( self , cr , uid , context = None ) :
obj_data = self . pool . get ( ' ir.model.data ' )
2014-11-05 10:45:04 +00:00
type_obj = self . pool . get ( ' stock.picking.type ' )
user_obj = self . pool . get ( ' res.users ' )
company_id = user_obj . browse ( cr , uid , uid , context = context ) . company_id . id
pick_type = obj_data . get_object_reference ( cr , uid , ' stock ' , ' picking_type_in ' ) and obj_data . get_object_reference ( cr , uid , ' stock ' , ' picking_type_in ' ) [ 1 ] or False
if pick_type :
type = type_obj . browse ( cr , uid , pick_type , context = context )
if type and type . warehouse_id and type . warehouse_id . company_id . id == company_id :
return pick_type
2014-11-04 16:28:43 +00:00
types = type_obj . search ( cr , uid , [ ( ' code ' , ' = ' , ' incoming ' ) , ( ' warehouse_id.company_id ' , ' = ' , company_id ) ] , context = context )
2014-11-05 10:45:04 +00:00
if not types :
2014-11-04 16:28:43 +00:00
types = type_obj . search ( cr , uid , [ ( ' code ' , ' = ' , ' incoming ' ) , ( ' warehouse_id ' , ' = ' , False ) ] , context = context )
if not types :
raise osv . except_osv ( _ ( ' Error! ' ) , _ ( " Make sure you have at least an incoming picking type defined " ) )
2014-11-05 10:45:04 +00:00
return types [ 0 ]
2013-08-29 07:35:16 +00:00
2014-02-17 17:04:59 +00:00
def _get_picking_ids ( self , cr , uid , ids , field_names , args , context = None ) :
2013-10-14 14:25:40 +00:00
res = { }
2014-03-27 10:38:55 +00:00
for po_id in ids :
res [ po_id ] = [ ]
2014-02-24 13:41:26 +00:00
query = """
SELECT picking_id , po . id FROM stock_picking p , stock_move m , purchase_order_line pol , purchase_order po
WHERE po . id in % s and po . id = pol . order_id and pol . id = m . purchase_line_id and m . picking_id = p . id
GROUP BY picking_id , po . id
"""
cr . execute ( query , ( tuple ( ids ) , ) )
picks = cr . fetchall ( )
2014-03-18 11:57:46 +00:00
for pick_id , po_id in picks :
2014-03-27 10:38:55 +00:00
res [ po_id ] . append ( pick_id )
2013-10-14 14:25:40 +00:00
return res
2014-02-03 17:30:00 +00:00
2014-03-26 12:40:02 +00:00
def _count_all ( self , cr , uid , ids , field_name , arg , context = None ) :
2014-05-07 14:45:32 +00:00
return {
purchase . id : {
' shipment_count ' : len ( purchase . picking_ids ) ,
' invoice_count ' : len ( purchase . invoice_ids ) ,
}
for purchase in self . browse ( cr , uid , ids , context = context )
}
2014-03-26 12:40:02 +00:00
2010-09-03 09:38:12 +00:00
STATE_SELECTION = [
2012-02-27 13:15:57 +00:00
( ' draft ' , ' Draft PO ' ) ,
2014-02-03 17:30:00 +00:00
( ' sent ' , ' RFQ ' ) ,
2013-05-29 09:51:50 +00:00
( ' bid ' , ' Bid Received ' ) ,
2011-01-12 15:28:42 +00:00
( ' confirmed ' , ' Waiting Approval ' ) ,
2013-05-29 09:51:50 +00:00
( ' approved ' , ' Purchase Confirmed ' ) ,
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 ' )
]
2012-12-18 22:06:17 +00:00
_track = {
' state ' : {
2013-06-27 14:46:47 +00:00
' purchase.mt_rfq_confirmed ' : lambda self , cr , uid , obj , ctx = None : obj . state == ' confirmed ' ,
' purchase.mt_rfq_approved ' : lambda self , cr , uid , obj , ctx = None : obj . state == ' approved ' ,
' purchase.mt_rfq_done ' : lambda self , cr , uid , obj , ctx = None : obj . state == ' done ' ,
2012-12-18 22:06:17 +00:00
} ,
}
2008-07-22 15:11:28 +00:00
_columns = {
2014-07-06 14:44:26 +00:00
' name ' : fields . char ( ' Order Reference ' , required = True , select = True , copy = False ,
help = " Unique number of the purchase order, "
" computed automatically when the purchase order is created. " ) ,
' origin ' : fields . char ( ' Source Document ' , copy = False ,
help = " Reference of the document that generated this purchase order "
" request; a sales order or an internal procurement request. " ) ,
' partner_ref ' : fields . char ( ' Supplier Reference ' , states = { ' confirmed ' : [ ( ' readonly ' , True ) ] ,
' approved ' : [ ( ' readonly ' , True ) ] ,
' done ' : [ ( ' readonly ' , True ) ] } ,
copy = False ,
help = " Reference of the sales order or bid 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. " ) ,
2014-07-08 14:04:09 +00:00
' date_order ' : fields . datetime ( ' Order Date ' , required = True , states = { ' confirmed ' : [ ( ' readonly ' , True ) ] ,
2014-07-06 14:44:26 +00:00
' approved ' : [ ( ' readonly ' , True ) ] } ,
2014-07-08 14:04:09 +00:00
select = True , help = " Depicts the date where the Quotation should be validated and converted into a Purchase Order, by default it ' s the creation date. " ,
2014-07-06 14:44:26 +00:00
copy = False ) ,
' date_approve ' : fields . date ( ' Date Approved ' , readonly = 1 , select = True , copy = False ,
help = " Date on which purchase order has been approved " ) ,
2012-12-18 22:06:17 +00:00
' partner_id ' : fields . many2one ( ' res.partner ' , ' Supplier ' , required = True , states = { ' confirmed ' : [ ( ' readonly ' , True ) ] , ' approved ' : [ ( ' readonly ' , True ) ] , ' done ' : [ ( ' readonly ' , True ) ] } ,
2012-12-20 11:47:30 +00:00
change_default = True , track_visibility = ' always ' ) ,
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-11-09 07:15:28 +00:00
' location_id ' : fields . many2one ( ' stock.location ' , ' Destination ' , required = True , domain = [ ( ' usage ' , ' <> ' , ' view ' ) ] , states = { ' confirmed ' : [ ( ' readonly ' , True ) ] , ' approved ' : [ ( ' readonly ' , True ) ] , ' done ' : [ ( ' readonly ' , True ) ] } ) ,
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. " ) ,
2013-03-04 10:55:32 +00:00
' currency_id ' : fields . many2one ( ' res.currency ' , ' Currency ' , readonly = True , required = True , states = { ' draft ' : [ ( ' readonly ' , False ) ] , ' sent ' : [ ( ' readonly ' , False ) ] } ) ,
2014-07-06 14:44:26 +00:00
' state ' : fields . selection ( STATE_SELECTION , ' Status ' , readonly = True ,
help = " The status of the purchase order or the quotation request. "
" A request for 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 "
2014-07-16 09:58:22 +00:00
" the invoice or in the receipt of goods, the status becomes "
2014-07-06 14:44:26 +00:00
" in exception. " ,
select = True , copy = False ) ,
' order_line ' : fields . one2many ( ' purchase.order.line ' , ' order_id ' , ' Order Lines ' ,
states = { ' approved ' : [ ( ' readonly ' , True ) ] ,
' done ' : [ ( ' readonly ' , True ) ] } ,
copy = True ) ,
' validator ' : fields . many2one ( ' res.users ' , ' Validated by ' , readonly = True , copy = False ) ,
2012-06-09 19:49:46 +00:00
' notes ' : fields . text ( ' Terms and Conditions ' ) ,
2014-07-06 14:44:26 +00:00
' invoice_ids ' : fields . many2many ( ' account.invoice ' , ' purchase_invoice_rel ' , ' purchase_id ' ,
' invoice_id ' , ' Invoices ' , copy = False ,
help = " Invoices generated for a purchase order " ) ,
2014-07-16 09:58:22 +00:00
' picking_ids ' : fields . function ( _get_picking_ids , method = True , type = ' one2many ' , relation = ' stock.picking ' , string = ' Picking List ' , help = " This is the list of receipts that have been generated for this purchase order. " ) ,
2014-07-06 14:44:26 +00:00
' shipped ' : fields . boolean ( ' Received ' , readonly = True , select = True , copy = False ,
help = " It indicates that a picking has been done " ) ,
2012-12-05 08:37:31 +00:00
' shipped_rate ' : fields . function ( _shipped_rate , string = ' Received Ratio ' , type = ' float ' ) ,
2014-07-06 14:44:26 +00:00
' invoiced ' : fields . function ( _invoiced , string = ' Invoice Received ' , type = ' boolean ' , copy = False ,
help = " It indicates that an invoice has been validated " ) ,
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 ,
2012-11-22 11:45:37 +00:00
readonly = True , states = { ' draft ' : [ ( ' readonly ' , False ) ] , ' sent ' : [ ( ' readonly ' , False ) ] } ,
2013-06-07 13:02:40 +00:00
help = " Based on Purchase Order lines: place individual lines in ' Invoice Control / On Purchase Order 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 " \
2014-07-16 09:58:22 +00:00
" Based on incoming shipments: let you create an invoice when receipts 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 ) ,
}
) ,
2013-09-27 12:21:48 +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 ) ,
2012-12-20 11:47:30 +00:00
} , multi = " sums " , help = " The amount without tax " , track_visibility = ' always ' ) ,
2013-09-27 12:21:48 +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 " ) ,
2013-09-27 12:21:48 +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 ) ,
2013-09-27 12:21:48 +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 ' ) ,
2012-11-30 17:11:30 +00:00
' payment_term_id ' : fields . many2one ( ' account.payment.term ' , ' Payment Term ' ) ,
2013-06-04 13:15:58 +00:00
' incoterm_id ' : fields . many2one ( ' stock.incoterms ' , ' Incoterm ' , help = " International Commercial Terms are a series of predefined commercial terms used in international transactions. " ) ,
2013-09-27 12:21:48 +00:00
' product_id ' : fields . related ( ' order_line ' , ' product_id ' , type = ' many2one ' , relation = ' product.product ' , string = ' Product ' ) ,
' create_uid ' : fields . many2one ( ' res.users ' , ' Responsible ' ) ,
' company_id ' : fields . many2one ( ' res.company ' , ' Company ' , required = True , select = 1 , states = { ' confirmed ' : [ ( ' readonly ' , True ) ] , ' approved ' : [ ( ' readonly ' , True ) ] } ) ,
2012-10-25 10:05:15 +00:00
' journal_id ' : fields . many2one ( ' account.journal ' , ' Journal ' ) ,
2013-05-29 09:51:50 +00:00
' bid_date ' : fields . date ( ' Bid Received On ' , readonly = True , help = " Date on which the bid was received " ) ,
' bid_validity ' : fields . date ( ' Bid Valid Until ' , help = " Date on which the bid expired " ) ,
2013-09-27 12:21:48 +00:00
' picking_type_id ' : fields . many2one ( ' stock.picking.type ' , ' Deliver To ' , help = " This will determine picking type of incoming shipment " , required = True ,
states = { ' confirmed ' : [ ( ' readonly ' , True ) ] , ' approved ' : [ ( ' readonly ' , True ) ] , ' done ' : [ ( ' readonly ' , True ) ] } ) ,
2013-10-14 14:25:40 +00:00
' related_location_id ' : fields . related ( ' picking_type_id ' , ' default_location_dest_id ' , type = ' many2one ' , relation = ' stock.location ' , string = " Related location " , store = True ) ,
2014-03-26 12:40:02 +00:00
' shipment_count ' : fields . function ( _count_all , type = ' integer ' , string = ' Incoming Shipments ' , multi = True ) ,
' invoice_count ' : fields . function ( _count_all , type = ' integer ' , string = ' Invoices ' , multi = True )
2008-07-22 15:11:28 +00:00
}
_defaults = {
2014-07-08 14:04:09 +00:00
' date_order ' : fields . datetime . now ,
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 ,
2013-09-27 12:21:48 +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 ,
2013-08-29 07:35:16 +00:00
' currency_id ' : lambda self , cr , uid , context : self . pool . get ( ' res.users ' ) . browse ( cr , uid , uid , context = context ) . company_id . currency_id . id ,
' picking_type_id ' : _get_picking_in ,
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 "
2014-01-15 14:05:27 +00:00
_order = ' date_order desc, id 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-26 18:28:11 +00:00
if vals . get ( ' name ' , ' / ' ) == ' / ' :
vals [ ' name ' ] = self . pool . get ( ' ir.sequence ' ) . get ( cr , uid , ' purchase.order ' ) or ' / '
2014-07-06 14:44:26 +00:00
context = dict ( context or { } , mail_create_nolog = True )
2012-03-13 10:07:26 +00:00
order = super ( purchase_order , self ) . create ( cr , uid , vals , context = context )
2013-02-20 05:42:49 +00:00
self . message_post ( cr , uid , [ order ] , body = _ ( " RFQ created " ) , 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
2014-07-07 09:50:30 +00:00
self . signal_workflow ( cr , uid , unlink_ids , ' purchase_cancel ' )
2010-02-02 21:37:52 +00:00
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
2013-05-31 07:38:56 +00:00
def set_order_line_status ( self , cr , uid , ids , status , context = None ) :
line = self . pool . get ( ' purchase.order.line ' )
2014-04-23 15:55:57 +00:00
order_line_ids = [ ]
2014-04-22 15:34:00 +00:00
proc_obj = self . pool . get ( ' procurement.order ' )
2013-05-31 07:38:56 +00:00
for order in self . browse ( cr , uid , ids , context = context ) :
2014-04-23 15:55:57 +00:00
order_line_ids + = [ po_line . id for po_line in order . order_line ]
if order_line_ids :
2013-07-31 17:16:25 +00:00
line . write ( cr , uid , order_line_ids , { ' state ' : status } , context = context )
2014-04-23 15:55:57 +00:00
if order_line_ids and status == ' cancel ' :
procs = proc_obj . search ( cr , uid , [ ( ' purchase_line_id ' , ' in ' , order_line_ids ) ] , context = context )
2014-04-22 15:34:00 +00:00
if procs :
2014-04-23 08:28:08 +00:00
proc_obj . write ( cr , uid , procs , { ' state ' : ' exception ' } , context = context )
2013-05-31 07:38:56 +00:00
return True
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 } }
2014-07-04 02:58:15 +00:00
#Destination address is used when dropshipping
def onchange_dest_address_id ( self , cr , uid , ids , address_id , context = None ) :
2011-10-20 07:30:04 +00:00
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 ' )
2013-09-03 13:46:00 +00:00
values = { }
2014-07-04 02:58:15 +00:00
supplier = address . browse ( cr , uid , address_id , context = context )
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
2013-09-03 12:46:49 +00:00
def onchange_picking_type_id ( self , cr , uid , ids , picking_type_id , context = None ) :
value = { }
if picking_type_id :
picktype = self . pool . get ( " stock.picking.type " ) . browse ( cr , uid , picking_type_id , context = context )
if picktype . default_location_dest_id :
value . update ( { ' location_id ' : picktype . default_location_dest_id . id } )
value . update ( { ' related_location_id ' : picktype . default_location_dest_id and picktype . default_location_dest_id . id or False } )
return { ' value ' : value }
2014-07-04 02:58:15 +00:00
def onchange_partner_id ( self , cr , uid , ids , partner_id , context = None ) :
2011-10-20 07:30:04 +00:00
partner = self . pool . get ( ' res.partner ' )
if not partner_id :
2012-07-11 11:14:34 +00:00
return { ' value ' : {
' fiscal_position ' : False ,
2012-11-30 17:11:30 +00:00
' payment_term_id ' : False ,
2012-07-11 11:14:34 +00:00
} }
2014-07-04 02:58:15 +00:00
supplier_address = partner . address_get ( cr , uid , [ partner_id ] , [ ' default ' ] , context = context )
supplier = partner . browse ( cr , uid , partner_id , context = context )
2012-07-11 11:14:34 +00:00
return { ' value ' : {
' pricelist_id ' : supplier . property_product_pricelist_purchase . id ,
' fiscal_position ' : supplier . property_account_position and supplier . property_account_position . id or False ,
2012-11-30 17:11:30 +00:00
' payment_term_id ' : supplier . property_supplier_payment_term . id or False ,
2012-07-11 11:14:34 +00:00
} }
2008-07-22 15:11:28 +00:00
2012-11-02 06:05:20 +00:00
def invoice_open ( self , cr , uid , ids , context = None ) :
mod_obj = self . pool . get ( ' ir.model.data ' )
act_obj = self . pool . get ( ' ir.actions.act_window ' )
result = mod_obj . get_object_reference ( cr , uid , ' account ' , ' action_invoice_tree2 ' )
id = result and result [ 1 ] or False
result = act_obj . read ( cr , uid , [ id ] , context = context ) [ 0 ]
inv_ids = [ ]
for po in self . browse ( cr , uid , ids , context = context ) :
inv_ids + = [ invoice . id for invoice in po . invoice_ids ]
if not inv_ids :
raise osv . except_osv ( _ ( ' Error! ' ) , _ ( ' Please create Invoices. ' ) )
#choose the view_mode accordingly
if len ( inv_ids ) > 1 :
result [ ' domain ' ] = " [( ' id ' , ' in ' ,[ " + ' , ' . join ( map ( str , inv_ids ) ) + " ])] "
else :
res = mod_obj . get_object_reference ( cr , uid , ' account ' , ' invoice_supplier_form ' )
result [ ' views ' ] = [ ( res and res [ 1 ] or False , ' form ' ) ]
result [ ' res_id ' ] = inv_ids and inv_ids [ 0 ] or False
return result
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
'''
2012-12-21 16:48:08 +00:00
This function returns an action that display existing invoices of given sales order ids . It can either be a in a list or in a form view , if there is only one invoice to show .
2012-05-25 15:17:40 +00:00
'''
2014-07-06 14:44:26 +00:00
context = dict ( context or { } )
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
'''
2013-08-06 07:57:44 +00:00
This function returns an action that display existing picking orders of given purchase order ids .
2012-05-25 15:17:40 +00:00
'''
2013-08-29 11:30:13 +00:00
if context is None :
context = { }
2012-02-28 11:59:49 +00:00
mod_obj = self . pool . get ( ' ir.model.data ' )
2013-10-17 16:07:25 +00:00
dummy , action_id = tuple ( mod_obj . get_object_reference ( cr , uid , ' stock ' , ' action_picking_tree ' ) )
action = self . pool . get ( ' ir.actions.act_window ' ) . read ( cr , uid , action_id , context = context )
2012-02-28 11:59:49 +00:00
pick_ids = [ ]
for po in self . browse ( cr , uid , ids , context = context ) :
pick_ids + = [ picking . id for picking in po . picking_ids ]
2013-10-17 16:07:25 +00:00
#override the context to get rid of the default filtering on picking type
action [ ' context ' ] = { }
2013-10-14 14:25:40 +00:00
#choose the view_mode accordingly
if len ( pick_ids ) > 1 :
2013-10-17 16:07:25 +00:00
action [ ' domain ' ] = " [( ' id ' , ' in ' ,[ " + ' , ' . join ( map ( str , pick_ids ) ) + " ])] "
2013-10-14 14:25:40 +00:00
else :
res = mod_obj . get_object_reference ( cr , uid , ' stock ' , ' view_picking_form ' )
2013-10-17 16:07:25 +00:00
action [ ' views ' ] = [ ( res and res [ 1 ] or False , ' form ' ) ]
action [ ' res_id ' ] = pick_ids and pick_ids [ 0 ] or False
2012-09-14 13:32:53 +00:00
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
2013-05-29 09:51:50 +00:00
def wkf_bid_received ( self , cr , uid , ids , context = None ) :
return self . write ( cr , uid , ids , { ' state ' : ' bid ' , ' bid_date ' : fields . date . context_today ( self , cr , uid , context = context ) } )
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
'''
2013-05-29 09:51:50 +00:00
if not context :
context = { }
2012-10-23 11:56:28 +00:00
ir_model_data = self . pool . get ( ' ir.model.data ' )
try :
2013-05-29 09:51:50 +00:00
if context . get ( ' send_rfq ' , False ) :
template_id = ir_model_data . get_object_reference ( cr , uid , ' purchase ' , ' email_template_edi_purchase ' ) [ 1 ]
else :
template_id = ir_model_data . get_object_reference ( cr , uid , ' purchase ' , ' email_template_edi_purchase_done ' ) [ 1 ]
2012-10-23 11:56:28 +00:00
except ValueError :
template_id = False
try :
compose_form_id = ir_model_data . get_object_reference ( cr , uid , ' mail ' , ' email_compose_message_wizard_form ' ) [ 1 ]
except ValueError :
compose_form_id = False
2012-09-11 14:16:50 +00:00
ctx = dict ( context )
ctx . update ( {
' default_model ' : ' purchase.order ' ,
' default_res_id ' : ids [ 0 ] ,
2012-10-23 11:56:28 +00:00
' default_use_template ' : bool ( template_id ) ,
2012-09-11 14:16:50 +00:00
' default_template_id ' : template_id ,
2012-11-13 11:20:38 +00:00
' default_composition_mode ' : ' comment ' ,
2012-10-23 11:56:28 +00:00
} )
2012-02-27 13:15:57 +00:00
return {
2013-03-21 05:38:55 +00:00
' name ' : _ ( ' Compose Email ' ) ,
2012-10-23 11:56:28 +00:00
' type ' : ' ir.actions.act_window ' ,
2012-02-27 13:15:57 +00:00
' view_type ' : ' form ' ,
' view_mode ' : ' form ' ,
' res_model ' : ' mail.compose.message ' ,
2012-10-23 11:56:28 +00:00
' views ' : [ ( compose_form_id , ' form ' ) ] ,
' view_id ' : compose_form_id ,
2012-02-27 13:15:57 +00:00
' target ' : ' new ' ,
2012-02-29 10:16:53 +00:00
' context ' : ctx ,
2012-02-27 13:15:57 +00:00
}
2012-02-28 11:59:49 +00:00
2012-11-23 13:36:19 +00:00
def print_quotation ( self , cr , uid , ids , context = None ) :
'''
This function prints the request for quotation and mark it as sent , so that we can see more easily the next step of the workflow
'''
assert len ( ids ) == 1 , ' This option should only be used for a single id at a time '
2014-07-07 09:50:30 +00:00
self . signal_workflow ( cr , uid , ids , ' send_rfq ' )
2014-03-20 15:42:26 +00:00
return self . pool [ ' report ' ] . get_action ( cr , uid , ids , ' purchase.report_purchasequotation ' , context = context )
2012-11-23 13:36:19 +00:00
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 ' :
2013-10-14 14:25:40 +00:00
todo . append ( line . id )
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
2013-06-24 09:07:41 +00:00
def _choose_account_from_po_line ( self , cr , uid , po_line , context = None ) :
fiscal_obj = self . pool . get ( ' account.fiscal.position ' )
property_obj = self . pool . get ( ' ir.property ' )
if po_line . product_id :
acc_id = po_line . product_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 :
2014-01-24 12:30:45 +00:00
raise osv . except_osv ( _ ( ' Error! ' ) , _ ( ' Define an expense account for this product: " %s " (id: %d ). ' ) % ( po_line . product_id . name , po_line . product_id . id , ) )
2013-06-24 09:07:41 +00:00
else :
2013-11-26 17:05:05 +00:00
acc_id = property_obj . get ( cr , uid , ' property_account_expense_categ ' , ' product.category ' , context = context ) . id
2013-06-24 09:07:41 +00:00
fpos = po_line . order_id . fiscal_position or False
return fiscal_obj . map_account ( cr , uid , fpos , acc_id )
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 ,
2013-06-24 09:03:29 +00:00
' purchase_line_id ' : order_line . id ,
2011-11-15 07:02:27 +00:00
}
2008-07-22 15:11:28 +00:00
2014-04-30 14:01:03 +00:00
def _prepare_invoice ( self , cr , uid , order , line_ids , context = None ) :
""" Prepare the dict of values to create the new invoice for a
purchase order . This method may be overridden to implement custom
invoice generation ( making sure to call super ( ) to establish
a clean extension chain ) .
: param browse_record order : purchase . order record to invoice
: param list ( int ) line_ids : list of invoice line IDs that must be
attached to the invoice
: return : dict of value to create ( ) the invoice
"""
journal_ids = self . pool [ ' account.journal ' ] . search (
cr , uid , [ ( ' type ' , ' = ' , ' purchase ' ) ,
( ' company_id ' , ' = ' , order . company_id . id ) ] ,
limit = 1 )
if not journal_ids :
raise osv . except_osv (
_ ( ' Error! ' ) ,
_ ( ' Define purchase journal for this company: " %s " (id: %d ). ' ) % \
( order . company_id . name , order . company_id . id ) )
return {
' name ' : order . partner_ref or order . name ,
' reference ' : order . partner_ref or order . name ,
' account_id ' : order . partner_id . property_account_payable . id ,
' type ' : ' in_invoice ' ,
' partner_id ' : order . partner_id . id ,
' currency_id ' : order . currency_id . id ,
' journal_id ' : len ( journal_ids ) and journal_ids [ 0 ] or False ,
' invoice_line ' : [ ( 6 , 0 , line_ids ) ] ,
' origin ' : order . name ,
' fiscal_position ' : order . fiscal_position . id or False ,
' payment_term ' : order . payment_term_id . id or False ,
' company_id ' : order . company_id . id ,
}
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 } )
2013-05-31 07:38:56 +00:00
self . set_order_line_status ( cr , uid , ids , ' draft ' , context = context )
2008-10-13 13:08:08 +00:00
for p_id in ids :
2010-02-05 11:35:25 +00:00
# Deleting the existing instance of workflow for PO
2013-01-29 12:28:08 +00:00
self . delete_workflow ( cr , uid , [ p_id ] ) # TODO is it necessary to interleave the calls?
self . create_workflow ( cr , uid , [ p_id ] )
2008-10-13 13:08:08 +00:00
return True
2013-05-31 07:38:56 +00:00
def wkf_po_done ( self , cr , uid , ids , context = None ) :
self . write ( cr , uid , ids , { ' state ' : ' done ' } , context = context )
self . set_order_line_status ( cr , uid , ids , ' done ' , context = context )
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
"""
2014-07-06 14:44:26 +00:00
context = dict ( context or { } )
2014-04-30 14:01:03 +00:00
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
2013-11-26 17:05:05 +00:00
res = False
uid_company_id = self . pool . get ( ' res.users ' ) . browse ( cr , uid , uid , context = context ) . company_id . id
2011-11-15 06:43:53 +00:00
for order in self . browse ( cr , uid , ids , context = context ) :
2013-11-26 17:05:05 +00:00
context . pop ( ' force_company ' , None )
if order . company_id . id != uid_company_id :
#if the company of the document is different than the current user company, force the company in the context
#then re-do a browse to read the property fields for the good company.
context [ ' force_company ' ] = order . company_id . id
order = self . browse ( cr , uid , order . id , context = context )
2014-04-30 14:01:03 +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 :
2013-06-24 09:07:41 +00:00
acc_id = self . _choose_account_from_po_line ( cr , uid , po_line , context = context )
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 )
2014-07-06 14:44:26 +00:00
po_line . write ( { ' invoice_lines ' : [ ( 4 , inv_line_id ) ] } )
2011-11-15 06:43:53 +00:00
2011-11-15 07:02:27 +00:00
# get invoice data and create invoice
2014-04-30 14:01:03 +00:00
inv_data = self . _prepare_invoice ( cr , uid , order , inv_lines , context = context )
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
2014-07-06 14:44:26 +00:00
order . write ( { ' invoice_ids ' : [ ( 4 , inv_id ) ] } )
2011-07-08 06:04:11 +00:00
res = inv_id
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 ) :
2013-12-10 15:13:26 +00:00
self . write ( cr , uid , ids , { ' state ' : ' approved ' } , 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 :
2012-07-27 01:52:02 +00:00
if order_line . product_id and order_line . product_id . type in ( ' product ' , ' consu ' ) :
2008-07-22 15:11:28 +00:00
return True
return False
2014-09-05 12:50:41 +00:00
def wkf_action_cancel ( self , cr , uid , ids , context = None ) :
2014-09-09 17:06:26 +00:00
self . write ( cr , uid , ids , { ' state ' : ' cancel ' } , context = context )
2014-09-05 12:50:41 +00:00
self . set_order_line_status ( cr , uid , ids , ' cancel ' , context = context )
2010-11-19 13:48:01 +00:00
def action_cancel ( self , cr , uid , ids , context = None ) :
for purchase in self . browse ( cr , uid , ids , context = context ) :
2008-10-13 13:08:08 +00:00
for pick in purchase . picking_ids :
2014-09-17 15:54:34 +00:00
for move in pick . move_lines :
if pick . state == ' done ' :
raise osv . except_osv (
_ ( ' Unable to cancel the purchase order %s . ' ) % ( purchase . name ) ,
_ ( ' You have already received some goods for it. ' ) )
self . pool . get ( ' stock.picking ' ) . action_cancel ( cr , uid , [ x . id for x in purchase . picking_ids if x . state != ' cancel ' ] , context = context )
2010-12-15 10:14:49 +00:00
for inv in purchase . invoice_ids :
2013-12-10 15:13:26 +00:00
if inv and inv . state not in ( ' cancel ' , ' draft ' ) :
2010-12-15 10:14:49 +00:00
raise osv . except_osv (
2012-07-12 06:00:49 +00:00
_ ( ' Unable to cancel this purchase order. ' ) ,
2014-04-23 08:28:08 +00:00
_ ( ' You must first cancel all invoices related to this purchase order. ' ) )
2013-01-25 16:53:55 +00:00
self . pool . get ( ' account.invoice ' ) \
2014-07-07 09:50:30 +00:00
. signal_workflow ( cr , uid , map ( attrgetter ( ' id ' ) , purchase . invoice_ids ) , ' invoice_cancel ' )
self . signal_workflow ( cr , uid , ids , ' purchase_cancel ' )
2008-10-13 13:08:08 +00:00
return True
2013-09-13 09:42:16 +00:00
def _prepare_order_line_move ( self , cr , uid , order , order_line , picking_id , group_id , context = None ) :
2013-12-18 15:56:47 +00:00
''' prepare the stock move data from the PO line. This function returns a list of dictionary ready to be used in stock.move ' s create() '''
product_uom = self . pool . get ( ' product.uom ' )
2013-09-05 10:34:35 +00:00
price_unit = order_line . price_unit
if order_line . product_uom . id != order_line . product_id . uom_id . id :
2014-10-17 12:10:06 +00:00
price_unit * = order_line . product_uom . factor / order_line . product_id . uom_id . factor
2013-09-05 10:34:35 +00:00
if order . currency_id . id != order . company_id . currency_id . id :
2013-11-04 16:49:39 +00:00
#we don't round the price_unit, as we may want to store the standard price with more digits than allowed by the currency
price_unit = self . pool . get ( ' res.currency ' ) . compute ( cr , uid , order . currency_id . id , order . company_id . currency_id . id , price_unit , round = False , context = context )
2014-02-14 14:12:02 +00:00
res = [ ]
move_template = {
2012-12-17 22:28:27 +00:00
' name ' : order_line . name or ' ' ,
2011-10-03 21:27:47 +00:00
' product_id ' : order_line . product_id . id ,
' product_uom ' : order_line . product_uom . id ,
' product_uos ' : order_line . product_uom . id ,
2014-07-08 14:04:09 +00:00
' date ' : order . date_order ,
2014-04-16 15:48:30 +00:00
' date_expected ' : fields . date . date_to_datetime ( self , cr , uid , order_line . date_planned , context ) ,
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 ,
2014-02-14 14:12:02 +00:00
' move_dest_id ' : False ,
2011-10-03 21:27:47 +00:00
' state ' : ' draft ' ,
' purchase_line_id ' : order_line . id ,
2011-10-16 01:31:25 +00:00
' company_id ' : order . company_id . id ,
2013-09-05 10:34:35 +00:00
' price_unit ' : price_unit ,
2013-12-10 15:13:26 +00:00
' picking_type_id ' : order . picking_type_id . id ,
' group_id ' : group_id ,
2014-02-14 14:12:02 +00:00
' procurement_id ' : False ,
2014-01-20 10:30:48 +00:00
' origin ' : order . name ,
2013-10-17 16:07:25 +00:00
' route_ids ' : order . picking_type_id . warehouse_id and [ ( 6 , 0 , [ x . id for x in order . picking_type_id . warehouse_id . route_ids ] ) ] or [ ] ,
2014-04-03 15:57:35 +00:00
' warehouse_id ' : order . picking_type_id . warehouse_id . id ,
2014-09-03 17:15:10 +00:00
' invoice_state ' : order . invoice_method == ' picking ' and ' 2binvoiced ' or ' none ' ,
2014-02-14 14:12:02 +00:00
}
diff_quantity = order_line . product_qty
for procurement in order_line . procurement_ids :
procurement_qty = product_uom . _compute_qty ( cr , uid , procurement . product_uom . id , procurement . product_qty , to_uom_id = order_line . product_uom . id )
tmp = move_template . copy ( )
tmp . update ( {
' product_uom_qty ' : min ( procurement_qty , diff_quantity ) ,
' product_uos_qty ' : min ( procurement_qty , diff_quantity ) ,
2014-06-13 15:44:03 +00:00
' move_dest_id ' : procurement . move_dest_id . id , #move destination is same as procurement destination
' group_id ' : procurement . group_id . id or group_id , #move group is same as group of procurements if it exists, otherwise take another group
2014-02-14 14:12:02 +00:00
' procurement_id ' : procurement . id ,
2014-08-22 12:20:34 +00:00
' invoice_state ' : procurement . rule_id . invoice_state or ( procurement . location_id and procurement . location_id . usage == ' customer ' and procurement . invoice_state == ' picking ' and ' 2binvoiced ' ) or ( order . invoice_method == ' picking ' and ' 2binvoiced ' ) or ' none ' , #dropship case takes from sale
2014-09-01 13:39:32 +00:00
' propagate ' : procurement . rule_id . propagate ,
2014-02-14 14:12:02 +00:00
} )
diff_quantity - = min ( procurement_qty , diff_quantity )
res . append ( tmp )
2013-12-18 15:56:47 +00:00
#if the order line has a bigger quantity than the procurement it was for (manually changed or minimal quantity), then
#split the future stock move in two because the route followed may be different.
2014-02-14 14:12:02 +00:00
if diff_quantity > 0 :
move_template [ ' product_uom_qty ' ] = diff_quantity
move_template [ ' product_uos_qty ' ] = diff_quantity
res . append ( move_template )
2013-12-18 15:56:47 +00:00
return res
2011-10-03 21:27:47 +00:00
2013-10-17 16:07:25 +00:00
def _create_stock_moves ( self , cr , uid , order , order_lines , picking_id = False , context = None ) :
""" Creates appropriate stock moves for given order lines, whose can optionally create a
picking if none is given or no suitable is found , then confirms the moves , makes them
available , and confirms the pickings .
2011-10-16 01:31:25 +00:00
2013-10-17 16:07:25 +00:00
If ` ` picking_id ` ` is provided , the stock moves will be added to it , otherwise a standard
incoming picking will be created to wrap the stock moves ( default behavior of the stock . move )
2011-10-16 01:31:25 +00:00
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 .
2013-10-17 16:07:25 +00:00
: return : None
2011-10-03 21:27:47 +00:00
"""
2011-11-10 11:21:39 +00:00
stock_move = self . pool . get ( ' stock.move ' )
2013-12-18 15:56:47 +00:00
todo_moves = [ ]
2014-02-14 14:12:02 +00:00
new_group = self . pool . get ( " procurement.group " ) . create ( cr , uid , { ' name ' : order . name , ' partner_id ' : order . partner_id . id } , context = context )
2013-12-18 15:56:47 +00:00
2011-10-03 21:27:47 +00:00
for order_line in order_lines :
if not order_line . product_id :
continue
2013-12-18 15:56:47 +00:00
2011-11-10 11:21:39 +00:00
if order_line . product_id . type in ( ' product ' , ' consu ' ) :
2014-02-14 14:12:02 +00:00
for vals in self . _prepare_order_line_move ( cr , uid , order , order_line , picking_id , new_group , context = context ) :
2013-12-18 15:56:47 +00:00
move = stock_move . create ( cr , uid , vals , context = context )
todo_moves . append ( move )
2014-02-10 14:54:46 +00:00
todo_moves = stock_move . action_confirm ( cr , uid , todo_moves )
2011-11-10 11:21:39 +00:00
stock_move . force_assign ( cr , uid , todo_moves )
2013-07-18 08:57:56 +00:00
def test_moves_done ( self , cr , uid , ids , context = None ) :
2013-09-04 15:01:37 +00:00
''' PO is done at the delivery side if all the incoming shipments are done '''
2013-07-18 08:57:56 +00:00
for purchase in self . browse ( cr , uid , ids , context = context ) :
2013-09-04 15:01:37 +00:00
for picking in purchase . picking_ids :
if picking . state != ' done ' :
return False
return True
2013-07-18 08:57:56 +00:00
def test_moves_except ( self , cr , uid , ids , context = None ) :
2013-09-04 15:01:37 +00:00
''' PO is in exception at the delivery side if one of the picking is canceled
and the other pickings are completed ( done or canceled )
2013-07-18 08:57:56 +00:00
'''
2013-09-04 15:01:37 +00:00
at_least_one_canceled = False
2013-07-18 08:57:56 +00:00
alldoneorcancel = True
for purchase in self . browse ( cr , uid , ids , context = context ) :
2013-09-04 15:01:37 +00:00
for picking in purchase . picking_ids :
if picking . state == ' cancel ' :
at_least_one_canceled = True
if picking . state not in [ ' done ' , ' cancel ' ] :
alldoneorcancel = False
return at_least_one_canceled and alldoneorcancel
2013-07-18 13:18:16 +00:00
def move_lines_get ( self , cr , uid , ids , * args ) :
res = [ ]
for order in self . browse ( cr , uid , ids , context = { } ) :
for line in order . order_line :
res + = [ x . id for x in line . move_ids ]
return res
2012-03-05 18:40:03 +00:00
def action_picking_create ( self , cr , uid , ids , context = None ) :
2008-07-22 15:11:28 +00:00
for order in self . browse ( cr , uid , ids ) :
2014-07-18 10:17:54 +00:00
picking_vals = {
' picking_type_id ' : order . picking_type_id . id ,
' partner_id ' : order . dest_address_id . id or order . partner_id . id ,
2014-09-01 13:39:32 +00:00
' date ' : max ( [ l . date_planned for l in order . order_line ] ) ,
' origin ' : order . name
2014-07-18 10:17:54 +00:00
}
picking_id = self . pool . get ( ' stock.picking ' ) . create ( cr , uid , picking_vals , context = context )
2014-02-14 14:12:02 +00:00
self . _create_stock_moves ( cr , uid , order , order . order_line , picking_id , context = context )
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 )
2014-04-22 15:34:00 +00:00
# Do check on related procurements:
proc_obj = self . pool . get ( " procurement.order " )
po_lines = [ ]
for po in self . browse ( cr , uid , ids , context = context ) :
po_lines + = [ x . id for x in po . order_line ]
if po_lines :
procs = proc_obj . search ( cr , uid , [ ( ' purchase_line_id ' , ' in ' , po_lines ) ] , context = context )
if procs :
proc_obj . check ( cr , uid , procs , context = context )
2013-02-20 05:42:49 +00:00
self . message_post ( cr , uid , ids , body = _ ( " Products received " ) , context = context )
2012-03-13 10:07:26 +00:00
return True
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
def make_key ( br , fields ) :
2010-03-25 08:25:11 +00:00
list_key = [ ]
for field in fields :
field_val = getattr ( br , field )
2014-02-14 14:12:02 +00:00
if field in ( ' product_id ' , ' account_analytic_id ' ) :
2010-03-25 08:25:11 +00:00
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
2014-07-06 14:44:26 +00:00
elif isinstance ( field_val , browse_record_list ) :
2010-03-25 08:25:11 +00:00
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 )
2014-07-06 14:44:26 +00:00
context = dict ( context or { } )
2013-03-05 10:48:01 +00:00
2012-10-02 10:29:15 +00:00
# Compute what the new orders should contain
2010-03-25 08:25:11 +00:00
new_orders = { }
2010-05-28 09:48:40 +00:00
2013-10-02 13:42:42 +00:00
order_lines_to_move = [ ]
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 ]
2013-10-08 12:36:59 +00:00
2010-03-25 08:25:11 +00:00
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 ,
2013-09-03 13:46:00 +00:00
' picking_type_id ' : porder . picking_type_id . id ,
2010-08-13 12:20:05 +00:00
' 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 :
2013-10-02 13:42:42 +00:00
order_lines_to_move + = [ order_line . id ]
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 ) )
2013-10-02 13:42:42 +00:00
order_data [ ' order_line ' ] = [ ( 6 , 0 , order_lines_to_move ) ]
2010-03-25 08:25:11 +00:00
# create the new order
2013-03-05 10:48:01 +00:00
context . update ( { ' mail_create_nolog ' : True } )
2010-03-25 08:25:11 +00:00
neworder_id = self . create ( cr , uid , order_data )
2013-02-20 05:42:49 +00:00
self . message_post ( cr , uid , [ neworder_id ] , body = _ ( " RFQ created " ) , context = context )
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 :
2013-01-29 12:28:08 +00:00
self . redirect_workflow ( cr , uid , [ ( old_id , neworder_id ) ] )
2014-07-07 09:50:30 +00:00
self . signal_workflow ( cr , uid , [ old_id ] , ' purchase_cancel ' )
2013-10-08 12:36:59 +00:00
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
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 ) :
2012-10-31 10:52:24 +00:00
taxes = tax_obj . compute_all ( cr , uid , line . taxes_id , line . price_unit , line . product_qty , line . product_id , line . order_id . partner_id )
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
2013-11-29 14:35:22 +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
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 ' ) ,
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 ) ,
2014-07-06 14:44:26 +00:00
' state ' : fields . selection ( [ ( ' draft ' , ' Draft ' ) , ( ' confirmed ' , ' Confirmed ' ) , ( ' done ' , ' Done ' ) , ( ' cancel ' , ' Cancelled ' ) ] ,
' Status ' , required = True , readonly = True , copy = False ,
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. ' ) ,
2014-07-06 14:44:26 +00:00
' invoice_lines ' : fields . many2many ( ' account.invoice.line ' , ' purchase_order_line_invoice_rel ' ,
' order_line_id ' , ' invoice_id ' , ' Invoice Lines ' ,
readonly = True , copy = False ) ,
' invoiced ' : fields . boolean ( ' Invoiced ' , readonly = True , copy = False ) ,
2014-07-08 14:04:09 +00:00
' partner_id ' : fields . related ( ' order_id ' , ' partner_id ' , string = ' Partner ' , readonly = True , type = " many2one " , relation = " res.partner " , store = True ) ,
' date_order ' : fields . related ( ' order_id ' , ' date_order ' , string = ' Order Date ' , readonly = True , type = " datetime " ) ,
2013-09-13 09:42:16 +00:00
' procurement_ids ' : fields . one2many ( ' procurement.order ' , ' purchase_line_id ' , string = ' Associated procurements ' ) ,
2008-07-22 15:11:28 +00:00
}
_defaults = {
2013-11-29 14:35:22 +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
2014-01-20 17:07:29 +00:00
def unlink ( self , cr , uid , ids , context = None ) :
for line in self . browse ( cr , uid , ids , context = context ) :
2014-05-28 16:00:11 +00:00
if line . state not in [ ' draft ' , ' cancel ' ] :
raise osv . except_osv ( _ ( ' Invalid Action! ' ) , _ ( ' Cannot delete a purchase order line which is in state \' %s \' . ' ) % ( line . state , ) )
2014-02-04 09:09:01 +00:00
procurement_obj = self . pool . get ( ' procurement.order ' )
procurement_ids_to_cancel = procurement_obj . search ( cr , uid , [ ( ' purchase_line_id ' , ' in ' , ids ) ] , context = context )
2014-01-20 17:07:29 +00:00
if procurement_ids_to_cancel :
2014-02-04 09:09:01 +00:00
self . pool [ ' procurement.order ' ] . cancel ( cr , uid , procurement_ids_to_cancel )
2014-01-20 17:07:29 +00:00
return super ( purchase_order_line , self ) . unlink ( cr , uid , ids , context = context )
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 ,
2013-06-06 14:15:36 +00:00
name = False , price_unit = False , state = ' draft ' , context = None ) :
2012-01-12 07:02:40 +00:00
"""
onchange handler of product_uom .
"""
2013-12-06 12:15:52 +00:00
if context is None :
context = { }
2012-01-12 07:02:40 +00:00
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 } }
2013-12-06 12:15:52 +00:00
context = dict ( context , purchase_uom_check = True )
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 ,
2013-06-17 08:49:16 +00:00
name = name , price_unit = price_unit , state = state , 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 )
2014-07-08 14:04:09 +00:00
: param str date_order_str : date of order field , as a string in
DEFAULT_SERVER_DATETIME_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
2014-07-08 14:04:09 +00:00
return datetime . strptime ( date_order_str , DEFAULT_SERVER_DATETIME_FORMAT ) + relativedelta ( days = supplier_delay )
2012-02-15 22:47:43 +00:00
2013-10-22 08:58:56 +00:00
def action_cancel ( self , cr , uid , ids , context = None ) :
self . write ( cr , uid , ids , { ' state ' : ' cancel ' } , context = context )
for po_line in self . browse ( cr , uid , ids , context = context ) :
if all ( [ l . state == ' cancel ' for l in po_line . order_id . order_line ] ) :
self . pool . get ( ' purchase.order ' ) . action_cancel ( cr , uid , [ po_line . order_id . id ] , context = context )
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 ,
2013-06-06 14:15:36 +00:00
name = False , price_unit = False , state = ' draft ' , 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_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 )
2013-05-07 06:49:45 +00:00
#call name_get() with partner in the context to eventually match name and description in the seller_ids field
dummy , name = product_product . name_get ( cr , uid , product_id , context = context_partner ) [ 0 ]
2012-07-14 20:51:50 +00:00
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 :
2013-12-06 12:15:52 +00:00
if context . get ( ' purchase_uom_check ' ) and self . _check_product_uom_group ( cr , uid , context = context ) :
2012-09-17 08:45:34 +00:00
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 :
2014-07-08 14:04:09 +00:00
date_order = fields . datetime . now ( )
2012-01-06 12:16:12 +00:00
2012-12-08 14:28:19 +00:00
2012-02-16 12:34:08 +00:00
supplierinfo = False
2014-07-16 13:45:56 +00:00
precision = self . pool . get ( ' decimal.precision ' ) . precision_get ( cr , uid , ' Product Unit of Measure ' )
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 )
2014-07-16 13:45:56 +00:00
if float_compare ( min_qty , qty , precision_digits = precision ) == 1 : # If the supplier quantity is greater than entered from user, set minimal.
2012-12-08 14:28:19 +00:00
if qty :
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-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-12-08 14:28:19 +00:00
qty = qty or 1.0
res [ ' value ' ] . update ( { ' date_planned ' : date_planned or dt } )
if qty :
res [ ' value ' ] . update ( { ' product_qty ' : qty } )
2012-01-12 07:02:40 +00:00
2013-06-13 15:11:20 +00:00
price = price_unit
2014-09-30 08:40:42 +00:00
if price_unit is False or price_unit is None :
2013-06-06 13:10:26 +00:00
# - determine price_unit and taxes_id
if pricelist_id :
2014-07-08 14:04:09 +00:00
date_order_str = datetime . strptime ( date_order , DEFAULT_SERVER_DATETIME_FORMAT ) . strftime ( DEFAULT_SERVER_DATE_FORMAT )
2013-06-06 13:10:26 +00:00
price = product_pricelist . price_get ( cr , uid , [ pricelist_id ] ,
2014-07-08 14:04:09 +00:00
product . id , qty or 1.0 , partner_id or False , { ' uom ' : uom_id , ' date ' : date_order_str } ) [ pricelist_id ]
2013-06-06 13:10:26 +00:00
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
2013-07-29 09:40:43 +00:00
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
2014-09-17 14:47:57 +00:00
2013-07-10 10:16:16 +00:00
class procurement_rule ( osv . osv ) :
_inherit = ' procurement.rule '
def _get_action ( self , cr , uid , context = None ) :
2014-05-07 17:14:31 +00:00
return [ ( ' buy ' , _ ( ' Buy ' ) ) ] + super ( procurement_rule , self ) . _get_action ( cr , uid , context = context )
2013-07-10 10:16:16 +00:00
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 = {
2013-10-22 08:58:56 +00:00
' purchase_line_id ' : fields . many2one ( ' purchase.order.line ' , ' Purchase Order Line ' ) ,
' purchase_id ' : fields . related ( ' purchase_line_id ' , ' order_id ' , type = ' many2one ' , relation = ' purchase.order ' , string = ' Purchase Order ' ) ,
2010-04-29 13:30:07 +00:00
}
2010-05-28 09:48:40 +00:00
2013-10-21 15:12:35 +00:00
def propagate_cancel ( self , cr , uid , procurement , context = None ) :
if procurement . rule_id . action == ' buy ' and procurement . purchase_line_id :
2014-03-28 10:29:34 +00:00
purchase_line_obj = self . pool . get ( ' purchase.order.line ' )
2014-03-28 13:43:08 +00:00
if procurement . purchase_line_id . product_qty > procurement . product_qty and procurement . purchase_line_id . order_id . state == ' draft ' :
purchase_line_obj . write ( cr , uid , [ procurement . purchase_line_id . id ] , { ' product_qty ' : procurement . purchase_line_id . product_qty - procurement . product_qty } , context = context )
2014-03-28 10:29:34 +00:00
else :
2014-03-28 13:43:08 +00:00
purchase_line_obj . action_cancel ( cr , uid , [ procurement . purchase_line_id . id ] , context = context )
2013-10-21 15:12:35 +00:00
return super ( procurement_order , self ) . propagate_cancel ( cr , uid , procurement , context = context )
2014-03-28 13:43:08 +00:00
2013-07-10 10:16:16 +00:00
def _run ( self , cr , uid , procurement , context = None ) :
if procurement . rule_id and procurement . rule_id . action == ' buy ' :
#make a purchase order for the procurement
2013-08-19 13:46:15 +00:00
return self . make_po ( cr , uid , [ procurement . id ] , context = context ) [ procurement . id ]
2013-07-10 10:16:16 +00:00
return super ( procurement_order , self ) . _run ( cr , uid , procurement , context = context )
def _check ( self , cr , uid , procurement , context = None ) :
2013-09-13 09:42:16 +00:00
if procurement . purchase_line_id and procurement . purchase_line_id . order_id . shipped : # TOCHECK: does it work for several deliveries?
2013-07-10 10:16:16 +00:00
return True
return super ( procurement_order , self ) . _check ( cr , uid , procurement , context = context )
def _check_supplier_info ( self , cr , uid , ids , context = None ) :
''' Check the supplier info field of a product and write an error message on the procurement if needed.
Returns True if all needed information is there , False if some configuration mistake is detected .
2012-12-14 16:39:21 +00:00
'''
2012-12-17 08:13:51 +00:00
partner_obj = self . pool . get ( ' res.partner ' )
2012-12-14 19:11:23 +00:00
user = self . pool . get ( ' res.users ' ) . browse ( cr , uid , uid , context = context )
2012-12-14 16:39:21 +00:00
for procurement in self . browse ( cr , uid , ids , context = context ) :
2013-10-04 14:06:02 +00:00
message = ' '
2012-12-14 16:39:21 +00:00
partner = procurement . product_id . seller_id #Taken Main Supplier of Product of Procurement.
2013-10-04 14:06:02 +00:00
if not procurement . product_id . seller_ids :
message = _ ( ' No supplier defined for this product ! ' )
elif not partner :
2012-12-14 16:39:21 +00:00
message = _ ( ' No default supplier defined for this product ' )
2013-10-04 14:06:02 +00:00
elif not partner_obj . address_get ( cr , uid , [ partner . id ] , [ ' delivery ' ] ) [ ' delivery ' ] :
message = _ ( ' No address defined for the supplier ' )
2013-10-07 08:10:14 +00:00
if message :
if procurement . message != message :
2013-10-09 12:27:14 +00:00
cr . execute ( ' update procurement_order set message= %s where id= %s ' , ( message , procurement . id ) )
2012-12-14 16:39:21 +00:00
return False
2013-10-04 14:06:02 +00:00
2012-12-14 16:39:21 +00:00
if user . company_id and user . company_id . partner_id :
if partner . id == user . company_id . partner_id . id :
raise osv . except_osv ( _ ( ' Configuration Error! ' ) , _ ( ' The product " %s " has been defined with your company as reseller which seems to be a configuration error! ' % procurement . product_id . name ) )
return True
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
2013-11-07 11:20:53 +00:00
def _get_product_supplier ( self , cr , uid , procurement , context = None ) :
''' returns the main supplier of the procurement ' s product given as argument '''
return procurement . product_id . seller_id
def _get_po_line_values_from_proc ( self , cr , uid , procurement , partner , company , schedule_date , context = None ) :
if context is None :
context = { }
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 ' )
seller_qty = procurement . product_id . seller_qty
pricelist_id = partner . property_product_pricelist_purchase . id
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 )
if seller_qty :
qty = max ( qty , seller_qty )
price = pricelist_obj . price_get ( cr , uid , [ pricelist_id ] , procurement . product_id . id , qty , partner . id , { ' uom ' : uom_id } ) [ pricelist_id ]
#Passing partner_id to context for purchase order line integrity of Line name
new_context = context . copy ( )
new_context . update ( { ' lang ' : partner . lang , ' partner_id ' : partner . id } )
product = prod_obj . browse ( cr , uid , procurement . product_id . id , context = new_context )
taxes_ids = procurement . product_id . supplier_taxes_id
taxes = acc_pos_obj . map_tax ( cr , uid , partner . property_account_position , taxes_ids )
2014-09-17 15:54:34 +00:00
name = product . display_name
2013-11-07 11:20:53 +00:00
if product . description_purchase :
name + = ' \n ' + product . description_purchase
return {
' name ' : name ,
' product_qty ' : qty ,
' product_id ' : procurement . product_id . id ,
' product_uom ' : uom_id ,
' price_unit ' : price or 0.0 ,
' date_planned ' : schedule_date . strftime ( DEFAULT_SERVER_DATETIME_FORMAT ) ,
' taxes_id ' : [ ( 6 , 0 , taxes ) ] ,
}
2010-11-19 13:48:01 +00:00
def make_po ( self , cr , uid , ids , context = None ) :
2014-04-03 07:10:10 +00:00
""" Resolve the purchase from procurement, which may result in a new PO creation, a new PO line creation or a quantity change on existing PO line.
Note that some operations ( as the PO creation ) are made as SUPERUSER because the current user may not have rights to do it ( mto product launched by a sale for example )
@return : dictionary giving for each procurement its related resolving PO line .
2010-04-29 13:30:07 +00:00
"""
res = { }
2010-11-19 13:48:01 +00:00
company = self . pool . get ( ' res.users ' ) . browse ( cr , uid , uid , context = context ) . company_id
2013-10-03 13:14:48 +00:00
po_obj = self . pool . get ( ' purchase.order ' )
po_line_obj = self . pool . get ( ' purchase.order.line ' )
2011-09-15 08:33:33 +00:00
seq_obj = self . pool . get ( ' ir.sequence ' )
2013-08-12 12:37:17 +00:00
pass_ids = [ ]
2013-10-03 13:22:21 +00:00
linked_po_ids = [ ]
2014-02-14 14:12:02 +00:00
sum_po_line_ids = [ ]
2010-11-22 10:37:53 +00:00
for procurement in self . browse ( cr , uid , ids , context = context ) :
2013-11-07 11:20:53 +00:00
partner = self . _get_product_supplier ( cr , uid , procurement , context = context )
2013-10-03 13:14:48 +00:00
if not partner :
self . message_post ( cr , uid , [ procurement . id ] , _ ( ' There is no supplier associated to product %s ' ) % ( procurement . product_id . name ) )
2013-08-19 13:46:15 +00:00
res [ procurement . id ] = False
2013-08-12 12:37:17 +00:00
else :
schedule_date = self . _get_purchase_schedule_date ( cr , uid , procurement , company , context = context )
2014-06-11 12:05:13 +00:00
purchase_date = self . _get_purchase_order_date ( cr , uid , procurement , company , schedule_date , context = context )
2013-11-07 11:20:53 +00:00
line_vals = self . _get_po_line_values_from_proc ( cr , uid , procurement , partner , company , schedule_date , context = context )
2013-10-03 13:14:48 +00:00
#look for any other draft PO for the same supplier, to attach the new line on instead of creating a new draft one
available_draft_po_ids = po_obj . search ( cr , uid , [
2013-11-07 11:20:53 +00:00
( ' partner_id ' , ' = ' , partner . id ) , ( ' state ' , ' = ' , ' draft ' ) , ( ' picking_type_id ' , ' = ' , procurement . rule_id . picking_type_id . id ) ,
2014-03-27 12:11:40 +00:00
( ' location_id ' , ' = ' , procurement . location_id . id ) , ( ' company_id ' , ' = ' , procurement . company_id . id ) , ( ' dest_address_id ' , ' = ' , procurement . partner_dest_id . id ) ] , context = context )
2013-10-03 13:14:48 +00:00
if available_draft_po_ids :
po_id = available_draft_po_ids [ 0 ]
2014-06-11 12:05:13 +00:00
po_rec = po_obj . browse ( cr , uid , po_id , context = context )
2014-06-18 14:33:53 +00:00
#if the product has to be ordered earlier those in the existing PO, we replace the purchase date on the order to avoid ordering it too late
2014-07-08 14:04:09 +00:00
if datetime . strptime ( po_rec . date_order , DEFAULT_SERVER_DATETIME_FORMAT ) > purchase_date :
po_obj . write ( cr , uid , [ po_id ] , { ' date_order ' : purchase_date . strftime ( DEFAULT_SERVER_DATETIME_FORMAT ) } , context = context )
2014-02-14 14:12:02 +00:00
#look for any other PO line in the selected PO with same product and UoM to sum quantities instead of creating a new po line
available_po_line_ids = po_line_obj . search ( cr , uid , [ ( ' order_id ' , ' = ' , po_id ) , ( ' product_id ' , ' = ' , line_vals [ ' product_id ' ] ) , ( ' product_uom ' , ' = ' , line_vals [ ' product_uom ' ] ) ] , context = context )
if available_po_line_ids :
po_line = po_line_obj . browse ( cr , uid , available_po_line_ids [ 0 ] , context = context )
2014-04-03 07:10:10 +00:00
po_line_obj . write ( cr , SUPERUSER_ID , po_line . id , { ' product_qty ' : po_line . product_qty + line_vals [ ' product_qty ' ] } , context = context )
2014-02-14 14:12:02 +00:00
po_line_id = po_line . id
sum_po_line_ids . append ( procurement . id )
else :
line_vals . update ( order_id = po_id )
2014-04-03 07:10:10 +00:00
po_line_id = po_line_obj . create ( cr , SUPERUSER_ID , line_vals , context = context )
2014-02-14 14:12:02 +00:00
linked_po_ids . append ( procurement . id )
2013-10-03 13:14:48 +00:00
else :
name = seq_obj . get ( cr , uid , ' purchase.order ' ) or _ ( ' PO: %s ' ) % procurement . name
po_vals = {
' name ' : name ,
' origin ' : procurement . origin ,
2013-11-07 11:20:53 +00:00
' partner_id ' : partner . id ,
2013-10-03 13:14:48 +00:00
' location_id ' : procurement . location_id . id ,
' picking_type_id ' : procurement . rule_id . picking_type_id . id ,
2013-11-07 11:20:53 +00:00
' pricelist_id ' : partner . property_product_pricelist_purchase . id ,
2014-10-10 16:06:48 +00:00
' currency_id ' : partner . property_product_pricelist_purchase and partner . property_product_pricelist_purchase . currency_id . id or procurement . company_id . currency_id . id ,
2013-10-03 13:14:48 +00:00
' date_order ' : purchase_date . strftime ( DEFAULT_SERVER_DATETIME_FORMAT ) ,
' company_id ' : procurement . company_id . id ,
' fiscal_position ' : partner . property_account_position and partner . property_account_position . id or False ,
' payment_term_id ' : partner . property_supplier_payment_term . id or False ,
2014-03-27 12:11:40 +00:00
' dest_address_id ' : procurement . partner_dest_id . id ,
2013-10-03 13:14:48 +00:00
}
2014-04-03 07:10:10 +00:00
po_id = self . create_procurement_purchase_order ( cr , SUPERUSER_ID , procurement , po_vals , line_vals , context = context )
2013-10-03 13:14:48 +00:00
po_line_id = po_obj . browse ( cr , uid , po_id , context = context ) . order_line [ 0 ] . id
2013-10-03 13:22:21 +00:00
pass_ids . append ( procurement . id )
2013-10-03 13:14:48 +00:00
res [ procurement . id ] = po_line_id
self . write ( cr , uid , [ procurement . id ] , { ' purchase_line_id ' : po_line_id } , context = context )
2013-08-12 12:37:17 +00:00
if pass_ids :
self . message_post ( cr , uid , pass_ids , body = _ ( " Draft Purchase Order created " ) , context = context )
2013-10-03 13:22:21 +00:00
if linked_po_ids :
self . message_post ( cr , uid , linked_po_ids , body = _ ( " Purchase line created and linked to an existing Purchase Order " ) , context = context )
2014-02-14 14:12:02 +00:00
if sum_po_line_ids :
self . message_post ( cr , uid , sum_po_line_ids , body = _ ( " Quantity added in existing Purchase Order Line " ) , context = context )
2010-04-29 13:30:07 +00:00
return res
2012-12-19 17:02:28 +00:00
2012-03-27 11:44:44 +00:00
2012-12-21 09:59:02 +00:00
class mail_mail ( osv . Model ) :
2012-09-19 13:04:12 +00:00
_name = ' mail.mail '
_inherit = ' mail.mail '
2012-08-07 11:06:16 +00:00
2014-03-19 08:47:48 +00:00
def _postprocess_sent_message ( self , cr , uid , mail , context = None , mail_sent = True ) :
2014-04-08 12:40:53 +00:00
if mail_sent and mail . model == ' purchase.order ' :
2013-05-29 09:51:50 +00:00
obj = self . pool . get ( ' purchase.order ' ) . browse ( cr , uid , mail . res_id , context = context )
if obj . state == ' draft ' :
2014-07-07 09:50:30 +00:00
self . pool . get ( ' purchase.order ' ) . signal_workflow ( cr , uid , [ mail . res_id ] , ' send_rfq ' )
2014-03-19 08:47:48 +00:00
return super ( mail_mail , self ) . _postprocess_sent_message ( cr , uid , mail = mail , context = context , mail_sent = mail_sent )
2012-03-27 11:44:44 +00:00
2012-10-02 10:29:15 +00:00
2012-12-21 09:59:02 +00:00
class product_template ( osv . Model ) :
2012-10-03 06:48:40 +00:00
_name = ' product.template '
_inherit = ' product.template '
2014-03-24 13:56:21 +00:00
2014-06-02 07:58:09 +00:00
def _get_buy_route ( self , cr , uid , context = None ) :
2014-06-18 14:33:53 +00:00
buy_route = self . pool . get ( ' ir.model.data ' ) . xmlid_to_res_id ( cr , uid , ' purchase.route_warehouse0_buy ' )
if buy_route :
return [ buy_route ]
return [ ]
2014-06-02 07:58:09 +00:00
2014-05-27 07:42:52 +00:00
def _purchase_count ( self , cr , uid , ids , field_name , arg , context = None ) :
res = dict . fromkeys ( ids , 0 )
for template in self . browse ( cr , uid , ids , context = context ) :
res [ template . id ] = sum ( [ p . purchase_count for p in template . product_variant_ids ] )
return res
2014-07-07 19:45:11 +00:00
2012-10-03 06:48:40 +00:00
_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. " ) ,
2014-05-27 07:42:52 +00:00
' purchase_count ' : fields . function ( _purchase_count , string = ' # Purchases ' , type = ' integer ' ) ,
2012-10-10 20:38:18 +00:00
}
2014-07-07 19:45:11 +00:00
2012-10-03 06:48:40 +00:00
_defaults = {
2012-10-10 20:38:18 +00:00
' purchase_ok ' : 1 ,
2014-06-02 07:58:09 +00:00
' route_ids ' : _get_buy_route ,
2012-10-10 20:38:18 +00:00
}
2012-10-03 06:48:40 +00:00
2014-07-07 19:45:11 +00:00
def action_view_purchases ( self , cr , uid , ids , context = None ) :
products = self . _get_products ( cr , uid , ids , context = context )
2014-07-16 09:58:22 +00:00
result = self . _get_act_window_dict ( cr , uid , ' purchase.action_purchase_line_product_tree ' , context = context )
2014-07-07 19:45:11 +00:00
result [ ' domain ' ] = " [( ' product_id ' , ' in ' ,[ " + ' , ' . join ( map ( str , products ) ) + " ])] "
return result
2014-04-15 13:54:11 +00:00
class product_product ( osv . Model ) :
_name = ' product.product '
_inherit = ' product.product '
2014-03-24 13:56:21 +00:00
def _purchase_count ( self , cr , uid , ids , field_name , arg , context = None ) :
2014-05-06 15:07:32 +00:00
Purchase = self . pool [ ' purchase.order ' ]
return {
product_id : Purchase . search_count ( cr , uid , [ ( ' order_line.product_id ' , ' = ' , product_id ) ] , context = context )
for product_id in ids
}
2012-10-03 06:48:40 +00:00
_columns = {
2014-03-24 13:56:21 +00:00
' purchase_count ' : fields . function ( _purchase_count , string = ' # Purchases ' , type = ' integer ' ) ,
2012-10-10 20:38:18 +00:00
}
2014-04-15 13:54:11 +00:00
2012-10-03 06:48:40 +00:00
2012-10-03 10:10:55 +00:00
2012-12-21 09:59:02 +00:00
class mail_compose_message ( osv . Model ) :
2012-11-23 06:26:18 +00:00
_inherit = ' mail.compose.message '
2012-12-21 09:59:02 +00:00
2012-11-23 06:26:18 +00:00
def send_mail ( self , cr , uid , ids , context = None ) :
context = context or { }
if context . get ( ' default_model ' ) == ' purchase.order ' and context . get ( ' default_res_id ' ) :
2012-12-21 09:59:02 +00:00
context = dict ( context , mail_post_autofollow = True )
2014-07-07 09:50:30 +00:00
self . pool . get ( ' purchase.order ' ) . signal_workflow ( cr , uid , [ context [ ' default_res_id ' ] ] , ' send_rfq ' )
2012-11-23 06:26:18 +00:00
return super ( mail_compose_message , self ) . send_mail ( cr , uid , ids , context = context )
2013-03-05 10:48:01 +00:00
2013-02-06 05:04:05 +00:00
class account_invoice ( osv . Model ) :
2013-03-05 10:48:01 +00:00
""" Override account_invoice to add Chatter messages on the related purchase
2014-07-16 09:58:22 +00:00
orders , logging the invoice receipt or payment . """
2013-02-06 05:04:05 +00:00
_inherit = ' account.invoice '
2013-02-15 11:21:17 +00:00
2013-02-06 05:04:05 +00:00
def invoice_validate ( self , cr , uid , ids , context = None ) :
2013-03-05 10:48:01 +00:00
res = super ( account_invoice , self ) . invoice_validate ( cr , uid , ids , context = context )
purchase_order_obj = self . pool . get ( ' purchase.order ' )
2013-10-29 09:23:53 +00:00
# read access on purchase.order object is not required
if not purchase_order_obj . check_access_rights ( cr , uid , ' read ' , raise_exception = False ) :
user_id = SUPERUSER_ID
else :
user_id = uid
po_ids = purchase_order_obj . search ( cr , user_id , [ ( ' invoice_ids ' , ' in ' , ids ) ] , context = context )
2014-04-15 08:12:11 +00:00
for order in purchase_order_obj . browse ( cr , uid , po_ids , context = context ) :
2014-04-15 13:09:51 +00:00
purchase_order_obj . message_post ( cr , user_id , order . id , body = _ ( " Invoice received " ) , context = context )
2014-04-15 08:12:11 +00:00
invoiced = [ ]
for po_line in order . order_line :
if any ( line . invoice_id . state not in [ ' draft ' , ' cancel ' ] for line in po_line . invoice_lines ) :
invoiced . append ( po_line . id )
if invoiced :
self . pool [ ' purchase.order.line ' ] . write ( cr , uid , invoiced , { ' invoiced ' : True } )
2014-04-15 13:09:51 +00:00
workflow . trg_write ( uid , ' purchase.order ' , order . id , cr )
2013-03-05 10:48:01 +00:00
return res
2013-02-15 13:41:51 +00:00
def confirm_paid ( self , cr , uid , ids , context = None ) :
2013-03-05 10:48:01 +00:00
res = super ( account_invoice , self ) . confirm_paid ( cr , uid , ids , context = context )
purchase_order_obj = self . pool . get ( ' purchase.order ' )
2013-10-29 09:34:04 +00:00
# read access on purchase.order object is not required
if not purchase_order_obj . check_access_rights ( cr , uid , ' read ' , raise_exception = False ) :
user_id = SUPERUSER_ID
else :
user_id = uid
po_ids = purchase_order_obj . search ( cr , user_id , [ ( ' invoice_ids ' , ' in ' , ids ) ] , context = context )
2013-11-28 11:09:37 +00:00
for po_id in po_ids :
purchase_order_obj . message_post ( cr , user_id , po_id , body = _ ( " Invoice paid " ) , context = context )
2013-02-15 13:41:51 +00:00
return res
2013-02-06 05:04:05 +00:00
2013-06-24 09:03:29 +00:00
class account_invoice_line ( osv . Model ) :
""" Override account_invoice_line to add the link to the purchase order line it is related to """
_inherit = ' account.invoice.line '
_columns = {
' purchase_line_id ' : fields . many2one ( ' purchase.order.line ' ,
' Purchase Order Line ' , ondelete = ' set null ' , select = True ,
readonly = True ) ,
}
2013-02-14 06:08:46 +00:00
2013-09-19 14:13:19 +00:00
2010-07-09 14:31:07 +00:00
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: