~openerp-spain-team/openerp-spain/6.0-git

« back to all changes in this revision

Viewing changes to l10n_es/l10n_ES_cierre_ejercicio/wizard/wizard_run.py

  • Committer: Borja L.S.
  • Date: 2010-10-18 10:04:25 UTC
  • Revision ID: git-v1:271c47a993616dbba60585d48b8b98d603199d93
[REF] *: Refactorización para portar a 6.0 - Paso 1.

- Se han renombrado los módulos para usar la nomenclatura propuesta
  por OpenERP: l10n_es para el módulo base de localización (plan de 
  cuentas), l10n_es_* para el resto de módulos.

- Se eliminan los módulos extra_addons/* que deberían moverse a 
  los extra-addons genéricos (no son específicos de España).

- Se renombran los __terp__.py por __openerp__.py

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# -*- coding: utf-8 -*-
2
 
# -*- encoding: utf-8 -*-
3
 
##############################################################################
4
 
#
5
 
#    OpenERP - Import operations model 347 engine
6
 
#    Copyright (C) 2009 Asr Oss. All Rights Reserved
7
 
#    $Id$
8
 
#
9
 
#    This program is free software: you can redistribute it and/or modify
10
 
#    it under the terms of the GNU General Public License as published by
11
 
#    the Free Software Foundation, either version 3 of the License, or
12
 
#    (at your option) any later version.
13
 
#
14
 
#    This program is distributed in the hope that it will be useful,
15
 
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
16
 
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17
 
#    GNU General Public License for more details.
18
 
#
19
 
#    You should have received a copy of the GNU General Public License
20
 
#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
21
 
#
22
 
##############################################################################
23
 
 
24
 
"""
25
 
Create FYC entries wizards
26
 
"""
27
 
__author__ = """Borja López Soilán (Pexego) - borjals@pexego.es"""
28
 
 
29
 
from tools.translate import _
30
 
import wizard
31
 
import pooler
32
 
import time
33
 
import threading
34
 
import sql_db
35
 
import netsvc
36
 
from tools import config
37
 
 
38
 
class wizard_run(wizard.interface):
39
 
    """
40
 
    Wizard to create the FYC entries.
41
 
    """
42
 
 
43
 
    ############################################################################
44
 
    # Forms
45
 
    ############################################################################
46
 
 
47
 
    _init_run_form = """<?xml version="1.0" encoding="utf-8"?>
48
 
    <form string="Fiscal Year Closing" colspan="4" width="400">
49
 
        <label string="This wizard will perform the selected operations." colspan="4"/>
50
 
        <label string="" colspan="4"/>
51
 
        <label string="It will create account moves for the operations you selected, skipping those already created." colspan="4"/>
52
 
        <label string="Non-selected operations will be canceled." colspan="4"/>
53
 
    </form>"""
54
 
 
55
 
    _init_cancel_form = """<?xml version="1.0" encoding="utf-8"?>
56
 
    <form string="Fiscal Year Closing" colspan="4" width="400">
57
 
        <label string="This wizard will cancel the selected operations." colspan="4"/>
58
 
        <label string="" colspan="4"/>
59
 
        <label string="It will remove the previously generated account moves." colspan="4"/>
60
 
        <label string="Closed periods, and the fiscal year, will be reopened." colspan="4"/>
61
 
    </form>"""
62
 
 
63
 
    _progress_form = '''<?xml version="1.0"?>
64
 
    <form string="Fiscal Year Closing - Working" colspan="4" width="400">
65
 
        <label string="The process may take a while." colspan="4"/>
66
 
        <label string="" colspan="4"/>
67
 
        <field name="task_progress" widget="progressbar" colspan="4"/>
68
 
        <field name="progress" widget="progressbar" colspan="4"/>
69
 
    </form>'''
70
 
 
71
 
    _progress_fields = {
72
 
        'task_progress': { 'string': 'Task Progress', 'type':'float' },
73
 
        'progress': { 'string': 'Total Progress', 'type':'float' },
74
 
    }
75
 
 
76
 
 
77
 
    _done_form = """<?xml version="1.0" encoding="utf-8"?>
78
 
    <form string="Fiscal Year Closing - Done" colspan="4" width="400">
79
 
        <label string="The selected operations have been performed sucessfuly." colspan="4"/>
80
 
        <label string="" colspan="4"/>
81
 
    </form>"""
82
 
 
83
 
    _show_exception_form = """<?xml version="1.0" encoding="utf-8"?>
84
 
    <form string="Fiscal Year Closing - Error!" colspan="4" width="400">
85
 
        <label string="Error: One of the selected operations has failed!" colspan="4"/>
86
 
        <label string="" colspan="4"/>
87
 
        <separator string="Details"/>
88
 
        <field name="exception_text" colspan="4" nolabel="1"/>
89
 
    </form>"""
90
 
 
91
 
    _show_exception_fields = {
92
 
        'exception_text': {'string': 'Exception', 'type':'text' },
93
 
    }
94
 
 
95
 
    ############################################################################
96
 
    # CHECK OPERATIONS
97
 
    ############################################################################
98
 
 
99
 
    def _check_invalid_period_moves(self, cr, uid, fyc, data, context):
100
 
        """
101
 
        Checks for moves with invalid period on the fiscal year that is being closed
102
 
        """
103
 
        pool = pooler.get_pool(cr.dbname)
104
 
        data['process_task_progress'] = 0.0
105
 
 
106
 
        # Consider all the periods of the fiscal year.
107
 
        period_ids = [period.id for period in fyc.closing_fiscalyear_id.period_ids]
108
 
 
109
 
        # Find moves on the closing fiscal year with dates of previous years
110
 
        account_move_ids = pool.get('account.move').search(cr, uid, [
111
 
                                ('period_id', 'in', period_ids),
112
 
                                ('date', '<', fyc.closing_fiscalyear_id.date_start),
113
 
                            ], context=context)
114
 
 
115
 
        # Find moves on the closing fiscal year with dates of next years
116
 
        account_move_ids.extend(pool.get('account.move').search(cr, uid, [
117
 
                                ('period_id', 'in', period_ids),
118
 
                                ('date', '>', fyc.closing_fiscalyear_id.date_stop),
119
 
                            ], context=context))
120
 
 
121
 
        # Find moves not on the closing fiscal year with dates on its year
122
 
        account_move_ids.extend(pool.get('account.move').search(cr, uid, [
123
 
                                ('period_id', 'not in', period_ids),
124
 
                                ('date', '>=', fyc.closing_fiscalyear_id.date_start),
125
 
                                ('date', '<=', fyc.closing_fiscalyear_id.date_stop),
126
 
                            ], context=context))
127
 
 
128
 
        #
129
 
        # If one or more moves where found, raise an exception
130
 
        #
131
 
        if len(account_move_ids):
132
 
            invalid_period_moves = pool.get('account.move').browse(cr, uid, account_move_ids, context)
133
 
            str_invalid_period_moves = '\n'.join(['id: %s, date: %s, number: %s, ref: %s' % (move.id, move.date, move.name, move.ref) for move in invalid_period_moves])
134
 
            raise wizard.except_wizard(_('Error'), _('One or more moves with invalid period or date found on the fiscal year: \n%s') % str_invalid_period_moves)
135
 
 
136
 
        data['process_task_progress'] = 100.0
137
 
 
138
 
 
139
 
    def _check_draft_moves(self, cr, uid, fyc, data, context):
140
 
        """
141
 
        Checks for draft moves on the fiscal year that is being closed
142
 
        """
143
 
        pool = pooler.get_pool(cr.dbname)
144
 
        data['process_task_progress'] = 0.0
145
 
 
146
 
        #
147
 
        # Consider all the periods of the fiscal year *BUT* the L&P,
148
 
        # Net L&P and the Closing one.
149
 
        #
150
 
        period_ids = []
151
 
        for period in fyc.closing_fiscalyear_id.period_ids:
152
 
            if period.id != fyc.lp_period_id.id \
153
 
                    and period.id != fyc.nlp_period_id.id \
154
 
                    and period.id != fyc.c_period_id.id:
155
 
                period_ids.append(period.id)
156
 
 
157
 
        # Find the moves on the given periods
158
 
        account_move_ids = pool.get('account.move').search(cr, uid, [
159
 
                                ('period_id', 'in', period_ids),
160
 
                                ('state', '=', 'draft'),
161
 
                            ], context=context)
162
 
 
163
 
        #
164
 
        # If one or more draft moves where found, raise an exception
165
 
        #
166
 
        if len(account_move_ids):
167
 
            draft_moves = pool.get('account.move').browse(cr, uid, account_move_ids, context)
168
 
            str_draft_moves = '\n'.join(['id: %s, date: %s, number: %s, ref: %s' % (move.id, move.date, move.name, move.ref) for move in draft_moves])
169
 
            raise wizard.except_wizard(_('Error'), _('One or more draft moves found: \n%s') % str_draft_moves)
170
 
 
171
 
        data['process_task_progress'] = 100.0
172
 
 
173
 
 
174
 
    def _check_unbalanced_moves(self, cr, uid, fyc, data, context):
175
 
        """
176
 
        Checks for unbalanced moves on the fiscal year that is being closed
177
 
        """
178
 
        pool = pooler.get_pool(cr.dbname)
179
 
        data['process_task_progress'] = 0.0
180
 
 
181
 
        #
182
 
        # Consider all the periods of the fiscal year *BUT* the L&P,
183
 
        # Net L&P and the Closing one.
184
 
        #
185
 
        period_ids = []
186
 
        for period in fyc.closing_fiscalyear_id.period_ids:
187
 
            if period.id != fyc.lp_period_id.id \
188
 
                    and period.id != fyc.nlp_period_id.id \
189
 
                    and period.id != fyc.c_period_id.id:
190
 
                period_ids.append(period.id)
191
 
 
192
 
        # Find the moves on the given periods
193
 
        account_move_ids = pool.get('account.move').search(cr, uid, [
194
 
                                ('period_id', 'in', period_ids),
195
 
                                ('state', '!=', 'draft'),
196
 
                            ], context=context)
197
 
 
198
 
        #
199
 
        # For each found move, check it
200
 
        #
201
 
        unbalanced_moves = []
202
 
        total_accounts = len(account_move_ids)
203
 
        accounts_done = 0
204
 
        for move in pool.get('account.move').browse(cr, uid, account_move_ids, context):
205
 
            amount = 0
206
 
            for line in move.line_id:
207
 
                amount += (line.debit - line.credit)
208
 
 
209
 
            if abs(amount) > 0.5 * 10 ** -int(config['price_accuracy']):
210
 
                unbalanced_moves.append(move)
211
 
 
212
 
            accounts_done += 1
213
 
            data['process_task_progress'] = (accounts_done * 90.0) / total_accounts
214
 
 
215
 
        #
216
 
        # If one or more unbalanced moves where found, raise an exception
217
 
        #
218
 
        if len(unbalanced_moves):
219
 
            str_unbalanced_moves = '\n'.join(['id: %s, date: %s, number: %s, ref: %s' % (move.id, move.date, move.name, move.ref) for move in unbalanced_moves])
220
 
            raise wizard.except_wizard(_('Error'), _('One or more unbalanced moves found: \n%s') % str_unbalanced_moves)
221
 
 
222
 
        data['process_task_progress'] = 100.0
223
 
 
224
 
 
225
 
 
226
 
    ############################################################################
227
 
    # CLOSING/OPENING OPERATIONS
228
 
    ############################################################################
229
 
 
230
 
    def create_closing_move(self, cr, uid, operation, fyc, data, context):
231
 
        """
232
 
        Create a closing move (L&P, NL&P or Closing move).
233
 
        """
234
 
        pool = pooler.get_pool(cr.dbname)
235
 
 
236
 
        data['process_task_progress'] = 0.0
237
 
 
238
 
        move_lines = []
239
 
        dest_accounts_totals = {}
240
 
        period_ids = []
241
 
        account_mapping_ids = []
242
 
        description = None
243
 
        date = None
244
 
        period_id = None
245
 
        journal_id = None
246
 
        fiscalyear_id = fyc.closing_fiscalyear_id.id
247
 
 
248
 
        #
249
 
        # Depending on the operation we will use different data
250
 
        #
251
 
        if operation == 'loss_and_profit':
252
 
            #
253
 
            # Consider all the periods of the fiscal year *BUT* the L&P,
254
 
            # Net L&P and the Closing one.
255
 
            #
256
 
            for period in fyc.closing_fiscalyear_id.period_ids:
257
 
                if period.id != fyc.lp_period_id.id \
258
 
                        and period.id != fyc.nlp_period_id.id \
259
 
                        and period.id != fyc.c_period_id.id:
260
 
                    period_ids.append(period.id)
261
 
            #
262
 
            # Set the accounts to use
263
 
            #
264
 
            account_mapping_ids = fyc.lp_account_mapping_ids
265
 
            for account_map in account_mapping_ids:
266
 
                if not account_map.dest_account_id:
267
 
                    raise wizard.except_wizard(_('UserError'), _("The L&P account mappings are not properly configured: %s") % account_map.name)
268
 
 
269
 
            #
270
 
            # Get the values for the lines
271
 
            #
272
 
            if not fyc.lp_description:
273
 
                raise wizard.except_wizard(_('UserError'), _("The L&P description must be defined"))
274
 
            if not fyc.lp_date:
275
 
                raise wizard.except_wizard(_('UserError'), _("The L&P date must be defined"))
276
 
            if not (fyc.lp_period_id and fyc.lp_period_id.id):
277
 
                raise wizard.except_wizard(_('UserError'), _("The L&P period must be defined"))
278
 
            if not (fyc.lp_journal_id and fyc.lp_journal_id.id):
279
 
                raise wizard.except_wizard(_('UserError'), _("The L&P journal must be defined"))
280
 
            description = fyc.lp_description
281
 
            date = fyc.lp_date
282
 
            period_id = fyc.lp_period_id.id
283
 
            journal_id = fyc.lp_journal_id.id
284
 
        elif operation == 'net_loss_and_profit':
285
 
            #
286
 
            # Consider all the periods of the fiscal year *BUT* the 
287
 
            # Net L&P and the Closing one.
288
 
            #
289
 
            for period in fyc.closing_fiscalyear_id.period_ids:
290
 
                if period.id != fyc.nlp_period_id.id \
291
 
                        and period.id != fyc.c_period_id.id:
292
 
                    period_ids.append(period.id)
293
 
            #
294
 
            # Set the accounts to use
295
 
            #
296
 
            account_mapping_ids = fyc.nlp_account_mapping_ids
297
 
            for account_map in account_mapping_ids:
298
 
                if not account_map.dest_account_id:
299
 
                    raise wizard.except_wizard(_('UserError'), _("The Net L&P account mappings are not properly configured: %s") % account_map.name)
300
 
            #
301
 
            # Get the values for the lines
302
 
            #
303
 
            if not fyc.nlp_description:
304
 
                raise wizard.except_wizard(_('UserError'), _("The Net L&P description must be defined"))
305
 
            if not fyc.nlp_date:
306
 
                raise wizard.except_wizard(_('UserError'), _("The Net L&P date must be defined"))
307
 
            if not (fyc.nlp_period_id and fyc.nlp_period_id.id):
308
 
                raise wizard.except_wizard(_('UserError'), _("The Net L&P period must be defined"))
309
 
            if not (fyc.nlp_journal_id and fyc.nlp_journal_id.id):
310
 
                raise wizard.except_wizard(_('UserError'), _("The Net L&P journal must be defined"))
311
 
            description = fyc.nlp_description
312
 
            date = fyc.nlp_date
313
 
            period_id = fyc.nlp_period_id.id
314
 
            journal_id = fyc.nlp_journal_id.id
315
 
        elif operation == 'close':
316
 
            # Require the user to have performed the L&P operation
317
 
            if not (fyc.loss_and_profit_move_id and fyc.loss_and_profit_move_id.id):
318
 
                raise wizard.except_wizard(_('UserError'), _("The L&P move must exist before creating the closing one"))
319
 
            #
320
 
            # Consider all the periods of the fiscal year *BUT* the Closing one.
321
 
            #
322
 
            for period in fyc.closing_fiscalyear_id.period_ids:
323
 
                if period.id != fyc.c_period_id.id:
324
 
                    period_ids.append(period.id)
325
 
            # Set the accounts to use
326
 
            account_mapping_ids = fyc.c_account_mapping_ids
327
 
            #
328
 
            # Get the values for the lines
329
 
            #
330
 
            if not fyc.c_description:
331
 
                raise wizard.except_wizard(_('UserError'), _("The closing description must be defined"))
332
 
            if not fyc.c_date:
333
 
                raise wizard.except_wizard(_('UserError'), _("The closing date must be defined"))
334
 
            if not (fyc.c_period_id and fyc.c_period_id.id):
335
 
                raise wizard.except_wizard(_('UserError'), _("The closing period must be defined"))
336
 
            if not (fyc.c_journal_id and fyc.c_journal_id.id):
337
 
                raise wizard.except_wizard(_('UserError'), _("The closing journal must be defined"))
338
 
            description = fyc.c_description
339
 
            date = fyc.c_date
340
 
            period_id = fyc.c_period_id.id
341
 
            journal_id = fyc.c_journal_id.id
342
 
        else:
343
 
            assert operation in ('loss_and_profit', 'net_loss_and_profit', 'close'), "The operation must be a supported one"
344
 
 
345
 
 
346
 
        #
347
 
        # For each (parent) account in the mapping list
348
 
        #
349
 
        total_accounts = len(account_mapping_ids)
350
 
        accounts_done = 0
351
 
        for account_map in account_mapping_ids:
352
 
            # Init (if needed) the dictionary that will store the totals for the dest accounts
353
 
            if account_map.dest_account_id:
354
 
                dest_accounts_totals[account_map.dest_account_id.id] = dest_accounts_totals.get(account_map.dest_account_id.id, 0)
355
 
 
356
 
            # Find its children accounts (recursively)
357
 
            # FIXME: _get_children_and_consol is a protected member of account_account but the OpenERP code base uses it like this :(
358
 
            child_ids = pool.get('account.account')._get_children_and_consol(cr, uid, [account_map.source_account_id.id], context)
359
 
 
360
 
            # For each children account. (Notice the context filter! the computed balanced is based on this filter)
361
 
            for account in pool.get('account.account').browse(cr, uid, child_ids, context={'fiscalyear': fiscalyear_id, 'periods': period_ids}):
362
 
                # Check if the children account needs to (and can) be closed
363
 
                # Note: We currently ignore the close_method (account.user_type.close_method)
364
 
                #       and always do a balance close.
365
 
                if account.type != 'view': 
366
 
                    # Compute the balance for the account (uses the previous browse context filter)
367
 
                    balance = account.balance
368
 
                    # Check if the balance is greater than the limit
369
 
                    if abs(balance) >= 0.5 * 10 ** -int(config['price_accuracy']):
370
 
                        #
371
 
                        # Add a new line to the move
372
 
                        #
373
 
                        move_lines.append({
374
 
                                'account_id': account.id,
375
 
                                'debit': balance<0 and -balance,
376
 
                                'credit': balance>0 and balance,
377
 
                                'name': description,
378
 
                                'date': date,
379
 
                                'period_id': period_id,
380
 
                                'journal_id': journal_id,
381
 
                            })
382
 
 
383
 
                        # Update the dest account total (with the inverse of the balance)
384
 
                        if account_map.dest_account_id:
385
 
                            dest_accounts_totals[account_map.dest_account_id.id] -= balance
386
 
            accounts_done += 1
387
 
            data['process_task_progress'] = (accounts_done * 90.0) / total_accounts
388
 
 
389
 
        #
390
 
        # Add the dest lines
391
 
        #
392
 
        for dest_account_id in dest_accounts_totals.keys():
393
 
            balance = dest_accounts_totals[dest_account_id]
394
 
            move_lines.append({
395
 
                    'account_id': dest_account_id,
396
 
                    'debit': balance<0 and -balance,
397
 
                    'credit': balance>0 and balance,
398
 
                    'name': description,
399
 
                    'date': date,
400
 
                    'period_id': period_id,
401
 
                    'journal_id': journal_id,
402
 
                })
403
 
        data['process_task_progress'] = 95.0
404
 
 
405
 
        #
406
 
        # Finally create the account move with all the lines (if needed)
407
 
        #
408
 
        if len(move_lines):
409
 
            move_id = pool.get('account.move').create(cr, uid, {
410
 
                            'ref': description,
411
 
                            'date': date,
412
 
                            'period_id': period_id,
413
 
                            'journal_id': journal_id,
414
 
                            'line_id': [(0,0,line) for line in move_lines],
415
 
                        }, context=context)
416
 
        else:
417
 
            move_id = None
418
 
        data['process_task_progress'] = 99.0
419
 
 
420
 
        #
421
 
        # Save the reference to the created account move into the fyc object
422
 
        #
423
 
        if operation == 'loss_and_profit':
424
 
            pool.get('l10n_es_cierre_ejercicio.fyc').write(cr, uid, [fyc.id], { 'loss_and_profit_move_id': move_id })
425
 
        elif operation == 'net_loss_and_profit':
426
 
            pool.get('l10n_es_cierre_ejercicio.fyc').write(cr, uid, [fyc.id], { 'net_loss_and_profit_move_id': move_id })
427
 
        elif operation == 'close':
428
 
            pool.get('l10n_es_cierre_ejercicio.fyc').write(cr, uid, [fyc.id], { 'closing_move_id': move_id })
429
 
        else:
430
 
            assert operation in ('loss_and_profit', 'net_loss_and_profit', 'close'), "The operation must be a supported one"
431
 
 
432
 
        data['process_task_progress'] = 100.0
433
 
        return move_id
434
 
 
435
 
 
436
 
    def create_opening_move(self, cr, uid, operation, fyc, data, context):
437
 
        """
438
 
        Create an opening move (based on the closing one)
439
 
        """
440
 
        pool = pooler.get_pool(cr.dbname)
441
 
        data['process_task_progress'] = 0.0
442
 
 
443
 
        move_lines = []
444
 
        description = None
445
 
        date = None
446
 
        period_id = None
447
 
        journal_id = None
448
 
        closing_move = None
449
 
 
450
 
        #
451
 
        # Depending on the operation we will use one or other closing move
452
 
        # as the base for the opening move.
453
 
        # Note: Yes, currently only one 'closing' move exists,
454
 
        #       but I want this to be extensible :)
455
 
        #
456
 
        if operation == 'open':
457
 
            closing_move = fyc.closing_move_id
458
 
            # Require the user to have performed the closing operation
459
 
            if not (closing_move and closing_move.id):
460
 
                raise wizard.except_wizard(_('UserError'), _("The closing move must exist to create the opening one"))
461
 
            if not closing_move.line_id:
462
 
                raise wizard.except_wizard(_('UserError'), _("The closing move shouldn't be empty"))
463
 
            #
464
 
            # Get the values for the lines
465
 
            #
466
 
            if not fyc.o_description:
467
 
                raise wizard.except_wizard(_('UserError'), _("The opening description must be defined"))
468
 
            if not fyc.o_date:
469
 
                raise wizard.except_wizard(_('UserError'), _("The opening date must be defined"))
470
 
            if not (fyc.o_period_id and fyc.o_period_id.id):
471
 
                raise wizard.except_wizard(_('UserError'), _("The opening period must be defined"))
472
 
            if not (fyc.o_journal_id and fyc.o_journal_id.id):
473
 
                raise wizard.except_wizard(_('UserError'), _("The opening journal must be defined"))
474
 
            description = fyc.o_description
475
 
            date = fyc.o_date
476
 
            period_id = fyc.o_period_id.id
477
 
            journal_id = fyc.o_journal_id.id
478
 
        else:
479
 
            assert operation in ('open'), "The operation must be a supported one"
480
 
 
481
 
        #
482
 
        # Read the lines from the closing move, and append the inverse lines
483
 
        # to the opening move lines.
484
 
        #
485
 
        total_accounts = len(closing_move.line_id)
486
 
        accounts_done = 0
487
 
        for line in closing_move.line_id:
488
 
            move_lines.append({
489
 
                    'account_id': line.account_id.id,
490
 
                    'debit': line.credit,
491
 
                    'credit': line.debit,
492
 
                    'name': description,
493
 
                    'date': date,
494
 
                    'period_id': period_id,
495
 
                    'journal_id': journal_id,
496
 
                })
497
 
            accounts_done += 1
498
 
            data['process_task_progress'] = (accounts_done * 90.0) / total_accounts
499
 
 
500
 
        #
501
 
        # Finally create the account move with all the lines (if needed)
502
 
        #
503
 
        if len(move_lines):
504
 
            move_id = pool.get('account.move').create(cr, uid, {
505
 
                            'ref': description,
506
 
                            'date': date,
507
 
                            'period_id': period_id,
508
 
                            'journal_id': journal_id,
509
 
                            'line_id': [(0,0,line) for line in move_lines],
510
 
                        }, context=context)
511
 
        else:
512
 
            move_id = None
513
 
        data['process_task_progress'] = 99.0
514
 
 
515
 
        #
516
 
        # Save the reference to the created account move into the fyc object
517
 
        #
518
 
        if operation == 'open':
519
 
            pool.get('l10n_es_cierre_ejercicio.fyc').write(cr, uid, [fyc.id], { 'opening_move_id': move_id })
520
 
        else:
521
 
            assert operation in ('open'), "The operation must be a supported one"
522
 
 
523
 
        data['process_task_progress'] = 100.0
524
 
        return move_id
525
 
 
526
 
 
527
 
    def remove_move(self, cr, uid, operation, fyc, data, context):
528
 
        """
529
 
        Remove a account move (L&P, NL&P, Closing or Opening move)
530
 
        """
531
 
        pool = pooler.get_pool(cr.dbname)
532
 
        data['process_task_progress'] = 0.0
533
 
 
534
 
        #
535
 
        # Depending on the operation we will delete one or other move
536
 
        #
537
 
        move = None
538
 
        if operation == 'loss_and_profit':
539
 
            move = fyc.loss_and_profit_move_id
540
 
            pool.get('l10n_es_cierre_ejercicio.fyc').write(cr, uid, fyc.id, { 'loss_and_profit_move_id': None })
541
 
        elif operation == 'net_loss_and_profit':
542
 
            move = fyc.net_loss_and_profit_move_id
543
 
            pool.get('l10n_es_cierre_ejercicio.fyc').write(cr, uid, fyc.id, { 'net_loss_and_profit_move_id': None })
544
 
        elif operation == 'close':
545
 
            move = fyc.closing_move_id
546
 
            pool.get('l10n_es_cierre_ejercicio.fyc').write(cr, uid, fyc.id, { 'closing_move_id': None })
547
 
        elif operation == 'open':
548
 
            move = fyc.opening_move_id
549
 
            pool.get('l10n_es_cierre_ejercicio.fyc').write(cr, uid, fyc.id, { 'opening_move_id': None })
550
 
        else:
551
 
            assert operation in ('loss_and_profit', 'net_loss_and_profit', 'close', 'open'), "The operation must be a supported one"
552
 
        data['process_task_progress'] = 15.0
553
 
 
554
 
        assert move and move.id, "The move to delete must be defined"
555
 
 
556
 
        #
557
 
        # Unreconcile the move if needed
558
 
        #
559
 
        reconcile_ids = []
560
 
        for line in move.line_id:
561
 
            if line.reconcile_id and (line.reconcile_id.id not in reconcile_ids):
562
 
                reconcile_ids.append(line.reconcile_id.id)
563
 
            if line.reconcile_partial_id and (line.reconcile_partial_id.id not in reconcile_ids):
564
 
                reconcile_ids.append(line.reconcile_partial_id.id)
565
 
        if reconcile_ids:
566
 
            pool.get('account.move.reconcile').unlink(cr, uid, reconcile_ids, context)
567
 
        data['process_task_progress'] = 30.0
568
 
 
569
 
        #
570
 
        # Remove the move after changing it's state to draft
571
 
        #
572
 
        pool.get('account.move').write(cr, uid, [move.id], {'state': 'draft'}, context)
573
 
        pool.get('account.move').unlink(cr, uid, [move.id], context)
574
 
 
575
 
        data['process_task_progress'] = 100.0
576
 
        return move.id
577
 
 
578
 
 
579
 
    ############################################################################
580
 
    # Wizard Actions
581
 
    ############################################################################
582
 
 
583
 
    def _init_choice(self, cr, uid, data, context):
584
 
        """
585
 
        Choice-like action that checks whether the operations must be run
586
 
        or canceled.
587
 
        """
588
 
        if context is None:
589
 
            context = {}
590
 
        if context.get('cancel_mode'):
591
 
            data['cancel_mode'] = True
592
 
            return 'init_cancel'
593
 
        else:
594
 
            data['cancel_mode'] = False
595
 
            return 'init_run'
596
 
        
597
 
 
598
 
    def _run(self, db_name, uid, data, context=None):
599
 
        """
600
 
        Creates / removes FYC entries
601
 
        """
602
 
        data['process_progress'] = 0
603
 
        data['process_task_progress'] = 0
604
 
        data['process_task'] = None
605
 
        try:
606
 
            conn = sql_db.db_connect(db_name)
607
 
            cr = conn.cursor()
608
 
            pool = pooler.get_pool(cr.dbname)
609
 
 
610
 
            #
611
 
            # If the wizard is in cancel mode, run the objects cancel action
612
 
            # to let it undo the confirmation action, before running the wizard.
613
 
            #
614
 
            if data.get('cancel_mode'):
615
 
                wf_service = netsvc.LocalService("workflow")
616
 
                wf_service.trg_validate(uid, 'l10n_es_cierre_ejercicio.fyc', data['id'], 'cancel', cr)
617
 
 
618
 
            # Read the object
619
 
            fyc = pool.get('l10n_es_cierre_ejercicio.fyc').browse(cr, uid, data['id'], context=context)
620
 
 
621
 
            #
622
 
            # Calculate the operations to perform (needed to calculate the progress)
623
 
            #
624
 
            total_operations = 0
625
 
            operations_done = 0
626
 
            if fyc.check_invalid_period_moves:
627
 
                total_operations += 1
628
 
            if fyc.check_draft_moves:
629
 
                total_operations += 1
630
 
            if fyc.check_unbalanced_moves:
631
 
                total_operations += 1
632
 
            if (fyc.create_loss_and_profit and not fyc.loss_and_profit_move_id) \
633
 
                or ((not fyc.create_loss_and_profit) and fyc.loss_and_profit_move_id):
634
 
                total_operations += 1
635
 
            if (fyc.create_net_loss_and_profit and not fyc.net_loss_and_profit_move_id) \
636
 
                or ((not fyc.create_net_loss_and_profit) and fyc.net_loss_and_profit_move_id):
637
 
                total_operations += 1
638
 
            if (fyc.create_closing and not fyc.closing_move_id) \
639
 
                or ((not fyc.create_closing) and fyc.closing_move_id):
640
 
                total_operations += 1
641
 
            if (fyc.create_opening and not fyc.opening_move_id) \
642
 
                or ((not fyc.create_opening) and fyc.opening_move_id):
643
 
                total_operations += 1
644
 
                
645
 
            if total_operations > 0:
646
 
                #
647
 
                # Check for invalid period moves if needed
648
 
                #
649
 
                if fyc.check_invalid_period_moves:
650
 
                    data['process_task'] = 'Check invalid period/date moves'
651
 
                    self._check_invalid_period_moves(cr, uid, fyc, data, context)
652
 
                    operations_done += 1
653
 
                    data['process_progress'] = (operations_done * 100.0) / total_operations
654
 
 
655
 
                #
656
 
                # Check for draft moves if needed
657
 
                #
658
 
                if fyc.check_draft_moves:
659
 
                    data['process_task'] = 'Check draft moves'
660
 
                    self._check_draft_moves(cr, uid, fyc, data, context)
661
 
                    operations_done += 1
662
 
                    data['process_progress'] = (operations_done * 100.0) / total_operations
663
 
 
664
 
                #
665
 
                # Check for unbalanced moves if needed
666
 
                #
667
 
                if fyc.check_unbalanced_moves:
668
 
                    data['process_task'] = 'Check unbalanced moves'
669
 
                    self._check_unbalanced_moves(cr, uid, fyc, data, context)
670
 
                    operations_done += 1
671
 
                    data['process_progress'] = (operations_done * 100.0) / total_operations
672
 
 
673
 
                #
674
 
                # Create L&P move if needed
675
 
                #
676
 
                if fyc.create_loss_and_profit and not fyc.loss_and_profit_move_id:
677
 
                    data['process_task'] = 'Create L&P move'
678
 
                    self.create_closing_move(cr, uid, 'loss_and_profit', fyc, data, context)
679
 
                    operations_done += 1
680
 
                    data['process_progress'] = (operations_done * 100.0) / total_operations
681
 
                #
682
 
                # Remove the L&P move if needed
683
 
                #
684
 
                if (not fyc.create_loss_and_profit) and fyc.loss_and_profit_move_id:
685
 
                    data['process_task'] = 'Remove L&P move'
686
 
                    self.remove_move(cr, uid, 'loss_and_profit', fyc, data, context)
687
 
                    operations_done += 1
688
 
                    data['process_progress'] = (operations_done * 100.0) / total_operations
689
 
 
690
 
                # Refresh the cached fyc object
691
 
                fyc = pool.get('l10n_es_cierre_ejercicio.fyc').browse(cr, uid, data['id'], context=context)
692
 
 
693
 
 
694
 
                #
695
 
                # Create the Net L&P move if needed
696
 
                #
697
 
                if fyc.create_net_loss_and_profit and not fyc.net_loss_and_profit_move_id:
698
 
                    data['process_task'] = 'Create NL&P move'
699
 
                    self.create_closing_move(cr, uid, 'net_loss_and_profit', fyc, data, context)
700
 
                    operations_done += 1
701
 
                    data['process_progress'] = (operations_done * 100.0) / total_operations
702
 
                #
703
 
                # Remove the Net L&P move if needed
704
 
                #
705
 
                if (not fyc.create_net_loss_and_profit) and fyc.net_loss_and_profit_move_id:
706
 
                    data['process_task'] = 'Remove NL&P move'
707
 
                    self.remove_move(cr, uid, 'net_loss_and_profit', fyc, data, context)
708
 
                    operations_done += 1
709
 
                    data['process_progress'] = (operations_done * 100.0) / total_operations
710
 
 
711
 
                # Refresh the cached fyc object
712
 
                fyc = pool.get('l10n_es_cierre_ejercicio.fyc').browse(cr, uid, data['id'], context=context)
713
 
 
714
 
 
715
 
                #
716
 
                # Create the closing move if needed
717
 
                #
718
 
                if fyc.create_closing and not fyc.closing_move_id:
719
 
                    data['process_task'] = 'Create closing move'
720
 
                    self.create_closing_move(cr, uid, 'close', fyc, data, context)
721
 
                    operations_done += 1
722
 
                    data['process_progress'] = (operations_done * 100.0) / total_operations
723
 
                #
724
 
                # Remove the closing move if needed
725
 
                #
726
 
                if (not fyc.create_closing) and fyc.closing_move_id:
727
 
                    data['process_task'] = 'Remove closing move'
728
 
                    self.remove_move(cr, uid, 'close', fyc, data, context)
729
 
                    operations_done += 1
730
 
                    data['process_progress'] = (operations_done * 100.0) / total_operations
731
 
 
732
 
                # Refresh the cached fyc object
733
 
                fyc = pool.get('l10n_es_cierre_ejercicio.fyc').browse(cr, uid, data['id'], context=context)
734
 
 
735
 
                
736
 
                #
737
 
                # Create the opening move if needed
738
 
                #
739
 
                if fyc.create_opening and not fyc.opening_move_id:
740
 
                    data['process_task'] = 'Create opening move'
741
 
                    self.create_opening_move(cr, uid, 'open', fyc, data, context)
742
 
                    operations_done += 1
743
 
                    data['process_progress'] = (operations_done * 100.0) / total_operations
744
 
                #
745
 
                # Remove the opening move if needed
746
 
                #
747
 
                if (not fyc.create_opening) and fyc.opening_move_id:
748
 
                    data['process_task'] = 'Remove opening move'
749
 
                    self.remove_move(cr, uid, 'open', fyc, data, context)
750
 
                    operations_done += 1
751
 
                    data['process_progress'] = (operations_done * 100.0) / total_operations
752
 
 
753
 
            #
754
 
            # Set the as done (if not in cancel_mode)
755
 
            #
756
 
            if not data.get('cancel_mode'):
757
 
                wf_service = netsvc.LocalService("workflow")
758
 
                wf_service.trg_validate(uid, 'l10n_es_cierre_ejercicio.fyc', fyc.id, 'run', cr)
759
 
 
760
 
            data['process_progress'] = 100
761
 
            cr.commit()
762
 
        except Exception, ex:
763
 
            data['process_exception'] = ex
764
 
            cr.rollback()
765
 
            raise
766
 
        finally:
767
 
            cr.close()
768
 
            data['process_done'] = True
769
 
        return {}
770
 
 
771
 
 
772
 
    def _run_in_background_choice(self, cr, uid, data, context):
773
 
        """
774
 
        Choice-like action that runs the process on background,
775
 
        waiting for it to end or timeout.
776
 
        """
777
 
        if not data.get('process_thread'):
778
 
            # Run the calculation in background
779
 
            data['process_done'] = False
780
 
            data['process_exception'] = None
781
 
            data['process_thread'] = threading.Thread(target=self._run, args=(cr.dbname, uid, data, context))
782
 
            data['process_thread'].start()
783
 
        #
784
 
        # Wait up some seconds seconds for the task to end.
785
 
        #
786
 
        time_left = 20
787
 
        while not data['process_done'] and time_left > 0:
788
 
            time_left = time_left - 1
789
 
            time.sleep(1)
790
 
            message = "Fiscal year closing progress: %s%% (%s: %s%%)" % (data.get('process_progress'), data.get('process_task'), data.get('process_task_progress'))
791
 
            netsvc.Logger().notifyChannel('fyc', netsvc.LOG_DEBUG, message)
792
 
        #
793
 
        # Check if we are done
794
 
        #
795
 
        if data['process_done']:
796
 
            if data['process_exception']:
797
 
                return 'show_exception'
798
 
            else:
799
 
                return 'done'
800
 
        else:
801
 
            return 'progress'
802
 
 
803
 
 
804
 
    def _progress_action(self, cr, uid, data, context):
805
 
        """
806
 
        Action that gets the current progress
807
 
        """
808
 
        return {
809
 
            'task_progress': data['process_task_progress'],
810
 
            'progress': data['process_progress']
811
 
        }
812
 
 
813
 
    def _show_exception_action(self, cr, uid, data, context):
814
 
        """
815
 
        Action that gets the calculation exception text
816
 
        """
817
 
        exception_text = ''
818
 
        if data.get('process_exception'):
819
 
            if isinstance(data['process_exception'], wizard.except_wizard):
820
 
                exception_text = data['process_exception'].value
821
 
            else:
822
 
                try:
823
 
                    exception_text = unicode(data['process_exception'])
824
 
                except:
825
 
                    exception_text = str(data['process_exception'])
826
 
        return { 'exception_text': exception_text }
827
 
 
828
 
    ############################################################################
829
 
    # States
830
 
    ############################################################################
831
 
 
832
 
    states = {
833
 
        'init': {
834
 
            'actions': [],
835
 
            'result': {'type': 'choice', 'next_state': _init_choice}
836
 
        },
837
 
        'init_run': {
838
 
            'actions': [],
839
 
            'result': {'type':'form', 'arch': _init_run_form, 'fields': {}, 'state':[('end', 'Cancel', 'gtk-cancel', True), ('run', 'Run', 'gtk-apply', True)]}
840
 
        },
841
 
        'init_cancel': {
842
 
            'actions': [],
843
 
            'result': {'type':'form', 'arch': _init_cancel_form, 'fields': {}, 'state':[('end', 'Cancel', 'gtk-cancel', True), ('run', 'Run', 'gtk-apply', True)]}
844
 
        },
845
 
        'run': {
846
 
            'actions': [],
847
 
            'result': {'type': 'choice', 'next_state': _run_in_background_choice}
848
 
        },
849
 
        'progress': {
850
 
            'actions': [_progress_action],
851
 
            'result': {'type': 'form', 'arch': _progress_form, 'fields': _progress_fields, 'state':[('end','Close (continues in background)', 'gtk-cancel', True),('run','Keep waiting', 'gtk-go-forward', True)]}
852
 
        },
853
 
        'done': {
854
 
            'actions': [],
855
 
            'result': {'type': 'form', 'arch': _done_form, 'fields': {}, 'state':[('end','Done', 'gtk-ok', True)]}
856
 
        },
857
 
        'show_exception': {
858
 
            'actions': [_show_exception_action],
859
 
            'result': {'type': 'form', 'arch': _show_exception_form, 'fields': _show_exception_fields, 'state':[('end','Done', 'gtk-ok', True)]}
860
 
        }
861
 
    }
862
 
 
863
 
 
864
 
wizard_run('l10n_es_cierre_ejercicio.wizard_run')
865
 
 
866
 
 
867
 
class wizard_cancel(wizard_run):
868
 
    """
869
 
    Wizard to remove the FYC entries.
870
 
    """
871
 
 
872
 
    def _init_choice(self, cr, uid, data, context):
873
 
        """
874
 
        Choice-like action that checks whether the operations must be run
875
 
        or canceled. => Always cancel on wizard_cancel.
876
 
        """
877
 
        data['cancel_mode'] = True
878
 
        return 'init_cancel'
879
 
 
880
 
    states = wizard_run.states.copy()
881
 
    
882
 
    states['init'] = {
883
 
            'actions': [],
884
 
            'result': {'type': 'choice', 'next_state': _init_choice}
885
 
        }
886
 
 
887
 
 
888
 
wizard_cancel('l10n_es_cierre_ejercicio.wizard_cancel')
889