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
#
##############################################################################
2010-08-10 05:46:09 +00:00
from datetime import datetime , timedelta
2010-06-22 06:38:37 +00:00
from dateutil . relativedelta import relativedelta
2010-09-29 11:33:49 +00:00
import time
2011-08-03 10:18:38 +00:00
import pooler
2006-12-07 13:41:40 +00:00
from osv import fields , osv
2008-07-08 08:13:12 +00:00
from tools . translate import _
2011-12-27 09:33:15 +00:00
from tools import DEFAULT_SERVER_DATE_FORMAT , DEFAULT_SERVER_DATETIME_FORMAT , float_compare
2010-03-06 20:52:19 +00:00
import decimal_precision as dp
2010-09-27 13:44:03 +00:00
import netsvc
2010-03-06 20:52:19 +00:00
2006-12-07 13:41:40 +00:00
class sale_shop ( osv . osv ) :
2008-07-22 15:11:28 +00:00
_name = " sale.shop "
2010-12-13 06:07:23 +00:00
_description = " Sales Shop "
2008-07-22 15:11:28 +00:00
_columns = {
2009-08-26 13:41:53 +00:00
' name ' : fields . char ( ' Shop Name ' , size = 64 , required = True ) ,
' payment_default_id ' : fields . many2one ( ' account.payment.term ' , ' Default Payment Term ' , required = True ) ,
' warehouse_id ' : fields . many2one ( ' stock.warehouse ' , ' Warehouse ' ) ,
2008-07-22 15:11:28 +00:00
' pricelist_id ' : fields . many2one ( ' product.pricelist ' , ' Pricelist ' ) ,
2010-08-19 12:05:01 +00:00
' project_id ' : fields . many2one ( ' account.analytic.account ' , ' Analytic Account ' , domain = [ ( ' parent_id ' , ' != ' , False ) ] ) ,
2010-09-29 12:45:05 +00:00
' company_id ' : fields . many2one ( ' res.company ' , ' Company ' , required = False ) ,
}
_defaults = {
' company_id ' : lambda s , cr , uid , c : s . pool . get ( ' res.company ' ) . _company_default_get ( cr , uid , ' sale.shop ' , context = c ) ,
2008-07-22 15:11:28 +00:00
}
2011-11-25 10:52:37 +00:00
2010-06-22 06:38:37 +00:00
sale_shop ( )
2009-08-26 13:41:53 +00:00
2006-12-07 13:41:40 +00:00
class sale_order ( osv . osv ) :
2008-07-22 15:11:28 +00:00
_name = " sale.order "
2010-12-13 06:07:23 +00:00
_description = " Sales Order "
2009-08-26 13:41:53 +00:00
2010-04-13 10:35:39 +00:00
def copy ( self , cr , uid , id , default = None , context = None ) :
2008-07-22 15:11:28 +00:00
if not default :
default = { }
default . update ( {
2009-08-26 13:41:53 +00:00
' state ' : ' draft ' ,
' shipped ' : False ,
' invoice_ids ' : [ ] ,
' picking_ids ' : [ ] ,
2010-10-21 09:30:04 +00:00
' date_confirm ' : False ,
2008-07-22 15:11:28 +00:00
' name ' : self . pool . get ( ' ir.sequence ' ) . get ( cr , uid , ' sale.order ' ) ,
} )
2010-04-13 10:35:39 +00:00
return super ( sale_order , self ) . copy ( cr , uid , id , default , context = context )
2008-07-22 15:11:28 +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
2010-06-05 21:39:55 +00:00
for c in self . pool . get ( ' account.tax ' ) . compute_all ( cr , uid , line . tax_id , line . price_unit * ( 1 - ( line . discount or 0.0 ) / 100.0 ) , line . product_uom_qty , line . order_id . partner_invoice_id . id , 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
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
2010-06-26 15:15:23 +00:00
# This is False
2008-08-24 23:23:45 +00:00
def _picked_rate ( self , cr , uid , ids , name , arg , context = None ) :
2009-08-26 13:41:53 +00:00
if not ids :
return { }
2008-08-24 23:23:45 +00:00
res = { }
2011-01-31 14:16:29 +00:00
tmp = { }
2008-08-24 23:23:45 +00:00
for id in ids :
2011-01-31 14:16:29 +00:00
tmp [ id ] = { ' picked ' : 0.0 , ' total ' : 0.0 }
2008-08-24 23:23:45 +00:00
cr . execute ( ''' SELECT
2011-01-31 14:16:29 +00:00
p . sale_id as sale_order_id , sum ( m . product_qty ) as nbr , mp . state as procurement_state , m . state as move_state , p . type as picking_type
2008-08-24 23:23:45 +00:00
FROM
stock_move m
LEFT JOIN
stock_picking p on ( p . id = m . picking_id )
2010-05-10 11:00:27 +00:00
LEFT JOIN
2010-05-27 12:47:06 +00:00
procurement_order mp on ( mp . move_id = m . id )
2008-08-24 23:23:45 +00:00
WHERE
2011-01-24 05:41:30 +00:00
p . sale_id IN % s GROUP BY m . state , mp . state , p . sale_id , p . type ''' , (tuple(ids),))
2011-07-06 09:35:40 +00:00
2011-01-31 14:16:29 +00:00
for item in cr . dictfetchall ( ) :
if item [ ' move_state ' ] == ' cancel ' :
2008-08-24 23:23:45 +00:00
continue
2011-11-04 04:50:18 +00:00
2011-01-31 14:16:29 +00:00
if item [ ' picking_type ' ] == ' in ' : #this is a returned picking
tmp [ item [ ' sale_order_id ' ] ] [ ' total ' ] - = item [ ' nbr ' ] or 0.0 # Deducting the return picking qty
if item [ ' procurement_state ' ] == ' done ' or item [ ' move_state ' ] == ' done ' :
tmp [ item [ ' sale_order_id ' ] ] [ ' picked ' ] - = item [ ' nbr ' ] or 0.0
2008-08-24 23:23:45 +00:00
else :
2011-01-31 14:16:29 +00:00
tmp [ item [ ' sale_order_id ' ] ] [ ' total ' ] + = item [ ' nbr ' ] or 0.0
if item [ ' procurement_state ' ] == ' done ' or item [ ' move_state ' ] == ' done ' :
tmp [ item [ ' sale_order_id ' ] ] [ ' picked ' ] + = item [ ' nbr ' ] or 0.0
2011-04-14 11:49:50 +00:00
2010-04-13 10:35:39 +00:00
for order in self . browse ( cr , uid , ids , context = context ) :
2008-09-08 22:53:40 +00:00
if order . shipped :
res [ order . id ] = 100.0
2011-01-31 14:16:29 +00:00
else :
res [ order . id ] = tmp [ order . id ] [ ' total ' ] and ( 100.0 * tmp [ order . id ] [ ' picked ' ] / tmp [ order . id ] [ ' total ' ] ) or 0.0
2011-11-04 04:50:18 +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
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
if not invoice_existence :
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 :
if arg [ 1 ] == ' = ' :
if arg [ 2 ] :
clause + = ' AND inv.state = \' paid \' '
else :
2010-11-03 12:30:07 +00:00
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 '
2008-07-22 15:11:28 +00:00
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 ( )
2011-01-17 11:20:56 +00:00
2008-07-22 15:11:28 +00:00
_columns = {
2011-10-04 10:14:10 +00:00
' name ' : fields . char ( ' Order Reference ' , size = 64 , required = True ,
2010-06-24 06:40:29 +00:00
readonly = True , states = { ' draft ' : [ ( ' readonly ' , False ) ] } , select = True ) ,
2009-08-26 13:41:53 +00:00
' shop_id ' : fields . many2one ( ' sale.shop ' , ' Shop ' , required = True , readonly = True , states = { ' draft ' : [ ( ' readonly ' , False ) ] } ) ,
2010-12-13 06:07:23 +00:00
' origin ' : fields . char ( ' Source Document ' , size = 64 , help = " Reference of the document that generated this sales order request. " ) ,
2009-12-22 09:38:41 +00:00
' client_order_ref ' : fields . char ( ' Customer Reference ' , size = 64 ) ,
2008-07-22 15:11:28 +00:00
' state ' : fields . selection ( [
2009-08-26 13:41:53 +00:00
( ' draft ' , ' Quotation ' ) ,
( ' waiting_date ' , ' Waiting Schedule ' ) ,
2011-09-15 05:36:24 +00:00
( ' manual ' , ' To Invoice ' ) ,
2009-08-26 13:41:53 +00:00
( ' progress ' , ' In Progress ' ) ,
( ' shipping_except ' , ' Shipping Exception ' ) ,
( ' invoice_except ' , ' Invoice Exception ' ) ,
( ' done ' , ' Done ' ) ,
( ' cancel ' , ' Cancelled ' )
2012-01-05 08:57:53 +00:00
] , ' Order State ' , readonly = True , help = " Gives the state of the quotation or sales order. \n The exception state is automatically set when a cancel operation occurs in the invoice validation (Invoice Exception) or in the picking list process (Shipping Exception). \n The ' Waiting Schedule ' state is set when the invoice is confirmed but waiting for the scheduler to run on the order date. " , select = True ) ,
2011-12-11 11:09:56 +00:00
' date_order ' : fields . date ( ' Date ' , required = True , readonly = True , select = True , states = { ' draft ' : [ ( ' readonly ' , 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. " ) ,
2011-01-17 11:20:56 +00:00
' date_confirm ' : fields . date ( ' Confirmation Date ' , readonly = True , select = True , help = " Date on which sales order is confirmed. " ) ,
2009-08-26 13:41:53 +00:00
' user_id ' : fields . many2one ( ' res.users ' , ' Salesman ' , states = { ' draft ' : [ ( ' readonly ' , False ) ] } , select = True ) ,
' partner_id ' : fields . many2one ( ' res.partner ' , ' Customer ' , readonly = True , states = { ' draft ' : [ ( ' readonly ' , False ) ] } , required = True , change_default = True , select = True ) ,
2010-12-13 06:07:23 +00:00
' partner_invoice_id ' : fields . many2one ( ' res.partner.address ' , ' Invoice Address ' , readonly = True , required = True , states = { ' draft ' : [ ( ' readonly ' , False ) ] } , help = " Invoice address for current sales order. " ) ,
2009-12-21 09:50:54 +00:00
' partner_order_id ' : fields . many2one ( ' res.partner.address ' , ' Ordering Contact ' , readonly = True , required = True , states = { ' draft ' : [ ( ' readonly ' , False ) ] } , help = " The name and address of the contact who requested the order or quotation. " ) ,
2010-12-13 06:07:23 +00:00
' partner_shipping_id ' : fields . many2one ( ' res.partner.address ' , ' Shipping Address ' , readonly = True , required = True , states = { ' draft ' : [ ( ' readonly ' , False ) ] } , help = " Shipping address for current sales order. " ) ,
2008-07-22 15:11:28 +00:00
2010-12-13 06:07:23 +00:00
' incoterm ' : fields . many2one ( ' stock.incoterms ' , ' Incoterm ' , help = " Incoterm which stands for ' International Commercial terms ' implies its a series of sales terms which are used in the commercial transaction. " ) ,
2011-11-02 23:52:24 +00:00
' picking_policy ' : fields . selection ( [ ( ' direct ' , ' Deliver each product when available ' ) , ( ' one ' , ' Deliver all products at once ' ) ] ,
2010-07-09 13:57:32 +00:00
' Picking Policy ' , required = True , readonly = True , states = { ' draft ' : [ ( ' readonly ' , False ) ] } , help = """ If you don ' t have enough stock available to deliver all at once, do you accept partial shipments or not? """ ) ,
2008-07-22 15:11:28 +00:00
' order_policy ' : fields . selection ( [
2012-03-02 06:47:09 +00:00
( ' prepaid ' , ' Before Delivery ' ) ,
( ' manual ' , ' On Demand ' ) ,
( ' picking ' , ' On Delivery Order ' ) ,
] , ' Create Invoice ' , required = True , readonly = True , states = { ' draft ' : [ ( ' readonly ' , False ) ] } ,
help = """ The Create Invoice is used to synchronise invoice and delivery operations.
- The ' Before Delivery ' choice will first generate the invoice and then generate the picking order after the payment of this invoice .
- The ' On Demand ' will create the picking order directly and wait for the user to manually click on the ' Invoice ' button to generate the draft invoice based on the sale order or the sale order lines .
- The ' On Delivery Order ' choice will generate the draft invoice based on sales order after all picking lists have been finished . """ ),
2010-12-13 06:07:23 +00:00
' pricelist_id ' : fields . many2one ( ' product.pricelist ' , ' Pricelist ' , required = True , readonly = True , states = { ' draft ' : [ ( ' readonly ' , False ) ] } , help = " Pricelist for current sales order. " ) ,
2011-11-16 15:09:41 +00:00
' project_id ' : fields . many2one ( ' account.analytic.account ' , ' Contract/Analytic Account ' , readonly = True , states = { ' draft ' : [ ( ' readonly ' , False ) ] } , help = " The analytic account related to a sales order. " ) ,
2008-07-22 15:11:28 +00:00
2009-08-26 13:41:53 +00:00
' order_line ' : fields . one2many ( ' sale.order.line ' , ' order_id ' , ' Order Lines ' , readonly = True , states = { ' draft ' : [ ( ' readonly ' , False ) ] } ) ,
2010-12-13 06:07:23 +00:00
' invoice_ids ' : fields . many2many ( ' account.invoice ' , ' sale_order_invoice_rel ' , ' order_id ' , ' invoice_id ' , ' Invoices ' , readonly = True , 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). " ) ,
' picking_ids ' : fields . one2many ( ' stock.picking ' , ' sale_id ' , ' Related Picking ' , readonly = True , help = " This is a list of picking that has been generated for this sales order. " ) ,
' shipped ' : fields . boolean ( ' Delivered ' , readonly = True , help = " It indicates that the sales order has been delivered. This field is updated only after the scheduler(s) have been launched. " ) ,
2011-07-01 23:41:24 +00:00
' picked_rate ' : fields . function ( _picked_rate , string = ' Picked ' , type = ' float ' ) ,
' invoiced_rate ' : fields . function ( _invoiced_rate , string = ' Invoiced ' , type = ' float ' ) ,
' 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. " ) ,
2010-06-12 18:32:16 +00:00
' note ' : fields . text ( ' Notes ' ) ,
2008-12-07 12:45:27 +00:00
2011-07-01 23:41:24 +00:00
' amount_untaxed ' : fields . function ( _amount_all , digits_compute = dp . get_precision ( ' Sale Price ' ) , string = ' Untaxed Amount ' ,
2009-08-26 13:41:53 +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 amount without tax. " ) ,
2011-07-01 23:41:24 +00:00
' amount_tax ' : fields . function ( _amount_all , digits_compute = dp . get_precision ( ' Sale Price ' ) , string = ' Taxes ' ,
2009-08-26 13:41:53 +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. " ) ,
2011-07-01 23:41:24 +00:00
' amount_total ' : fields . function ( _amount_all , digits_compute = dp . get_precision ( ' Sale Price ' ) , string = ' Total ' ,
2009-08-26 13:41:53 +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
2010-12-14 16:07:29 +00:00
' invoice_quantity ' : fields . selection ( [ ( ' order ' , ' Ordered Quantities ' ) , ( ' procurement ' , ' Shipped Quantities ' ) ] , ' Invoice on ' , help = " The sale order will automatically create the invoice proposition (draft invoice). Ordered and delivered quantities may not be the same. You have to choose if you want your invoice based on ordered or shipped quantities. If the product is a service, shipped quantities means hours spent on the associated tasks. " , required = True , readonly = True , states = { ' draft ' : [ ( ' readonly ' , False ) ] } ) ,
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 ' ) ,
2011-01-05 13:40:50 +00:00
' company_id ' : fields . related ( ' shop_id ' , ' company_id ' , type = ' many2one ' , relation = ' res.company ' , string = ' Company ' , store = True , readonly = True )
2008-07-22 15:11:28 +00:00
}
_defaults = {
2010-06-22 07:15:50 +00:00
' picking_policy ' : ' direct ' ,
2012-02-13 18:07:41 +00:00
' date_order ' : fields . date . context_today ,
2010-12-15 08:38:33 +00:00
' order_policy ' : ' manual ' ,
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 ,
' name ' : lambda obj , cr , uid , context : obj . pool . get ( ' ir.sequence ' ) . get ( cr , uid , ' sale.order ' ) ,
2010-06-22 07:15:50 +00:00
' invoice_quantity ' : ' order ' ,
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_order_id ' : lambda self , cr , uid , context : context . get ( ' partner_id ' , False ) and self . pool . get ( ' res.partner ' ) . address_get ( cr , uid , [ context [ ' partner_id ' ] ] , [ ' contact ' ] ) [ ' contact ' ] ,
' 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 ' ] ,
}
2010-12-21 05:04:12 +00:00
_sql_constraints = [
2011-11-14 09:05:20 +00:00
( ' name_uniq ' , ' unique(name, company_id) ' , ' Order Reference must be unique per Company! ' ) ,
2010-12-21 05:04:12 +00:00
]
2008-07-22 15:11:28 +00:00
_order = ' name desc '
# 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 :
2011-09-18 13:41:16 +00:00
raise osv . except_osv ( _ ( ' Invalid action ! ' ) , _ ( ' In order to delete a confirmed sale order, you must cancel it before ! To cancel a sale order, you must first cancel related picking or delivery orders. ' ) )
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
2008-07-22 15:11:28 +00:00
def onchange_shop_id ( self , cr , uid , ids , shop_id ) :
2009-08-26 13:41:53 +00:00
v = { }
2008-07-22 15:11:28 +00:00
if shop_id :
2009-08-26 13:41:53 +00:00
shop = self . pool . get ( ' sale.shop ' ) . browse ( cr , uid , shop_id )
v [ ' project_id ' ] = shop . project_id . id
2008-07-22 15:11:28 +00:00
# Que faire si le client a une pricelist a lui ?
if shop . pricelist_id . id :
2009-08-26 13:41:53 +00:00
v [ ' pricelist_id ' ] = shop . pricelist_id . id
return { ' value ' : v }
2008-07-22 15:11:28 +00:00
def action_cancel_draft ( self , cr , uid , ids , * args ) :
if not len ( ids ) :
return False
2010-10-15 08:48:13 +00:00
cr . execute ( ' select id from sale_order_line where order_id IN %s and state= %s ' , ( tuple ( ids ) , ' cancel ' ) )
2008-07-22 15:11:28 +00:00
line_ids = map ( lambda x : x [ 0 ] , cr . fetchall ( ) )
2009-08-26 13:41:53 +00:00
self . write ( cr , uid , ids , { ' state ' : ' draft ' , ' invoice_ids ' : [ ] , ' shipped ' : 0 } )
2010-08-04 18:59:19 +00:00
self . pool . get ( ' sale.order.line ' ) . write ( cr , uid , line_ids , { ' invoiced ' : False , ' state ' : ' draft ' , ' invoice_lines ' : [ ( 6 , 0 , [ ] ) ] } )
2008-07-22 15:11:28 +00:00
wf_service = netsvc . LocalService ( " workflow " )
for inv_id in ids :
2010-02-05 11:35:25 +00:00
# Deleting the existing instance of workflow for SO
wf_service . trg_delete ( uid , ' sale.order ' , inv_id , cr )
2008-07-22 15:11:28 +00:00
wf_service . trg_create ( uid , ' sale.order ' , inv_id , cr )
2010-06-15 05:48:29 +00:00
for ( id , name ) in self . name_get ( cr , uid , ids ) :
2010-12-13 06:07:23 +00:00
message = _ ( " The sales order ' %s ' has been set in draft state. " ) % ( name , )
2010-06-22 06:38:37 +00:00
self . log ( cr , uid , id , message )
2008-07-22 15:11:28 +00:00
return True
2011-09-21 07:33:59 +00:00
def onchange_pricelist_id ( self , cr , uid , ids , pricelist_id , order_lines , context = { } ) :
if ( not pricelist_id ) or ( not order_lines ) :
return { }
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. ' )
}
return { ' warning ' : warning }
2011-12-05 10:27:29 +00:00
def onchange_partner_order_id ( self , cr , uid , ids , order_id , invoice_id = False , shipping_id = False , context = { } ) :
if not order_id :
return { }
val = { }
if not invoice_id :
val [ ' partner_invoice_id ' ] = order_id
if not shipping_id :
val [ ' partner_shipping_id ' ] = order_id
return { ' value ' : val }
2008-07-22 15:11:28 +00:00
def onchange_partner_id ( self , cr , uid , ids , part ) :
if not part :
2009-08-26 13:41:53 +00:00
return { ' value ' : { ' partner_invoice_id ' : False , ' partner_shipping_id ' : False , ' partner_order_id ' : False , ' payment_term ' : False , ' fiscal_position ' : False } }
2010-02-03 11:43:14 +00:00
2009-08-26 13:41:53 +00:00
addr = self . pool . get ( ' res.partner ' ) . address_get ( cr , uid , [ part ] , [ ' delivery ' , ' invoice ' , ' contact ' ] )
2008-10-20 20:49:11 +00:00
part = self . pool . get ( ' res.partner ' ) . browse ( cr , uid , part )
pricelist = part . property_product_pricelist and part . property_product_pricelist . id or False
payment_term = part . property_payment_term and part . property_payment_term . id or False
2009-01-19 13:59:11 +00:00
fiscal_position = part . property_account_position and part . property_account_position . 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_order_id ' : addr [ ' contact ' ] ,
' partner_shipping_id ' : addr [ ' delivery ' ] ,
' payment_term ' : payment_term ,
' fiscal_position ' : fiscal_position ,
' user_id ' : dedicated_salesman ,
}
2009-03-08 18:54:10 +00:00
if pricelist :
val [ ' pricelist_id ' ] = pricelist
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 shipping_policy_change ( self , cr , uid , ids , policy , context = None ) :
2008-12-01 13:00:56 +00:00
if not policy :
return { }
inv_qty = ' order '
2009-08-26 13:41:53 +00:00
if policy == ' prepaid ' :
2008-12-01 13:00:56 +00:00
inv_qty = ' order '
2009-08-26 13:41:53 +00:00
elif policy == ' picking ' :
2008-12-01 13:00:56 +00:00
inv_qty = ' procurement '
2009-08-26 13:41:53 +00:00
return { ' value ' : { ' invoice_quantity ' : inv_qty } }
2008-12-01 13:00:56 +00:00
def write ( self , cr , uid , ids , vals , context = None ) :
2010-10-21 09:30:04 +00:00
if vals . get ( ' order_policy ' , False ) :
2009-08-26 13:41:53 +00:00
if vals [ ' order_policy ' ] == ' prepaid ' :
vals . update ( { ' invoice_quantity ' : ' order ' } )
elif vals [ ' order_policy ' ] == ' picking ' :
vals . update ( { ' invoice_quantity ' : ' procurement ' } )
2008-12-01 13:00:56 +00:00
return super ( sale_order , self ) . write ( cr , uid , ids , vals , context = context )
2010-04-13 10:35:39 +00:00
def create ( self , cr , uid , vals , context = None ) :
2010-10-21 09:30:04 +00:00
if vals . get ( ' order_policy ' , False ) :
2009-08-26 13:41:53 +00:00
if vals [ ' order_policy ' ] == ' prepaid ' :
vals . update ( { ' invoice_quantity ' : ' order ' } )
if vals [ ' order_policy ' ] == ' picking ' :
vals . update ( { ' invoice_quantity ' : ' procurement ' } )
2008-12-01 13:00:56 +00:00
return super ( sale_order , self ) . create ( cr , uid , vals , context = context )
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
sale order . This method may be overridden to implement custom
invoice generation ( making sure to call super ( ) to establish
a clean extension chain ) .
: param browse_record order : 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 = { }
2012-02-01 18:31:23 +00:00
journal_ids = self . pool . get ( ' account.journal ' ) . search ( cr , uid ,
[ ( ' type ' , ' = ' , ' sale ' ) , ( ' company_id ' , ' = ' , order . company_id . id ) ] ,
limit = 1 )
2010-06-22 07:00:51 +00:00
if not journal_ids :
raise osv . except_osv ( _ ( ' Error ! ' ) ,
2010-12-13 06:07:23 +00:00
_ ( ' There is no sales journal defined 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 ,
2012-02-01 18:31:23 +00:00
' account_id ' : order . partner_id . property_account_receivable . id ,
2008-07-22 15:11:28 +00:00
' partner_id ' : order . partner_id . id ,
2009-12-02 11:20:06 +00:00
' journal_id ' : journal_ids [ 0 ] ,
2008-07-22 15:11:28 +00:00
' address_invoice_id ' : order . partner_invoice_id . id ,
2009-09-25 13:28:13 +00:00
' address_contact_id ' : order . partner_order_id . 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 ,
2010-05-19 06:31:40 +00:00
' fiscal_position ' : order . fiscal_position . id or order . partner_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 ,
2010-10-21 09:30:04 +00:00
' user_id ' : order . user_id and order . user_id . id or False
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
2010-10-11 11:43:11 +00:00
def manual_invoice ( self , cr , uid , ids , context = None ) :
2010-10-14 08:13:33 +00:00
mod_obj = self . pool . get ( ' ir.model.data ' )
2010-10-11 11:43:11 +00:00
wf_service = netsvc . LocalService ( " workflow " )
inv_ids = set ( )
inv_ids1 = set ( )
for id in ids :
for record in self . pool . get ( ' sale.order ' ) . browse ( cr , uid , id ) . invoice_ids :
inv_ids . add ( record . id )
# inv_ids would have old invoices if any
for id in ids :
wf_service . trg_validate ( uid , ' sale.order ' , id , ' manual_invoice ' , cr )
for record in self . pool . get ( ' sale.order ' ) . browse ( cr , uid , id ) . invoice_ids :
inv_ids1 . add ( record . id )
inv_ids = list ( inv_ids1 . difference ( inv_ids ) )
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 ' ,
2010-10-14 08:13:33 +00:00
' res_id ' : inv_ids and inv_ids [ 0 ] or False ,
2010-10-17 11:59:01 +00:00
}
2010-10-11 11:43:11 +00:00
2010-06-22 07:15:50 +00:00
def action_invoice_create ( self , cr , uid , ids , grouped = False , states = [ ' confirmed ' , ' done ' , ' exception ' ] , date_inv = False , context = None ) :
2008-07-22 15:11:28 +00:00
res = False
invoices = { }
invoice_ids = [ ]
2010-06-22 07:15:50 +00:00
picking_obj = self . pool . get ( ' stock.picking ' )
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 = { }
2010-06-22 07:15:50 +00:00
if context is None :
context = { }
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
if date_inv :
context [ ' date_inv ' ] = date_inv
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 (
_ ( ' Error ! ' ) ,
_ ( ' 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 :
invoices . setdefault ( o . partner_id . id , [ ] ) . append ( ( o , created_lines ) )
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 = ' '
2009-11-26 13:54:00 +00:00
for o , l in val :
2010-06-24 09:45:18 +00:00
invoice_ref + = o . name + ' | '
2009-11-26 13:54:00 +00:00
self . write ( cr , uid , [ o . id ] , { ' state ' : ' progress ' } )
if o . order_policy == ' picking ' :
picking_obj . write ( cr , uid , map ( lambda x : x . id , o . picking_ids ) , { ' invoice_state ' : ' invoiced ' } )
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 ) )
2010-06-24 09:45:18 +00:00
invoice . write ( cr , uid , [ res ] , { ' origin ' : invoice_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 ' } )
if order . order_policy == ' picking ' :
picking_obj . write ( cr , uid , map ( lambda x : x . id , order . picking_ids ) , { ' invoice_state ' : ' invoiced ' } )
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 ) )
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 ) :
if context is None :
context = { }
2010-12-13 06:43:09 +00:00
for sale in self . browse ( cr , uid , ids , context = context ) :
2008-07-22 15:11:28 +00:00
for line in sale . order_line :
2010-09-29 15:46:27 +00:00
#
# Check if the line is invoiced (has asociated invoice
# lines from non-cancelled invoices).
#
2009-08-26 13:41:53 +00:00
invoiced = False
2010-08-04 18:59:19 +00:00
for iline in line . invoice_lines :
2010-09-29 15:46:27 +00:00
if iline . invoice_id and iline . invoice_id . state != ' cancel ' :
2010-08-04 18:59:19 +00:00
invoiced = True
2010-09-29 15:46:27 +00:00
break
# Update the line (only when needed)
if line . invoiced != invoiced :
self . pool . get ( ' sale.order.line ' ) . write ( cr , uid , [ line . id ] , { ' invoiced ' : invoiced } , context = context )
self . write ( cr , uid , ids , { ' state ' : ' invoice_except ' , ' invoice_ids ' : False } , 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 ) :
for order in self . browse ( cr , uid , ids , context = context ) :
2010-09-29 15:46:27 +00:00
#
# Update the sale order lines state (and invoiced flag).
#
2009-12-16 06:26:10 +00:00
for line in order . order_line :
2010-09-29 15:46:27 +00:00
vals = { }
#
# Check if the line is invoiced (has asociated invoice
# lines from non-cancelled invoices).
#
invoiced = False
for iline in line . invoice_lines :
if iline . invoice_id and iline . invoice_id . state != ' cancel ' :
invoiced = True
break
if line . invoiced != invoiced :
vals [ ' invoiced ' ] = invoiced
# If the line was in exception state, now it gets confirmed.
2009-12-16 06:26:10 +00:00
if line . state == ' exception ' :
2010-09-29 15:46:27 +00:00
vals [ ' state ' ] = ' confirmed '
# Update the line (only when needed).
if vals :
self . pool . get ( ' sale.order.line ' ) . write ( cr , uid , [ line . id ] , vals , context = context )
#
2010-12-13 06:07:23 +00:00
# Update the sales order state.
2010-09-29 15:46:27 +00:00
#
2010-05-11 11:12:40 +00:00
if order . state == ' invoice_except ' :
2010-10-15 09:42:54 +00:00
self . write ( cr , uid , [ order . id ] , { ' state ' : ' progress ' } , context = context )
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 ) :
2010-10-15 09:24:14 +00:00
wf_service = netsvc . LocalService ( " workflow " )
2010-04-13 10:35:39 +00:00
if context is None :
context = { }
2008-07-22 15:11:28 +00:00
sale_order_line_obj = self . pool . get ( ' sale.order.line ' )
2010-12-17 11:48:53 +00:00
proc_obj = self . pool . get ( ' procurement.order ' )
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 pick in sale . picking_ids :
2009-08-26 13:41:53 +00:00
if pick . state not in ( ' draft ' , ' cancel ' ) :
2008-07-22 15:11:28 +00:00
raise osv . except_osv (
2010-12-13 06:07:23 +00:00
_ ( ' Could not cancel sales order ! ' ) ,
_ ( ' You must first cancel all picking attached to this sales order. ' ) )
2010-12-17 11:48:53 +00:00
if pick . state == ' cancel ' :
for mov in pick . move_lines :
proc_ids = proc_obj . search ( cr , uid , [ ( ' move_id ' , ' = ' , mov . id ) ] )
if proc_ids :
for proc in proc_ids :
wf_service . trg_validate ( uid , ' procurement.order ' , proc , ' button_check ' , cr )
2009-08-26 13:41:53 +00:00
for r in self . read ( cr , uid , ids , [ ' picking_ids ' ] ) :
2008-07-22 15:11:28 +00:00
for pick in r [ ' picking_ids ' ] :
wf_service . trg_validate ( uid , ' stock.picking ' , pick , ' button_cancel ' , cr )
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 (
2010-12-13 06:07:23 +00:00
_ ( ' Could not cancel this sales order ! ' ) ,
_ ( ' You must first cancel all invoices attached to this sales order. ' ) )
2009-08-26 13:41:53 +00:00
for r in self . read ( cr , uid , ids , [ ' invoice_ids ' ] ) :
2008-07-22 15:11:28 +00:00
for inv in r [ ' invoice_ids ' ] :
wf_service . trg_validate ( uid , ' account.invoice ' , inv , ' invoice_cancel ' , cr )
sale_order_line_obj . write ( cr , uid , [ l . id for l in sale . order_line ] ,
{ ' state ' : ' cancel ' } )
2010-12-13 06:07:23 +00:00
message = _ ( " The sales order ' %s ' has been cancelled. " ) % ( sale . name , )
2010-07-22 13:50:24 +00:00
self . log ( cr , uid , sale . id , message )
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-02-13 18:07:41 +00:00
def action_wait ( self , cr , uid , ids , context = None ) :
2008-07-22 15:11:28 +00:00
for o in self . browse ( cr , uid , ids ) :
2011-11-15 05:59:06 +00:00
if not o . order_line :
2011-11-16 09:25:55 +00:00
raise osv . except_osv ( _ ( ' Error ! ' ) , _ ( ' You cannot confirm a sale order which has no line. ' ) )
2008-11-06 21:44:09 +00:00
if ( o . order_policy == ' manual ' ) :
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 ) } )
2008-07-22 15:11:28 +00:00
else :
2012-02-14 12:25:20 +00:00
self . write ( cr , uid , [ o . id ] , { ' state ' : ' progress ' , ' date_confirm ' : fields . date . context_today ( self , cr , uid , context = context ) } )
2008-07-22 15:11:28 +00:00
self . pool . get ( ' sale.order.line ' ) . button_confirm ( cr , uid , [ x . id for x in o . order_line ] )
2010-12-13 06:07:23 +00:00
message = _ ( " The quotation ' %s ' has been converted to a sales order. " ) % ( o . name , )
2010-07-22 13:50:24 +00:00
self . log ( cr , uid , o . id , message )
2010-06-22 07:15:50 +00:00
return True
2008-07-22 15:11:28 +00:00
def procurement_lines_get ( self , cr , uid , ids , * args ) :
res = [ ]
for order in self . browse ( cr , uid , ids , context = { } ) :
for line in order . order_line :
if line . procurement_id :
res . append ( line . procurement_id . id )
return res
2009-11-13 05:41:16 +00:00
2008-07-22 15:11:28 +00:00
# if mode == 'finished':
# returns True if all lines are done, False otherwise
# if mode == 'canceled':
# returns True if there is at least one canceled line, False otherwise
def test_state ( self , cr , uid , ids , mode , * args ) :
assert mode in ( ' finished ' , ' canceled ' ) , _ ( " invalid mode for test_state " )
finished = True
2008-12-01 13:00:56 +00:00
canceled = False
2008-10-14 12:36:28 +00:00
notcanceled = False
2008-07-22 15:11:28 +00:00
write_done_ids = [ ]
write_cancel_ids = [ ]
for order in self . browse ( cr , uid , ids , context = { } ) :
2009-11-13 05:41:16 +00:00
for line in order . order_line :
2009-12-03 17:31:13 +00:00
if ( not line . procurement_id ) or ( line . procurement_id . state == ' done ' ) :
2008-12-14 16:46:57 +00:00
if line . state != ' done ' :
write_done_ids . append ( line . id )
2008-10-29 13:03:53 +00:00
else :
2008-07-22 15:11:28 +00:00
finished = False
2008-10-29 13:03:53 +00:00
if line . procurement_id :
2008-12-14 16:46:57 +00:00
if ( line . procurement_id . state == ' cancel ' ) :
2008-10-29 13:03:53 +00:00
canceled = True
2008-12-14 16:46:57 +00:00
if line . state != ' exception ' :
2008-10-29 13:03:53 +00:00
write_cancel_ids . append ( line . id )
else :
notcanceled = True
2008-07-22 15:11:28 +00:00
if write_done_ids :
self . pool . get ( ' sale.order.line ' ) . write ( cr , uid , write_done_ids , { ' state ' : ' done ' } )
if write_cancel_ids :
2008-12-14 16:46:57 +00:00
self . pool . get ( ' sale.order.line ' ) . write ( cr , uid , write_cancel_ids , { ' state ' : ' exception ' } )
2008-07-22 15:11:28 +00:00
2009-08-26 13:41:53 +00:00
if mode == ' finished ' :
2008-07-22 15:11:28 +00:00
return finished
2009-08-26 13:41:53 +00:00
elif mode == ' canceled ' :
2008-10-29 13:03:53 +00:00
return canceled
2008-10-14 12:36:28 +00:00
if notcanceled :
return False
2008-07-22 15:11:28 +00:00
return canceled
2011-11-21 22:25:15 +00:00
def _prepare_order_line_procurement ( self , cr , uid , order , line , move_id , date_planned , context = None ) :
2011-10-02 17:39:52 +00:00
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 ,
' 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 ,
' location_id ' : order . shop_id . warehouse_id . lot_stock_id . id ,
' procure_method ' : line . type ,
' move_id ' : move_id ,
' company_id ' : order . company_id . id ,
2011-12-09 06:11:20 +00:00
' note ' : line . notes
2011-10-02 17:39:52 +00:00
}
2010-08-10 05:46:09 +00:00
2011-11-21 22:25:15 +00:00
def _prepare_order_line_move ( self , cr , uid , order , line , picking_id , date_planned , context = None ) :
2011-10-02 17:39:52 +00:00
location_id = order . shop_id . warehouse_id . lot_stock_id . id
output_id = order . shop_id . warehouse_id . lot_output_id . id
return {
2011-12-07 18:12:53 +00:00
' name ' : line . name [ : 250 ] ,
2011-10-02 17:39:52 +00:00
' picking_id ' : picking_id ,
' product_id ' : line . product_id . id ,
' date ' : date_planned ,
' date_expected ' : date_planned ,
' product_qty ' : line . product_uom_qty ,
' product_uom ' : line . product_uom . id ,
2011-12-01 05:05:47 +00:00
' product_uos_qty ' : ( line . product_uos and line . product_uos_qty ) or line . product_uom_qty ,
2011-10-02 17:39:52 +00:00
' product_uos ' : ( line . product_uos and line . product_uos . id ) \
or line . product_uom . id ,
' product_packaging ' : line . product_packaging . id ,
' address_id ' : line . address_allotment_id . id or order . partner_shipping_id . id ,
' location_id ' : location_id ,
' location_dest_id ' : output_id ,
' sale_line_id ' : line . id ,
' tracking_id ' : False ,
' state ' : ' draft ' ,
#'state': 'waiting',
' note ' : line . notes ,
' company_id ' : order . company_id . id ,
' price_unit ' : line . product_id . standard_price or 0.0
}
2011-11-21 22:25:15 +00:00
def _prepare_order_picking ( self , cr , uid , order , context = None ) :
2011-10-02 17:39:52 +00:00
pick_name = self . pool . get ( ' ir.sequence ' ) . get ( cr , uid , ' stock.picking.out ' )
return {
' name ' : pick_name ,
' origin ' : order . name ,
2011-11-04 04:50:18 +00:00
' date ' : order . date_order ,
2011-10-02 17:39:52 +00:00
' type ' : ' out ' ,
' state ' : ' auto ' ,
' move_type ' : order . picking_policy ,
' sale_id ' : order . id ,
' address_id ' : order . partner_shipping_id . id ,
' note ' : order . note ,
' invoice_state ' : ( order . order_policy == ' picking ' and ' 2binvoiced ' ) or ' none ' ,
' company_id ' : order . company_id . id ,
}
2011-11-22 06:12:35 +00:00
2011-11-23 09:53:34 +00:00
def ship_recreate ( self , cr , uid , order , line , move_id , proc_id ) :
2011-12-01 05:05:47 +00:00
# FIXME: deals with potentially cancelled shipments, seems broken (specially if shipment has production lot)
2011-11-23 09:53:34 +00:00
"""
Define ship_recreate for process after shipping exception
param order : sale order to which the order lines belong
param line : sale order line records to procure
param move_id : the ID of stock move
param proc_id : the ID of procurement
"""
move_obj = self . pool . get ( ' stock.move ' )
2011-11-15 09:58:36 +00:00
if order . state == ' shipping_except ' :
for pick in order . picking_ids :
for move in pick . move_lines :
if move . state == ' cancel ' :
mov_ids = move_obj . search ( cr , uid , [ ( ' state ' , ' = ' , ' cancel ' ) , ( ' sale_line_id ' , ' = ' , line . id ) , ( ' picking_id ' , ' = ' , pick . id ) ] )
if mov_ids :
for mov in move_obj . browse ( cr , uid , mov_ids ) :
# FIXME: the following seems broken: what if move_id doesn't exist? What if there are several mov_ids? Shouldn't that be a sum?
move_obj . write ( cr , uid , [ move_id ] , { ' product_qty ' : mov . product_qty , ' product_uos_qty ' : mov . product_uos_qty } )
self . pool . get ( ' procurement.order ' ) . write ( cr , uid , [ proc_id ] , { ' product_qty ' : mov . product_qty , ' product_uos_qty ' : mov . product_uos_qty } )
2011-11-22 06:12:35 +00:00
return True
2011-12-21 13:10:28 +00:00
def _get_date_planned ( self , cr , uid , order , line , start_date , context = None ) :
2011-12-20 09:41:10 +00:00
date_planned = datetime . strptime ( start_date , DEFAULT_SERVER_DATE_FORMAT ) + relativedelta ( days = line . delay or 0.0 )
date_planned = ( date_planned - timedelta ( days = order . company_id . security_lead ) ) . strftime ( DEFAULT_SERVER_DATETIME_FORMAT )
return date_planned
2011-12-19 16:54:40 +00:00
def _create_pickings_and_procurements ( self , cr , uid , order , order_lines , picking_id = False , context = None ) :
2011-10-11 14:03:35 +00:00
""" Create the required procurements to supply sale order lines, also connecting
the procurements to appropriate stock moves in order to bring the goods to the
sale order ' s requested location.
If ` ` picking_id ` ` is provided , the stock moves will be added to it , otherwise
a standard outgoing picking will be created to wrap the stock moves , as returned
by : meth : ` ~ . _prepare_order_picking ` .
Modules that wish to customize the procurements or partition the stock moves over
multiple stock pickings may override this method and call ` ` super ( ) ` ` with
different subsets of ` ` order_lines ` ` and / or preset ` ` picking_id ` ` values .
: param browse_record order : sale order to which the order lines belong
: param list ( browse_record ) order_lines : sale order line records to procure
: param int picking_id : optional ID of a stock picking to which the created stock moves
will be added . A new picking will be created if ommitted .
2011-10-03 14:25:54 +00:00
: return : True
"""
2011-11-18 06:11:40 +00:00
move_obj = self . pool . get ( ' stock.move ' )
picking_obj = self . pool . get ( ' stock.picking ' )
2011-11-18 13:14:04 +00:00
procurement_obj = self . pool . get ( ' procurement.order ' )
2011-10-02 17:39:52 +00:00
proc_ids = [ ]
2011-11-25 10:52:37 +00:00
2011-10-02 17:39:52 +00:00
for line in order_lines :
if line . state == ' done ' :
continue
2011-12-21 13:10:28 +00:00
date_planned = self . _get_date_planned ( cr , uid , order , line , order . date_order , context = context )
2011-10-11 14:03:35 +00:00
2011-10-02 17:39:52 +00:00
if line . product_id :
if line . product_id . product_tmpl_id . type in ( ' product ' , ' consu ' ) :
2008-07-22 15:11:28 +00:00
if not picking_id :
2011-12-21 13:10:28 +00:00
picking_id = picking_obj . create ( cr , uid , self . _prepare_order_picking ( cr , uid , order , context = context ) )
move_id = move_obj . create ( cr , uid , self . _prepare_order_line_move ( cr , uid , order , line , picking_id , date_planned , context = context ) )
2011-10-11 14:03:35 +00:00
else :
# a service has no stock move
2011-10-02 17:39:52 +00:00
move_id = False
2011-12-21 13:10:28 +00:00
proc_id = procurement_obj . create ( cr , uid , self . _prepare_order_line_procurement ( cr , uid , order , line , move_id , date_planned , context = context ) )
2011-10-02 17:39:52 +00:00
proc_ids . append ( proc_id )
2011-10-11 14:03:35 +00:00
line . write ( { ' procurement_id ' : proc_id } )
2011-11-23 09:53:34 +00:00
self . ship_recreate ( cr , uid , order , line , move_id , proc_id )
2010-12-31 12:47:48 +00:00
2011-10-02 17:39:52 +00:00
wf_service = netsvc . LocalService ( " workflow " )
if picking_id :
wf_service . trg_validate ( uid , ' stock.picking ' , picking_id , ' button_confirm ' , cr )
2008-12-14 16:46:57 +00:00
2011-10-02 17:39:52 +00:00
for proc_id in proc_ids :
wf_service . trg_validate ( uid , ' procurement.order ' , proc_id , ' button_confirm ' , cr )
val = { }
if order . state == ' shipping_except ' :
val [ ' state ' ] = ' progress '
val [ ' shipped ' ] = False
if ( order . order_policy == ' manual ' ) :
for line in order . order_line :
if ( not line . invoiced ) and ( line . state not in ( ' cancel ' , ' draft ' ) ) :
val [ ' state ' ] = ' manual '
break
2011-10-11 14:03:35 +00:00
order . write ( val )
2011-10-02 17:39:52 +00:00
return True
2011-12-19 16:54:40 +00:00
def action_ship_create ( self , cr , uid , ids , context = None ) :
for order in self . browse ( cr , uid , ids , context = context ) :
2011-12-21 13:10:28 +00:00
self . _create_pickings_and_procurements ( cr , uid , order , order . order_line , None , context = context )
2008-07-22 15:11:28 +00:00
return True
2010-04-13 10:35:39 +00:00
def action_ship_end ( self , cr , uid , ids , context = None ) :
for order in self . browse ( cr , uid , ids , context = context ) :
2009-08-26 13:41:53 +00:00
val = { ' shipped ' : True }
if order . state == ' shipping_except ' :
2008-12-14 16:46:57 +00:00
val [ ' state ' ] = ' progress '
if ( order . order_policy == ' manual ' ) :
for line in order . order_line :
2009-08-26 13:41:53 +00:00
if ( not line . invoiced ) and ( line . state not in ( ' cancel ' , ' draft ' ) ) :
2008-12-14 16:46:57 +00:00
val [ ' state ' ] = ' manual '
break
for line in order . order_line :
towrite = [ ]
2009-08-26 13:41:53 +00:00
if line . state == ' exception ' :
2008-12-14 16:46:57 +00:00
towrite . append ( line . id )
if towrite :
2009-08-26 13:41:53 +00:00
self . pool . get ( ' sale.order.line ' ) . write ( cr , uid , towrite , { ' state ' : ' done ' } , context = context )
2008-07-22 15:11:28 +00:00
self . write ( cr , uid , [ order . id ] , val )
return True
def _log_event ( self , cr , uid , ids , factor = 0.7 , name = ' Open Order ' ) :
2009-08-26 13:41:53 +00:00
invs = self . read ( cr , uid , ids , [ ' date_order ' , ' partner_id ' , ' amount_untaxed ' ] )
2008-07-22 15:11:28 +00:00
for inv in invs :
2009-08-26 13:41:53 +00:00
part = inv [ ' partner_id ' ] and inv [ ' partner_id ' ] [ 0 ]
2008-07-22 15:11:28 +00:00
pr = inv [ ' amount_untaxed ' ] or 0.0
partnertype = ' customer '
eventtype = ' sale '
2009-08-26 13:41:53 +00:00
event = {
' name ' : ' Order: ' + name ,
' som ' : False ,
' description ' : ' Order ' + str ( inv [ ' id ' ] ) ,
' document ' : ' ' ,
' partner_id ' : part ,
2011-10-11 14:19:21 +00:00
' date ' : time . strftime ( DEFAULT_SERVER_DATE_FORMAT ) ,
2009-08-26 13:41:53 +00:00
' user_id ' : uid ,
' partner_type ' : partnertype ,
' probability ' : 1.0 ,
' planned_revenue ' : pr ,
' planned_cost ' : 0.0 ,
' type ' : eventtype
}
self . pool . get ( ' res.partner.event ' ) . create ( cr , uid , event )
2008-07-22 15:11:28 +00:00
2009-08-26 13:41:53 +00:00
def has_stockable_products ( self , cr , uid , ids , * args ) :
2008-07-22 15:11:28 +00:00
for order in self . browse ( cr , uid , ids ) :
for order_line in order . order_line :
if order_line . product_id and order_line . product_id . product_tmpl_id . type in ( ' product ' , ' consu ' ) :
return True
return False
2006-12-07 13:41:40 +00:00
sale_order ( )
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
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 ) :
2010-07-13 08:22:18 +00:00
price = line . price_unit * ( 1 - ( line . discount or 0.0 ) / 100.0 )
taxes = tax_obj . compute_all ( cr , uid , line . tax_id , price , line . product_uom_qty , line . order_id . partner_invoice_id . id , 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
2010-04-13 10:35:39 +00:00
def _number_packages ( self , cr , uid , ids , field_name , arg , context = None ) :
2008-07-22 15:11:28 +00:00
res = { }
2010-04-13 10:35:39 +00:00
for line in self . browse ( cr , uid , ids , context = context ) :
2008-07-22 15:11:28 +00:00
try :
2010-06-05 21:39:55 +00:00
res [ line . id ] = int ( ( line . product_uom_qty + line . product_packaging . qty - 0.0001 ) / line . product_packaging . qty )
2008-07-22 15:11:28 +00:00
except :
res [ line . id ] = 1
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
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 ) ] } ) ,
2010-10-21 09:30:04 +00:00
' name ' : fields . char ( ' Description ' , size = 256 , required = True , select = 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. " ) ,
2010-11-08 09:51:38 +00:00
' delay ' : fields . float ( ' Delivery Lead Time ' , required = True , help = " Number of days between the order confirmation the shipping of the products to the customer " , readonly = True , states = { ' draft ' : [ ( ' readonly ' , False ) ] } ) ,
2009-08-26 13:41:53 +00:00
' product_id ' : fields . many2one ( ' product.product ' , ' Product ' , domain = [ ( ' sale_ok ' , ' = ' , True ) ] , change_default = True ) ,
2010-08-04 18:59:19 +00:00
' invoice_lines ' : fields . many2many ( ' account.invoice.line ' , ' sale_order_line_invoice_rel ' , ' order_line_id ' , ' invoice_id ' , ' Invoice Lines ' , readonly = True ) ,
2008-07-22 15:11:28 +00:00
' invoiced ' : fields . boolean ( ' Invoiced ' , readonly = True ) ,
2010-05-27 12:47:06 +00:00
' procurement_id ' : fields . many2one ( ' procurement.order ' , ' Procurement ' ) ,
2010-10-21 09:30:04 +00:00
' price_unit ' : fields . float ( ' Unit Price ' , required = True , digits_compute = dp . get_precision ( ' Sale Price ' ) , readonly = True , states = { ' draft ' : [ ( ' readonly ' , False ) ] } ) ,
2011-07-01 23:41:24 +00:00
' price_subtotal ' : fields . function ( _amount_line , string = ' Subtotal ' , digits_compute = dp . get_precision ( ' Sale 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 ) ] } ) ,
2011-09-17 12:01:08 +00:00
' type ' : fields . selection ( [ ( ' make_to_stock ' , ' from stock ' ) , ( ' make_to_order ' , ' on order ' ) ] , ' Procurement Method ' , required = True , readonly = True , states = { ' draft ' : [ ( ' readonly ' , False ) ] } ,
help = " If ' on order ' , it triggers a procurement when the sale order is confirmed to create a task, purchase order or manufacturing order linked to this sale order line. " ) ,
2010-10-21 09:30:04 +00:00
' property_ids ' : fields . many2many ( ' mrp.property ' , ' sale_order_line_property_rel ' , ' order_id ' , ' property_id ' , ' Properties ' , readonly = True , states = { ' draft ' : [ ( ' readonly ' , False ) ] } ) ,
2009-08-26 13:41:53 +00:00
' address_allotment_id ' : fields . many2one ( ' res.partner.address ' , ' Allotment Partner ' ) ,
2011-10-04 09:44:22 +00:00
' product_uom_qty ' : fields . float ( ' Quantity (UoM) ' , 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 ' ) ,
2008-09-07 23:29:30 +00:00
' product_packaging ' : fields . many2one ( ' product.packaging ' , ' Packaging ' ) ,
2008-07-22 15:11:28 +00:00
' move_ids ' : fields . one2many ( ' stock.move ' , ' sale_line_id ' , ' Inventory Moves ' , readonly = True ) ,
2010-10-21 09:30:04 +00:00
' discount ' : fields . float ( ' Discount ( % ) ' , digits = ( 16 , 2 ) , readonly = True , states = { ' draft ' : [ ( ' readonly ' , False ) ] } ) ,
2011-07-01 23:41:24 +00:00
' number_packages ' : fields . function ( _number_packages , type = ' integer ' , string = ' Number Packages ' ) ,
2010-06-12 18:32:16 +00:00
' notes ' : fields . text ( ' Notes ' ) ,
2010-10-21 09:30:04 +00:00
' th_weight ' : fields . float ( ' Weight ' , readonly = True , states = { ' draft ' : [ ( ' readonly ' , False ) ] } ) ,
2011-12-30 11:31:22 +00:00
' state ' : fields . selection ( [ ( ' cancel ' , ' Cancelled ' ) , ( ' draft ' , ' Draft ' ) , ( ' confirmed ' , ' Confirmed ' ) , ( ' exception ' , ' Exception ' ) , ( ' done ' , ' Done ' ) ] , ' State ' , required = True , readonly = True ,
2010-12-13 06:07:23 +00:00
help = ' * The \' Draft \' state is set when the related sales order in draft state. \
\n * The \' Confirmed \' state is set when the related sales order is confirmed. \
\n * The \' Exception \' state is set when the related sales order is set as exception. \
\n * The \' Done \' state is set when the sales order line has been picked. \
\n * The \' Cancelled \' state 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 ' ) ,
2010-11-04 08:56:12 +00:00
' salesman_id ' : fields . related ( ' order_id ' , ' user_id ' , type = ' many2one ' , relation = ' res.users ' , store = True , string = ' Salesman ' ) ,
2011-01-19 08:38:17 +00:00
' company_id ' : fields . related ( ' order_id ' , ' company_id ' , type = ' many2one ' , relation = ' res.company ' , string = ' Company ' , store = True , readonly = True ) ,
2008-07-22 15:11:28 +00:00
}
2011-02-22 09:27:33 +00:00
_order = ' 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 ,
' delay ' : 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
' invoiced ' : 0 ,
' state ' : ' draft ' ,
' type ' : ' make_to_stock ' ,
2010-12-31 06:45:06 +00:00
' product_packaging ' : False ,
' price_unit ' : 0.0 ,
2008-07-22 15:11:28 +00:00
}
2009-08-26 13:41:53 +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
sale order line . This method may be overridden to implement custom
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
"""
2010-07-13 03:52:11 +00:00
2008-07-22 15:11:28 +00:00
def _get_line_qty ( line ) :
if ( line . order_id . invoice_quantity == ' order ' ) or not line . procurement_id :
if line . product_uos :
return line . product_uos_qty or 0.0
return line . product_uom_qty
else :
2010-05-27 12:47:06 +00:00
return self . pool . get ( ' procurement.order ' ) . quantity_get ( cr , uid ,
2010-04-13 10:35:39 +00:00
line . procurement_id . id , context = context )
2008-07-22 15:11:28 +00:00
def _get_line_uom ( line ) :
if ( line . order_id . invoice_quantity == ' order ' ) or not line . procurement_id :
if line . product_uos :
return line . product_uos . id
return line . product_uom . id
else :
2010-05-27 12:47:06 +00:00
return self . pool . get ( ' procurement.order ' ) . uom_get ( cr , uid ,
2010-04-13 10:35:39 +00:00
line . procurement_id . id , context = context )
2008-07-22 15:11:28 +00:00
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-01-27 04:54:58 +00:00
account_id = line . product_id . product_tmpl_id . property_account_income . id
if not account_id :
account_id = line . product_id . categ_id . property_account_income_categ . id
if not account_id :
2008-07-22 15:11:28 +00:00
raise osv . except_osv ( _ ( ' Error ! ' ) ,
2012-01-31 10:53:22 +00:00
_ ( ' There is no income account defined 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
uosqty = _get_line_qty ( line )
uos_id = _get_line_uom ( line )
pu = 0.0
if uosqty :
pu = round ( line . price_unit * line . product_uom_qty / uosqty ,
self . pool . get ( ' decimal.precision ' ) . precision_get ( cr , uid , ' Sale Price ' ) )
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 :
raise osv . except_osv ( _ ( ' Error ! ' ) ,
_ ( ' There is no income category account defined in default Properties for Product Category or Fiscal Position is not defined ! ' ) )
return {
' name ' : line . name ,
' 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 ] ) ] ,
' note ' : line . notes ,
' 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
return False
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 )
2010-08-04 18:59:19 +00:00
cr . execute ( ' insert into sale_order_line_invoice_rel (order_line_id,invoice_id) values ( %s , %s ) ' , ( line . id , inv_id ) )
self . write ( cr , uid , [ line . id ] , { ' invoiced ' : True } )
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
wf_service = netsvc . LocalService ( " workflow " )
2012-01-31 10:53:22 +00:00
for sale_id in sales :
wf_service . 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 ) :
2008-12-14 16:46:57 +00:00
for line in self . browse ( cr , uid , ids , context = context ) :
if line . invoiced :
2011-09-18 13:41:16 +00:00
raise osv . except_osv ( _ ( ' Invalid action ! ' ) , _ ( ' You cannot cancel a sale order line that has already been invoiced! ' ) )
2010-08-03 04:55:12 +00:00
for move_line in line . move_ids :
if move_line . state != ' cancel ' :
2010-07-20 13:02:23 +00:00
raise osv . except_osv (
2010-12-13 06:07:23 +00:00
_ ( ' Could not cancel sales order line! ' ) ,
_ ( ' You must first cancel stock moves attached to this sales order line. ' ) )
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 ) :
2008-07-22 15:11:28 +00:00
wf_service = netsvc . LocalService ( " workflow " )
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 ) :
2008-07-22 15:11:28 +00:00
wf_service . trg_write ( uid , ' sale.order ' , line . order_id . id , cr )
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
2010-04-13 10:35:39 +00:00
def copy_data ( self , cr , uid , id , default = None , context = None ) :
2008-07-22 15:11:28 +00:00
if not default :
default = { }
2010-08-04 18:59:19 +00:00
default . update ( { ' state ' : ' draft ' , ' move_ids ' : [ ] , ' invoiced ' : False , ' invoice_lines ' : [ ] } )
2010-04-13 10:35:39 +00:00
return super ( sale_order_line , self ) . copy_data ( cr , uid , id , default , context = context )
2008-07-22 15:11:28 +00:00
2011-07-06 09:29:17 +00:00
def product_packaging_change ( self , cr , uid , ids , pricelist , product , qty = 0 , uom = False ,
2011-07-05 13:11:02 +00:00
partner_id = False , packaging = False , flag = False , context = None ) :
2011-07-06 09:29:17 +00:00
if not product :
return { ' value ' : { ' product_packaging ' : False } }
2011-06-24 09:39:06 +00:00
product_obj = self . pool . get ( ' product.product ' )
product_uom_obj = self . pool . get ( ' product.uom ' )
2011-07-05 13:11:02 +00:00
pack_obj = self . pool . get ( ' product.packaging ' )
2009-08-26 13:41:53 +00:00
warning = { }
2011-06-24 09:39:06 +00:00
result = { }
2011-04-29 11:32:27 +00:00
warning_msgs = ' '
2011-07-05 13:11:02 +00:00
if flag :
2011-07-06 09:29:17 +00:00
res = self . product_id_change ( cr , uid , ids , pricelist = pricelist ,
2011-07-05 13:11:02 +00:00
product = product , qty = qty , uom = uom , partner_id = partner_id ,
2011-07-18 09:53:44 +00:00
packaging = packaging , flag = False , context = context )
2011-07-05 13:11:02 +00:00
warning_msgs = res . get ( ' warning ' ) and res [ ' warning ' ] [ ' message ' ]
2011-07-06 09:29:17 +00:00
2011-07-05 13:11:02 +00:00
products = product_obj . browse ( cr , uid , product , context = context )
if not products . packaging :
packaging = result [ ' product_packaging ' ] = False
elif not packaging and products . packaging and not flag :
2011-06-24 09:39:06 +00:00
packaging = products . packaging [ 0 ] . id
2009-01-18 22:28:05 +00:00
result [ ' product_packaging ' ] = packaging
2011-07-06 09:29:17 +00:00
2008-09-07 23:24:39 +00:00
if packaging :
2011-06-24 09:39:06 +00:00
default_uom = products . uom_id and products . uom_id . id
2011-07-05 13:11:02 +00:00
pack = pack_obj . browse ( cr , uid , packaging , context = context )
2008-09-07 23:24:39 +00:00
q = product_uom_obj . _compute_qty ( cr , uid , uom , pack . qty , default_uom )
2008-09-16 08:51:19 +00:00
# qty = qty - qty % q + q
2009-05-14 10:27:20 +00:00
if qty and ( q and not ( qty % q ) == 0 ) :
2010-12-07 13:36:57 +00:00
ean = pack . ean or _ ( ' (n/a) ' )
2008-09-16 08:51:19 +00:00
qty_pack = pack . qty
type_ul = pack . ul
2011-07-05 13:11:02 +00:00
if not warning_msgs :
warn_msg = _ ( " You selected a quantity of %d Units. \n "
" But it ' s not compatible with the selected packaging. \n "
" Here is a proposition of quantities according to the packaging: \n "
" EAN: %s Quantity: %s Type of ul: %s " ) % \
( qty , ean , qty_pack , type_ul . name )
warning_msgs + = _ ( " Picking Information ! : " ) + warn_msg + " \n \n "
warning = {
2011-06-24 09:39:06 +00:00
' title ' : _ ( ' Configuration Error ! ' ) ,
2011-07-05 13:11:02 +00:00
' message ' : warning_msgs
}
result [ ' product_uom_qty ' ] = qty
2011-06-24 09:39:06 +00:00
return { ' value ' : result , ' warning ' : warning }
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 { }
2011-09-17 11:40:55 +00:00
lang = lang or context . get ( ' lang ' , False )
2009-01-23 13:45:30 +00:00
if not partner_id :
2010-12-13 06:07:23 +00:00
raise osv . except_osv ( _ ( ' No Customer Defined ! ' ) , _ ( ' You have to select a customer in the sales form ! \n Please set one customer before choosing a product. ' ) )
2009-08-26 13:41:53 +00:00
warning = { }
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 ' )
2011-09-17 11:40:55 +00:00
context = { ' lang ' : lang , ' partner_id ' : partner_id }
2008-07-22 15:11:28 +00:00
if partner_id :
lang = partner_obj . browse ( cr , uid , partner_id ) . lang
2011-09-17 11:40:55 +00:00
context_partner = { ' lang ' : lang , ' partner_id ' : partner_id }
2008-07-22 15:11:28 +00:00
if not product :
2009-08-26 13:41:53 +00:00
return { ' value ' : { ' th_weight ' : 0 , ' product_packaging ' : False ,
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
2011-07-06 09:35:40 +00:00
res = self . product_packaging_change ( cr , uid , ids , pricelist , product , qty , uom , partner_id , packaging , context = context )
2011-06-24 09:39:06 +00:00
result = res . get ( ' value ' , { } )
2011-07-05 13:11:02 +00:00
warning_msgs = res . get ( ' warning ' ) and res [ ' warning ' ] [ ' message ' ] or ' '
2008-09-07 23:24:39 +00:00
product_obj = product_obj . browse ( cr , uid , product , context = context )
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
2008-09-07 23:24:39 +00:00
if product_obj . description_sale :
result [ ' notes ' ] = product_obj . description_sale
2009-01-20 06:04:07 +00:00
fpos = fiscal_position and self . pool . get ( ' account.fiscal.position ' ) . browse ( cr , uid , fiscal_position ) or False
2008-07-22 15:11:28 +00:00
if update_tax : #The quantity only have changed
2008-09-07 23:24:39 +00:00
result [ ' delay ' ] = ( product_obj . sale_delay or 0.0 )
2009-01-20 06:04:07 +00:00
result [ ' tax_id ' ] = self . pool . get ( ' account.fiscal.position ' ) . map_tax ( cr , uid , fpos , product_obj . taxes_id )
2010-02-19 12:55:26 +00:00
result . update ( { ' type ' : product_obj . procure_method } )
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 ]
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 ) ] }
2009-02-04 22:18:53 +00:00
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
2012-02-15 17:12:15 +00:00
compare_qty = float_compare ( product_obj . virtual_available * uom2 . factor , qty * product_obj . uom_id . factor , precision_rounding = product_obj . uom_id . rounding )
2011-12-29 04:43:21 +00:00
if ( product_obj . type == ' product ' ) and int ( compare_qty ) == - 1 \
2010-06-26 15:15:23 +00:00
and ( product_obj . procure_method == ' make_to_stock ' ) :
2011-05-02 07:12:07 +00:00
warn_msg = _ ( ' You plan to sell %.2f %s but you only have %.2f %s available ! \n The real stock is %.2f %s . (without reservations) ' ) % \
2010-06-26 15:15:23 +00:00
( qty , uom2 and uom2 . name or product_obj . uom_id . name ,
max ( 0 , product_obj . virtual_available ) , product_obj . uom_id . name ,
max ( 0 , product_obj . qty_available ) , product_obj . uom_id . name )
2011-05-04 09:49:34 +00:00
warning_msgs + = _ ( " Not enough stock ! : " ) + warn_msg + " \n \n "
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 :
price = self . pool . get ( ' product.pricelist ' ) . price_get ( cr , uid , [ pricelist ] ,
product , qty or 1.0 , partner_id , {
2011-04-04 10:01:41 +00:00
' uom ' : uom or result . get ( ' product_uom ' ) ,
2008-09-09 13:57:16 +00:00
' date ' : date_order ,
} ) [ pricelist ]
if price is False :
2011-05-02 07:12:07 +00:00
warn_msg = _ ( " Couldn ' t find a pricelist line matching this product and quantity. \n "
" 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 :
2008-09-29 08:49:12 +00:00
result . update ( { ' price_unit ' : price } )
2011-05-02 07:12:07 +00:00
if warning_msgs :
warning = {
2011-05-04 09:49:34 +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 ' ] )
2008-07-22 15:11:28 +00:00
res = 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 ,
2011-12-22 12:09:59 +00:00
date_order = date_order , context = context )
2008-07-22 15:11:28 +00:00
if ' product_uom ' in res [ ' value ' ] :
del res [ ' value ' ] [ ' product_uom ' ]
if not uom :
res [ ' value ' ] [ ' price_unit ' ] = 0.0
return res
2009-08-26 13:41:53 +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 ' ] :
2011-09-18 13:41: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
2006-12-07 13:41:40 +00:00
sale_order_line ( )
2008-06-20 11:49:23 +00:00
class sale_config_picking_policy ( osv . osv_memory ) :
2009-08-26 13:41:53 +00:00
_name = ' sale.config.picking_policy '
2009-12-15 15:55:52 +00:00
_inherit = ' res.config '
2011-11-04 04:50:18 +00:00
2008-07-22 15:11:28 +00:00
_columns = {
2009-08-26 13:41:53 +00:00
' name ' : fields . char ( ' Name ' , size = 64 ) ,
2011-08-03 10:18:38 +00:00
' sale_orders ' : fields . boolean ( ' Based on Sales Orders ' , ) ,
' deli_orders ' : fields . boolean ( ' Based on Delivery Orders ' ) ,
' task_work ' : fields . boolean ( ' Based on Tasks \' Work ' ) ,
' timesheet ' : fields . boolean ( ' Based on Timesheet ' ) ,
2008-09-23 20:56:51 +00:00
' order_policy ' : fields . selection ( [
2009-08-26 13:41:53 +00:00
( ' manual ' , ' Invoice Based on Sales Orders ' ) ,
( ' picking ' , ' Invoice Based on Deliveries ' ) ,
2011-08-06 13:24:20 +00:00
] , ' Main Method Based On ' , required = True , help = " You can generate invoices based on sales orders or based on shippings. " ) ,
2011-10-02 12:59:58 +00:00
' charge_delivery ' : fields . boolean ( ' Do you charge the delivery? ' ) ,
2011-08-06 13:24:20 +00:00
' time_unit ' : fields . many2one ( ' product.uom ' , ' Main Working Time Unit ' )
2008-07-22 15:11:28 +00:00
}
2009-08-26 13:41:53 +00:00
_defaults = {
2010-08-18 06:22:59 +00:00
' order_policy ' : ' manual ' ,
2011-10-18 06:48:04 +00:00
' time_unit ' : lambda self , cr , uid , c : self . pool . get ( ' product.uom ' ) . search ( cr , uid , [ ( ' name ' , ' = ' , _ ( ' Hour ' ) ) ] , context = c ) and self . pool . get ( ' product.uom ' ) . search ( cr , uid , [ ( ' name ' , ' = ' , _ ( ' Hour ' ) ) ] , context = c ) [ 0 ] or False ,
2008-06-20 11:49:23 +00:00
}
2009-08-26 13:41:53 +00:00
2011-08-03 10:18:38 +00:00
def onchange_order ( self , cr , uid , ids , sale , deli , context = None ) :
res = { }
2011-10-02 12:59:58 +00:00
if sale :
2011-08-03 10:18:38 +00:00
res . update ( { ' order_policy ' : ' manual ' } )
2011-10-02 12:59:58 +00:00
elif deli :
res . update ( { ' order_policy ' : ' picking ' } )
2011-08-03 10:18:38 +00:00
return { ' value ' : res }
2011-08-06 13:24:20 +00:00
2009-12-15 15:55:52 +00:00
def execute ( self , cr , uid , ids , context = None ) :
2011-04-15 09:13:34 +00:00
ir_values_obj = self . pool . get ( ' ir.values ' )
2011-08-03 10:18:38 +00:00
data_obj = self . pool . get ( ' ir.model.data ' )
menu_obj = self . pool . get ( ' ir.ui.menu ' )
module_obj = self . pool . get ( ' ir.module.module ' )
module_upgrade_obj = self . pool . get ( ' base.module.upgrade ' )
module_name = [ ]
2011-08-06 13:24:20 +00:00
2011-10-02 12:59:58 +00:00
group_id = data_obj . get_object ( cr , uid , ' base ' , ' group_sale_salesman ' ) . id
2011-08-06 13:24:20 +00:00
2011-08-03 10:18:38 +00:00
wizard = self . browse ( cr , uid , ids ) [ 0 ]
2011-08-06 13:24:20 +00:00
2011-08-03 10:18:38 +00:00
if wizard . sale_orders :
2011-10-02 12:59:58 +00:00
menu_id = data_obj . get_object ( cr , uid , ' sale ' , ' menu_invoicing_sales_order_lines ' ) . id
2011-11-04 04:50:18 +00:00
menu_obj . write ( cr , uid , menu_id , { ' groups_id ' : [ ( 4 , group_id ) ] } )
2011-08-06 13:24:20 +00:00
2011-08-03 10:18:38 +00:00
if wizard . deli_orders :
2011-10-02 12:59:58 +00:00
menu_id = data_obj . get_object ( cr , uid , ' sale ' , ' menu_action_picking_list_to_invoice ' ) . id
menu_obj . write ( cr , uid , menu_id , { ' groups_id ' : [ ( 4 , group_id ) ] } )
2011-08-06 13:24:20 +00:00
2011-08-03 10:18:38 +00:00
if wizard . task_work :
module_name . append ( ' project_timesheet ' )
2011-11-08 15:21:42 +00:00
module_name . append ( ' project_mrp ' )
2011-08-03 10:18:38 +00:00
module_name . append ( ' account_analytic_analysis ' )
2011-08-06 13:24:20 +00:00
2011-08-03 10:18:38 +00:00
if wizard . timesheet :
module_name . append ( ' account_analytic_analysis ' )
2011-08-06 13:24:20 +00:00
2011-08-03 10:18:38 +00:00
if wizard . charge_delivery :
2011-10-02 12:59:58 +00:00
module_name . append ( ' delivery ' )
2011-08-06 13:24:20 +00:00
2011-08-03 10:18:38 +00:00
if len ( module_name ) :
module_ids = [ ]
need_install = False
module_ids = [ ]
for module in module_name :
2011-09-27 08:38:09 +00:00
data_id = module_obj . name_search ( cr , uid , module , [ ] , ' = ' )
2011-08-03 10:18:38 +00:00
module_ids . append ( data_id [ 0 ] [ 0 ] )
2011-08-06 13:24:20 +00:00
2011-08-03 10:18:38 +00:00
for module in module_obj . browse ( cr , uid , module_ids ) :
if module . state == ' uninstalled ' :
module_obj . state_update ( cr , uid , [ module . id ] , ' to install ' , [ ' uninstalled ' ] , context )
need_install = True
cr . commit ( )
if need_install :
pooler . restart_pool ( cr . dbname , update_module = True ) [ 1 ]
2011-08-06 13:24:20 +00:00
2011-10-02 12:59:58 +00:00
if wizard . time_unit :
2011-10-18 15:31:52 +00:00
prod_id = data_obj . get_object ( cr , uid , ' product ' , ' product_consultant ' ) . id
2011-10-02 12:59:58 +00:00
product_obj = self . pool . get ( ' product.product ' )
product_obj . write ( cr , uid , prod_id , { ' uom_id ' : wizard . time_unit . id , ' uom_po_id ' : wizard . time_unit . id } )
2011-11-04 04:50:18 +00:00
ir_values_obj . set ( cr , uid , ' default ' , False , ' order_policy ' , [ ' sale.order ' ] , wizard . order_policy )
2011-10-02 12:59:58 +00:00
if wizard . task_work and wizard . time_unit :
company_id = self . pool . get ( ' res.users ' ) . browse ( cr , uid , uid ) . company_id . id
self . pool . get ( ' res.company ' ) . write ( cr , uid , [ company_id ] , {
' project_time_mode_id ' : wizard . time_unit . id
} , context = context )
2010-08-06 12:13:12 +00:00
2009-08-26 13:41:53 +00:00
sale_config_picking_policy ( )
2008-07-23 14:41:47 +00:00
2010-09-01 20:29:38 +00:00
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: