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
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 _
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 "
_description = " Sale Shop "
_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
}
2006-12-07 13:41:40 +00:00
2010-06-22 06:38:37 +00:00
sale_shop ( )
2009-08-26 13:41:53 +00:00
2010-04-13 10:35:39 +00:00
def _incoterm_get ( self , cr , uid , context = None ) :
if context is None :
2010-08-06 12:13:12 +00:00
context = { }
2008-07-22 15:11:28 +00:00
cr . execute ( ' select code, code|| \' , \' ||name from stock_incoterms where active ' )
return cr . fetchall ( )
2006-12-07 13:41:40 +00:00
class sale_order ( osv . osv ) :
2008-07-22 15:11:28 +00:00
_name = " sale.order "
_description = " Sale 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 ) :
if context is None :
context = { }
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 ) :
if context is None :
context = { }
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 ' )
2010-04-13 10:35:39 +00:00
if context is None :
context = { }
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 ) :
2010-04-13 10:35:39 +00:00
if context is None :
context = { }
2009-08-26 13:41:53 +00:00
if not ids :
return { }
2008-08-24 23:23:45 +00:00
res = { }
for id in ids :
2009-08-26 13:41:53 +00:00
res [ id ] = [ 0.0 , 0.0 ]
2008-08-24 23:23:45 +00:00
cr . execute ( ''' SELECT
2010-10-15 08:48:13 +00:00
p . sale_id , sum ( m . product_qty ) , mp . state as mp_state
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
2010-10-15 08:48:13 +00:00
p . sale_id IN % s GROUP BY mp . state , p . sale_id ''' , (tuple(ids),))
2010-05-10 11:00:27 +00:00
for oid , nbr , mp_state in cr . fetchall ( ) :
if mp_state == ' cancel ' :
2008-08-24 23:23:45 +00:00
continue
2010-05-10 11:00:27 +00:00
if mp_state == ' done ' :
2008-08-24 23:23:45 +00:00
res [ oid ] [ 0 ] + = nbr or 0.0
res [ oid ] [ 1 ] + = nbr or 0.0
else :
res [ oid ] [ 1 ] + = nbr or 0.0
for r in res :
if not res [ r ] [ 1 ] :
res [ r ] = 0.0
else :
res [ r ] = 100.0 * res [ r ] [ 0 ] / res [ r ] [ 1 ]
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
2008-08-24 23:23:45 +00:00
return res
def _invoiced_rate ( self , cursor , user , ids , name , arg , context = None ) :
2010-04-13 10:35:39 +00:00
if context is None :
context = { }
2008-08-24 23:23:45 +00:00
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 ) :
2010-04-13 10:35:39 +00:00
if context is None :
context = { }
2008-07-22 15:11:28 +00:00
res = { }
for sale in self . browse ( cursor , user , ids , context = context ) :
res [ sale . id ] = True
for invoice in sale . invoice_ids :
2009-08-26 13:41:53 +00:00
if invoice . state != ' paid ' :
2008-07-22 15:11:28 +00:00
res [ sale . id ] = False
break
if not sale . invoice_ids :
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 ) :
if context is None :
context = { }
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 ) :
if context is None :
context = { }
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 ( )
2008-07-22 15:11:28 +00:00
_columns = {
2010-06-24 06:40:29 +00:00
' name ' : fields . char ( ' Order Reference ' , size = 64 , required = True ,
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-02-09 08:31:46 +00:00
' origin ' : fields . char ( ' Source document ' , size = 64 , help = " Reference of the document that generated this sale 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 ' ) ,
( ' manual ' , ' Manual In Progress ' ) ,
( ' progress ' , ' In Progress ' ) ,
( ' shipping_except ' , ' Shipping Exception ' ) ,
( ' invoice_except ' , ' Invoice Exception ' ) ,
( ' done ' , ' Done ' ) ,
( ' cancel ' , ' Cancelled ' )
2010-07-17 13:29:03 +00:00
] , ' Order State ' , readonly = True , help = " Gives the state of the quotation or sale 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 date ' Ordered Date ' . " , select = True ) ,
2010-02-16 17:21:39 +00:00
' date_order ' : fields . date ( ' Ordered Date ' , required = True , readonly = True , states = { ' draft ' : [ ( ' readonly ' , False ) ] } ) ,
2010-06-21 13:59:11 +00:00
' create_date ' : fields . date ( ' Creation Date ' , readonly = True , help = " Date on which sale order is created " ) ,
' date_confirm ' : fields . date ( ' Confirmation Date ' , readonly = True , help = " Date on which sale 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-06-21 13:59:11 +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 sale 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-05-25 10:38:59 +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 sale order " ) ,
2008-07-22 15:11:28 +00:00
2010-06-21 13:59:11 +00:00
' incoterm ' : fields . selection ( _incoterm_get , ' Incoterm ' , size = 3 , help = " Incoterm which stands for ' International Commercial terms ' implies its a series of sales terms which are used in the commercial transaction " ) ,
2009-08-26 13:41:53 +00:00
' picking_policy ' : fields . selection ( [ ( ' direct ' , ' Partial Delivery ' ) , ( ' one ' , ' Complete Delivery ' ) ] ,
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 ( [
2009-08-26 13:41:53 +00:00
( ' prepaid ' , ' Payment Before Delivery ' ) ,
( ' manual ' , ' Shipping & Manual Invoice ' ) ,
( ' postpaid ' , ' Invoice on Order After Delivery ' ) ,
2010-10-18 15:26:41 +00:00
( ' picking ' , ' Invoice from Delivery ' ) ,
2009-08-26 13:41:53 +00:00
] , ' Shipping Policy ' , required = True , readonly = True , states = { ' draft ' : [ ( ' readonly ' , False ) ] } ,
2008-07-22 15:11:28 +00:00
help = """ The Shipping Policy is used to synchronise invoice and delivery operations.
2010-02-09 08:31:46 +00:00
- The ' Pay before delivery ' choice will first generate the invoice and then generate the picking order after the payment of this invoice .
- The ' Shipping & Manual Invoice ' will create the picking order directly and wait for the user to manually click on the ' Invoice ' button to generate the draft invoice .
2009-11-25 09:31:44 +00:00
- The ' Invoice on Order After Delivery ' choice will generate the draft invoice based on sale order after all picking lists have been finished .
2010-02-09 08:31:46 +00:00
- The ' Invoice from the picking ' choice is used to create an invoice during the picking process . """ ),
2010-05-25 10:38:59 +00:00
' pricelist_id ' : fields . many2one ( ' product.pricelist ' , ' Pricelist ' , required = True , readonly = True , states = { ' draft ' : [ ( ' readonly ' , False ) ] } , help = " Pricelist version for current sale order " ) ,
2010-06-21 13:59:11 +00:00
' project_id ' : fields . many2one ( ' account.analytic.account ' , ' Analytic Account ' , readonly = True , states = { ' draft ' : [ ( ' readonly ' , False ) ] } , help = " The analytic account related to a sale 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 ) ] } ) ,
2009-10-13 07:31:44 +00:00
' invoice_ids ' : fields . many2many ( ' account.invoice ' , ' sale_order_invoice_rel ' , ' order_id ' , ' invoice_id ' , ' Invoices ' , help = " This is the list of invoices that have been generated for this sale order. The same sale order may have been invoiced in several times (by line for example). " ) ,
2009-11-13 05:41:16 +00:00
' picking_ids ' : fields . one2many ( ' stock.picking ' , ' sale_id ' , ' Related Picking ' , readonly = True , help = " This is the list of picking list that have been generated for this invoice " ) ,
2010-10-10 17:32:39 +00:00
' shipped ' : fields . boolean ( ' Delivered ' , readonly = True , help = " It indicates that the sale order has been delivered. This field is updated only after the scheduler have been launched ! " ) ,
2008-08-24 23:23:45 +00:00
' picked_rate ' : fields . function ( _picked_rate , method = True , string = ' Picked ' , type = ' float ' ) ,
' invoiced_rate ' : fields . function ( _invoiced_rate , method = True , string = ' Invoiced ' , type = ' float ' ) ,
2008-07-22 15:11:28 +00:00
' invoiced ' : fields . function ( _invoiced , method = True , string = ' Paid ' ,
2010-06-21 13:59:11 +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
2010-03-06 20:52:19 +00:00
' amount_untaxed ' : fields . function ( _amount_all , method = True , 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-06-21 13:59:11 +00:00
multi = ' sums ' , help = " The amount without tax " ) ,
2010-03-06 20:52:19 +00:00
' amount_tax ' : fields . function ( _amount_all , method = True , 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-06-21 13:59:11 +00:00
multi = ' sums ' , help = " The tax amount " ) ,
2010-03-06 20:52:19 +00:00
' amount_total ' : fields . function ( _amount_all , method = True , 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-06-21 13:59:11 +00:00
multi = ' sums ' , help = " The total amount " ) ,
2008-12-07 12:45:27 +00:00
2010-09-18 05:23:21 +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 ) ,
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 ' ) ,
2010-10-08 21:32:05 +00:00
' company_id ' : fields . related ( ' shop_id ' , ' company_id ' , type = ' many2one ' , relation = ' res.company ' , string = ' Company ' , store = True )
2008-07-22 15:11:28 +00:00
}
_defaults = {
2010-06-22 07:15:50 +00:00
' picking_policy ' : ' direct ' ,
' date_order ' : time . strftime ( ' % Y- % m- %d ' ) ,
' order_policy ' : ' manual ' ,
' 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 ' ] ,
}
_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
if context is None :
context = { }
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 :
raise osv . except_osv ( _ ( ' Invalid action ! ' ) , _ ( ' Cannot delete Sale Order(s) which are already confirmed ! ' ) )
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-10-16 14:02:41 +00:00
message = _ ( " The sale 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
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 ) :
if context is None :
context = { }
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-04-13 10:35:39 +00:00
if context is None :
context = { }
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 ) :
if context is None :
context = { }
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 ) :
if context is None :
context = { }
2008-07-22 15:11:28 +00:00
return True
2006-12-07 13:41:40 +00:00
2010-03-03 13:58:10 +00:00
#FIXME: the method should return the list of invoices created (invoice_ids)
# and not the id of the last invoice created (res). The problem is that we
# cannot change it directly since the method is called by the sale order
# workflow and I suppose it expects a single id...
2010-04-13 10:35:39 +00:00
def _inv_get ( self , cr , uid , order , context = None ) :
if context is None :
context = { }
2008-07-22 15:11:28 +00:00
return { }
2010-04-13 10:35:39 +00:00
def _make_invoice ( self , cr , uid , order , lines , context = None ) :
2010-06-22 07:00:51 +00:00
journal_obj = self . pool . get ( ' account.journal ' )
inv_obj = self . pool . get ( ' account.invoice ' )
2010-10-15 09:42:54 +00:00
obj_invoice_line = self . pool . get ( ' account.invoice.line ' )
2010-04-13 10:35:39 +00:00
if context is None :
context = { }
2010-06-22 07:00:51 +00:00
2010-10-15 08:48:13 +00:00
journal_ids = journal_obj . 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 ! ' ) ,
_ ( ' There is no sale journal defined for this company: " %s " (id: %d ) ' ) % ( order . company_id . name , order . company_id . id ) )
2008-07-22 15:11:28 +00:00
a = order . partner_id . property_account_receivable . id
2010-10-21 09:30:04 +00:00
pay_term = order . payment_term and order . payment_term . id or False
2010-10-05 16:11:30 +00:00
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 )
2008-07-22 15:11:28 +00:00
for preinv in order . invoice_ids :
2010-10-05 16:11:30 +00:00
if preinv . state not in ( ' cancel ' , ) and preinv . id not in from_line_invoice_ids :
2008-07-22 15:11:28 +00:00
for preline in preinv . invoice_line :
2010-10-15 09:42:54 +00:00
inv_line_id = obj_invoice_line . copy ( cr , uid , preline . id , { ' invoice_id ' : False , ' price_unit ' : - preline . price_unit } )
2008-07-22 15:11:28 +00:00
lines . append ( inv_line_id )
inv = {
' name ' : order . client_order_ref or order . name ,
' origin ' : order . name ,
' type ' : ' out_invoice ' ,
2009-08-26 13:41:53 +00:00
' reference ' : " P %d SO %d " % ( order . partner_id . id , order . id ) ,
2008-07-22 15:11:28 +00:00
' account_id ' : a ,
' 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 ,
' payment_term ' : pay_term ,
2010-05-19 06:31:40 +00:00
' fiscal_position ' : order . fiscal_position . id or order . partner_id . property_account_position . id ,
2010-10-15 09:42:54 +00:00
' date_invoice ' : context . get ( ' date_invoice ' , False ) ,
' 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
}
2008-09-08 22:53:40 +00:00
inv . update ( self . _inv_get ( cr , uid , order ) )
2010-04-13 10:35:39 +00:00
inv_id = inv_obj . create ( cr , uid , inv , context = context )
2009-08-26 13:41:53 +00:00
data = inv_obj . onchange_payment_term_date_invoice ( cr , uid , [ inv_id ] , pay_term , time . strftime ( ' % Y- % m- %d ' ) )
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 ) )
2010-10-15 08:55:18 +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 ,
return {
2010-10-15 10:27: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 ' )
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
2009-11-26 13:54:00 +00:00
for o in self . browse ( cr , uid , ids ) :
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 :
for o in self . browse ( cr , uid , ids ) :
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-09-29 15:46:27 +00:00
for sale in self . browse ( cr , uid , ids ) :
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 )
#
# Update the sale order state.
#
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-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 (
_ ( ' Could not cancel sale order ! ' ) ,
2010-02-09 08:31:46 +00:00
_ ( ' You must first cancel all picking attached to this sale order. ' ) )
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 (
_ ( ' Could not cancel this sale order ! ' ) ,
_ ( ' You must first cancel all invoices attached to this sale 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-10-16 14:02:41 +00:00
message = _ ( " The sale 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
def action_wait ( self , cr , uid , ids , * args ) :
for o in self . browse ( cr , uid , ids ) :
2008-11-06 21:44:09 +00:00
if ( o . order_policy == ' manual ' ) :
2010-03-03 13:58:10 +00:00
self . write ( cr , uid , [ o . id ] , { ' state ' : ' manual ' , ' date_confirm ' : time . strftime ( ' % Y- % m- %d ' ) } )
2008-07-22 15:11:28 +00:00
else :
2010-03-03 13:58:10 +00:00
self . write ( cr , uid , [ o . id ] , { ' state ' : ' progress ' , ' date_confirm ' : time . strftime ( ' % Y- % m- %d ' ) } )
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-10-16 14:02:41 +00:00
message = _ ( " The quotation ' %s ' has been converted to a sale 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
def action_ship_create ( self , cr , uid , ids , * args ) :
2010-10-15 09:24:14 +00:00
wf_service = netsvc . LocalService ( " workflow " )
2009-08-26 13:41:53 +00:00
picking_id = False
2008-09-09 09:11:44 +00:00
company = self . pool . get ( ' res.users ' ) . browse ( cr , uid , uid ) . company_id
2008-07-22 15:11:28 +00:00
for order in self . browse ( cr , uid , ids , context = { } ) :
2010-06-21 18:40:58 +00:00
proc_ids = [ ]
2008-07-22 15:11:28 +00:00
output_id = order . shop_id . warehouse_id . lot_output_id . id
picking_id = False
for line in order . order_line :
2009-08-26 13:41:53 +00:00
proc_id = False
2010-03-01 05:09:11 +00:00
date_planned = datetime . now ( ) + relativedelta ( days = line . delay or 0.0 )
2010-08-10 05:46:09 +00:00
date_planned = ( date_planned - timedelta ( days = company . security_lead ) ) . strftime ( ' % Y- % m- %d % H: % M: % S ' )
2008-07-22 15:11:28 +00:00
if line . state == ' done ' :
continue
2010-06-21 18:40:58 +00:00
move_id = False
2008-07-22 15:11:28 +00:00
if line . product_id and line . product_id . product_tmpl_id . type in ( ' product ' , ' consu ' ) :
location_id = order . shop_id . warehouse_id . lot_stock_id . id
if not picking_id :
2010-03-12 11:50:12 +00:00
pick_name = self . pool . get ( ' ir.sequence ' ) . get ( cr , uid , ' stock.picking.out ' )
2008-07-22 15:11:28 +00:00
picking_id = self . pool . get ( ' stock.picking ' ) . create ( cr , uid , {
2010-03-12 11:50:12 +00:00
' name ' : pick_name ,
2008-07-22 15:11:28 +00:00
' origin ' : order . name ,
' 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 ' ,
2009-12-01 14:54:18 +00:00
' company_id ' : order . company_id . id ,
2008-07-22 15:11:28 +00:00
} )
move_id = self . pool . get ( ' stock.move ' ) . create ( cr , uid , {
' name ' : line . name [ : 64 ] ,
' picking_id ' : picking_id ,
' product_id ' : line . product_id . id ,
2010-09-21 12:34:53 +00:00
' date ' : date_planned ,
2010-06-13 17:32:49 +00:00
' date_expected ' : date_planned ,
2008-07-22 15:11:28 +00:00
' product_qty ' : line . product_uom_qty ,
' product_uom ' : line . product_uom . id ,
' product_uos_qty ' : line . product_uos_qty ,
' product_uos ' : ( line . product_uos and line . product_uos . id ) \
or line . product_uom . id ,
2009-08-26 13:41:53 +00:00
' product_packaging ' : line . product_packaging . id ,
' address_id ' : line . address_allotment_id . id or order . partner_shipping_id . id ,
2008-07-22 15:11:28 +00:00
' location_id ' : location_id ,
' location_dest_id ' : output_id ,
' sale_line_id ' : line . id ,
' tracking_id ' : False ,
2008-09-26 09:24:48 +00:00
' state ' : ' draft ' ,
#'state': 'waiting',
2008-07-22 15:11:28 +00:00
' note ' : line . notes ,
2009-12-01 14:54:18 +00:00
' company_id ' : order . company_id . id ,
2008-07-22 15:11:28 +00:00
} )
2010-06-21 18:40:58 +00:00
if line . product_id :
2010-05-27 12:47:06 +00:00
proc_id = self . pool . get ( ' procurement.order ' ) . create ( cr , uid , {
2010-06-21 18:40:58 +00:00
' name ' : line . name ,
2008-07-22 15:11:28 +00:00
' 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 ,
2009-02-06 05:08:12 +00:00
' product_uos_qty ' : ( line . product_uos and line . product_uos_qty ) \
or line . product_uom_qty ,
2008-07-22 15:11:28 +00:00
' 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 ,
' property_ids ' : [ ( 6 , 0 , [ x . id for x in line . property_ids ] ) ] ,
2009-12-01 14:54:18 +00:00
' company_id ' : order . company_id . id ,
2008-07-22 15:11:28 +00:00
} )
2010-06-21 18:40:58 +00:00
proc_ids . append ( proc_id )
2008-07-22 15:11:28 +00:00
self . pool . get ( ' sale.order.line ' ) . write ( cr , uid , [ line . id ] , { ' procurement_id ' : proc_id } )
val = { }
2010-06-21 18:40:58 +00:00
for proc_id in proc_ids :
wf_service . trg_validate ( uid , ' procurement.order ' , proc_id , ' button_confirm ' , cr )
2010-09-01 06:45:19 +00:00
if picking_id :
wf_service . trg_validate ( uid , ' stock.picking ' , picking_id , ' button_confirm ' , cr )
2010-10-08 06:13:09 +00:00
2009-08-26 13:41:53 +00:00
if order . state == ' shipping_except ' :
2008-07-22 15:11:28 +00:00
val [ ' state ' ] = ' progress '
2008-12-14 16:46:57 +00:00
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
2008-07-22 15:11:28 +00:00
self . write ( cr , uid , [ order . id ] , val )
return True
2010-04-13 10:35:39 +00:00
def action_ship_end ( self , cr , uid , ids , context = None ) :
if context is None :
context = { }
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 ,
' date ' : time . strftime ( ' % Y- % m- %d ' ) ,
' canal_id ' : False ,
' 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 = { }
context = context or { }
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 ) :
if context is None :
context = { }
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
_name = ' sale.order.line '
2010-05-19 18:32:32 +00:00
_description = ' Sale 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 ) ] } ) ,
2009-12-21 13:14:12 +00:00
' sequence ' : fields . integer ( ' Sequence ' , help = " Gives the sequence order when displaying a list of sale order lines. " ) ,
2010-10-21 09:30:04 +00:00
' delay ' : fields . float ( ' Delivery Lead Time ' , required = True , help = " Number of days between the order confirmation the 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 ) ] } ) ,
2010-03-06 20:52:19 +00:00
' price_subtotal ' : fields . function ( _amount_line , method = True , 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 ) ] } ) ,
' type ' : fields . selection ( [ ( ' make_to_stock ' , ' from stock ' ) , ( ' make_to_order ' , ' on order ' ) ] , ' Procurement Method ' , required = True , readonly = True , states = { ' draft ' : [ ( ' readonly ' , False ) ] } ) ,
' 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 ' ) ,
2010-10-21 09:30:04 +00:00
' product_uom_qty ' : fields . float ( ' Quantity (UoM) ' , digits = ( 16 , 2 ) , required = True , readonly = True , states = { ' draft ' : [ ( ' readonly ' , False ) ] } ) ,
' product_uom ' : fields . many2one ( ' product.uom ' , ' Unit of Measure ' , required = True , readonly = True , states = { ' draft ' : [ ( ' readonly ' , False ) ] } ) ,
' product_uos_qty ' : fields . float ( ' Quantity (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 ) ] } ) ,
2009-01-27 11:15:46 +00:00
' number_packages ' : fields . function ( _number_packages , method = True , 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 ) ] } ) ,
2009-12-30 05:56:01 +00:00
' state ' : fields . selection ( [ ( ' draft ' , ' Draft ' ) , ( ' confirmed ' , ' Confirmed ' ) , ( ' done ' , ' Done ' ) , ( ' cancel ' , ' Cancelled ' ) , ( ' exception ' , ' Exception ' ) ] , ' State ' , required = True , readonly = True ,
2010-07-18 19:43:57 +00:00
help = ' * The \' Draft \' state is set when the related sale order in draft state. \
\n * The \' Confirmed \' state is set when the related sale order is confirmed. \
\n * The \' Exception \' state is set when the related sale order is set as exception. \
\n * The \' Done \' state is set when the sale order line has been picked. \
\n * The \' Cancelled \' state is set when a user cancel the sale order related. ' ) ,
2009-10-28 10:13:32 +00:00
' order_partner_id ' : fields . related ( ' order_id ' , ' partner_id ' , type = ' many2one ' , relation = ' res.partner ' , string = ' Customer ' ) ,
2010-06-16 11:51:39 +00:00
' salesman_id ' : fields . related ( ' order_id ' , ' user_id ' , type = ' many2one ' , relation = ' res.users ' , string = ' Salesman ' ) ,
2010-10-21 09:30:04 +00:00
' company_id ' : fields . related ( ' order_id ' , ' company_id ' , type = ' many2one ' , relation = ' res.company ' , string = ' Company ' , store = True , readonly = True , states = { ' draft ' : [ ( ' readonly ' , False ) ] } ) ,
2008-07-22 15:11:28 +00:00
}
2010-10-15 08:55:18 +00:00
_order = ' sequence, id desc '
2008-07-22 15:11:28 +00:00
_defaults = {
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 ' ,
' product_packaging ' : False
2008-07-22 15:11:28 +00:00
}
2009-08-26 13:41:53 +00:00
2010-04-13 10:35:39 +00:00
def invoice_line_create ( self , cr , uid , ids , context = None ) :
if context is None :
context = { }
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
create_ids = [ ]
2009-05-20 13:47:11 +00:00
sales = { }
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
if not line . invoiced :
if line . product_id :
2009-08-26 13:41:53 +00:00
a = line . product_id . product_tmpl_id . property_account_income . id
2008-07-22 15:11:28 +00:00
if not a :
a = line . product_id . categ_id . property_account_income_categ . id
if not a :
raise osv . except_osv ( _ ( ' Error ! ' ) ,
_ ( ' There is no income account defined ' \
' for this product: " %s " (id: %d ) ' ) % \
( line . product_id . name , line . product_id . id , ) )
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 )
a = prop and prop . id or False
2008-07-22 15:11:28 +00:00
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 ,
2010-03-06 20:52:19 +00:00
self . pool . get ( ' decimal.precision ' ) . precision_get ( cr , uid , ' Sale Price ' ) )
2009-01-20 06:04:07 +00:00
fpos = line . order_id . fiscal_position or False
a = self . pool . get ( ' account.fiscal.position ' ) . map_account ( cr , uid , fpos , a )
2009-10-13 14:45:38 +00:00
if not a :
raise osv . except_osv ( _ ( ' Error ! ' ) ,
_ ( ' There is no income category account defined in default Properties for Product Category or Fiscal Position is not defined ! ' ) )
2008-07-22 15:11:28 +00:00
inv_id = self . pool . get ( ' account.invoice.line ' ) . create ( cr , uid , {
' name ' : line . name ,
2009-08-26 13:41:53 +00:00
' origin ' : line . order_id . name ,
2008-07-22 15:11:28 +00:00
' account_id ' : a ,
' price_unit ' : pu ,
' quantity ' : uosqty ,
' discount ' : line . discount ,
' uos_id ' : uos_id ,
' product_id ' : line . product_id . id or False ,
2009-08-26 13:41:53 +00:00
' invoice_line_tax_id ' : [ ( 6 , 0 , [ x . id for x in line . tax_id ] ) ] ,
2008-07-22 15:11:28 +00:00
' note ' : line . notes ,
2009-09-15 13:01:18 +00:00
' account_analytic_id ' : line . order_id . project_id and line . order_id . project_id . id or False ,
2008-07-22 15:11:28 +00:00
} )
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 } )
2009-05-20 13:47:11 +00:00
sales [ line . order_id . id ] = True
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 " )
for sid in sales . keys ( ) :
wf_service . trg_write ( uid , ' sale.order ' , sid , 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 ) :
if context is None :
context = { }
2008-12-14 16:46:57 +00:00
for line in self . browse ( cr , uid , ids , context = context ) :
if line . invoiced :
2009-02-05 07:05:49 +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 (
_ ( ' Could not cancel sale order line! ' ) ,
2010-08-03 04:55:12 +00:00
_ ( ' You must first cancel stock moves attached to this sale 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 ) :
if context is None :
context = { }
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 ) :
if context is None :
context = { }
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 ) :
if context is None :
context = { }
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
def product_id_change ( self , cr , uid , ids , pricelist , product , qty = 0 ,
uom = False , qty_uos = 0 , uos = False , name = ' ' , partner_id = False ,
2009-04-06 09:31:07 +00:00
lang = False , update_tax = True , date_order = False , packaging = False , fiscal_position = False , flag = False ) :
2009-01-23 13:45:30 +00:00
if not partner_id :
2009-02-05 07:05:49 +00:00
raise osv . except_osv ( _ ( ' No Customer Defined ! ' ) , _ ( ' You have to select a customer in the sale 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 ' )
if partner_id :
lang = partner_obj . browse ( cr , uid , partner_id ) . lang
context = { ' lang ' : lang , ' partner_id ' : partner_id }
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 :
date_order = time . strftime ( ' % Y- % m- %d ' )
2008-09-07 23:24:39 +00:00
result = { }
product_obj = product_obj . browse ( cr , uid , product , context = context )
2009-01-18 22:28:05 +00:00
if not packaging and product_obj . packaging :
packaging = product_obj . packaging [ 0 ] . id
result [ ' product_packaging ' ] = packaging
2008-09-07 23:24:39 +00:00
if packaging :
default_uom = product_obj . uom_id and product_obj . uom_id . id
2010-04-13 10:35:39 +00:00
pack = self . pool . get ( ' product.packaging ' ) . 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 ) :
2008-09-16 08:51:19 +00:00
ean = pack . ean
qty_pack = pack . qty
type_ul = pack . ul
2009-07-06 19:39:59 +00:00
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: " ) % ( qty )
2009-08-26 13:41:53 +00:00
warn_msg = warn_msg + " \n \n " + _ ( " EAN: " ) + str ( ean ) + _ ( " Quantity: " ) + str ( qty_pack ) + _ ( " Type of ul: " ) + str ( type_ul . name )
warning = {
2009-11-13 05:41:16 +00:00
' title ' : _ ( ' Picking Information ! ' ) ,
2008-09-16 08:51:19 +00:00
' message ' : warn_msg
}
2008-09-15 12:39:01 +00:00
result [ ' product_uom_qty ' ] = qty
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 :
2009-11-30 16:02:20 +00:00
result [ ' name ' ] = self . pool . get ( ' product.product ' ) . name_get ( cr , uid , [ product_obj . id ] , context = context ) [ 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
2010-06-26 16:05:28 +00:00
if ( product_obj . type == ' product ' ) and ( product_obj . virtual_available * uom2 . factor < qty * product_obj . uom_id . factor ) \
2010-06-26 15:15:23 +00:00
and ( product_obj . procure_method == ' make_to_stock ' ) :
warning = {
' title ' : _ ( ' Not enough stock ! ' ) ,
' message ' : _ ( ' You plan to sell %.2f %s but you only have %.2f %s available ! \n The real stock is %.2f %s . (without reservations) ' ) %
( 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 )
}
2008-09-09 13:57:16 +00:00
# get unit price
if not pricelist :
2009-08-26 13:41:53 +00:00
warning = {
' title ' : ' No Pricelist ! ' ,
2008-09-09 13:57:16 +00:00
' message ' :
2010-05-28 09:48:40 +00:00
' You have to select a pricelist or a customer in the sale form ! \n '
2008-09-09 13:57:16 +00:00
' Please set one before choosing a product. '
}
else :
price = self . pool . get ( ' product.pricelist ' ) . price_get ( cr , uid , [ pricelist ] ,
product , qty or 1.0 , partner_id , {
' uom ' : uom ,
' date ' : date_order ,
} ) [ pricelist ]
if price is False :
2009-08-26 13:41:53 +00:00
warning = {
' title ' : ' No valid pricelist line found ! ' ,
2008-09-09 13:57:16 +00:00
' message ' :
" Couldn ' t find a pricelist line matching this product and quantity. \n "
" You have to change either the product, the quantity or the pricelist. "
2008-09-29 08:49:12 +00:00
}
2008-09-09 13:57:16 +00:00
else :
2008-09-29 08:49:12 +00:00
result . update ( { ' price_unit ' : price } )
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 ,
lang = False , update_tax = True , date_order = False ) :
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 ,
date_order = date_order )
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 = { }
2009-07-28 12:57:21 +00:00
""" Allows to delete sale order lines in draft,cancel states """
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 ' ] :
2010-10-21 09:30:04 +00:00
raise osv . except_osv ( _ ( ' Invalid action ! ' ) , _ ( ' Cannot delete a sale order line which is %s ! ' ) % ( rec . state , ) )
2009-07-28 12:57:21 +00:00
return super ( sale_order_line , self ) . unlink ( cr , uid , ids , context = context )
2009-08-26 13:41:53 +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 '
2008-07-22 15:11:28 +00:00
_columns = {
2009-08-26 13:41:53 +00:00
' name ' : fields . char ( ' Name ' , size = 64 ) ,
2008-09-23 20:56:51 +00:00
' picking_policy ' : fields . selection ( [
2009-08-26 13:41:53 +00:00
( ' direct ' , ' Direct Delivery ' ) ,
( ' one ' , ' All at Once ' )
2010-10-08 14:40:49 +00:00
] , ' Picking Default Policy ' , required = True , help = " The Shipping Policy is used to configure per order if you want to deliver as soon as possible when one product is available or you wait that all products are available.. " ) ,
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 ' ) ,
2010-04-18 19:11:09 +00:00
] , ' Shipping Default Policy ' , required = True ,
help = " You can generate invoices based on sales orders or based on shippings. " ) ,
2008-10-29 16:48:02 +00:00
' step ' : fields . selection ( [
2009-08-26 13:41:53 +00:00
( ' one ' , ' Delivery Order Only ' ) ,
2009-11-13 05:41:16 +00:00
( ' two ' , ' Picking List & Delivery Order ' )
2008-10-29 17:08:42 +00:00
] , ' Steps To Deliver a Sale Order ' , required = True ,
2010-08-10 11:35:06 +00:00
help = " By default, OpenERP is able to manage complex routing and paths " \
2008-10-29 17:08:42 +00:00
" of products in your warehouse and partner locations. This will configure " \
" the most common and simple methods to deliver products to the customer " \
" in one or two operations by the worker. " )
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
' picking_policy ' : ' direct ' ,
' order_policy ' : ' manual ' ,
' step ' : ' one '
2008-06-20 11:49:23 +00:00
}
2009-08-26 13:41:53 +00:00
2009-12-15 15:55:52 +00:00
def execute ( self , cr , uid , ids , context = None ) :
2010-04-13 10:35:39 +00:00
if context is None :
context = { }
2008-09-23 20:56:51 +00:00
for o in self . browse ( cr , uid , ids , context = context ) :
ir_values_obj = self . pool . get ( ' ir.values ' )
2009-08-26 13:41:53 +00:00
ir_values_obj . set ( cr , uid , ' default ' , False , ' picking_policy ' , [ ' sale.order ' ] , o . picking_policy )
ir_values_obj . set ( cr , uid , ' default ' , False , ' order_policy ' , [ ' sale.order ' ] , o . order_policy )
if o . step == ' one ' :
2008-10-29 17:08:42 +00:00
md = self . pool . get ( ' ir.model.data ' )
2010-10-28 13:16:19 +00:00
location_id = md . get_object_reference ( cr , uid , ' stock ' , ' stock_location_output ' )
2010-10-29 04:17:01 +00:00
location_id = location_id and location_id [ 1 ] or False
2009-08-26 13:41:53 +00:00
self . pool . get ( ' stock.location ' ) . write ( cr , uid , [ location_id ] , { ' chained_auto_packing ' : ' transparent ' } )
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: