2009-10-13 05:58:37 +00:00
# -*- coding: utf-8 -*-
2006-12-07 13:41:40 +00:00
##############################################################################
2009-11-13 05:41:16 +00:00
#
2008-12-01 13:00:56 +00:00
# OpenERP, Open Source Management Solution
2010-01-12 09:18:39 +00:00
# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
2006-12-07 13:41:40 +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-13 05:41:16 +00:00
# along with this program. If not, see <http://www.gnu.org/licenses/>.
2006-12-07 13:41:40 +00:00
#
##############################################################################
2014-04-22 07:23:03 +00:00
2014-05-12 12:56:01 +00:00
from datetime import datetime , timedelta
2010-09-29 11:33:49 +00:00
import time
2015-08-04 13:22:55 +00:00
from openerp import SUPERUSER_ID
2012-12-06 14:56:32 +00:00
from openerp . osv import fields , osv
from openerp . tools . translate import _
2014-04-14 09:47:43 +00:00
from openerp . tools import DEFAULT_SERVER_DATE_FORMAT , DEFAULT_SERVER_DATETIME_FORMAT
2012-12-17 15:23:03 +00:00
import openerp . addons . decimal_precision as dp
2013-11-21 12:06:11 +00:00
from openerp import workflow
2010-03-06 20:52:19 +00:00
2014-08-19 07:48:05 +00:00
class res_company ( osv . Model ) :
_inherit = " res.company "
_columns = {
' sale_note ' : fields . text ( ' Default Terms and Conditions ' , translate = True , help = " Default terms and conditions for quotations. " ) ,
}
2006-12-07 13:41:40 +00:00
class sale_order ( osv . osv ) :
2008-07-22 15:11:28 +00:00
_name = " sale.order "
2012-09-09 10:11:56 +00:00
_inherit = [ ' mail.thread ' , ' ir.needaction_mixin ' ]
2010-12-13 06:07:23 +00:00
_description = " Sales Order "
2012-12-18 18:17:18 +00:00
_track = {
' state ' : {
2015-06-26 13:06:14 +00:00
' sale.mt_order_confirmed ' : lambda self , cr , uid , obj , ctx = None : obj . state in [ ' manual ' , ' progress ' ] ,
2013-06-27 14:46:47 +00:00
' sale.mt_order_sent ' : lambda self , cr , uid , obj , ctx = None : obj . state in [ ' sent ' ]
2012-12-18 18:17:18 +00:00
} ,
}
2009-08-26 13:41:53 +00:00
2010-04-13 10:35:39 +00:00
def _amount_line_tax ( self , cr , uid , line , context = None ) :
2008-12-07 12:45:27 +00:00
val = 0.0
2014-07-31 07:38:03 +00:00
line_obj = self . pool [ ' sale.order.line ' ]
price = line_obj . _calc_line_base_price ( cr , uid , line , context = context )
qty = line_obj . _calc_line_quantity ( cr , uid , line , context = context )
for c in self . pool [ ' account.tax ' ] . compute_all (
cr , uid , line . tax_id , price , qty , line . product_id ,
line . order_id . partner_id ) [ ' taxes ' ] :
2010-10-08 06:13:09 +00:00
val + = c . get ( ' amount ' , 0.0 )
2008-12-07 12:45:27 +00:00
return val
2008-07-22 15:11:28 +00:00
2013-12-18 13:19:42 +00:00
def _amount_all_wrapper ( self , cr , uid , ids , field_name , arg , context = None ) :
""" Wrapper because of direct method passing as parameter for function fields """
return self . _amount_all ( cr , uid , ids , field_name , arg , context = context )
2010-04-13 10:35:39 +00:00
def _amount_all ( self , cr , uid , ids , field_name , arg , context = None ) :
2010-06-22 07:15:50 +00:00
cur_obj = self . pool . get ( ' res.currency ' )
2008-07-22 15:11:28 +00:00
res = { }
2010-06-22 07:15:50 +00:00
for order in self . browse ( cr , uid , ids , context = context ) :
2008-12-07 12:45:27 +00:00
res [ order . id ] = {
' amount_untaxed ' : 0.0 ,
' amount_tax ' : 0.0 ,
' amount_total ' : 0.0 ,
}
val = val1 = 0.0
2009-08-26 13:41:53 +00:00
cur = order . pricelist_id . currency_id
2008-07-22 15:11:28 +00:00
for line in order . order_line :
2008-12-07 12:45:27 +00:00
val1 + = line . price_subtotal
2010-04-13 10:35:39 +00:00
val + = self . _amount_line_tax ( cr , uid , line , context = context )
2009-08-26 13:41:53 +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
2008-08-24 23:23:45 +00:00
def _invoiced_rate ( self , cursor , user , ids , name , arg , context = None ) :
res = { }
for sale in self . browse ( cursor , user , ids , context = context ) :
2008-09-08 22:53:40 +00:00
if sale . invoiced :
res [ sale . id ] = 100.0
continue
2008-08-24 23:23:45 +00:00
tot = 0.0
for invoice in sale . invoice_ids :
2009-08-26 13:41:53 +00:00
if invoice . state not in ( ' draft ' , ' cancel ' ) :
2008-08-24 23:23:45 +00:00
tot + = invoice . amount_untaxed
if tot :
2009-05-27 07:22:12 +00:00
res [ sale . id ] = min ( 100.0 , tot * 100.0 / ( sale . amount_untaxed or 1.00 ) )
2008-08-24 23:23:45 +00:00
else :
res [ sale . id ] = 0.0
return res
2012-09-11 11:21:33 +00:00
def _invoice_exists ( self , cursor , user , ids , name , arg , context = None ) :
2012-09-07 11:28:34 +00:00
res = { }
for sale in self . browse ( cursor , user , ids , context = context ) :
2012-09-11 11:21:33 +00:00
res [ sale . id ] = False
if sale . invoice_ids :
res [ sale . id ] = True
2012-09-07 11:28:34 +00:00
return res
2008-07-22 15:11:28 +00:00
def _invoiced ( self , cursor , user , ids , name , arg , context = None ) :
res = { }
for sale in self . browse ( cursor , user , ids , context = context ) :
res [ sale . id ] = True
2012-02-16 16:52:53 +00:00
invoice_existence = False
2008-07-22 15:11:28 +00:00
for invoice in sale . invoice_ids :
2012-02-14 12:56:53 +00:00
if invoice . state != ' cancel ' :
2012-02-16 16:52:53 +00:00
invoice_existence = True
if invoice . state != ' paid ' :
res [ sale . id ] = False
break
2012-09-11 11:21:33 +00:00
if not invoice_existence or sale . state == ' manual ' :
2008-07-22 15:11:28 +00:00
res [ sale . id ] = False
return res
2010-04-13 10:35:39 +00:00
def _invoiced_search ( self , cursor , user , obj , name , args , context = None ) :
2008-07-22 15:11:28 +00:00
if not len ( args ) :
return [ ]
clause = ' '
2010-11-03 12:30:07 +00:00
sale_clause = ' '
2008-07-22 15:11:28 +00:00
no_invoiced = False
for arg in args :
2014-06-03 14:19:05 +00:00
if ( arg [ 1 ] == ' = ' and arg [ 2 ] ) or ( arg [ 1 ] == ' != ' and not arg [ 2 ] ) :
clause + = ' AND inv.state = \' paid \' '
else :
clause + = ' AND inv.state != \' cancel \' AND sale.state != \' cancel \' AND inv.state <> \' paid \' AND rel.order_id = sale.id '
sale_clause = ' , sale_order AS sale '
no_invoiced = True
2010-11-03 11:33:16 +00:00
2008-07-22 15:11:28 +00:00
cursor . execute ( ' SELECT rel.order_id ' \
2010-11-03 12:30:07 +00:00
' FROM sale_order_invoice_rel AS rel, account_invoice AS inv ' + sale_clause + \
2008-07-22 15:11:28 +00:00
' WHERE rel.invoice_id = inv.id ' + clause )
res = cursor . fetchall ( )
if no_invoiced :
cursor . execute ( ' SELECT sale.id ' \
' FROM sale_order AS sale ' \
' WHERE sale.id NOT IN ' \
' (SELECT rel.order_id ' \
2010-11-03 12:30:07 +00:00
' FROM sale_order_invoice_rel AS rel) and sale.state != \' cancel \' ' )
2008-07-22 15:11:28 +00:00
res . extend ( cursor . fetchall ( ) )
if not res :
return [ ( ' id ' , ' = ' , 0 ) ]
return [ ( ' id ' , ' in ' , [ x [ 0 ] for x in res ] ) ]
2010-04-13 10:35:39 +00:00
def _get_order ( self , cr , uid , ids , context = None ) :
2008-12-13 06:05:19 +00:00
result = { }
for line in self . pool . get ( ' sale.order.line ' ) . browse ( cr , uid , ids , context = context ) :
result [ line . order_id . id ] = True
return result . keys ( )
2013-06-14 14:16:43 +00:00
2013-01-22 11:08:57 +00:00
def _get_default_company ( self , cr , uid , context = None ) :
2013-06-14 14:33:17 +00:00
company_id = self . pool . get ( ' res.users ' ) . _get_company ( cr , uid , context = context )
2013-01-22 11:08:57 +00:00
if not company_id :
raise osv . except_osv ( _ ( ' Error! ' ) , _ ( ' There is no default company for the current user! ' ) )
return company_id
2014-05-12 12:56:01 +00:00
2014-03-28 09:58:35 +00:00
def _get_default_section_id ( self , cr , uid , context = None ) :
""" Gives default section by checking if present in the context """
2014-05-12 12:56:01 +00:00
section_id = self . _resolve_section_id_from_context ( cr , uid , context = context ) or False
if not section_id :
section_id = self . pool . get ( ' res.users ' ) . browse ( cr , uid , uid , context ) . default_section_id . id or False
2014-03-28 09:58:35 +00:00
return section_id
2011-01-17 11:20:56 +00:00
2014-05-12 12:56:01 +00:00
def _resolve_section_id_from_context ( self , cr , uid , context = None ) :
""" Returns ID of section based on the value of ' section_id '
context key , or None if it cannot be resolved to a single
Sales Team .
"""
if context is None :
context = { }
if type ( context . get ( ' default_section_id ' ) ) in ( int , long ) :
return context . get ( ' default_section_id ' )
if isinstance ( context . get ( ' default_section_id ' ) , basestring ) :
section_ids = self . pool . get ( ' crm.case.section ' ) . name_search ( cr , uid , name = context [ ' default_section_id ' ] , context = context )
if len ( section_ids ) == 1 :
return int ( section_ids [ 0 ] [ 0 ] )
return None
2008-07-22 15:11:28 +00:00
_columns = {
2014-07-06 14:44:26 +00:00
' name ' : fields . char ( ' Order Reference ' , required = True , copy = False ,
2012-05-23 12:55:34 +00:00
readonly = True , states = { ' draft ' : [ ( ' readonly ' , False ) ] , ' sent ' : [ ( ' readonly ' , False ) ] } , select = True ) ,
2014-05-21 09:52:05 +00:00
' origin ' : fields . char ( ' Source Document ' , help = " Reference of the document that generated this sales order request. " ) ,
2014-07-06 14:44:26 +00:00
' client_order_ref ' : fields . char ( ' Reference/Description ' , copy = False ) ,
2008-07-22 15:11:28 +00:00
' state ' : fields . selection ( [
2012-02-29 09:46:32 +00:00
( ' draft ' , ' Draft Quotation ' ) ,
( ' sent ' , ' Quotation Sent ' ) ,
2012-05-01 12:34:46 +00:00
( ' cancel ' , ' Cancelled ' ) ,
2009-08-26 13:41:53 +00:00
( ' waiting_date ' , ' Waiting Schedule ' ) ,
2012-12-20 11:43:56 +00:00
( ' progress ' , ' Sales Order ' ) ,
2012-09-20 17:22:49 +00:00
( ' manual ' , ' Sale to Invoice ' ) ,
2013-09-10 15:48:22 +00:00
( ' shipping_except ' , ' Shipping Exception ' ) ,
2009-08-26 13:41:53 +00:00
( ' invoice_except ' , ' Invoice Exception ' ) ,
( ' done ' , ' Done ' ) ,
2014-07-06 14:44:26 +00:00
] , ' Status ' , readonly = True , copy = False , help = " Gives the status of the quotation or sales order. \
2013-09-10 15:48:22 +00:00
\nThe exception status is automatically set when a cancel operation occurs \
in the invoice validation ( Invoice Exception ) or in the picking list process ( Shipping Exception ) . \nThe ' Waiting Schedule ' status is set when the invoice is confirmed \
but waiting for the scheduler to run on the order date . " , select=True),
2014-07-06 14:44:26 +00:00
' date_order ' : fields . datetime ( ' Date ' , required = True , readonly = True , select = True , states = { ' draft ' : [ ( ' readonly ' , False ) ] , ' sent ' : [ ( ' readonly ' , False ) ] } , copy = False ) ,
2011-07-07 09:25:31 +00:00
' create_date ' : fields . datetime ( ' Creation Date ' , readonly = True , select = True , help = " Date on which sales order is created. " ) ,
2014-07-06 14:44:26 +00:00
' date_confirm ' : fields . date ( ' Confirmation Date ' , readonly = True , select = True , help = " Date on which sales order is confirmed. " , copy = False ) ,
2012-12-20 11:47:30 +00:00
' user_id ' : fields . many2one ( ' res.users ' , ' Salesperson ' , states = { ' draft ' : [ ( ' readonly ' , False ) ] , ' sent ' : [ ( ' readonly ' , False ) ] } , select = True , track_visibility = ' onchange ' ) ,
' partner_id ' : fields . many2one ( ' res.partner ' , ' Customer ' , readonly = True , states = { ' draft ' : [ ( ' readonly ' , False ) ] , ' sent ' : [ ( ' readonly ' , False ) ] } , required = True , change_default = True , select = True , track_visibility = ' always ' ) ,
2012-05-23 12:55:34 +00:00
' partner_invoice_id ' : fields . many2one ( ' res.partner ' , ' Invoice Address ' , readonly = True , required = True , states = { ' draft ' : [ ( ' readonly ' , False ) ] , ' sent ' : [ ( ' readonly ' , False ) ] } , help = " Invoice address for current sales order. " ) ,
2012-10-03 09:29:39 +00:00
' partner_shipping_id ' : fields . many2one ( ' res.partner ' , ' Delivery Address ' , readonly = True , required = True , states = { ' draft ' : [ ( ' readonly ' , False ) ] , ' sent ' : [ ( ' readonly ' , False ) ] } , help = " Delivery address for current sales order. " ) ,
2008-07-22 15:11:28 +00:00
' order_policy ' : fields . selection ( [
2012-09-09 10:11:56 +00:00
( ' manual ' , ' On Demand ' ) ,
2012-05-23 12:55:34 +00:00
] , ' Create Invoice ' , required = True , readonly = True , states = { ' draft ' : [ ( ' readonly ' , False ) ] , ' sent ' : [ ( ' readonly ' , False ) ] } ,
2013-04-04 15:14:51 +00:00
help = """ This field controls how invoice and delivery operations are synchronized. """ ) ,
2012-05-23 12:55:34 +00:00
' pricelist_id ' : fields . many2one ( ' product.pricelist ' , ' Pricelist ' , required = True , readonly = True , states = { ' draft ' : [ ( ' readonly ' , False ) ] , ' sent ' : [ ( ' readonly ' , False ) ] } , help = " Pricelist for current sales order. " ) ,
2012-12-05 08:37:31 +00:00
' currency_id ' : fields . related ( ' pricelist_id ' , ' currency_id ' , type = " many2one " , relation = " res.currency " , string = " Currency " , readonly = True , required = True ) ,
2012-10-26 15:34:36 +00:00
' project_id ' : fields . many2one ( ' account.analytic.account ' , ' Contract / Analytic ' , readonly = True , states = { ' draft ' : [ ( ' readonly ' , False ) ] , ' sent ' : [ ( ' readonly ' , False ) ] } , help = " The analytic account related to a sales order. " ) ,
2008-07-22 15:11:28 +00:00
2014-07-06 14:44:26 +00:00
' order_line ' : fields . one2many ( ' sale.order.line ' , ' order_id ' , ' Order Lines ' , readonly = True , states = { ' draft ' : [ ( ' readonly ' , False ) ] , ' sent ' : [ ( ' readonly ' , False ) ] } , copy = True ) ,
' invoice_ids ' : fields . many2many ( ' account.invoice ' , ' sale_order_invoice_rel ' , ' order_id ' , ' invoice_id ' , ' Invoices ' , readonly = True , copy = False , help = " This is the list of invoices that have been generated for this sales order. The same sales order may have been invoiced in several times (by line for example). " ) ,
2012-12-06 06:52:14 +00:00
' invoiced_rate ' : fields . function ( _invoiced_rate , string = ' Invoiced Ratio ' , type = ' float ' ) ,
2011-07-01 23:41:24 +00:00
' invoiced ' : fields . function ( _invoiced , string = ' Paid ' ,
2010-12-13 06:07:23 +00:00
fnct_search = _invoiced_search , type = ' boolean ' , help = " It indicates that an invoice has been paid. " ) ,
2012-09-11 11:21:33 +00:00
' invoice_exists ' : fields . function ( _invoice_exists , string = ' Invoiced ' ,
2012-12-21 16:48:08 +00:00
fnct_search = _invoiced_search , type = ' boolean ' , help = " It indicates that sales order has at least one invoice. " ) ,
2012-06-28 12:51:23 +00:00
' note ' : fields . text ( ' Terms and conditions ' ) ,
2008-12-07 12:45:27 +00:00
2013-12-18 13:19:42 +00:00
' amount_untaxed ' : fields . function ( _amount_all_wrapper , digits_compute = dp . get_precision ( ' Account ' ) , string = ' Untaxed Amount ' ,
2012-12-18 18:17:18 +00:00
store = {
2009-08-20 15:32:05 +00:00
' sale.order ' : ( lambda self , cr , uid , ids , c = { } : ids , [ ' order_line ' ] , 10 ) ,
2009-08-26 13:41:53 +00:00
' sale.order.line ' : ( _get_order , [ ' price_unit ' , ' tax_id ' , ' discount ' , ' product_uom_qty ' ] , 10 ) ,
2008-12-13 06:05:19 +00:00
} ,
2012-12-20 11:47:30 +00:00
multi = ' sums ' , help = " The amount without tax. " , track_visibility = ' always ' ) ,
2013-12-18 13:19:42 +00:00
' amount_tax ' : fields . function ( _amount_all_wrapper , digits_compute = dp . get_precision ( ' Account ' ) , string = ' Taxes ' ,
2012-12-18 18:17:18 +00:00
store = {
2009-08-20 15:32:05 +00:00
' sale.order ' : ( lambda self , cr , uid , ids , c = { } : ids , [ ' order_line ' ] , 10 ) ,
2009-08-26 13:41:53 +00:00
' sale.order.line ' : ( _get_order , [ ' price_unit ' , ' tax_id ' , ' discount ' , ' product_uom_qty ' ] , 10 ) ,
2008-12-13 06:05:19 +00:00
} ,
2010-12-13 06:07:23 +00:00
multi = ' sums ' , help = " The tax amount. " ) ,
2013-12-18 13:19:42 +00:00
' amount_total ' : fields . function ( _amount_all_wrapper , digits_compute = dp . get_precision ( ' Account ' ) , string = ' Total ' ,
2012-12-18 18:17:18 +00:00
store = {
2009-08-20 15:32:05 +00:00
' sale.order ' : ( lambda self , cr , uid , ids , c = { } : ids , [ ' order_line ' ] , 10 ) ,
2009-08-26 13:41:53 +00:00
' sale.order.line ' : ( _get_order , [ ' price_unit ' , ' tax_id ' , ' discount ' , ' product_uom_qty ' ] , 10 ) ,
2008-12-13 06:05:19 +00:00
} ,
2010-12-13 06:07:23 +00:00
multi = ' sums ' , help = " The total amount. " ) ,
2008-12-07 12:45:27 +00:00
2009-08-26 13:41:53 +00:00
' payment_term ' : fields . many2one ( ' account.payment.term ' , ' Payment Term ' ) ,
2009-11-20 13:47:42 +00:00
' fiscal_position ' : fields . many2one ( ' account.fiscal.position ' , ' Fiscal Position ' ) ,
2013-06-14 14:16:43 +00:00
' company_id ' : fields . many2one ( ' res.company ' , ' Company ' ) ,
2014-03-28 09:58:35 +00:00
' section_id ' : fields . many2one ( ' crm.case.section ' , ' Sales Team ' ) ,
2014-07-06 14:44:26 +00:00
' procurement_group_id ' : fields . many2one ( ' procurement.group ' , ' Procurement group ' , copy = False ) ,
2014-09-17 14:47:57 +00:00
' product_id ' : fields . related ( ' order_line ' , ' product_id ' , type = ' many2one ' , relation = ' product.product ' , string = ' Product ' ) ,
2008-07-22 15:11:28 +00:00
}
2014-09-17 14:47:57 +00:00
2008-07-22 15:11:28 +00:00
_defaults = {
2013-08-02 11:33:08 +00:00
' date_order ' : fields . datetime . now ,
2012-09-09 10:11:56 +00:00
' order_policy ' : ' manual ' ,
2013-01-22 11:08:57 +00:00
' company_id ' : _get_default_company ,
2010-06-22 07:15:50 +00:00
' state ' : ' draft ' ,
2008-07-22 15:11:28 +00:00
' user_id ' : lambda obj , cr , uid , context : uid ,
2012-11-29 05:57:43 +00:00
' name ' : lambda obj , cr , uid , context : ' / ' ,
2008-07-22 15:11:28 +00:00
' partner_invoice_id ' : lambda self , cr , uid , context : context . get ( ' partner_id ' , False ) and self . pool . get ( ' res.partner ' ) . address_get ( cr , uid , [ context [ ' partner_id ' ] ] , [ ' invoice ' ] ) [ ' invoice ' ] ,
' partner_shipping_id ' : lambda self , cr , uid , context : context . get ( ' partner_id ' , False ) and self . pool . get ( ' res.partner ' ) . address_get ( cr , uid , [ context [ ' partner_id ' ] ] , [ ' delivery ' ] ) [ ' delivery ' ] ,
2014-03-28 09:58:35 +00:00
' note ' : lambda self , cr , uid , context : self . pool . get ( ' res.users ' ) . browse ( cr , uid , uid , context = context ) . company_id . sale_note ,
' section_id ' : lambda s , cr , uid , c : s . _get_default_section_id ( cr , uid , c ) ,
2008-07-22 15:11:28 +00:00
}
2010-12-21 05:04:12 +00:00
_sql_constraints = [
2011-11-14 09:05:20 +00:00
( ' name_uniq ' , ' unique(name, company_id) ' , ' Order Reference must be unique per Company! ' ) ,
2010-12-21 05:04:12 +00:00
]
2014-01-15 14:05:27 +00:00
_order = ' date_order desc, id desc '
2008-07-22 15:11:28 +00:00
# Form filling
2009-01-21 11:21:21 +00:00
def unlink ( self , cr , uid , ids , context = None ) :
2010-04-13 10:35:39 +00:00
sale_orders = self . read ( cr , uid , ids , [ ' state ' ] , context = context )
2008-09-20 12:36:51 +00:00
unlink_ids = [ ]
for s in sale_orders :
2009-08-26 13:41:53 +00:00
if s [ ' state ' ] in [ ' draft ' , ' cancel ' ] :
2008-09-20 12:36:51 +00:00
unlink_ids . append ( s [ ' id ' ] )
else :
2013-04-29 07:15:57 +00:00
raise osv . except_osv ( _ ( ' Invalid Action! ' ) , _ ( ' In order to delete a confirmed sales order, you must cancel it before! ' ) )
2011-09-18 13:41:16 +00:00
2009-01-21 11:21:21 +00:00
return osv . osv . unlink ( self , cr , uid , unlink_ids , context = context )
2008-12-01 13:00:56 +00:00
2012-11-09 06:35:31 +00:00
def copy_quotation ( self , cr , uid , ids , context = None ) :
2014-07-09 09:39:58 +00:00
id = self . copy ( cr , uid , ids [ 0 ] , context = context )
2012-11-09 05:27:48 +00:00
view_ref = self . pool . get ( ' ir.model.data ' ) . get_object_reference ( cr , uid , ' sale ' , ' view_order_form ' )
view_id = view_ref and view_ref [ 1 ] or False ,
return {
' type ' : ' ir.actions.act_window ' ,
' name ' : _ ( ' Sales Order ' ) ,
' res_model ' : ' sale.order ' ,
' res_id ' : id ,
' view_type ' : ' form ' ,
' view_mode ' : ' form ' ,
' view_id ' : view_id ,
' target ' : ' current ' ,
2012-11-09 06:35:31 +00:00
' nodestroy ' : True ,
2012-11-09 05:27:48 +00:00
}
2012-03-05 17:14:23 +00:00
def onchange_pricelist_id ( self , cr , uid , ids , pricelist_id , order_lines , context = None ) :
2012-11-13 08:09:08 +00:00
context = context or { }
2012-10-10 11:12:05 +00:00
if not pricelist_id :
2011-09-21 07:33:59 +00:00
return { }
2012-10-10 11:12:05 +00:00
value = {
' currency_id ' : self . pool . get ( ' product.pricelist ' ) . browse ( cr , uid , pricelist_id , context = context ) . currency_id . id
}
2014-07-15 14:30:38 +00:00
if not order_lines or order_lines == [ ( 6 , 0 , [ ] ) ] :
2012-10-10 11:12:05 +00:00
return { ' value ' : value }
2011-09-21 07:33:59 +00:00
warning = {
' title ' : _ ( ' Pricelist Warning! ' ) ,
' message ' : _ ( ' If you change the pricelist of this order (and eventually the currency), prices of existing order lines will not be updated. ' )
}
2012-10-10 11:12:05 +00:00
return { ' warning ' : warning , ' value ' : value }
2011-09-21 07:33:59 +00:00
2013-02-25 09:02:36 +00:00
def get_salenote ( self , cr , uid , ids , partner_id , context = None ) :
2016-01-15 19:08:13 +00:00
if context is None :
context = { }
context_lang = context . copy ( )
2013-02-15 13:33:09 +00:00
if partner_id :
partner_lang = self . pool . get ( ' res.partner ' ) . browse ( cr , uid , partner_id , context = context ) . lang
context_lang . update ( { ' lang ' : partner_lang } )
2013-02-25 09:02:36 +00:00
return self . pool . get ( ' res.users ' ) . browse ( cr , uid , uid , context = context_lang ) . company_id . sale_note
2013-08-04 11:53:25 +00:00
2014-06-29 21:31:16 +00:00
def onchange_delivery_id ( self , cr , uid , ids , company_id , partner_id , delivery_id , fiscal_position , context = None ) :
r = { ' value ' : { } }
2015-06-17 10:22:54 +00:00
if not company_id :
company_id = self . _get_default_company ( cr , uid , context = context )
fiscal_position = self . pool [ ' account.fiscal.position ' ] . get_fiscal_position ( cr , uid , company_id , partner_id , delivery_id , context = context )
if fiscal_position :
r [ ' value ' ] [ ' fiscal_position ' ] = fiscal_position
2014-06-29 21:31:16 +00:00
return r
2012-11-26 09:26:58 +00:00
def onchange_partner_id ( self , cr , uid , ids , part , context = None ) :
2008-07-22 15:11:28 +00:00
if not part :
2012-03-05 13:25:43 +00:00
return { ' value ' : { ' partner_invoice_id ' : False , ' partner_shipping_id ' : False , ' payment_term ' : False , ' fiscal_position ' : False } }
2010-02-03 11:43:14 +00:00
2012-11-26 09:26:58 +00:00
part = self . pool . get ( ' res.partner ' ) . browse ( cr , uid , part , context = context )
addr = self . pool . get ( ' res.partner ' ) . address_get ( cr , uid , [ part . id ] , [ ' delivery ' , ' invoice ' , ' contact ' ] )
2008-10-20 20:49:11 +00:00
pricelist = part . property_product_pricelist and part . property_product_pricelist . id or False
2015-04-02 07:14:46 +00:00
invoice_part = self . pool . get ( ' res.partner ' ) . browse ( cr , uid , addr [ ' invoice ' ] , context = context )
payment_term = invoice_part . property_payment_term and invoice_part . property_payment_term . id or False
2010-02-05 08:56:30 +00:00
dedicated_salesman = part . user_id and part . user_id . id or uid
2010-02-03 11:43:14 +00:00
val = {
' partner_invoice_id ' : addr [ ' invoice ' ] ,
' partner_shipping_id ' : addr [ ' delivery ' ] ,
' payment_term ' : payment_term ,
' user_id ' : dedicated_salesman ,
}
2014-06-29 21:31:16 +00:00
delivery_onchange = self . onchange_delivery_id ( cr , uid , ids , False , part . id , addr [ ' delivery ' ] , False , context = context )
val . update ( delivery_onchange [ ' value ' ] )
2009-03-08 18:54:10 +00:00
if pricelist :
val [ ' pricelist_id ' ] = pricelist
2015-04-20 09:10:53 +00:00
if not self . _get_default_section_id ( cr , uid , context = context ) and part . section_id :
val [ ' section_id ' ] = part . section_id . id
2013-02-25 09:02:36 +00:00
sale_note = self . get_salenote ( cr , uid , ids , part . id , context = context )
2013-03-01 08:29:28 +00:00
if sale_note : val . update ( { ' note ' : sale_note } )
2009-08-26 13:41:53 +00:00
return { ' value ' : val }
2008-07-22 15:11:28 +00:00
2010-04-13 10:35:39 +00:00
def create ( self , cr , uid , vals , context = None ) :
2013-03-05 10:48:01 +00:00
if context is None :
2014-06-29 21:31:16 +00:00
context = { }
2013-03-05 10:48:01 +00:00
if vals . get ( ' name ' , ' / ' ) == ' / ' :
2015-05-21 15:52:03 +00:00
vals [ ' name ' ] = self . pool . get ( ' ir.sequence ' ) . get ( cr , uid , ' sale.order ' , context = context ) or ' / '
2014-06-29 21:31:16 +00:00
if vals . get ( ' partner_id ' ) and any ( f not in vals for f in [ ' partner_invoice_id ' , ' partner_shipping_id ' , ' pricelist_id ' , ' fiscal_position ' ] ) :
defaults = self . onchange_partner_id ( cr , uid , [ ] , vals [ ' partner_id ' ] , context = context ) [ ' value ' ]
if not vals . get ( ' fiscal_position ' ) and vals . get ( ' partner_shipping_id ' ) :
delivery_onchange = self . onchange_delivery_id ( cr , uid , [ ] , vals . get ( ' company_id ' ) , None , vals [ ' partner_id ' ] , vals . get ( ' partner_shipping_id ' ) , context = context )
defaults . update ( delivery_onchange [ ' value ' ] )
2013-12-06 13:51:11 +00:00
vals = dict ( defaults , * * vals )
2014-07-06 14:44:26 +00:00
ctx = dict ( context or { } , mail_create_nolog = True )
new_id = super ( sale_order , self ) . create ( cr , uid , vals , context = ctx )
self . message_post ( cr , uid , [ new_id ] , body = _ ( " Quotation created " ) , context = ctx )
2013-03-05 10:48:01 +00:00
return new_id
2008-12-01 13:00:56 +00:00
2010-04-13 10:35:39 +00:00
def button_dummy ( self , cr , uid , ids , context = None ) :
2008-07-22 15:11:28 +00:00
return True
2006-12-07 13:41:40 +00:00
2012-02-15 17:09:11 +00:00
# FIXME: deprecated method, overriders should be using _prepare_invoice() instead.
# can be removed after 6.1.
2010-04-13 10:35:39 +00:00
def _inv_get ( self , cr , uid , order , context = None ) :
2008-07-22 15:11:28 +00:00
return { }
2012-02-01 18:31:23 +00:00
def _prepare_invoice ( self , cr , uid , order , lines , context = None ) :
2012-02-15 17:09:11 +00:00
""" Prepare the dict of values to create the new invoice for a
2012-12-21 16:48:08 +00:00
sales order . This method may be overridden to implement custom
2012-02-15 17:09:11 +00:00
invoice generation ( making sure to call super ( ) to establish
a clean extension chain ) .
: param browse_record order : sale . order record to invoice
: param list ( int ) line : list of invoice line IDs that must be
attached to the invoice
: return : dict of value to create ( ) the invoice
2012-02-01 18:31:23 +00:00
"""
2010-04-13 10:35:39 +00:00
if context is None :
context = { }
[FIX] sale: use default_get to define the invoice journal
When creating the invoice of a sales order, from the sales order
(`Create invoice` button on the sale order),
the journal used for the invoice was forced
with a specific domain. Besides, the only reason the journal
is forced is to check there is a sale journal for the
quotation company, and raise a warning if not.
This check was added in 1578c2858df1ca398bcdd07cc0c3e6a83ff23b9c.
This prevented to use the user defined defaults,
(`Set defaults` in the `debug` menu)
to set a different default journal per user.
Using the `default_get` instead solves this issue,
as it uses first the user defined defaults. Besides,
if no user defined defaults are set, it then uses
the default value set in the field definition, which
in this case returns the same journal then the forced
domain mentioned above, the domain used being the same.
There is therefore no change of behavior, while giving
the possibility to use the user defined defaults.
Fixes #8786
2015-09-30 13:21:32 +00:00
journal_id = self . pool [ ' account.invoice ' ] . default_get ( cr , uid , [ ' journal_id ' ] , context = context ) [ ' journal_id ' ]
if not journal_id :
2012-08-07 11:31:37 +00:00
raise osv . except_osv ( _ ( ' Error! ' ) ,
2012-07-25 11:07:02 +00:00
_ ( ' Please define sales journal for this company: " %s " (id: %d ). ' ) % ( order . company_id . name , order . company_id . id ) )
2012-02-15 17:09:11 +00:00
invoice_vals = {
2010-12-29 14:53:10 +00:00
' name ' : order . client_order_ref or ' ' ,
2008-07-22 15:11:28 +00:00
' origin ' : order . name ,
' type ' : ' out_invoice ' ,
2011-01-17 08:05:56 +00:00
' reference ' : order . client_order_ref or order . name ,
2015-04-02 07:14:46 +00:00
' account_id ' : order . partner_invoice_id . property_account_receivable . id ,
2012-11-26 15:51:39 +00:00
' partner_id ' : order . partner_invoice_id . id ,
[FIX] sale: use default_get to define the invoice journal
When creating the invoice of a sales order, from the sales order
(`Create invoice` button on the sale order),
the journal used for the invoice was forced
with a specific domain. Besides, the only reason the journal
is forced is to check there is a sale journal for the
quotation company, and raise a warning if not.
This check was added in 1578c2858df1ca398bcdd07cc0c3e6a83ff23b9c.
This prevented to use the user defined defaults,
(`Set defaults` in the `debug` menu)
to set a different default journal per user.
Using the `default_get` instead solves this issue,
as it uses first the user defined defaults. Besides,
if no user defined defaults are set, it then uses
the default value set in the field definition, which
in this case returns the same journal then the forced
domain mentioned above, the domain used being the same.
There is therefore no change of behavior, while giving
the possibility to use the user defined defaults.
Fixes #8786
2015-09-30 13:21:32 +00:00
' journal_id ' : journal_id ,
2009-08-26 13:41:53 +00:00
' invoice_line ' : [ ( 6 , 0 , lines ) ] ,
' currency_id ' : order . pricelist_id . currency_id . id ,
2008-07-22 15:11:28 +00:00
' comment ' : order . note ,
2012-02-01 18:31:23 +00:00
' payment_term ' : order . payment_term and order . payment_term . id or False ,
2015-04-02 07:14:46 +00:00
' fiscal_position ' : order . fiscal_position . id or order . partner_invoice_id . property_account_position . id ,
2012-02-01 18:31:23 +00:00
' date_invoice ' : context . get ( ' date_invoice ' , False ) ,
2010-10-15 09:42:54 +00:00
' company_id ' : order . company_id . id ,
2014-03-28 09:58:35 +00:00
' user_id ' : order . user_id and order . user_id . id or False ,
' section_id ' : order . section_id . id
2008-07-22 15:11:28 +00:00
}
2012-02-01 18:31:23 +00:00
2012-02-15 17:09:11 +00:00
# Care for deprecated _inv_get() hook - FIXME: to be removed after 6.1
invoice_vals . update ( self . _inv_get ( cr , uid , order , context = context ) )
return invoice_vals
2012-02-01 18:31:23 +00:00
def _make_invoice ( self , cr , uid , order , lines , context = None ) :
inv_obj = self . pool . get ( ' account.invoice ' )
obj_invoice_line = self . pool . get ( ' account.invoice.line ' )
if context is None :
context = { }
invoiced_sale_line_ids = self . pool . get ( ' sale.order.line ' ) . search ( cr , uid , [ ( ' order_id ' , ' = ' , order . id ) , ( ' invoiced ' , ' = ' , True ) ] , context = context )
from_line_invoice_ids = [ ]
for invoiced_sale_line_id in self . pool . get ( ' sale.order.line ' ) . browse ( cr , uid , invoiced_sale_line_ids , context = context ) :
for invoice_line_id in invoiced_sale_line_id . invoice_lines :
if invoice_line_id . invoice_id . id not in from_line_invoice_ids :
from_line_invoice_ids . append ( invoice_line_id . invoice_id . id )
for preinv in order . invoice_ids :
if preinv . state not in ( ' cancel ' , ) and preinv . id not in from_line_invoice_ids :
for preline in preinv . invoice_line :
inv_line_id = obj_invoice_line . copy ( cr , uid , preline . id , { ' invoice_id ' : False , ' price_unit ' : - preline . price_unit } )
lines . append ( inv_line_id )
inv = self . _prepare_invoice ( cr , uid , order , lines , context = context )
2010-04-13 10:35:39 +00:00
inv_id = inv_obj . create ( cr , uid , inv , context = context )
2012-02-01 18:31:23 +00:00
data = inv_obj . onchange_payment_term_date_invoice ( cr , uid , [ inv_id ] , inv [ ' payment_term ' ] , time . strftime ( DEFAULT_SERVER_DATE_FORMAT ) )
2009-08-26 13:41:53 +00:00
if data . get ( ' value ' , False ) :
2008-12-02 10:17:42 +00:00
inv_obj . write ( cr , uid , [ inv_id ] , data [ ' value ' ] , context = context )
2008-07-22 15:11:28 +00:00
inv_obj . button_compute ( cr , uid , [ inv_id ] )
return inv_id
2012-02-29 09:46:32 +00:00
def print_quotation ( self , cr , uid , ids , context = None ) :
2012-05-25 15:17:40 +00:00
'''
2012-12-21 16:48:08 +00:00
This function prints the sales order and mark it as sent , so that we can see more easily the next step of the workflow
2012-05-25 15:17:40 +00:00
'''
2012-05-25 13:35:36 +00:00
assert len ( ids ) == 1 , ' This option should only be used for a single id at a time '
2014-07-06 14:44:26 +00:00
self . signal_workflow ( cr , uid , ids , ' quotation_sent ' )
2014-02-12 23:00:02 +00:00
return self . pool [ ' report ' ] . get_action ( cr , uid , ids , ' sale.report_saleorder ' , context = context )
2012-08-07 11:06:16 +00:00
2010-10-11 11:43:11 +00:00
def manual_invoice ( self , cr , uid , ids , context = None ) :
2012-12-21 16:48:08 +00:00
""" create invoices for the given sales orders (ids), and open the form
2012-07-23 13:14:53 +00:00
view of one of the newly created invoices
"""
2010-10-14 08:13:33 +00:00
mod_obj = self . pool . get ( ' ir.model.data ' )
2013-01-31 04:46:08 +00:00
2012-12-21 16:48:08 +00:00
# create invoices through the sales orders' workflow
2012-07-23 13:14:53 +00:00
inv_ids0 = set ( inv . id for sale in self . browse ( cr , uid , ids , context ) for inv in sale . invoice_ids )
2014-07-06 14:44:26 +00:00
self . signal_workflow ( cr , uid , ids , ' manual_invoice ' )
2012-07-23 13:14:53 +00:00
inv_ids1 = set ( inv . id for sale in self . browse ( cr , uid , ids , context ) for inv in sale . invoice_ids )
# determine newly created invoices
new_inv_ids = list ( inv_ids1 - inv_ids0 )
2011-11-25 10:52:37 +00:00
2010-10-28 13:16:19 +00:00
res = mod_obj . get_object_reference ( cr , uid , ' account ' , ' invoice_form ' )
2010-10-29 04:17:01 +00:00
res_id = res and res [ 1 ] or False ,
2011-11-25 10:52:37 +00:00
2010-10-29 04:17:01 +00:00
return {
2011-05-09 09:41:00 +00:00
' name ' : _ ( ' Customer Invoices ' ) ,
2010-10-11 11:43:11 +00:00
' view_type ' : ' form ' ,
2010-10-14 08:13:33 +00:00
' view_mode ' : ' form ' ,
2010-10-29 04:17:01 +00:00
' view_id ' : [ res_id ] ,
2010-10-11 11:43:11 +00:00
' res_model ' : ' account.invoice ' ,
2010-10-14 08:13:33 +00:00
' context ' : " { ' type ' : ' out_invoice ' } " ,
2010-10-11 11:43:11 +00:00
' type ' : ' ir.actions.act_window ' ,
2010-10-21 09:30:04 +00:00
' nodestroy ' : True ,
2010-10-17 11:59:01 +00:00
' target ' : ' current ' ,
2012-07-23 13:14:53 +00:00
' res_id ' : new_inv_ids and new_inv_ids [ 0 ] or False ,
2010-10-17 11:59:01 +00:00
}
2010-10-11 11:43:11 +00:00
2012-03-06 05:08:47 +00:00
def action_view_invoice ( self , cr , uid , ids , context = None ) :
2012-05-25 13:35:36 +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 13:35:36 +00:00
'''
2012-03-06 05:08:47 +00:00
mod_obj = self . pool . get ( ' ir.model.data ' )
2012-08-24 07:04:44 +00:00
act_obj = self . pool . get ( ' ir.actions.act_window ' )
result = mod_obj . get_object_reference ( cr , uid , ' account ' , ' action_invoice_tree1 ' )
id = result and result [ 1 ] or False
result = act_obj . read ( cr , uid , [ id ] , context = context ) [ 0 ]
2012-05-25 13:35:36 +00:00
#compute the number of invoices to display
2012-03-06 05:08:47 +00:00
inv_ids = [ ]
for so in self . browse ( cr , uid , ids , context = context ) :
2012-05-25 13:35:36 +00:00
inv_ids + = [ invoice . id for invoice in so . invoice_ids ]
#choose the view_mode accordingly
2012-03-19 05:27:15 +00:00
if len ( inv_ids ) > 1 :
2012-08-24 07:04:44 +00:00
result [ ' domain ' ] = " [( ' id ' , ' in ' ,[ " + ' , ' . join ( map ( str , inv_ids ) ) + " ])] "
2012-03-19 05:27:15 +00:00
else :
res = mod_obj . get_object_reference ( cr , uid , ' account ' , ' invoice_form ' )
2012-08-24 07:04:44 +00:00
result [ ' views ' ] = [ ( res and res [ 1 ] or False , ' form ' ) ]
result [ ' res_id ' ] = inv_ids and inv_ids [ 0 ] or False
2012-03-19 05:27:15 +00:00
return result
2012-09-16 14:45:29 +00:00
def test_no_product ( self , cr , uid , order , context ) :
for line in order . order_line :
2015-03-27 15:04:09 +00:00
if line . state == ' cancel ' :
continue
2012-09-16 15:05:17 +00:00
if line . product_id and ( line . product_id . type < > ' service ' ) :
2012-09-16 14:45:29 +00:00
return False
2012-09-06 13:13:10 +00:00
return True
2012-09-21 08:13:39 +00:00
2012-12-15 17:05:59 +00:00
def action_invoice_create ( self , cr , uid , ids , grouped = False , states = None , date_invoice = False , context = None ) :
2012-03-05 17:14:23 +00:00
if states is None :
states = [ ' confirmed ' , ' done ' , ' exception ' ]
2008-07-22 15:11:28 +00:00
res = False
invoices = { }
invoice_ids = [ ]
2010-06-24 09:45:18 +00:00
invoice = self . pool . get ( ' account.invoice ' )
2010-10-15 09:42:54 +00:00
obj_sale_order_line = self . pool . get ( ' sale.order.line ' )
2011-09-24 11:25:29 +00:00
partner_currency = { }
2009-11-13 05:41:16 +00:00
# If date was specified, use it as date invoiced, usefull when invoices are generated this month and put the
2009-09-24 10:46:21 +00:00
# last day of the last month as invoice date
2012-12-15 17:05:59 +00:00
if date_invoice :
2014-07-06 14:44:26 +00:00
context = dict ( context or { } , date_invoice = date_invoice )
2010-12-13 06:43:09 +00:00
for o in self . browse ( cr , uid , ids , context = context ) :
2011-09-24 11:25:29 +00:00
currency_id = o . pricelist_id . currency_id . id
if ( o . partner_id . id in partner_currency ) and ( partner_currency [ o . partner_id . id ] < > currency_id ) :
raise osv . except_osv (
2012-08-07 11:31:37 +00:00
_ ( ' Error! ' ) ,
2011-09-24 11:25:29 +00:00
_ ( ' You cannot group sales having different currencies for the same partner. ' ) )
partner_currency [ o . partner_id . id ] = currency_id
2008-07-22 15:11:28 +00:00
lines = [ ]
for line in o . order_line :
2010-07-18 19:43:57 +00:00
if line . invoiced :
2010-09-27 09:13:56 +00:00
continue
2010-07-18 19:43:57 +00:00
elif ( line . state in states ) :
2008-07-22 15:11:28 +00:00
lines . append ( line . id )
2010-10-15 09:42:54 +00:00
created_lines = obj_sale_order_line . invoice_line_create ( cr , uid , lines )
2008-07-22 15:11:28 +00:00
if created_lines :
2014-03-25 10:32:26 +00:00
invoices . setdefault ( o . partner_invoice_id . id or o . partner_id . id , [ ] ) . append ( ( o , created_lines ) )
2008-07-22 15:11:28 +00:00
if not invoices :
2010-12-13 06:43:09 +00:00
for o in self . browse ( cr , uid , ids , context = context ) :
2008-07-22 15:11:28 +00:00
for i in o . invoice_ids :
if i . state == ' draft ' :
return i . id
for val in invoices . values ( ) :
if grouped :
2010-10-15 08:48:13 +00:00
res = self . _make_invoice ( cr , uid , val [ 0 ] [ 0 ] , reduce ( lambda x , y : x + y , [ l for o , l in val ] , [ ] ) , context = context )
2010-06-24 09:45:18 +00:00
invoice_ref = ' '
2014-06-03 14:56:07 +00:00
origin_ref = ' '
2009-11-26 13:54:00 +00:00
for o , l in val :
2014-06-03 14:56:07 +00:00
invoice_ref + = ( o . client_order_ref or o . name ) + ' | '
origin_ref + = ( o . origin or o . name ) + ' | '
2009-11-26 13:54:00 +00:00
self . write ( cr , uid , [ o . id ] , { ' state ' : ' progress ' } )
2008-12-14 16:46:57 +00:00
cr . execute ( ' insert into sale_order_invoice_rel (order_id,invoice_id) values ( %s , %s ) ' , ( o . id , res ) )
2014-07-06 14:44:26 +00:00
self . invalidate_cache ( cr , uid , [ ' invoice_ids ' ] , [ o . id ] , context = context )
2013-03-21 11:30:59 +00:00
#remove last '|' in invoice_ref
2014-06-03 14:56:07 +00:00
if len ( invoice_ref ) > = 1 :
2013-03-20 14:43:36 +00:00
invoice_ref = invoice_ref [ : - 1 ]
2014-06-03 14:56:07 +00:00
if len ( origin_ref ) > = 1 :
origin_ref = origin_ref [ : - 1 ]
invoice . write ( cr , uid , [ res ] , { ' origin ' : origin_ref , ' name ' : invoice_ref } )
2008-07-22 15:11:28 +00:00
else :
for order , il in val :
2009-09-24 10:46:21 +00:00
res = self . _make_invoice ( cr , uid , order , il , context = context )
2008-07-22 15:11:28 +00:00
invoice_ids . append ( res )
2009-08-26 13:41:53 +00:00
self . write ( cr , uid , [ order . id ] , { ' state ' : ' progress ' } )
2008-12-14 16:46:57 +00:00
cr . execute ( ' insert into sale_order_invoice_rel (order_id,invoice_id) values ( %s , %s ) ' , ( order . id , res ) )
2014-07-06 14:44:26 +00:00
self . invalidate_cache ( cr , uid , [ ' invoice_ids ' ] , [ order . id ] , context = context )
2008-07-22 15:11:28 +00:00
return res
2010-04-13 10:35:39 +00:00
def action_invoice_cancel ( self , cr , uid , ids , context = None ) :
2012-11-29 15:33:30 +00:00
self . write ( cr , uid , ids , { ' state ' : ' invoice_except ' } , context = context )
2008-07-22 15:11:28 +00:00
return True
2010-02-09 08:31:46 +00:00
2010-04-13 10:35:39 +00:00
def action_invoice_end ( self , cr , uid , ids , context = None ) :
2012-11-29 16:26:29 +00:00
for this in self . browse ( cr , uid , ids , context = context ) :
for line in this . order_line :
2009-12-16 06:26:10 +00:00
if line . state == ' exception ' :
2012-11-29 16:26:29 +00:00
line . write ( { ' state ' : ' confirmed ' } )
if this . state == ' invoice_except ' :
this . write ( { ' state ' : ' progress ' } )
2009-12-16 06:26:10 +00:00
return True
2010-02-09 08:31:46 +00:00
2010-04-13 10:35:39 +00:00
def action_cancel ( self , cr , uid , ids , context = None ) :
if context is None :
context = { }
2008-07-22 15:11:28 +00:00
sale_order_line_obj = self . pool . get ( ' sale.order.line ' )
[IMP] Workflow api changes: [account_payment, account_voucher, hr_holidays, hr_payroll, hr_timesheet_sheet, l10n_in_hr_payroll, point_of_sale, procurement, purchase, sale, sale_stock].
bzr revid: tta@openerp.com-20130204082943-vfevxeb7za6r4sy7
bzr revid: tta@openerp.com-20130204112949-07l1v3ru215iglut
2013-02-04 11:29:49 +00:00
account_invoice_obj = self . pool . get ( ' account.invoice ' )
2010-04-13 10:35:39 +00:00
for sale in self . browse ( cr , uid , ids , context = context ) :
2008-07-22 15:11:28 +00:00
for inv in sale . invoice_ids :
2009-08-26 13:41:53 +00:00
if inv . state not in ( ' draft ' , ' cancel ' ) :
2008-07-22 15:11:28 +00:00
raise osv . except_osv (
2012-07-25 11:07:02 +00:00
_ ( ' Cannot cancel this sales order! ' ) ,
_ ( ' First cancel all invoices attached to this sales order. ' ) )
2014-07-06 14:44:26 +00:00
inv . signal_workflow ( ' invoice_cancel ' )
2015-03-27 15:04:09 +00:00
line_ids = [ l . id for l in sale . order_line if l . state != ' cancel ' ]
sale_order_line_obj . button_cancel ( cr , uid , line_ids , context = context )
2009-08-26 13:41:53 +00:00
self . write ( cr , uid , ids , { ' state ' : ' cancel ' } )
2008-07-22 15:11:28 +00:00
return True
2012-07-13 22:51:31 +00:00
2012-05-18 11:39:37 +00:00
def action_button_confirm ( self , cr , uid , ids , context = None ) :
2015-07-09 13:47:46 +00:00
if not context :
context = { }
2012-07-25 11:07:02 +00:00
assert len ( ids ) == 1 , ' This option should only be used for a single id at a time. '
2014-07-06 14:44:26 +00:00
self . signal_workflow ( cr , uid , ids , ' order_confirm ' )
2015-07-09 13:47:46 +00:00
if context . get ( ' send_email ' ) :
2015-07-14 09:01:28 +00:00
self . force_quotation_send ( cr , uid , ids , context = context )
2014-08-21 12:55:49 +00:00
return True
2012-02-13 18:07:41 +00:00
def action_wait ( self , cr , uid , ids , context = None ) :
2012-11-13 08:09:08 +00:00
context = context or { }
2008-07-22 15:11:28 +00:00
for o in self . browse ( cr , uid , ids ) :
2015-03-27 15:04:09 +00:00
if not any ( line . state != ' cancel ' for line in o . order_line ) :
2012-12-21 16:48:08 +00:00
raise osv . except_osv ( _ ( ' Error! ' ) , _ ( ' You cannot confirm a sales order which has no line. ' ) )
2012-09-16 14:45:29 +00:00
noprod = self . test_no_product ( cr , uid , o , context )
if ( o . order_policy == ' manual ' ) or noprod :
2012-02-14 12:25:20 +00:00
self . write ( cr , uid , [ o . id ] , { ' state ' : ' manual ' , ' date_confirm ' : fields . date . context_today ( self , cr , uid , context = context ) } )
2012-09-17 06:21:00 +00:00
else :
self . write ( cr , uid , [ o . id ] , { ' state ' : ' progress ' , ' date_confirm ' : fields . date . context_today ( self , cr , uid , context = context ) } )
2015-03-27 15:04:09 +00:00
self . pool . get ( ' sale.order.line ' ) . button_confirm ( cr , uid , [ x . id for x in o . order_line if x . state != ' cancel ' ] )
2010-06-22 07:15:50 +00:00
return True
2008-07-22 15:11:28 +00:00
2012-05-25 13:35:36 +00:00
def action_quotation_send ( self , cr , uid , ids , context = None ) :
'''
This function opens a window to compose an email , with the edi sale template message loaded by default
'''
2012-07-25 11:07:02 +00:00
assert len ( ids ) == 1 , ' This option should only be used for a single id at a time. '
2012-10-23 11:56:28 +00:00
ir_model_data = self . pool . get ( ' ir.model.data ' )
try :
template_id = ir_model_data . get_object_reference ( cr , uid , ' sale ' , ' email_template_edi_sale ' ) [ 1 ]
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
[FIX] sale: do not propagate context on send email
Otherwise, active_id, active_ids, active_model params are propagated, and it leads to inconsistencies.
For instances, go to Sales > Customers, choose a customer which has quotations.
On the form, hit the "Quotations and sales" Button
Choose a quotation. Hit the "send by email" button.
The template preview should be displayed correctly. Now send the email, than
- Either, you have no traceback: The email sent is the email of another quotation, the quotation having as id the id of the partner you chose
- Either, you have a traceback: There is no quotation having as id the id of the partner you chose.
2014-07-18 14:49:45 +00:00
ctx = dict ( )
2012-09-11 09:40:07 +00:00
ctx . update ( {
' default_model ' : ' sale.order ' ,
' default_res_id ' : ids [ 0 ] ,
2012-10-23 11:56:28 +00:00
' default_use_template ' : bool ( template_id ) ,
2012-09-11 09:40:07 +00:00
' default_template_id ' : template_id ,
2012-11-13 11:20:38 +00:00
' default_composition_mode ' : ' comment ' ,
2012-09-20 09:58:25 +00:00
' mark_so_as_sent ' : True
} )
2012-02-29 09:46:32 +00:00
return {
2012-10-23 11:56:28 +00:00
' type ' : ' ir.actions.act_window ' ,
2012-02-29 09:46:32 +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-29 09:46:32 +00:00
' target ' : ' new ' ,
' context ' : ctx ,
}
2015-07-14 09:01:28 +00:00
def force_quotation_send ( self , cr , uid , ids , context = None ) :
for order_id in ids :
email_act = self . action_quotation_send ( cr , uid , [ order_id ] , context = context )
if email_act and email_act . get ( ' context ' ) :
composer_obj = self . pool [ ' mail.compose.message ' ]
composer_values = { }
email_ctx = email_act [ ' context ' ]
template_values = [
email_ctx . get ( ' default_template_id ' ) ,
email_ctx . get ( ' default_composition_mode ' ) ,
email_ctx . get ( ' default_model ' ) ,
email_ctx . get ( ' default_res_id ' ) ,
]
composer_values . update ( composer_obj . onchange_template_id ( cr , uid , None , * template_values , context = context ) . get ( ' value ' , { } ) )
if not composer_values . get ( ' email_from ' ) :
composer_values [ ' email_from ' ] = self . browse ( cr , uid , order_id , context = context ) . company_id . email
for key in [ ' attachment_ids ' , ' partner_ids ' ] :
if composer_values . get ( key ) :
composer_values [ key ] = [ ( 6 , 0 , composer_values [ key ] ) ]
composer_id = composer_obj . create ( cr , uid , composer_values , context = email_ctx )
composer_obj . send_mail ( cr , uid , [ composer_id ] , context = email_ctx )
return True
2012-09-09 10:11:56 +00:00
def action_done ( self , cr , uid , ids , context = None ) :
2013-09-17 06:43:59 +00:00
for order in self . browse ( cr , uid , ids , context = context ) :
2015-03-27 15:04:09 +00:00
self . pool . get ( ' sale.order.line ' ) . write ( cr , uid , [ line . id for line in order . order_line if line . state != ' cancel ' ] , { ' state ' : ' done ' } , context = context )
2012-09-09 10:11:56 +00:00
return self . write ( cr , uid , ids , { ' state ' : ' done ' } , context = context )
2012-09-21 08:13:39 +00:00
2013-08-02 11:33:08 +00:00
def _prepare_order_line_procurement ( self , cr , uid , order , line , group_id = False , context = None ) :
2013-07-10 15:02:07 +00:00
date_planned = self . _get_date_planned ( cr , uid , order , line , order . date_order , context = context )
return {
' name ' : line . name ,
' origin ' : order . name ,
' date_planned ' : date_planned ,
' product_id ' : line . product_id . id ,
' product_qty ' : line . product_uom_qty ,
' product_uom ' : line . product_uom . id ,
2013-08-02 11:33:08 +00:00
' product_uos_qty ' : ( line . product_uos and line . product_uos_qty ) or line . product_uom_qty ,
' product_uos ' : ( line . product_uos and line . product_uos . id ) or line . product_uom . id ,
2013-07-10 15:02:07 +00:00
' company_id ' : order . company_id . id ,
2013-08-02 11:33:08 +00:00
' group_id ' : group_id ,
2014-04-14 09:47:43 +00:00
' invoice_state ' : ( order . order_policy == ' picking ' ) and ' 2binvoiced ' or ' none ' ,
2013-08-04 11:53:25 +00:00
' sale_line_id ' : line . id
2013-07-10 15:02:07 +00:00
}
def _get_date_planned ( self , cr , uid , order , line , start_date , context = None ) :
2014-04-14 09:47:43 +00:00
date_planned = datetime . strptime ( start_date , DEFAULT_SERVER_DATETIME_FORMAT ) + timedelta ( days = line . delay or 0.0 )
2013-07-10 15:02:07 +00:00
return date_planned
2013-09-09 11:55:15 +00:00
def _prepare_procurement_group ( self , cr , uid , order , context = None ) :
2013-09-17 06:43:59 +00:00
return { ' name ' : order . name , ' partner_id ' : order . partner_shipping_id . id }
2013-09-11 14:00:34 +00:00
2013-09-17 06:43:59 +00:00
def procurement_needed ( self , cr , uid , ids , context = None ) :
#when sale is installed only, there is no need to create procurements, that's only
2014-05-21 10:27:24 +00:00
#further installed modules (sale_service, sale_stock) that will change this.
2013-09-17 06:43:59 +00:00
sale_line_obj = self . pool . get ( ' sale.order.line ' )
res = [ ]
for order in self . browse ( cr , uid , ids , context = context ) :
2015-03-27 15:04:09 +00:00
res . append ( sale_line_obj . need_procurement ( cr , uid , [ line . id for line in order . order_line if line . state != ' cancel ' ] , context = context ) )
2013-09-17 06:43:59 +00:00
return any ( res )
2013-09-13 09:38:15 +00:00
2014-01-24 09:27:58 +00:00
def action_ignore_delivery_exception ( self , cr , uid , ids , context = None ) :
for sale_order in self . browse ( cr , uid , ids , context = context ) :
self . write ( cr , uid , ids , { ' state ' : ' progress ' if sale_order . invoice_exists else ' manual ' } , context = context )
return True
2013-07-10 15:02:07 +00:00
def action_ship_create ( self , cr , uid , ids , context = None ) :
""" Create the required procurements to supply sales order lines, also connecting
the procurements to appropriate stock moves in order to bring the goods to the
sales order ' s requested location.
: return : True
"""
2015-11-13 11:26:50 +00:00
context = dict ( context or { } )
2015-02-23 10:17:05 +00:00
context [ ' lang ' ] = self . pool [ ' res.users ' ] . browse ( cr , uid , uid ) . lang
2013-07-10 15:02:07 +00:00
procurement_obj = self . pool . get ( ' procurement.order ' )
2013-09-17 06:43:59 +00:00
sale_line_obj = self . pool . get ( ' sale.order.line ' )
2013-07-10 15:02:07 +00:00
for order in self . browse ( cr , uid , ids , context = context ) :
proc_ids = [ ]
2013-09-09 11:55:15 +00:00
vals = self . _prepare_procurement_group ( cr , uid , order , context = context )
2014-01-23 16:06:38 +00:00
if not order . procurement_group_id :
group_id = self . pool . get ( " procurement.group " ) . create ( cr , uid , vals , context = context )
2014-11-13 11:30:26 +00:00
order . write ( { ' procurement_group_id ' : group_id } )
2013-12-18 10:17:17 +00:00
2013-07-10 15:02:07 +00:00
for line in order . order_line :
2015-03-27 15:04:09 +00:00
if line . state == ' cancel ' :
continue
2014-01-23 16:06:38 +00:00
#Try to fix exception procurement (possible when after a shipping exception the user choose to recreate)
2013-12-18 10:17:17 +00:00
if line . procurement_ids :
2014-02-07 11:13:49 +00:00
#first check them to see if they are in exception or not (one of the related moves is cancelled)
2014-01-23 16:06:38 +00:00
procurement_obj . check ( cr , uid , [ x . id for x in line . procurement_ids if x . state not in [ ' cancel ' , ' done ' ] ] )
2014-11-17 08:44:10 +00:00
line . refresh ( )
2014-02-07 11:13:49 +00:00
#run again procurement that are in exception in order to trigger another move
2015-03-06 10:54:09 +00:00
except_proc_ids = [ x . id for x in line . procurement_ids if x . state in ( ' exception ' , ' cancel ' ) ]
procurement_obj . reset_to_confirmed ( cr , uid , except_proc_ids , context = context )
proc_ids + = except_proc_ids
2014-01-23 16:06:38 +00:00
elif sale_line_obj . need_procurement ( cr , uid , [ line . id ] , context = context ) :
2013-09-17 06:43:59 +00:00
if ( line . state == ' done ' ) or not line . product_id :
continue
2014-11-05 10:45:04 +00:00
vals = self . _prepare_order_line_procurement ( cr , uid , order , line , group_id = order . procurement_group_id . id , context = context )
2015-02-27 17:43:14 +00:00
ctx = context . copy ( )
ctx [ ' procurement_autorun_defer ' ] = True
proc_id = procurement_obj . create ( cr , uid , vals , context = ctx )
2013-09-10 15:48:22 +00:00
proc_ids . append ( proc_id )
2013-09-17 06:43:59 +00:00
#Confirm procurement order such that rules will be applied on it
2014-02-07 11:13:49 +00:00
#note that the workflow normally ensure proc_ids isn't an empty list
procurement_obj . run ( cr , uid , proc_ids , context = context )
#if shipping was in exception and the user choose to recreate the delivery order, write the new status of SO
if order . state == ' shipping_except ' :
val = { ' state ' : ' progress ' , ' shipped ' : False }
2013-07-10 15:02:07 +00:00
if ( order . order_policy == ' manual ' ) :
2013-12-18 10:17:17 +00:00
for line in order . order_line :
2013-07-10 15:02:07 +00:00
if ( not line . invoiced ) and ( line . state not in ( ' cancel ' , ' draft ' ) ) :
val [ ' state ' ] = ' manual '
break
2014-02-07 11:13:49 +00:00
order . write ( val )
2013-07-10 15:02:07 +00:00
return True
2014-07-15 16:38:06 +00:00
2013-09-10 15:48:22 +00:00
2014-05-05 08:57:55 +00:00
def onchange_fiscal_position ( self , cr , uid , ids , fiscal_position , order_lines , context = None ) :
2014-05-06 08:05:25 +00:00
''' Update taxes of order lines for each line where a product is defined
: param list ids : not used
: param int fiscal_position : sale order fiscal position
: param list order_lines : command list for one2many write method
'''
2014-05-05 08:57:55 +00:00
order_line = [ ]
fiscal_obj = self . pool . get ( ' account.fiscal.position ' )
product_obj = self . pool . get ( ' product.product ' )
line_obj = self . pool . get ( ' sale.order.line ' )
fpos = False
if fiscal_position :
fpos = fiscal_obj . browse ( cr , uid , fiscal_position , context = context )
for line in order_lines :
# create (0, 0, { fields })
# update (1, ID, { fields })
if line [ 0 ] in [ 0 , 1 ] :
2014-05-06 08:05:25 +00:00
prod = None
2014-05-05 08:57:55 +00:00
if line [ 2 ] . get ( ' product_id ' ) :
2014-05-06 08:05:25 +00:00
prod = product_obj . browse ( cr , uid , line [ 2 ] [ ' product_id ' ] , context = context )
2014-05-05 08:57:55 +00:00
elif line [ 1 ] :
2014-05-06 08:05:25 +00:00
prod = line_obj . browse ( cr , uid , line [ 1 ] , context = context ) . product_id
if prod and prod . taxes_id :
2015-12-04 00:50:18 +00:00
line [ 2 ] [ ' tax_id ' ] = [ [ 6 , 0 , fiscal_obj . map_tax ( cr , uid , fpos , prod . taxes_id , context = context ) ] ]
2014-05-05 08:57:55 +00:00
order_line . append ( line )
# link (4, ID)
# link all (6, 0, IDS)
elif line [ 0 ] in [ 4 , 6 ] :
line_ids = line [ 0 ] == 4 and [ line [ 1 ] ] or line [ 2 ]
for line_id in line_ids :
2014-05-06 08:05:25 +00:00
prod = line_obj . browse ( cr , uid , line_id , context = context ) . product_id
if prod and prod . taxes_id :
2015-12-04 00:50:18 +00:00
order_line . append ( [ 1 , line_id , { ' tax_id ' : [ [ 6 , 0 , fiscal_obj . map_tax ( cr , uid , fpos , prod . taxes_id , context = context ) ] ] } ] )
2014-05-06 08:05:25 +00:00
else :
order_line . append ( [ 4 , line_id ] )
2014-05-05 08:57:55 +00:00
else :
order_line . append ( line )
2015-04-10 09:41:19 +00:00
return { ' value ' : { ' order_line ' : order_line , ' amount_untaxed ' : False , ' amount_tax ' : False , ' amount_total ' : False } }
2012-09-09 10:11:56 +00:00
2014-07-15 16:38:06 +00:00
def test_procurements_done ( self , cr , uid , ids , context = None ) :
for sale in self . browse ( cr , uid , ids , context = context ) :
for line in sale . order_line :
2015-03-27 15:04:09 +00:00
if line . state == ' cancel ' :
continue
2014-07-15 16:38:06 +00:00
if not all ( [ x . state == ' done ' for x in line . procurement_ids ] ) :
return False
return True
def test_procurements_except ( self , cr , uid , ids , context = None ) :
for sale in self . browse ( cr , uid , ids , context = context ) :
for line in sale . order_line :
2015-03-27 15:04:09 +00:00
if line . state == ' cancel ' :
continue
2014-07-15 16:38:06 +00:00
if any ( [ x . state == ' cancel ' for x in line . procurement_ids ] ) :
return True
return False
2006-12-07 13:41:40 +00:00
2007-09-11 14:14:15 +00:00
# TODO add a field price_unit_uos
# - update it on change product and unit price
# - use it in report if there is a uos
2006-12-07 13:41:40 +00:00
class sale_order_line ( osv . osv ) :
2010-08-18 10:49:46 +00:00
2013-09-17 06:43:59 +00:00
def need_procurement ( self , cr , uid , ids , context = None ) :
#when sale is installed only, there is no need to create procurements, that's only
2014-05-21 10:27:24 +00:00
#further installed modules (sale_service, sale_stock) that will change this.
2014-08-11 13:06:03 +00:00
prod_obj = self . pool . get ( ' product.product ' )
for line in self . browse ( cr , uid , ids , context = context ) :
if prod_obj . need_procurement ( cr , uid , [ line . product_id . id ] , context = context ) :
return True
2014-08-26 15:28:40 +00:00
return False
2013-09-17 06:43:59 +00:00
2014-07-31 07:38:03 +00:00
def _calc_line_base_price ( self , cr , uid , line , context = None ) :
return line . price_unit * ( 1 - ( line . discount or 0.0 ) / 100.0 )
def _calc_line_quantity ( self , cr , uid , line , context = None ) :
return line . product_uom_qty
2010-04-13 10:35:39 +00:00
def _amount_line ( self , cr , uid , ids , field_name , arg , context = None ) :
2010-06-05 21:39:55 +00:00
tax_obj = self . pool . get ( ' account.tax ' )
2009-08-26 13:41:53 +00:00
cur_obj = self . pool . get ( ' res.currency ' )
2010-06-22 07:15:50 +00:00
res = { }
2010-12-13 06:43:09 +00:00
if context is None :
context = { }
2010-04-13 10:35:39 +00:00
for line in self . browse ( cr , uid , ids , context = context ) :
2014-07-31 07:38:03 +00:00
price = self . _calc_line_base_price ( cr , uid , line , context = context )
qty = self . _calc_line_quantity ( cr , uid , line , context = context )
taxes = tax_obj . compute_all ( cr , uid , line . tax_id , price , 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
2011-09-07 05:07:01 +00:00
def _get_uom_id ( self , cr , uid , * args ) :
2011-09-15 08:54:54 +00:00
try :
proxy = self . pool . get ( ' ir.model.data ' )
result = proxy . get_object_reference ( cr , uid , ' product ' , ' product_uom_unit ' )
return result [ 1 ]
except Exception , ex :
return False
2011-11-04 04:50:18 +00:00
2012-11-29 16:26:29 +00:00
def _fnct_line_invoiced ( self , cr , uid , ids , field_name , args , context = None ) :
res = dict . fromkeys ( ids , False )
for this in self . browse ( cr , uid , ids , context = context ) :
res [ this . id ] = this . invoice_lines and \
all ( iline . invoice_id . state != ' cancel ' for iline in this . invoice_lines )
return res
def _order_lines_from_invoice ( self , cr , uid , ids , context = None ) :
# direct access to the m2m table is the less convoluted way to achieve this (and is ok ACL-wise)
cr . execute ( """ SELECT DISTINCT sol.id FROM sale_order_invoice_rel rel JOIN
sale_order_line sol ON ( sol . order_id = rel . order_id )
WHERE rel . invoice_id = ANY ( % s ) """ , (list(ids),))
return [ i [ 0 ] for i in cr . fetchall ( ) ]
2014-10-24 12:16:16 +00:00
def _get_price_reduce ( self , cr , uid , ids , field_name , arg , context = None ) :
res = dict . fromkeys ( ids , 0.0 )
for line in self . browse ( cr , uid , ids , context = context ) :
res [ line . id ] = line . price_subtotal / line . product_uom_qty
return res
2013-07-10 15:02:07 +00:00
2008-07-22 15:11:28 +00:00
_name = ' sale.order.line '
2010-12-13 06:07:23 +00:00
_description = ' Sales Order Line '
2008-07-22 15:11:28 +00:00
_columns = {
2010-02-16 14:27:23 +00:00
' order_id ' : fields . many2one ( ' sale.order ' , ' Order Reference ' , required = True , ondelete = ' cascade ' , select = True , readonly = True , states = { ' draft ' : [ ( ' readonly ' , False ) ] } ) ,
2013-05-22 13:08:42 +00:00
' name ' : fields . text ( ' Description ' , required = True , readonly = True , states = { ' draft ' : [ ( ' readonly ' , False ) ] } ) ,
2010-12-13 06:07:23 +00:00
' sequence ' : fields . integer ( ' Sequence ' , help = " Gives the sequence order when displaying a list of sales order lines. " ) ,
2014-05-27 07:42:52 +00:00
' product_id ' : fields . many2one ( ' product.product ' , ' Product ' , domain = [ ( ' sale_ok ' , ' = ' , True ) ] , change_default = True , readonly = True , states = { ' draft ' : [ ( ' readonly ' , False ) ] } , ondelete = ' restrict ' ) ,
2014-07-06 14:44:26 +00:00
' invoice_lines ' : fields . many2many ( ' account.invoice.line ' , ' sale_order_line_invoice_rel ' , ' order_line_id ' , ' invoice_id ' , ' Invoice Lines ' , readonly = True , copy = False ) ,
2012-11-29 16:26:29 +00:00
' invoiced ' : fields . function ( _fnct_line_invoiced , string = ' Invoiced ' , type = ' boolean ' ,
2013-03-12 12:12:18 +00:00
store = {
' account.invoice ' : ( _order_lines_from_invoice , [ ' state ' ] , 10 ) ,
2013-08-04 11:53:25 +00:00
' sale.order.line ' : ( lambda self , cr , uid , ids , ctx = None : ids , [ ' invoice_lines ' ] , 10 )
} ) ,
2012-07-11 13:06:15 +00:00
' price_unit ' : fields . float ( ' Unit Price ' , required = True , digits_compute = dp . get_precision ( ' Product Price ' ) , readonly = True , states = { ' draft ' : [ ( ' readonly ' , False ) ] } ) ,
2012-07-10 12:24:31 +00:00
' price_subtotal ' : fields . function ( _amount_line , string = ' Subtotal ' , digits_compute = dp . get_precision ( ' Account ' ) ) ,
2014-10-24 12:16:16 +00:00
' price_reduce ' : fields . function ( _get_price_reduce , type = ' float ' , string = ' Price Reduce ' , digits_compute = dp . get_precision ( ' Product Price ' ) ) ,
2010-10-21 09:30:04 +00:00
' tax_id ' : fields . many2many ( ' account.tax ' , ' sale_order_tax ' , ' order_line_id ' , ' tax_id ' , ' Taxes ' , readonly = True , states = { ' draft ' : [ ( ' readonly ' , False ) ] } ) ,
2012-10-31 11:03:26 +00:00
' address_allotment_id ' : fields . many2one ( ' res.partner ' , ' Allotment Partner ' , help = " A partner to whom the particular product needs to be allotted. " ) ,
2012-05-23 14:42:45 +00:00
' product_uom_qty ' : fields . float ( ' Quantity ' , digits_compute = dp . get_precision ( ' Product UoS ' ) , required = True , readonly = True , states = { ' draft ' : [ ( ' readonly ' , False ) ] } ) ,
2010-10-21 09:30:04 +00:00
' product_uom ' : fields . many2one ( ' product.uom ' , ' Unit of Measure ' , required = True , readonly = True , states = { ' draft ' : [ ( ' readonly ' , False ) ] } ) ,
2011-10-04 09:44:22 +00:00
' product_uos_qty ' : fields . float ( ' Quantity (UoS) ' , digits_compute = dp . get_precision ( ' Product UoS ' ) , readonly = True , states = { ' draft ' : [ ( ' readonly ' , False ) ] } ) ,
2009-01-27 11:15:46 +00:00
' product_uos ' : fields . many2one ( ' product.uom ' , ' Product UoS ' ) ,
2012-09-21 08:13:39 +00:00
' discount ' : fields . float ( ' Discount ( % ) ' , digits_compute = dp . get_precision ( ' Discount ' ) , readonly = True , states = { ' draft ' : [ ( ' readonly ' , False ) ] } ) ,
2015-12-04 18:02:36 +00:00
' th_weight ' : fields . float ( ' Weight ' , readonly = True , states = { ' draft ' : [ ( ' readonly ' , False ) ] } , digits_compute = dp . get_precision ( ' Stock Weight ' ) ) ,
2014-07-06 14:44:26 +00:00
' state ' : fields . selection (
[ ( ' cancel ' , ' Cancelled ' ) , ( ' draft ' , ' Draft ' ) , ( ' confirmed ' , ' Confirmed ' ) , ( ' exception ' , ' Exception ' ) , ( ' done ' , ' Done ' ) ] ,
' Status ' , required = True , readonly = True , copy = False ,
2012-10-12 11:42:58 +00:00
help = ' * The \' Draft \' status is set when the related sales order in draft status. \
\n * The \' Confirmed \' status is set when the related sales order is confirmed. \
\n * The \' Exception \' status is set when the related sales order is set as exception. \
\n * The \' Done \' status is set when the sales order line has been picked. \
\n * The \' Cancelled \' status is set when a user cancel the sales order related. ' ) ,
2010-12-09 12:52:30 +00:00
' order_partner_id ' : fields . related ( ' order_id ' , ' partner_id ' , type = ' many2one ' , relation = ' res.partner ' , store = True , string = ' Customer ' ) ,
2012-05-04 12:13:26 +00:00
' salesman_id ' : fields . related ( ' order_id ' , ' user_id ' , type = ' many2one ' , relation = ' res.users ' , store = True , string = ' Salesperson ' ) ,
2012-09-21 08:13:39 +00:00
' company_id ' : fields . related ( ' order_id ' , ' company_id ' , type = ' many2one ' , relation = ' res.company ' , string = ' Company ' , store = True , readonly = True ) ,
2013-07-10 15:02:07 +00:00
' delay ' : fields . float ( ' Delivery Lead Time ' , required = True , help = " Number of days between the order confirmation and the shipping of the products to the customer " , readonly = True , states = { ' draft ' : [ ( ' readonly ' , False ) ] } ) ,
2013-08-04 13:40:14 +00:00
' procurement_ids ' : fields . one2many ( ' procurement.order ' , ' sale_line_id ' , ' Procurements ' ) ,
2008-07-22 15:11:28 +00:00
}
2013-03-08 17:06:08 +00:00
_order = ' order_id desc, sequence, id '
2008-07-22 15:11:28 +00:00
_defaults = {
2011-09-07 05:07:01 +00:00
' product_uom ' : _get_uom_id ,
2010-06-22 07:15:50 +00:00
' discount ' : 0.0 ,
' product_uom_qty ' : 1 ,
' product_uos_qty ' : 1 ,
2010-10-21 09:30:04 +00:00
' sequence ' : 10 ,
2010-06-22 07:15:50 +00:00
' state ' : ' draft ' ,
2010-12-31 06:45:06 +00:00
' price_unit ' : 0.0 ,
2013-07-10 15:02:07 +00:00
' delay ' : 0.0 ,
2008-07-22 15:11:28 +00:00
}
2009-08-26 13:41:53 +00:00
2014-07-15 16:38:06 +00:00
2012-09-21 09:35:20 +00:00
def _get_line_qty ( self , cr , uid , line , context = None ) :
2013-08-03 19:03:59 +00:00
if line . product_uos :
return line . product_uos_qty or 0.0
2012-10-04 13:32:41 +00:00
return line . product_uom_qty
2012-09-21 08:13:39 +00:00
2012-09-21 09:35:20 +00:00
def _get_line_uom ( self , cr , uid , line , context = None ) :
2013-08-03 19:03:59 +00:00
if line . product_uos :
return line . product_uos . id
2012-10-04 13:32:41 +00:00
return line . product_uom . id
2012-09-21 08:13:39 +00:00
2012-02-01 18:28:09 +00:00
def _prepare_order_line_invoice_line ( self , cr , uid , line , account_id = False , context = None ) :
2012-02-15 17:09:11 +00:00
""" Prepare the dict of values to create the new invoice line for a
2012-12-21 16:48:08 +00:00
sales order line . This method may be overridden to implement custom
2012-02-15 17:09:11 +00:00
invoice generation ( making sure to call super ( ) to establish
a clean extension chain ) .
: param browse_record line : sale . order . line record to invoice
: param int account_id : optional ID of a G / L account to force
( this is used for returning products including service )
: return : dict of values to create ( ) the invoice line
2012-01-31 10:53:22 +00:00
"""
2012-09-09 10:11:56 +00:00
res = { }
2012-01-27 04:54:58 +00:00
if not line . invoiced :
if not account_id :
2008-07-22 15:11:28 +00:00
if line . product_id :
2012-07-27 01:52:02 +00:00
account_id = line . product_id . property_account_income . id
2012-01-27 04:54:58 +00:00
if not account_id :
account_id = line . product_id . categ_id . property_account_income_categ . id
if not account_id :
2012-08-07 11:31:37 +00:00
raise osv . except_osv ( _ ( ' Error! ' ) ,
2012-07-25 11:07:02 +00:00
_ ( ' Please define income account for this product: " %s " (id: %d ). ' ) % \
2012-01-27 04:54:58 +00:00
( line . product_id . name , line . product_id . id , ) )
2008-07-22 15:11:28 +00:00
else :
2010-10-20 11:06:17 +00:00
prop = self . pool . get ( ' ir.property ' ) . get ( cr , uid ,
2008-07-22 15:11:28 +00:00
' property_account_income_categ ' , ' product.category ' ,
2010-10-20 11:06:17 +00:00
context = context )
2012-01-27 04:54:58 +00:00
account_id = prop and prop . id or False
2012-09-21 09:35:20 +00:00
uosqty = self . _get_line_qty ( cr , uid , line , context = context )
uos_id = self . _get_line_uom ( cr , uid , line , context = context )
2012-01-27 04:54:58 +00:00
pu = 0.0
if uosqty :
pu = round ( line . price_unit * line . product_uom_qty / uosqty ,
2012-07-11 13:06:15 +00:00
self . pool . get ( ' decimal.precision ' ) . precision_get ( cr , uid , ' Product Price ' ) )
2012-01-27 04:54:58 +00:00
fpos = line . order_id . fiscal_position or False
account_id = self . pool . get ( ' account.fiscal.position ' ) . map_account ( cr , uid , fpos , account_id )
if not account_id :
2012-08-07 11:31:37 +00:00
raise osv . except_osv ( _ ( ' Error! ' ) ,
2012-07-31 12:42:10 +00:00
_ ( ' There is no Fiscal Position defined or Income category account defined for default properties of Product categories. ' ) )
2012-09-09 10:11:56 +00:00
res = {
2012-01-27 04:54:58 +00:00
' name ' : line . name ,
2012-10-08 12:17:45 +00:00
' sequence ' : line . sequence ,
2012-01-27 04:54:58 +00:00
' origin ' : line . order_id . name ,
' account_id ' : account_id ,
' price_unit ' : pu ,
' quantity ' : uosqty ,
' discount ' : line . discount ,
' uos_id ' : uos_id ,
' product_id ' : line . product_id . id or False ,
' invoice_line_tax_id ' : [ ( 6 , 0 , [ x . id for x in line . tax_id ] ) ] ,
' account_analytic_id ' : line . order_id . project_id and line . order_id . project_id . id or False ,
}
2012-01-31 10:53:22 +00:00
2012-09-09 10:11:56 +00:00
return res
2012-01-27 04:54:58 +00:00
def invoice_line_create ( self , cr , uid , ids , context = None ) :
if context is None :
context = { }
create_ids = [ ]
2012-01-31 10:53:22 +00:00
sales = set ( )
2012-01-27 04:54:58 +00:00
for line in self . browse ( cr , uid , ids , context = context ) :
2012-02-01 18:28:09 +00:00
vals = self . _prepare_order_line_invoice_line ( cr , uid , line , False , context )
2012-01-27 04:54:58 +00:00
if vals :
inv_id = self . pool . get ( ' account.invoice.line ' ) . create ( cr , uid , vals , context = context )
2013-03-12 12:12:18 +00:00
self . write ( cr , uid , [ line . id ] , { ' invoice_lines ' : [ ( 4 , inv_id ) ] } , context = context )
2012-01-31 10:53:22 +00:00
sales . add ( line . order_id . id )
2008-07-22 15:11:28 +00:00
create_ids . append ( inv_id )
2009-05-20 13:47:11 +00:00
# Trigger workflow events
2012-01-31 10:53:22 +00:00
for sale_id in sales :
2013-11-21 12:06:11 +00:00
workflow . trg_write ( uid , ' sale.order ' , sale_id , cr )
2008-07-22 15:11:28 +00:00
return create_ids
2010-04-13 10:35:39 +00:00
def button_cancel ( self , cr , uid , ids , context = None ) :
2015-03-27 15:04:09 +00:00
lines = self . browse ( cr , uid , ids , context = context )
for line in lines :
2008-12-14 16:46:57 +00:00
if line . invoiced :
2012-12-21 16:48:08 +00:00
raise osv . except_osv ( _ ( ' Invalid Action! ' ) , _ ( ' You cannot cancel a sales order line that has already been invoiced. ' ) )
2015-03-27 15:04:09 +00:00
procurement_obj = self . pool [ ' procurement.order ' ]
procurement_obj . cancel ( cr , uid , sum ( [ l . procurement_ids . ids for l in lines ] , [ ] ) , context = context )
2009-08-26 13:41:53 +00:00
return self . write ( cr , uid , ids , { ' state ' : ' cancel ' } )
2008-12-14 16:46:57 +00:00
2010-04-13 10:35:39 +00:00
def button_confirm ( self , cr , uid , ids , context = None ) :
2009-08-26 13:41:53 +00:00
return self . write ( cr , uid , ids , { ' state ' : ' confirmed ' } )
2008-07-22 15:11:28 +00:00
2010-04-13 10:35:39 +00:00
def button_done ( self , cr , uid , ids , context = None ) :
2009-08-26 13:41:53 +00:00
res = self . write ( cr , uid , ids , { ' state ' : ' done ' } )
2010-04-13 10:35:39 +00:00
for line in self . browse ( cr , uid , ids , context = context ) :
2013-11-21 12:06:11 +00:00
workflow . trg_write ( uid , ' sale.order ' , line . order_id . id , cr )
2008-07-22 15:11:28 +00:00
return res
def uos_change ( self , cr , uid , ids , product_uos , product_uos_qty = 0 , product_id = None ) :
product_obj = self . pool . get ( ' product.product ' )
if not product_id :
return { ' value ' : { ' product_uom ' : product_uos ,
2009-08-26 13:41:53 +00:00
' product_uom_qty ' : product_uos_qty } , ' domain ' : { } }
2008-07-22 15:11:28 +00:00
product = product_obj . browse ( cr , uid , product_id )
value = {
2009-08-26 13:41:53 +00:00
' product_uom ' : product . uom_id . id ,
2008-07-22 15:11:28 +00:00
}
# FIXME must depend on uos/uom of the product and not only of the coeff.
try :
value . update ( {
2009-08-26 13:41:53 +00:00
' product_uom_qty ' : product_uos_qty / product . uos_coeff ,
' th_weight ' : product_uos_qty / product . uos_coeff * product . weight
2008-07-22 15:11:28 +00:00
} )
except ZeroDivisionError :
pass
2009-08-26 13:41:53 +00:00
return { ' value ' : value }
2008-07-22 15:11:28 +00:00
2013-12-06 13:51:11 +00:00
def create ( self , cr , uid , values , context = None ) :
2015-09-07 08:37:26 +00:00
if values . get ( ' order_id ' ) and values . get ( ' product_id ' ) and any ( f not in values for f in [ ' name ' , ' price_unit ' , ' product_uom_qty ' , ' product_uom ' ] ) :
2013-12-06 13:51:11 +00:00
order = self . pool [ ' sale.order ' ] . read ( cr , uid , values [ ' order_id ' ] , [ ' pricelist_id ' , ' partner_id ' , ' date_order ' , ' fiscal_position ' ] , context = context )
defaults = self . product_id_change ( cr , uid , [ ] , order [ ' pricelist_id ' ] [ 0 ] , values [ ' product_id ' ] ,
qty = float ( values . get ( ' product_uom_qty ' , False ) ) ,
uom = values . get ( ' product_uom ' , False ) ,
qty_uos = float ( values . get ( ' product_uos_qty ' , False ) ) ,
uos = values . get ( ' product_uos ' , False ) ,
name = values . get ( ' name ' , False ) ,
partner_id = order [ ' partner_id ' ] [ 0 ] ,
date_order = order [ ' date_order ' ] ,
fiscal_position = order [ ' fiscal_position ' ] [ 0 ] if order [ ' fiscal_position ' ] else False ,
flag = False , # Force name update
2015-08-26 11:37:31 +00:00
context = dict ( context or { } , company_id = values . get ( ' company_id ' ) )
2013-12-06 13:51:11 +00:00
) [ ' value ' ]
2014-08-27 09:48:40 +00:00
if defaults . get ( ' tax_id ' ) :
defaults [ ' tax_id ' ] = [ [ 6 , 0 , defaults [ ' tax_id ' ] ] ]
2013-12-06 13:51:11 +00:00
values = dict ( defaults , * * values )
return super ( sale_order_line , self ) . create ( cr , uid , values , context = context )
2008-07-22 15:11:28 +00:00
def product_id_change ( self , cr , uid , ids , pricelist , product , qty = 0 ,
uom = False , qty_uos = 0 , uos = False , name = ' ' , partner_id = False ,
2011-07-01 15:12:37 +00:00
lang = False , update_tax = True , date_order = False , packaging = False , fiscal_position = False , flag = False , context = None ) :
context = context or { }
2013-11-06 07:55:22 +00:00
lang = lang or context . get ( ' lang ' , False )
2014-04-28 09:36:09 +00:00
if not partner_id :
2013-04-29 07:15:57 +00:00
raise osv . except_osv ( _ ( ' No Customer Defined! ' ) , _ ( ' Before choosing a product, \n select a customer in the sales form. ' ) )
2014-01-15 13:01:23 +00:00
warning = False
2008-07-22 15:11:28 +00:00
product_uom_obj = self . pool . get ( ' product.uom ' )
partner_obj = self . pool . get ( ' res.partner ' )
product_obj = self . pool . get ( ' product.product ' )
2014-04-28 09:36:09 +00:00
partner = partner_obj . browse ( cr , uid , partner_id )
lang = partner . lang
2015-07-03 12:09:31 +00:00
context_partner = context . copy ( )
context_partner . update ( { ' lang ' : lang , ' partner_id ' : partner_id } )
2008-07-22 15:11:28 +00:00
if not product :
2012-10-02 10:29:15 +00:00
return { ' value ' : { ' th_weight ' : 0 ,
2008-07-22 15:11:28 +00:00
' product_uos_qty ' : qty } , ' domain ' : { ' product_uom ' : [ ] ,
2008-09-16 08:51:19 +00:00
' product_uos ' : [ ] } }
2008-07-22 15:11:28 +00:00
if not date_order :
2011-10-11 14:19:21 +00:00
date_order = time . strftime ( DEFAULT_SERVER_DATE_FORMAT )
2008-09-07 23:24:39 +00:00
2012-09-09 10:11:56 +00:00
result = { }
2013-02-07 08:41:29 +00:00
warning_msgs = ' '
2012-11-01 12:57:56 +00:00
product_obj = product_obj . browse ( cr , uid , product , context = context_partner )
2008-09-29 08:49:12 +00:00
2010-06-26 15:15:23 +00:00
uom2 = False
2008-07-22 15:11:28 +00:00
if uom :
uom2 = product_uom_obj . browse ( cr , uid , uom )
2009-08-26 13:41:53 +00:00
if product_obj . uom_id . category_id . id != uom2 . category_id . id :
2008-07-22 15:11:28 +00:00
uom = False
if uos :
2008-09-07 23:24:39 +00:00
if product_obj . uos_id :
2008-07-22 15:11:28 +00:00
uos2 = product_uom_obj . browse ( cr , uid , uos )
2009-08-26 13:41:53 +00:00
if product_obj . uos_id . category_id . id != uos2 . category_id . id :
2008-07-22 15:11:28 +00:00
uos = False
else :
2010-05-25 10:38:59 +00:00
uos = False
2014-04-28 09:36:09 +00:00
fpos = False
if not fiscal_position :
fpos = partner . property_account_position or False
else :
fpos = self . pool . get ( ' account.fiscal.position ' ) . browse ( cr , uid , fiscal_position )
2015-12-02 13:47:33 +00:00
if uid == SUPERUSER_ID and context . get ( ' company_id ' ) :
taxes = product_obj . taxes_id . filtered ( lambda r : r . company_id . id == context [ ' company_id ' ] )
else :
taxes = product_obj . taxes_id
2015-12-04 00:50:18 +00:00
result [ ' tax_id ' ] = self . pool . get ( ' account.fiscal.position ' ) . map_tax ( cr , uid , fpos , taxes , context = context )
2010-02-19 12:55:26 +00:00
2009-08-26 13:41:53 +00:00
if not flag :
2011-09-17 11:40:55 +00:00
result [ ' name ' ] = self . pool . get ( ' product.product ' ) . name_get ( cr , uid , [ product_obj . id ] , context = context_partner ) [ 0 ] [ 1 ]
2012-07-13 22:51:31 +00:00
if product_obj . description_sale :
result [ ' name ' ] + = ' \n ' + product_obj . description_sale
2008-07-22 15:11:28 +00:00
domain = { }
2009-02-04 22:18:53 +00:00
if ( not uom ) and ( not uos ) :
2008-09-07 23:24:39 +00:00
result [ ' product_uom ' ] = product_obj . uom_id . id
if product_obj . uos_id :
result [ ' product_uos ' ] = product_obj . uos_id . id
result [ ' product_uos_qty ' ] = qty * product_obj . uos_coeff
uos_category_id = product_obj . uos_id . category_id . id
2008-07-22 15:11:28 +00:00
else :
result [ ' product_uos ' ] = False
result [ ' product_uos_qty ' ] = qty
uos_category_id = False
2008-09-07 23:24:39 +00:00
result [ ' th_weight ' ] = qty * product_obj . weight
2008-07-22 15:11:28 +00:00
domain = { ' product_uom ' :
2008-09-07 23:24:39 +00:00
[ ( ' category_id ' , ' = ' , product_obj . uom_id . category_id . id ) ] ,
2008-07-22 15:11:28 +00:00
' product_uos ' :
[ ( ' category_id ' , ' = ' , uos_category_id ) ] }
2010-05-12 13:31:33 +00:00
elif uos and not uom : # only happens if uom is False
2009-02-04 22:18:53 +00:00
result [ ' product_uom ' ] = product_obj . uom_id and product_obj . uom_id . id
result [ ' product_uom_qty ' ] = qty_uos / product_obj . uos_coeff
result [ ' th_weight ' ] = result [ ' product_uom_qty ' ] * product_obj . weight
2008-07-22 15:11:28 +00:00
elif uom : # whether uos is set or not
2008-09-07 23:24:39 +00:00
default_uom = product_obj . uom_id and product_obj . uom_id . id
2008-07-22 15:11:28 +00:00
q = product_uom_obj . _compute_qty ( cr , uid , uom , qty , default_uom )
2008-09-07 23:24:39 +00:00
if product_obj . uos_id :
result [ ' product_uos ' ] = product_obj . uos_id . id
2009-02-04 22:18:53 +00:00
result [ ' product_uos_qty ' ] = qty * product_obj . uos_coeff
2008-07-22 15:11:28 +00:00
else :
result [ ' product_uos ' ] = False
2009-02-04 22:18:53 +00:00
result [ ' product_uos_qty ' ] = qty
result [ ' th_weight ' ] = q * product_obj . weight # Round the quantity up
2008-09-09 13:57:16 +00:00
2010-06-26 15:15:23 +00:00
if not uom2 :
uom2 = product_obj . uom_id
2008-09-09 13:57:16 +00:00
# get unit price
2011-07-06 09:35:40 +00:00
2008-09-09 13:57:16 +00:00
if not pricelist :
2011-05-02 07:12:07 +00:00
warn_msg = _ ( ' You have to select a pricelist or a customer in the sales form ! \n '
' Please set one before choosing a product. ' )
2011-05-04 09:49:34 +00:00
warning_msgs + = _ ( " No Pricelist ! : " ) + warn_msg + " \n \n "
2008-09-09 13:57:16 +00:00
else :
2015-07-16 17:43:42 +00:00
ctx = dict (
context ,
uom = uom or result . get ( ' product_uom ' ) ,
date = date_order ,
)
2008-09-09 13:57:16 +00:00
price = self . pool . get ( ' product.pricelist ' ) . price_get ( cr , uid , [ pricelist ] ,
2015-07-16 17:43:42 +00:00
product , qty or 1.0 , partner_id , ctx ) [ pricelist ]
2008-09-09 13:57:16 +00:00
if price is False :
2012-07-25 11:07:02 +00:00
warn_msg = _ ( " Cannot find a pricelist line matching this product and quantity. \n "
2011-05-02 07:12:07 +00:00
" You have to change either the product, the quantity or the pricelist. " )
2011-05-04 09:49:34 +00:00
warning_msgs + = _ ( " No valid pricelist line found ! : " ) + warn_msg + " \n \n "
2008-09-09 13:57:16 +00:00
else :
2015-12-02 13:47:33 +00:00
price = self . pool [ ' account.tax ' ] . _fix_tax_included_price ( cr , uid , price , taxes , result [ ' tax_id ' ] )
2008-09-29 08:49:12 +00:00
result . update ( { ' price_unit ' : price } )
2015-07-17 06:55:42 +00:00
if context . get ( ' uom_qty_change ' , False ) :
2015-08-24 16:05:26 +00:00
values = { ' price_unit ' : price }
if result . get ( ' product_uos_qty ' ) :
values [ ' product_uos_qty ' ] = result [ ' product_uos_qty ' ]
return { ' value ' : values , ' domain ' : { } , ' warning ' : False }
2011-05-02 07:12:07 +00:00
if warning_msgs :
warning = {
2012-08-07 11:06:16 +00:00
' title ' : _ ( ' Configuration Error! ' ) ,
2011-07-06 09:35:40 +00:00
' message ' : warning_msgs
2011-05-02 07:12:07 +00:00
}
2009-08-26 13:41:53 +00:00
return { ' value ' : result , ' domain ' : domain , ' warning ' : warning }
2008-07-22 15:11:28 +00:00
def product_uom_change ( self , cursor , user , ids , pricelist , product , qty = 0 ,
uom = False , qty_uos = 0 , uos = False , name = ' ' , partner_id = False ,
2011-07-01 15:13:48 +00:00
lang = False , update_tax = True , date_order = False , context = None ) :
context = context or { }
lang = lang or ( ' lang ' in context and context [ ' lang ' ] )
2012-08-08 08:38:35 +00:00
if not uom :
return { ' value ' : { ' price_unit ' : 0.0 , ' product_uom ' : uom or False } }
return self . product_id_change ( cursor , user , ids , pricelist , product ,
2010-05-13 13:21:17 +00:00
qty = qty , uom = uom , qty_uos = qty_uos , uos = uos , name = name ,
2008-07-22 15:11:28 +00:00
partner_id = partner_id , lang = lang , update_tax = update_tax ,
2015-12-17 15:16:21 +00:00
date_order = date_order , fiscal_position = context . get ( ' fiscal_position ' , False ) , context = context )
2009-08-26 13:41:53 +00:00
2015-12-17 13:26:06 +00:00
def onchange_product_uom ( self , cursor , user , ids , pricelist , product , qty = 0 ,
uom = False , qty_uos = 0 , uos = False , name = ' ' , partner_id = False ,
lang = False , update_tax = True , date_order = False , fiscal_position = False , context = None ) :
2015-12-17 15:16:21 +00:00
ctx = dict ( context or { } , fiscal_position = fiscal_position )
return self . product_uom_change ( cursor , user , ids , pricelist , product ,
2015-12-17 13:26:06 +00:00
qty = qty , uom = uom , qty_uos = qty_uos , uos = uos , name = name ,
partner_id = partner_id , lang = lang , update_tax = update_tax ,
2015-12-17 15:16:21 +00:00
date_order = date_order , context = ctx )
2015-12-17 13:26:06 +00:00
2010-04-13 10:35:39 +00:00
def unlink ( self , cr , uid , ids , context = None ) :
if context is None :
context = { }
2010-12-13 06:07:23 +00:00
""" Allows to delete sales order lines in draft,cancel states """
2009-07-28 12:57:21 +00:00
for rec in self . browse ( cr , uid , ids , context = context ) :
2009-08-26 13:41:53 +00:00
if rec . state not in [ ' draft ' , ' cancel ' ] :
2012-08-07 11:06:16 +00:00
raise osv . except_osv ( _ ( ' Invalid Action! ' ) , _ ( ' Cannot delete a sales order line which is in state \' %s \' . ' ) % ( rec . state , ) )
2009-07-28 12:57:21 +00:00
return super ( sale_order_line , self ) . unlink ( cr , uid , ids , context = context )
2011-11-25 10:52:37 +00:00
2008-06-20 11:49:23 +00:00
2012-12-21 09:59:02 +00:00
class mail_compose_message ( osv . Model ) :
2012-09-20 09:58:25 +00:00
_inherit = ' mail.compose.message '
2012-12-21 09:59:02 +00:00
2012-09-20 09:58:25 +00:00
def send_mail ( self , cr , uid , ids , context = None ) :
context = context or { }
2013-12-11 17:14:29 +00:00
if context . get ( ' default_model ' ) == ' sale.order ' and context . get ( ' default_res_id ' ) and context . get ( ' mark_so_as_sent ' ) :
2012-12-21 09:59:02 +00:00
context = dict ( context , mail_post_autofollow = True )
2014-07-06 14:44:26 +00:00
self . pool . get ( ' sale.order ' ) . signal_workflow ( cr , uid , [ context [ ' default_res_id ' ] ] , ' quotation_sent ' )
2012-09-20 09:58:25 +00:00
return super ( mail_compose_message , self ) . send_mail ( cr , uid , ids , context = context )
2013-03-05 10:48:01 +00:00
2014-05-12 12:56:01 +00:00
2013-02-07 07:01:27 +00:00
class account_invoice ( osv . Model ) :
_inherit = ' account.invoice '
2014-05-12 12:56:01 +00:00
def _get_default_section_id ( self , cr , uid , context = None ) :
""" Gives default section by checking if present in the context """
section_id = self . _resolve_section_id_from_context ( cr , uid , context = context ) or False
if not section_id :
section_id = self . pool . get ( ' res.users ' ) . browse ( cr , uid , uid , context ) . default_section_id . id or False
return section_id
def _resolve_section_id_from_context ( self , cr , uid , context = None ) :
""" Returns ID of section based on the value of ' section_id '
context key , or None if it cannot be resolved to a single
Sales Team .
"""
if context is None :
context = { }
if type ( context . get ( ' default_section_id ' ) ) in ( int , long ) :
return context . get ( ' default_section_id ' )
if isinstance ( context . get ( ' default_section_id ' ) , basestring ) :
section_ids = self . pool . get ( ' crm.case.section ' ) . name_search ( cr , uid , name = context [ ' default_section_id ' ] , context = context )
if len ( section_ids ) == 1 :
return int ( section_ids [ 0 ] [ 0 ] )
return None
2014-03-28 09:58:35 +00:00
_columns = {
' section_id ' : fields . many2one ( ' crm.case.section ' , ' Sales Team ' ) ,
}
2014-05-12 12:56:01 +00:00
_defaults = {
' section_id ' : lambda self , cr , uid , c = None : self . _get_default_section_id ( cr , uid , context = c )
}
2013-02-07 07:01:27 +00:00
def confirm_paid ( self , cr , uid , ids , context = None ) :
2013-03-05 10:48:01 +00:00
sale_order_obj = self . pool . get ( ' sale.order ' )
res = super ( account_invoice , self ) . confirm_paid ( cr , uid , ids , context = context )
so_ids = sale_order_obj . search ( cr , uid , [ ( ' invoice_ids ' , ' in ' , ids ) ] , context = context )
2014-02-19 17:07:46 +00:00
for so_id in so_ids :
sale_order_obj . message_post ( cr , uid , so_id , body = _ ( " Invoice paid " ) , context = context )
2013-02-07 07:01:27 +00:00
return res
2013-03-05 10:48:01 +00:00
2013-04-18 09:00:11 +00:00
def unlink ( self , cr , uid , ids , context = None ) :
2013-04-18 16:36:16 +00:00
""" Overwrite unlink method of account invoice to send a trigger to the sale workflow upon invoice deletion """
2013-04-18 09:00:11 +00:00
invoice_ids = self . search ( cr , uid , [ ( ' id ' , ' in ' , ids ) , ( ' state ' , ' in ' , [ ' draft ' , ' cancel ' ] ) ] , context = context )
2013-04-18 16:36:16 +00:00
#if we can't cancel all invoices, do nothing
if len ( invoice_ids ) == len ( ids ) :
#Cancel invoice(s) first before deleting them so that if any sale order is associated with them
#it will trigger the workflow to put the sale order in an 'invoice exception' state
2013-04-18 09:00:11 +00:00
for id in ids :
2013-11-21 12:06:11 +00:00
workflow . trg_validate ( uid , ' account.invoice ' , id , ' invoice_cancel ' , cr )
2013-04-18 09:00:11 +00:00
return super ( account_invoice , self ) . unlink ( cr , uid , ids , context = context )
2014-05-07 18:29:17 +00:00
2013-08-04 11:53:25 +00:00
class procurement_order ( osv . osv ) :
_inherit = ' procurement.order '
2013-07-22 14:37:47 +00:00
_columns = {
2013-08-04 13:40:14 +00:00
' sale_line_id ' : fields . many2one ( ' sale.order.line ' , string = ' Sale Order Line ' ) ,
2013-08-04 11:53:25 +00:00
}
2014-05-07 18:29:17 +00:00
2014-07-15 16:38:06 +00:00
def write ( self , cr , uid , ids , vals , context = None ) :
if isinstance ( ids , ( int , long ) ) :
ids = [ ids ]
res = super ( procurement_order , self ) . write ( cr , uid , ids , vals , context = context )
from openerp import workflow
if vals . get ( ' state ' ) in [ ' done ' , ' cancel ' , ' exception ' ] :
for proc in self . browse ( cr , uid , ids , context = context ) :
2014-08-13 15:19:40 +00:00
if proc . sale_line_id and proc . sale_line_id . order_id :
2014-07-15 16:38:06 +00:00
order_id = proc . sale_line_id . order_id . id
if self . pool . get ( ' sale.order ' ) . test_procurements_done ( cr , uid , [ order_id ] , context = context ) :
workflow . trg_validate ( uid , ' sale.order ' , order_id , ' ship_end ' , cr )
if self . pool . get ( ' sale.order ' ) . test_procurements_except ( cr , uid , [ order_id ] , context = context ) :
workflow . trg_validate ( uid , ' sale.order ' , order_id , ' ship_except ' , cr )
return res
2014-05-07 18:29:17 +00:00
2014-03-24 13:56:21 +00:00
class product_product ( osv . Model ) :
_inherit = ' product.product '
2014-05-07 18:29:17 +00:00
2014-03-24 13:56:21 +00:00
def _sales_count ( self , cr , uid , ids , field_name , arg , context = None ) :
2015-02-02 10:54:44 +00:00
r = dict . fromkeys ( ids , 0 )
2015-01-28 00:57:53 +00:00
domain = [
2015-07-23 08:09:04 +00:00
( ' state ' , ' in ' , [ ' confirmed ' , ' done ' ] ) ,
2015-01-28 00:57:53 +00:00
( ' product_id ' , ' in ' , ids ) ,
]
2015-07-23 08:09:04 +00:00
for group in self . pool [ ' sale.report ' ] . read_group ( cr , uid , domain , [ ' product_id ' , ' product_uom_qty ' ] , [ ' product_id ' ] , context = context ) :
2015-01-28 00:57:53 +00:00
r [ group [ ' product_id ' ] [ 0 ] ] = group [ ' product_uom_qty ' ]
return r
2014-07-07 19:45:11 +00:00
2015-02-02 09:34:27 +00:00
def action_view_sales ( self , cr , uid , ids , context = None ) :
result = self . pool [ ' ir.model.data ' ] . xmlid_to_res_id ( cr , uid , ' sale.action_order_line_product_tree ' , raise_if_not_found = True )
result = self . pool [ ' ir.actions.act_window ' ] . read ( cr , uid , [ result ] , context = context ) [ 0 ]
result [ ' domain ' ] = " [( ' product_id ' , ' in ' ,[ " + ' , ' . join ( map ( str , ids ) ) + " ])] "
return result
2014-03-24 13:56:21 +00:00
_columns = {
' sales_count ' : fields . function ( _sales_count , string = ' # Sales ' , type = ' integer ' ) ,
}
2014-05-07 18:29:17 +00:00
2014-07-07 19:45:11 +00:00
class product_template ( osv . Model ) :
_inherit = ' product.template '
def _sales_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 . sales_count for p in template . product_variant_ids ] )
return res
def action_view_sales ( self , cr , uid , ids , context = None ) :
act_obj = self . pool . get ( ' ir.actions.act_window ' )
mod_obj = self . pool . get ( ' ir.model.data ' )
product_ids = [ ]
for template in self . browse ( cr , uid , ids , context = context ) :
product_ids + = [ x . id for x in template . product_variant_ids ]
2014-07-16 09:58:22 +00:00
result = mod_obj . xmlid_to_res_id ( cr , uid , ' sale.action_order_line_product_tree ' , raise_if_not_found = True )
result = act_obj . read ( cr , uid , [ result ] , context = context ) [ 0 ]
2014-07-07 19:45:11 +00:00
result [ ' domain ' ] = " [( ' product_id ' , ' in ' ,[ " + ' , ' . join ( map ( str , product_ids ) ) + " ])] "
return result
_columns = {
' sales_count ' : fields . function ( _sales_count , string = ' # Sales ' , type = ' integer ' ) ,
}
2014-05-07 18:29:17 +00:00
2010-09-01 20:29:38 +00:00
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: