2010-03-04 09:03:42 +00:00
# -*- coding: utf-8 -*-
2006-12-07 13:41:40 +00:00
##############################################################################
2010-01-28 13:26:53 +00:00
#
2010-03-04 09:05:56 +00:00
# OpenERP, Open Source Management Solution
2010-12-08 14:17:07 +00:00
# Copyright (C) 2004-TODAY OpenERP S.A. <http://www.openerp.com>
2008-06-16 11:00:21 +00:00
#
2008-11-03 18:27:16 +00:00
# This program is free software: you can redistribute it and/or modify
2010-12-08 14:17:07 +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
2010-12-08 14:17:07 +00:00
# GNU Affero General Public License for more details.
2006-12-07 13:41:40 +00:00
#
2010-12-08 14:17:07 +00:00
# You should have received a copy of the GNU Affero General Public License
2010-01-28 13:26:53 +00:00
# along with this program. If not, see <http://www.gnu.org/licenses/>.
2006-12-07 13:41:40 +00:00
#
##############################################################################
2011-09-29 12:27:53 +00:00
import logging
2006-12-07 13:41:40 +00:00
import time
2011-09-27 18:38:30 +00:00
import openerp
2011-09-29 12:27:53 +00:00
_logger = logging . getLogger ( ' ir_sequence ' )
2011-09-27 18:38:30 +00:00
class ir_sequence_type ( openerp . osv . osv . osv ) :
2008-07-22 14:24:36 +00:00
_name = ' ir.sequence.type '
2010-12-10 22:42:58 +00:00
_order = ' name '
2008-07-22 14:24:36 +00:00
_columns = {
2011-09-27 18:38:30 +00:00
' name ' : openerp . osv . fields . char ( ' Name ' , size = 64 , required = True ) ,
' code ' : openerp . osv . fields . char ( ' Code ' , size = 32 , required = True ) ,
2008-07-22 14:24:36 +00:00
}
2006-12-07 13:41:40 +00:00
2011-09-27 21:36:12 +00:00
_sql_constraints = [
( ' code_unique ' , ' unique(code) ' , ' `code` must be unique. ' ) ,
]
2006-12-07 13:41:40 +00:00
def _code_get ( self , cr , uid , context = { } ) :
2008-07-22 14:24:36 +00:00
cr . execute ( ' select code, name from ir_sequence_type ' )
return cr . fetchall ( )
2006-12-07 13:41:40 +00:00
2011-09-27 18:38:30 +00:00
class ir_sequence ( openerp . osv . osv . osv ) :
""" Sequence model.
The sequence model allows to define and use so - called sequence objects .
Such objects are used to generate unique identifiers in a transaction - safe
way .
"""
2008-07-22 14:24:36 +00:00
_name = ' ir.sequence '
2010-12-10 22:42:58 +00:00
_order = ' name '
2008-07-22 14:24:36 +00:00
_columns = {
2011-09-27 18:38:30 +00:00
' name ' : openerp . osv . fields . char ( ' Name ' , size = 64 , required = True ) ,
2011-09-28 10:30:09 +00:00
' code ' : openerp . osv . fields . selection ( _code_get , ' Code ' , size = 64 , required = True ) ,
2011-09-27 21:36:12 +00:00
' implementation ' : openerp . osv . fields . selection ( # TODO update the view
2011-09-29 12:27:53 +00:00
[ ( ' standard ' , ' Standard ' ) , ( ' no_gap ' , ' No gap ' ) ] ,
' Implementation ' , required = True ,
2011-09-27 21:36:12 +00:00
help = " Two sequence object implementations are offered: Standard "
" and ' No gap ' . The later is slower than the former but forbids any "
" gap in the sequence (while they are possible in the former). " ) ,
2011-09-27 18:38:30 +00:00
' active ' : openerp . osv . fields . boolean ( ' Active ' ) ,
' prefix ' : openerp . osv . fields . char ( ' Prefix ' , size = 64 , help = " Prefix value of the record for the sequence " ) ,
' suffix ' : openerp . osv . fields . char ( ' Suffix ' , size = 64 , help = " Suffix value of the record for the sequence " ) ,
' number_next ' : openerp . osv . fields . integer ( ' Next Number ' , required = True , help = " Next number of this sequence " ) ,
' number_increment ' : openerp . osv . fields . integer ( ' Increment Number ' , required = True , help = " The next number of the sequence will be incremented by this number " ) ,
2011-09-28 13:31:01 +00:00
' padding ' : openerp . osv . fields . integer ( ' Number Padding ' , required = True , help = " OpenERP will automatically adds some ' 0 ' on the left of the ' Next Number ' to get the required padding size. " ) ,
2011-09-27 18:38:30 +00:00
' company_id ' : openerp . osv . fields . many2one ( ' res.company ' , ' Company ' ) ,
2008-07-22 14:24:36 +00:00
}
_defaults = {
2011-09-27 21:36:12 +00:00
' implementation ' : ' standard ' ,
2011-09-27 18:38:30 +00:00
' active ' : True ,
2010-02-02 22:27:30 +00:00
' company_id ' : lambda s , cr , uid , c : s . pool . get ( ' res.company ' ) . _company_default_get ( cr , uid , ' ir.sequence ' , context = c ) ,
2011-09-27 18:38:30 +00:00
' number_increment ' : 1 ,
' number_next ' : 1 ,
' padding ' : 0 ,
2008-07-22 14:24:36 +00:00
}
2006-12-07 13:41:40 +00:00
2011-09-29 13:47:58 +00:00
def init ( self , cr ) :
2011-09-30 15:23:45 +00:00
return # Don't do the following index yet.
2011-09-29 13:47:58 +00:00
# CONSTRAINT/UNIQUE INDEX on (code, 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 (code, NULL) ir_sequences.
cr . execute ( """
SELECT indexname FROM pg_indexes WHERE indexname =
' ir_sequence_unique_code_company_id_idx ' """ )
if not cr . fetchone ( ) :
cr . execute ( """
CREATE UNIQUE INDEX ir_sequence_unique_code_company_id_idx
ON ir_sequence ( code , ( COALESCE ( company_id , - 1 ) ) ) """ )
2011-09-30 18:38:06 +00:00
def _create_sequence ( self , cr , id , number_increment , number_next ) :
""" Create a PostreSQL sequence.
There is no access rights check .
"""
assert isinstance ( id , ( int , long ) )
sql = " CREATE SEQUENCE ir_sequence_ %03d INCREMENT BY %% s START WITH %% s " % id
cr . execute ( sql , ( number_increment , number_next ) )
def _drop_sequence ( self , cr , ids ) :
""" Drop the PostreSQL sequence if it exists.
There is no access rights check .
"""
ids = ids if isinstance ( ids , ( list , tuple ) ) else [ ids ]
assert all ( isinstance ( i , ( int , long ) ) for i in ids ) , \
" Only ids in (int, long) allowed. "
names = ' , ' . join ( ' ir_sequence_ %03d ' % i for i in ids )
2011-09-27 21:36:12 +00:00
2011-09-30 18:38:06 +00:00
# RESTRICT is the default; it prevents dropping the sequence if an
# object depends on it.
cr . execute ( " DROP SEQUENCE IF EXISTS %s RESTRICT " % names )
def _alter_sequence ( self , cr , id , number_increment , number_next ) :
""" Alter a PostreSQL sequence.
There is no access rights check .
"""
assert isinstance ( id , ( int , long ) )
cr . execute ( """
ALTER SEQUENCE ir_sequence_ % 03 d INCREMENT BY % % s RESTART WITH % % s
""" % i d, (number_increment, number_next))
2011-09-28 09:13:09 +00:00
2011-09-30 18:38:06 +00:00
def create ( self , cr , uid , values , context = None ) :
""" Create a sequence, in implementation == standard a fast gaps-allowed PostgreSQL sequence is used.
2011-09-28 09:13:09 +00:00
"""
2011-09-30 18:38:06 +00:00
values = self . _add_missing_default_values ( cr , uid , values , context )
values [ ' id ' ] = super ( ir_sequence , self ) . create ( cr , uid , values , context )
if values [ ' implementation ' ] == ' standard ' :
f = self . _create_sequence ( cr , values [ ' id ' ] , values [ ' number_increment ' ] , values [ ' number_next ' ] )
return values [ ' id ' ]
2011-09-28 09:13:09 +00:00
def unlink ( self , cr , uid , ids , context = None ) :
super ( ir_sequence , self ) . unlink ( cr , uid , ids , context )
self . _drop_sequence ( cr , ids )
2011-09-28 13:05:48 +00:00
return True
2011-09-28 09:13:09 +00:00
def write ( self , cr , uid , ids , values , context = None ) :
2011-09-30 18:38:06 +00:00
if not isinstance ( ids , ( list , tuple ) ) :
ids = [ ids ]
2011-09-28 09:13:09 +00:00
new_implementation = values . get ( ' implementation ' )
2011-09-30 18:38:06 +00:00
rows = self . read ( cr , uid , ids , [ ' implementation ' , ' number_increment ' , ' number_next ' ] , context )
2011-09-28 09:13:09 +00:00
super ( ir_sequence , self ) . write ( cr , uid , ids , values , context )
2011-09-30 18:38:06 +00:00
2011-09-28 09:13:09 +00:00
for row in rows :
# 4 cases: we test the previous impl. against the new one.
if row [ ' implementation ' ] == ' standard ' :
i = values . get ( ' number_increment ' , row [ ' number_increment ' ] )
n = values . get ( ' number_next ' , row [ ' number_next ' ] )
if new_implementation in ( ' standard ' , None ) :
self . _alter_sequence ( cr , row [ ' id ' ] , i , n )
else :
self . _drop_sequence ( cr , row [ ' id ' ] )
else :
if new_implementation in ( ' no_gap ' , None ) :
pass
else :
self . _create_sequence ( cr , row [ ' id ' ] , i , n )
return True
2011-09-27 21:36:12 +00:00
def _interpolate ( self , s , d ) :
2011-09-30 18:38:06 +00:00
if s :
return s % d
return ' '
2011-09-27 21:36:12 +00:00
def _interpolation_dict ( self ) :
t = time . localtime ( ) # Actually, the server is always in UTC.
return {
' year ' : time . strftime ( ' % Y ' , t ) ,
' month ' : time . strftime ( ' % m ' , t ) ,
' day ' : time . strftime ( ' %d ' , t ) ,
' y ' : time . strftime ( ' % y ' , t ) ,
' doy ' : time . strftime ( ' % j ' , t ) ,
' woy ' : time . strftime ( ' % W ' , t ) ,
' weekday ' : time . strftime ( ' % w ' , t ) ,
' h24 ' : time . strftime ( ' % H ' , t ) ,
' h12 ' : time . strftime ( ' % I ' , t ) ,
' min ' : time . strftime ( ' % M ' , t ) ,
' sec ' : time . strftime ( ' % S ' , t ) ,
2008-11-22 00:05:33 +00:00
}
2006-12-07 13:41:40 +00:00
2011-09-30 18:38:06 +00:00
def _select_by_code_or_id ( self , cr , uid , sequence_code_or_id , code_or_id , for_update_no_wait , context = None ) :
""" Read a sequence object.
2011-09-28 09:13:09 +00:00
2011-09-30 18:38:06 +00:00
There is no access rights check on the sequence itself .
2011-09-29 12:27:53 +00:00
"""
2011-09-30 18:38:06 +00:00
assert code_or_id in ( ' code ' , ' id ' )
res_company = self . pool . get ( ' res.company ' )
company_ids = res_company . search ( cr , uid , [ ] , context = context )
sql = """
SELECT id , number_next , prefix , suffix , padding , implementation
FROM ir_sequence
WHERE % s = % % s
AND active = true
AND ( company_id in % % s or company_id is NULL )
""" % c ode_or_id
if for_update_no_wait :
sql + = ' FOR UPDATE NOWAIT '
cr . execute ( sql , ( sequence_code_or_id , tuple ( company_ids ) ) )
return cr . dictfetchone ( )
2011-09-29 12:27:53 +00:00
def _next ( self , cr , uid , sequence , context = None ) :
if not sequence :
2011-09-28 09:13:09 +00:00
return False
2011-09-29 12:27:53 +00:00
if sequence [ ' implementation ' ] == ' standard ' :
2011-09-30 18:38:06 +00:00
cr . execute ( " SELECT nextval( ' ir_sequence_ %03d ' ) " % sequence [ ' id ' ] )
2011-09-29 12:27:53 +00:00
sequence [ ' number_next ' ] = cr . fetchone ( )
2011-09-28 09:13:09 +00:00
else :
2011-09-28 11:42:29 +00:00
# Read again with FOR UPDATE NO WAIT.
2011-09-30 18:38:06 +00:00
sequence = self . _select_by_code_or_id ( cr , uid , sequence [ ' id ' ] , ' id ' , True , context )
2011-09-28 09:13:09 +00:00
cr . execute ( """
UPDATE ir_sequence
SET number_next = number_next + number_increment
WHERE id = % s
2011-09-29 12:27:53 +00:00
""" , (sequence[ ' id ' ],))
2011-09-28 09:13:09 +00:00
d = self . _interpolation_dict ( )
2011-09-29 12:27:53 +00:00
interpolated_prefix = self . _interpolate ( sequence [ ' prefix ' ] , d )
interpolated_suffix = self . _interpolate ( sequence [ ' suffix ' ] , d )
if sequence [ ' number_next ' ] :
return interpolated_prefix + ' %% 0 %s d ' % sequence [ ' padding ' ] % \
sequence [ ' number_next ' ] + interpolated_suffix
2011-09-28 09:13:09 +00:00
else :
# TODO what is this case used for ?
return interpolated_prefix + interpolated_suffix
2011-09-30 18:38:06 +00:00
def next_by_id ( self , cr , uid , sequence_id , context = None ) :
""" Draw an interpolated string using the specified sequence. """
self . check_read ( cr , uid )
res = self . _select_by_code_or_id ( cr , uid , sequence_id , ' id ' , False , context )
return self . _next ( cr , uid , res , context )
2011-09-27 21:36:12 +00:00
2011-09-30 18:38:06 +00:00
def next_by_code ( self , cr , uid , sequence_code , context = None ) :
""" Draw an interpolated string using the specified sequence. """
self . check_read ( cr , uid )
res = self . _select_by_code_or_id ( cr , uid , sequence_code , ' code ' , False , context )
return self . _next ( cr , uid , res , context )
2011-09-28 09:13:09 +00:00
2011-09-30 18:38:06 +00:00
def get_id ( self , cr , uid , sequence_code_or_id , code_or_id = ' id ' , context = None ) :
""" Draw an interpolated string using the specified sequence.
2011-09-28 09:13:09 +00:00
2011-09-30 18:38:06 +00:00
The sequence to use is specified by the ` ` sequence_code_or_id ` `
argument , which can be a code or an id ( as controlled by the
` ` code_or_id ` ` argument . This method is deprecated .
2011-09-28 09:13:09 +00:00
"""
2011-09-30 18:38:06 +00:00
_logger . warning ( " ir_sequence.get() and ir_sequence.get_id() are deprecated. "
" Please use ir_sequence.next_by_code() or ir_sequence.next_by_id(). " )
if code_or_id == ' id ' :
return self . next_by_id ( cr , uid , sequence_code_or_id , context )
else :
return self . next_by_code ( cr , uid , sequence_code_or_id , context )
2011-09-28 09:13:09 +00:00
2011-09-30 18:38:06 +00:00
def get ( self , cr , uid , code , context = None ) :
""" Draw an interpolated string using the specified sequence.
2011-09-28 09:13:09 +00:00
2011-09-30 18:38:06 +00:00
The sequence to use is specified by its code . This method is
deprecated .
2011-09-28 09:13:09 +00:00
"""
2011-09-30 18:38:06 +00:00
return self . get_id ( cr , uid , code , ' code ' , context )
2006-12-07 13:41:40 +00:00
2008-07-23 15:01:27 +00:00
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: