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
2012-01-24 11:47:30 +00:00
_logger = logging . getLogger ( __name__ )
2011-09-29 12:27:53 +00:00
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. ' ) ,
]
2011-11-07 15:19:49 +00:00
def _code_get ( self , cr , uid , context = None ) :
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-10-01 00:56:18 +00:00
' code ' : openerp . osv . fields . selection ( _code_get , ' Code ' , size = 64 ) ,
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.
2011-11-18 08:54:39 +00:00
i = values . get ( ' number_increment ' , row [ ' number_increment ' ] )
n = values . get ( ' number_next ' , row [ ' number_next ' ] )
2011-09-28 09:13:09 +00:00
if row [ ' implementation ' ] == ' standard ' :
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 23:24:39 +00:00
def _next ( self , cr , uid , seq_ids , context = None ) :
if not seq_ids :
2011-09-28 09:13:09 +00:00
return False
2011-11-14 11:04:47 +00:00
if context is None :
context = { }
force_company = context . get ( ' force_company ' )
2011-12-15 15:26:35 +00:00
if not force_company :
2011-11-14 11:04:47 +00:00
force_company = self . pool . get ( ' res.users ' ) . browse ( cr , uid , uid ) . company_id . id
2011-12-15 15:26:35 +00:00
sequences = self . read ( cr , uid , seq_ids , [ ' company_id ' , ' implementation ' , ' number_next ' , ' prefix ' , ' suffix ' , ' padding ' ] )
2011-11-14 11:04:47 +00:00
preferred_sequences = [ s for s in sequences if s [ ' company_id ' ] and s [ ' company_id ' ] [ 0 ] == force_company ]
seq = preferred_sequences [ 0 ] if preferred_sequences else sequences [ 0 ]
2011-09-30 23:24:39 +00:00
if seq [ ' implementation ' ] == ' standard ' :
cr . execute ( " SELECT nextval( ' ir_sequence_ %03d ' ) " % seq [ ' id ' ] )
seq [ ' number_next ' ] = cr . fetchone ( )
2011-09-28 09:13:09 +00:00
else :
2011-09-30 23:24:39 +00:00
cr . execute ( " SELECT number_next FROM ir_sequence WHERE id= %s FOR UPDATE NOWAIT " , ( seq [ ' id ' ] , ) )
cr . execute ( " UPDATE ir_sequence SET number_next=number_next+number_increment WHERE id= %s " , ( seq [ ' id ' ] , ) )
2011-09-28 09:13:09 +00:00
d = self . _interpolation_dict ( )
2011-09-30 23:24:39 +00:00
interpolated_prefix = self . _interpolate ( seq [ ' prefix ' ] , d )
interpolated_suffix = self . _interpolate ( seq [ ' suffix ' ] , d )
return interpolated_prefix + ' %% 0 %s d ' % seq [ ' padding ' ] % seq [ ' number_next ' ] + interpolated_suffix
2011-09-28 09:13:09 +00:00
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 )
2011-12-15 15:26:35 +00:00
company_ids = self . pool . get ( ' res.company ' ) . search ( cr , uid , [ ] , order = ' company_id ' , context = context ) + [ False ]
2011-09-30 23:24:39 +00:00
ids = self . search ( cr , uid , [ ' & ' , ( ' id ' , ' = ' , sequence_id ) , ( ' company_id ' , ' in ' , company_ids ) ] )
return self . _next ( cr , uid , ids , 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 ) :
2011-12-15 15:26:35 +00:00
""" Draw an interpolated string using a sequence with the requested code.
If several sequences with the correct code are available to the user
( multi - company cases ) , the one from the user ' s current company will
be used .
: param dict context : context dictionary may contain a
` ` force_company ` ` key with the ID of the company to
use instead of the user ' s current company for the
sequence selection . A matching sequence for that
specific company will get higher priority .
"""
2011-09-30 18:38:06 +00:00
self . check_read ( cr , uid )
2011-12-15 15:26:35 +00:00
company_ids = self . pool . get ( ' res.company ' ) . search ( cr , uid , [ ] , order = ' company_id ' , context = context ) + [ False ]
2011-09-30 23:24:39 +00:00
ids = self . search ( cr , uid , [ ' & ' , ( ' code ' , ' = ' , sequence_code ) , ( ' company_id ' , ' in ' , company_ids ) ] )
return self . _next ( cr , uid , ids , 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-10-07 14:54:06 +00:00
# TODO: bump up to warning after 6.1 release
_logger . debug ( " ir_sequence.get() and ir_sequence.get_id() are deprecated. "
2011-09-30 18:38:06 +00:00
" 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: