2009-10-20 10:52:23 +00:00
# -*- coding: utf-8 -*-
2006-12-07 13:41:40 +00:00
##############################################################################
2010-06-15 13:27:22 +00:00
#
2009-11-27 07:23:48 +00:00
# OpenERP, Open Source Management Solution
# Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>).
2006-12-07 13:41:40 +00:00
#
2008-11-03 18:27:16 +00:00
# This program is free software: you can redistribute it and/or modify
2009-11-27 07:23:48 +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 18:27:16 +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-11-27 07:23:48 +00:00
# GNU Affero General Public License for more details.
2006-12-07 13:41:40 +00:00
#
2009-11-27 07:23:48 +00:00
# You should have received a copy of the GNU Affero General Public License
2010-06-15 13:27:22 +00:00
# along with this program. If not, see <http://www.gnu.org/licenses/>.
2006-12-07 13:41:40 +00:00
#
##############################################################################
2012-12-10 15:27:23 +00:00
2011-10-05 00:24:16 +00:00
import re
2006-12-07 13:41:40 +00:00
import time
2014-09-04 09:32:16 +00:00
import math
2006-12-07 13:41:40 +00:00
2014-07-06 14:44:26 +00:00
from openerp import api , fields as fields2
2012-12-17 14:30:29 +00:00
from openerp import tools
2012-12-10 15:27:23 +00:00
from openerp . osv import fields , osv
from openerp . tools import float_round , float_is_zero , float_compare
from openerp . tools . translate import _
2015-08-24 17:46:24 +00:00
import simplejson as json
2006-12-07 13:41:40 +00:00
2011-10-05 00:24:16 +00:00
CURRENCY_DISPLAY_PATTERN = re . compile ( r ' ( \ w+) \ s*(?: \ ((.*) \ ))? ' )
2006-12-07 13:41:40 +00:00
class res_currency ( osv . osv ) :
2010-09-06 11:17:51 +00:00
def _current_rate ( self , cr , uid , ids , name , arg , context = None ) :
2013-08-20 09:16:38 +00:00
return self . _get_current_rate ( cr , uid , ids , context = context )
2013-07-31 10:06:26 +00:00
def _current_rate_silent ( self , cr , uid , ids , name , arg , context = None ) :
2013-08-20 09:16:38 +00:00
return self . _get_current_rate ( cr , uid , ids , raise_on_no_rate = False , context = context )
2013-05-07 12:01:49 +00:00
2013-08-20 09:16:38 +00:00
def _get_current_rate ( self , cr , uid , ids , raise_on_no_rate = True , context = None ) :
2010-09-06 11:17:51 +00:00
if context is None :
context = { }
res = { }
2013-10-11 10:51:20 +00:00
2015-10-07 13:00:01 +00:00
date = context . get ( ' date ' ) or fields2 . Datetime . now ( )
2014-04-23 07:35:11 +00:00
for id in ids :
2013-10-11 10:51:20 +00:00
cr . execute ( ' SELECT rate FROM res_currency_rate '
' WHERE currency_id = %s '
' AND name <= %s '
' ORDER BY name desc LIMIT 1 ' ,
2014-07-02 16:18:29 +00:00
( id , date ) )
2008-07-22 14:24:36 +00:00
if cr . rowcount :
2014-04-23 07:35:11 +00:00
res [ id ] = cr . fetchone ( ) [ 0 ]
2013-07-31 11:05:25 +00:00
elif not raise_on_no_rate :
2014-04-23 07:35:11 +00:00
res [ id ] = 0
2008-07-22 14:24:36 +00:00
else :
2014-04-23 07:35:11 +00:00
currency = self . browse ( cr , uid , id , context = context )
2014-02-12 11:46:11 +00:00
raise osv . except_osv ( _ ( ' Error! ' ) , _ ( " No currency rate associated for currency ' %s ' for the given period " % ( currency . name ) ) )
2008-07-22 14:24:36 +00:00
return res
2013-07-31 10:06:26 +00:00
2008-07-22 14:24:36 +00:00
_name = " res.currency "
_description = " Currency "
_columns = {
2011-02-21 10:15:49 +00:00
# Note: 'code' column was removed as of v6.0, the 'name' should now hold the ISO code.
2014-05-21 09:52:05 +00:00
' name ' : fields . char ( ' Currency ' , size = 3 , required = True , help = " Currency Code (ISO 4217) " ) ,
2012-07-23 11:32:40 +00:00
' symbol ' : fields . char ( ' Symbol ' , size = 4 , help = " Currency sign, to be used when printing amounts. " ) ,
2012-01-04 13:30:27 +00:00
' rate ' : fields . function ( _current_rate , string = ' Current Rate ' , digits = ( 12 , 6 ) ,
2011-04-19 13:11:54 +00:00
help = ' The rate of the currency to the currency of rate 1. ' ) ,
2013-07-31 10:06:26 +00:00
# Do not use for computation ! Same as rate field with silent failing
' rate_silent ' : fields . function ( _current_rate_silent , string = ' Current Rate ' , digits = ( 12 , 6 ) ,
help = ' The rate of the currency to the currency of rate 1 (0 if no rate defined). ' ) ,
2008-07-22 14:24:36 +00:00
' rate_ids ' : fields . one2many ( ' res.currency.rate ' , ' currency_id ' , ' Rates ' ) ,
' accuracy ' : fields . integer ( ' Computational Accuracy ' ) ,
2011-04-19 13:11:54 +00:00
' rounding ' : fields . float ( ' Rounding Factor ' , digits = ( 12 , 6 ) ) ,
2008-07-22 14:24:36 +00:00
' active ' : fields . boolean ( ' Active ' ) ,
2009-11-10 12:46:31 +00:00
' company_id ' : fields . many2one ( ' res.company ' , ' Company ' ) ,
2011-09-13 04:38:09 +00:00
' base ' : fields . boolean ( ' Base ' ) ,
2012-04-23 10:23:13 +00:00
' position ' : fields . selection ( [ ( ' after ' , ' After Amount ' ) , ( ' before ' , ' Before Amount ' ) ] , ' Symbol Position ' , help = " Determines where the currency symbol should be placed after or before the amount. " )
2008-07-22 14:24:36 +00:00
}
_defaults = {
2012-11-02 09:47:05 +00:00
' active ' : 1 ,
2011-09-21 23:21:50 +00:00
' position ' : ' after ' ,
2011-10-05 00:29:56 +00:00
' rounding ' : 0.01 ,
' accuracy ' : 4 ,
2013-05-17 08:02:46 +00:00
' company_id ' : False ,
2008-07-22 14:24:36 +00:00
}
2011-07-26 13:21:37 +00:00
_sql_constraints = [
2011-09-19 14:48:21 +00:00
# this constraint does not cover all cases due to SQL NULL handling for company_id,
# so it is complemented with a unique index (see below). The constraint and index
# share the same prefix so that IntegrityError triggered by the index will be caught
# and reported to the user with the constraint's error message.
( ' unique_name_company_id ' , ' unique (name, company_id) ' , ' The currency code must be unique per company! ' ) ,
2011-07-26 13:21:37 +00:00
]
2010-11-18 11:11:54 +00:00
_order = " name "
2006-12-07 13:41:40 +00:00
2011-09-19 14:48:21 +00:00
def init ( self , cr ) :
# CONSTRAINT/UNIQUE INDEX on (name,company_id)
# /!\ The unique constraint 'unique_name_company_id' is not sufficient, because SQL92
# only support field names in constraint definitions, and we need a function here:
# we need to special-case company_id to treat all NULL company_id as equal, otherwise
# we would allow duplicate "global" currencies (all having company_id == NULL)
cr . execute ( """ SELECT indexname FROM pg_indexes WHERE indexname = ' res_currency_unique_name_company_id_idx ' """ )
if not cr . fetchone ( ) :
cr . execute ( """ CREATE UNIQUE INDEX res_currency_unique_name_company_id_idx
ON res_currency
( name , ( COALESCE ( company_id , - 1 ) ) ) """ )
2014-07-06 14:44:26 +00:00
date = fields2 . Date ( compute = ' compute_date ' )
@api.one
@api.depends ( ' rate_ids.name ' )
def compute_date ( self ) :
self . date = self . rate_ids [ : 1 ] . name
2009-11-10 12:46:31 +00:00
2011-10-05 00:24:16 +00:00
def name_search ( self , cr , user , name = ' ' , args = None , operator = ' ilike ' , context = None , limit = 100 ) :
if not args :
args = [ ]
2011-10-11 16:34:35 +00:00
results = super ( res_currency , self ) \
. name_search ( cr , user , name , args , operator = operator , context = context , limit = limit )
if not results :
2011-10-05 00:24:16 +00:00
name_match = CURRENCY_DISPLAY_PATTERN . match ( name )
2011-10-11 16:34:35 +00:00
if name_match :
results = super ( res_currency , self ) \
. name_search ( cr , user , name_match . group ( 1 ) , args , operator = operator , context = context , limit = limit )
return results
2011-10-05 00:24:16 +00:00
2011-06-28 09:56:15 +00:00
def name_get ( self , cr , uid , ids , context = None ) :
2011-07-04 11:37:58 +00:00
if not ids :
2011-06-28 09:56:15 +00:00
return [ ]
if isinstance ( ids , ( int , long ) ) :
ids = [ ids ]
2011-07-04 11:22:06 +00:00
reads = self . read ( cr , uid , ids , [ ' name ' , ' symbol ' ] , context = context , load = ' _classic_write ' )
2012-11-24 17:54:33 +00:00
return [ ( x [ ' id ' ] , tools . ustr ( x [ ' name ' ] ) ) for x in reads ]
2011-06-28 09:56:15 +00:00
2014-07-06 14:44:26 +00:00
@api.v8
def round ( self , amount ) :
""" Return `amount` rounded according to currency `self`. """
return float_round ( amount , precision_rounding = self . rounding )
@api.v7
2008-07-22 14:24:36 +00:00
def round ( self , cr , uid , currency , amount ) :
2011-11-14 16:36:17 +00:00
""" Return ``amount`` rounded according to ``currency`` ' s
2011-11-14 18:23:10 +00:00
rounding rules .
2011-11-14 16:36:17 +00:00
2014-07-06 14:44:26 +00:00
: param Record currency : currency for which we are rounding
2011-11-14 16:36:17 +00:00
: param float amount : the amount to round
: return : rounded float
"""
return float_round ( amount , precision_rounding = currency . rounding )
2014-07-06 14:44:26 +00:00
@api.v8
def compare_amounts ( self , amount1 , amount2 ) :
""" Compare `amount1` and `amount2` after rounding them according to
` self ` ' s precision. An amount is considered lower/greater than
another amount if their rounded value is different . This is not the
same as having a non - zero difference !
For example 1.432 and 1.431 are equal at 2 digits precision , so this
method would return 0. However 0.006 and 0.002 are considered
different ( returns 1 ) because they respectively round to 0.01 and
0.0 , even though 0.006 - 0.002 = 0.004 which would be considered zero
at 2 digits precision .
"""
return float_compare ( amount1 , amount2 , precision_rounding = self . rounding )
@api.v7
2011-11-14 16:36:17 +00:00
def compare_amounts ( self , cr , uid , currency , amount1 , amount2 ) :
2011-11-14 18:23:10 +00:00
""" Compare ``amount1`` and ``amount2`` after rounding them according to the
given currency ' s precision..
An amount is considered lower / greater than another amount if their rounded
value is different . This is not the same as having a non - zero difference !
2011-11-14 16:36:17 +00:00
2011-11-14 18:23:10 +00:00
For example 1.432 and 1.431 are equal at 2 digits precision ,
so this method would return 0.
However 0.006 and 0.002 are considered different ( returns 1 ) because
they respectively round to 0.01 and 0.0 , even though
0.006 - 0.002 = 0.004 which would be considered zero at 2 digits precision .
2011-11-14 16:36:17 +00:00
2014-07-06 14:44:26 +00:00
: param Record currency : currency for which we are rounding
2011-11-14 16:36:17 +00:00
: param float amount1 : first amount to compare
: param float amount2 : second amount to compare
: return : ( resp . ) - 1 , 0 or 1 , if ` ` amount1 ` ` is ( resp . ) lower than ,
equal to , or greater than ` ` amount2 ` ` , according to
` ` currency ` ` ' s rounding.
"""
return float_compare ( amount1 , amount2 , precision_rounding = currency . rounding )
2006-12-27 16:20:27 +00:00
2014-07-06 14:44:26 +00:00
@api.v8
def is_zero ( self , amount ) :
""" Return true if `amount` is small enough to be treated as zero
according to currency ` self ` ' s rounding rules.
Warning : ` ` is_zero ( amount1 - amount2 ) ` ` is not always equivalent to
` ` compare_amounts ( amount1 , amount2 ) == 0 ` ` , as the former will round
after computing the difference , while the latter will round before ,
giving different results , e . g . , 0.006 and 0.002 at 2 digits precision .
"""
return float_is_zero ( amount , precision_rounding = self . rounding )
@api.v7
2008-07-22 14:24:36 +00:00
def is_zero ( self , cr , uid , currency , amount ) :
2011-11-14 16:36:17 +00:00
""" Returns true if ``amount`` is small enough to be treated as
zero according to ` ` currency ` ` ' s rounding rules.
2011-11-14 18:23:10 +00:00
Warning : ` ` is_zero ( amount1 - amount2 ) ` ` is not always equivalent to
` ` compare_amounts ( amount1 , amount2 ) == 0 ` ` , as the former will round after
computing the difference , while the latter will round before , giving
different results for e . g . 0.006 and 0.002 at 2 digits precision .
2014-07-06 14:44:26 +00:00
: param Record currency : currency for which we are rounding
2011-11-14 16:36:17 +00:00
: param float amount : amount to compare with currency ' s zero
"""
return float_is_zero ( amount , precision_rounding = currency . rounding )
2007-07-30 13:35:11 +00:00
2010-12-22 16:10:45 +00:00
def _get_conversion_rate ( self , cr , uid , from_currency , to_currency , context = None ) :
2010-09-06 11:17:51 +00:00
if context is None :
context = { }
2011-08-25 11:56:45 +00:00
ctx = context . copy ( )
2011-08-27 22:53:30 +00:00
from_currency = self . browse ( cr , uid , from_currency . id , context = ctx )
to_currency = self . browse ( cr , uid , to_currency . id , context = ctx )
2011-08-25 11:56:45 +00:00
2011-08-27 22:53:30 +00:00
if from_currency . rate == 0 or to_currency . rate == 0 :
2008-07-22 14:24:36 +00:00
date = context . get ( ' date ' , time . strftime ( ' % Y- % m- %d ' ) )
2011-08-27 22:53:30 +00:00
if from_currency . rate == 0 :
2010-11-19 05:06:26 +00:00
currency_symbol = from_currency . symbol
2008-07-22 14:24:36 +00:00
else :
2010-11-19 05:06:26 +00:00
currency_symbol = to_currency . symbol
2008-07-22 14:24:36 +00:00
raise osv . except_osv ( _ ( ' Error ' ) , _ ( ' No rate found \n ' \
' for the currency: %s \n ' \
2010-11-19 05:06:26 +00:00
' at the date: %s ' ) % ( currency_symbol , date ) )
2010-12-22 16:10:45 +00:00
return to_currency . rate / from_currency . rate
2014-07-02 16:18:29 +00:00
def _compute ( self , cr , uid , from_currency , to_currency , from_amount , round = True , context = None ) :
if ( to_currency . id == from_currency . id ) :
2008-07-22 14:24:36 +00:00
if round :
return self . round ( cr , uid , to_currency , from_amount )
else :
return from_amount
else :
2010-12-22 16:10:45 +00:00
rate = self . _get_conversion_rate ( cr , uid , from_currency , to_currency , context = context )
2008-07-22 14:24:36 +00:00
if round :
2008-10-23 17:45:49 +00:00
return self . round ( cr , uid , to_currency , from_amount * rate )
2008-07-22 14:24:36 +00:00
else :
2012-12-14 12:38:03 +00:00
return from_amount * rate
2008-10-23 17:45:49 +00:00
2014-07-06 14:44:26 +00:00
@api.v7
2014-07-02 16:18:29 +00:00
def compute ( self , cr , uid , from_currency_id , to_currency_id , from_amount ,
round = True , context = None ) :
context = context or { }
if not from_currency_id :
from_currency_id = to_currency_id
if not to_currency_id :
to_currency_id = from_currency_id
xc = self . browse ( cr , uid , [ from_currency_id , to_currency_id ] , context = context )
from_currency = ( xc [ 0 ] . id == from_currency_id and xc [ 0 ] ) or xc [ 1 ]
to_currency = ( xc [ 0 ] . id == to_currency_id and xc [ 0 ] ) or xc [ 1 ]
return self . _compute ( cr , uid , from_currency , to_currency , from_amount , round , context )
2011-07-14 09:27:37 +00:00
2014-07-06 14:44:26 +00:00
@api.v8
def compute ( self , from_amount , to_currency , round = True ) :
""" Convert `from_amount` from currency `self` to `to_currency`. """
assert self , " compute from unknown currency "
assert to_currency , " compute to unknown currency "
# apply conversion rate
if self == to_currency :
to_amount = from_amount
else :
to_amount = from_amount * self . _get_conversion_rate ( self , to_currency )
# apply rounding
return to_currency . round ( to_amount ) if round else to_amount
2014-09-04 09:32:16 +00:00
def get_format_currencies_js_function ( self , cr , uid , context = None ) :
""" Returns a string that can be used to instanciate a javascript function that formats numbers as currencies.
That function expects the number as first parameter and the currency id as second parameter . In case of failure it returns undefined . """
function = " "
for row in self . search_read ( cr , uid , domain = [ ] , fields = [ ' id ' , ' name ' , ' symbol ' , ' rounding ' , ' position ' ] , context = context ) :
2014-12-04 09:51:54 +00:00
digits = int ( math . ceil ( math . log10 ( 1 / row [ ' rounding ' ] ) ) )
2014-09-04 09:32:16 +00:00
symbol = row [ ' symbol ' ] or row [ ' name ' ]
format_number_str = " openerp.web.format_value(arguments[0], { type: ' float ' , digits: [69, " + str ( digits ) + " ]}, 0.00) "
if row [ ' position ' ] == ' after ' :
2015-08-24 17:46:24 +00:00
return_str = " return " + format_number_str + " + ' \\ xA0 ' + " + json . dumps ( symbol ) + " ; "
2014-09-04 09:32:16 +00:00
else :
2015-08-24 17:46:24 +00:00
return_str = " return " + json . dumps ( symbol ) + " + ' \\ xA0 ' + " + format_number_str + " ; "
2014-09-04 09:32:16 +00:00
function + = " if (arguments[1] === " + str ( row [ ' id ' ] ) + " ) { " + return_str + " } "
return function
2006-12-19 13:49:50 +00:00
class res_currency_rate ( osv . osv ) :
2008-07-22 14:24:36 +00:00
_name = " res.currency.rate "
_description = " Currency Rate "
2011-08-27 22:53:30 +00:00
2008-07-22 14:24:36 +00:00
_columns = {
2013-11-04 13:46:18 +00:00
' name ' : fields . datetime ( ' Date ' , required = True , select = True ) ,
' rate ' : fields . float ( ' Rate ' , digits = ( 12 , 6 ) , help = ' The rate of the currency to the currency of rate 1 ' ) ,
2008-07-22 14:24:36 +00:00
' currency_id ' : fields . many2one ( ' res.currency ' , ' Currency ' , readonly = True ) ,
}
_defaults = {
2014-07-06 14:44:26 +00:00
' name ' : lambda * a : time . strftime ( ' % Y- % m- %d 00:00:00 ' ) ,
2008-07-22 14:24:36 +00:00
}
_order = " name desc "
2011-07-14 09:27:37 +00:00
2015-01-30 14:00:21 +00:00
def name_search ( self , cr , user , name , args = None , operator = ' ilike ' , context = None , limit = 80 ) :
if operator in [ ' = ' , ' != ' ] :
try :
date_format = ' % Y- % m- %d '
if context . get ( ' lang ' ) :
lang_obj = self . pool [ ' res.lang ' ]
lang_ids = lang_obj . search ( cr , user , [ ( ' code ' , ' = ' , context [ ' lang ' ] ) ] , context = context )
if lang_ids :
date_format = lang_obj . browse ( cr , user , lang_ids [ 0 ] , context = context ) . date_format
name = time . strftime ( ' % Y- % m- %d ' , time . strptime ( name , date_format ) )
except ValueError :
try :
args . append ( ( ' rate ' , operator , float ( name ) ) )
except ValueError :
return [ ]
name = ' '
operator = ' ilike '
return super ( res_currency_rate , self ) . name_search ( cr , user , name , args = args , operator = operator , context = context , limit = limit )
2008-07-23 15:01:27 +00:00
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: