1
# -*- coding: utf-8 -*-
2
##############################################################################
4
# OpenERP - Account renumber wizard
5
# Copyright (C) 2009 Pexego Sistemas Informáticos. All Rights Reserved
8
# This program is free software: you can redistribute it and/or modify
9
# it under the terms of the GNU Affero General Public License as published
10
# by the Free Software Foundation, either version 3 of the License, or
11
# (at your option) any later version.
13
# This program is distributed in the hope that it will be useful,
14
# but WITHOUT ANY WARRANTY; without even the implied warranty of
15
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16
# GNU Affero General Public License for more details.
18
# You should have received a copy of the GNU Affero General Public License
19
# along with this program. If not, see <http://www.gnu.org/licenses/>.
21
##############################################################################
24
Account renumber wizard
26
__author__ = ["Borja López Soilán (Pexego)",
27
"Omar Castiñeira Saavedra (Pexego)"]
30
from openerp.osv import fields, orm
31
from openerp.tools.translate import _
32
from datetime import datetime
36
class wizard_renumber(orm.TransientModel):
37
_name = "wizard.renumber"
40
'journal_ids': fields.many2many('account.journal', 'account_journal_wzd_renumber_rel',
41
'wizard_id', 'journal_id', required=True, help="Journals to renumber", string="Journals"),
42
'period_ids': fields.many2many('account.period', 'account_period_wzd_renumber_rel', 'wizard_id', 'period_id', required=True,
43
help='Fiscal periods to renumber', string="Periods", ondelete='null'),
44
'number_next': fields.integer('First Number', required=True, help="Journal sequences will start counting on this number"),
45
'state': fields.selection([('init', 'Initial'), ('renumber', 'Renumbering')], readonly=True)
53
###############################
55
###############################
57
def _process(self, s, date_to_use=None):
59
Based on ir_sequence._process. We need to have our own method
60
as ir_sequence one will always use the current date.
61
We will use the given date instead.
63
date_to_use = date_to_use or time
65
'year': date_to_use.strftime('%Y'),
66
'month': date_to_use.strftime('%m'),
67
'day': date_to_use.strftime('%d'),
68
'y': date_to_use.strftime('%y'),
69
'doy': date_to_use.strftime('%j'),
70
'woy': date_to_use.strftime('%W'),
71
'weekday': date_to_use.strftime('%w'),
72
'h24': time.strftime('%H'),
73
'h12': time.strftime('%I'),
74
'min': time.strftime('%M'),
75
'sec': time.strftime('%S'),
78
def get_id(self, cr, uid, sequence_id, test='id=%s', context=None, date_to_use=None):
80
Based on ir_sequence.get_id. We need to have our own method
81
as ir_sequence one will always use the current date for the prefix
82
and sufix processing. We will use the given date instead.
85
cr.execute('SELECT id, number_next, prefix, suffix, padding FROM ir_sequence WHERE ' + test + ' AND active=%s FOR UPDATE', (sequence_id, True))
86
res = cr.dictfetchone()
88
cr.execute('UPDATE ir_sequence SET number_next=number_next+number_increment WHERE id=%s AND active=%s', (res['id'], True))
89
if res['number_next']:
90
return self._process(res['prefix'], date_to_use=date_to_use) + '%%0%sd' % res['padding'] % res['number_next'] + self._process(res['suffix'], date_to_use=date_to_use)
92
return self._process(res['prefix'], date_to_use=date_to_use) + self._process(res['suffix'], date_to_use=date_to_use)
97
def get_sequence_id_for_fiscalyear_id(self, cr, uid, sequence_id, fiscalyear_id, context=None):
99
Based on ir_sequence.get_id from the account module.
100
Allows us to get the real sequence for the given fiscal year.
102
cr.execute('SELECT id FROM ir_sequence WHERE id=%s AND active=%s',
103
(sequence_id, True,))
104
res = cr.dictfetchone()
106
seq_facade = self.pool.get('ir.sequence')
107
for line in seq_facade.browse(cr, uid, res['id'], context=context).fiscal_ids:
108
if line.fiscalyear_id.id == fiscalyear_id:
109
return line.sequence_id.id
112
############################################################################
113
# Renumber form/action
114
##########################################################################
116
def renumber(self, cr, uid, ids, context):
118
Action that renumbers all the posted moves on the given
119
journal and periods, and returns their ids.
121
logger = logging.getLogger("account_renumber")
122
obj = self.browse(cr, uid, ids[0])
124
period_ids = [x.id for x in obj.period_ids]
125
journal_ids = [x.id for x in obj.journal_ids]
126
number_next = obj.number_next or 1
128
if not (period_ids and journal_ids):
129
raise orm.except_orm(_('No Data Available'), _(
130
'No records found for your selection!'))
132
logger.debug("Searching for account moves to renumber.")
133
move_facade = self.pool.get('account.move')
134
move_ids = move_facade.search(cr, uid, [('journal_id', 'in', journal_ids), ('period_id', 'in', period_ids), ('state', '=', 'posted')], limit=0, order='date,id', context=context)
136
if len(move_ids) == 0:
137
raise orm.except_orm(_('No Data Available'), _(
138
'No records found for your selection!'))
140
logger.debug("Renumbering %d account moves." % len(move_ids))
142
for move in move_facade.browse(cr, uid, move_ids):
144
# Get the sequence to use for this move.
145
# Note: We will use the journal's sequence or one of its
146
# children (if it has children sequences per fiscalyear)
148
sequence_id = self.get_sequence_id_for_fiscalyear_id(cr, uid,
149
sequence_id=move.journal_id.sequence_id.id,
150
fiscalyear_id=move.period_id.fiscalyear_id.id)
151
if not sequence_id in sequences_seen:
152
# First time we see this sequence, reset it
153
self.pool.get('ir.sequence').write(
154
cr, uid, [sequence_id], {'number_next': number_next})
155
sequences_seen.append(sequence_id)
158
# Generate (using our own get_id) and write the new move number.
160
date_to_use = datetime.strptime(move.date, '%Y-%m-%d')
161
new_name = self.get_id(cr, uid, sequence_id,
162
context=context, date_to_use=date_to_use)
163
# Note: We can't just do a
164
# "move_facade.write(cr, uid, [move.id], {'name': new_name})"
165
# cause it might raise a "You can't do this modification on a confirmed entry"
167
cr.execute('UPDATE account_move SET name=%s WHERE id=%s',
170
logger.debug("%d account moves renumbered." % len(move_ids))
172
obj.write({'state': 'renumber'})
174
view_wizard = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'account_renumber', 'view_account_renumber_form')
175
view_wizard_id = view_wizard and view_wizard[1] or False,
177
'type': 'ir.actions.act_window',
178
'name': _("Wizard successfully executed "),
179
'res_model': 'wizard.renumber',
183
'view_id': view_wizard_id,
190
############################################################################
191
# Show results action
192
##########################################################################
193
def show_results(self, cr, uid, ids, context):
195
Action that shows the list of (non-draft) account moves from
196
the selected journals and periods, so the user can review
197
the renumbered account moves.
199
obj = self.browse(cr, uid, ids[0])
200
period_ids = [x.id for x in obj.period_ids]
201
journal_ids = [x.id for x in obj.journal_ids]
203
assert (period_ids and journal_ids)
205
view_ref = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'account', 'view_move_tree')
206
view_id = view_ref and view_ref[1] or False,
208
'type': 'ir.actions.act_window',
209
'name': _("Renumbered account moves"),
210
'res_model': 'account.move',
211
'domain': "[('journal_id','in',%s), ('period_id','in',%s), ('state','=','posted')]" % (repr(journal_ids), repr(period_ids)),