1
# -*- coding: utf-8 -*-
2
# -*- encoding: utf-8 -*-
3
##############################################################################
5
# OpenERP - Import operations model 347 engine
6
# Copyright (C) 2009 Asr Oss. All Rights Reserved
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.
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.
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/>.
22
##############################################################################
25
Import operations model 347 engine wizards
27
__author__ = """Alejandro Sanchez Ramirez Asr Oss - alejandro@asr-oss.com
28
Borja López Soilán (Pexego) - borjals@pexego.es"""
40
class wizard_calculate(wizard.interface):
42
Wizard to calculates the 347 model report from the OpenERP invoices/payments.
45
############################################################################
47
############################################################################
49
_init_form = """<?xml version="1.0" encoding="utf-8"?>
50
<form string="Calculate partner records" colspan="4" width="400">
51
<label string="This wizard will calculate the partner operations records of the 347 report." colspan="4"/>
52
<label string="" colspan="4"/>
53
<label string="It will create records for the next operations:" colspan="4"/>
54
<label string=" A - Purchases of goods and services over the limit (1)." colspan="4"/>
55
<label string=" B - Sales of goods and services over the limit (1)." colspan="4"/>
56
<!-- <label string=" C - Received payments on behalf of third parties over the limit (3)." colspan="4"/> -->
60
_progress_form = '''<?xml version="1.0"?>
61
<form string="Calculating partner records" colspan="4" width="400">
62
<label string="The calculation may take a while." colspan="4"/>
63
<label string="" colspan="4"/>
64
<field name="progress" widget="progressbar"/>
68
'progress': { 'string': 'Progress', 'type':'float' },
72
_done_form = """<?xml version="1.0" encoding="utf-8"?>
73
<form string="Calculation done" colspan="4" width="400">
74
<label string="The partner operation records have been calculated." colspan="4"/>
75
<label string="" colspan="4"/>
78
_show_exception_form = """<?xml version="1.0" encoding="utf-8"?>
79
<form string="Calculation failed!" colspan="4" width="400">
80
<label string="Error: The calculation operation has failed!" colspan="4"/>
81
<label string="" colspan="4"/>
82
<separator string="Details"/>
83
<field name="exception_text" colspan="4" nolabel="1"/>
86
_show_exception_fields = {
87
'exception_text': {'string': 'Exception', 'type':'text' },
90
############################################################################
92
############################################################################
94
def _calculate(self, db_name, uid, data, context=None):
96
Calculates the 347 model report from the OpenERP invoices/payments data.
99
conn = sql_db.db_connect(db_name)
101
pool = pooler.get_pool(cr.dbname)
103
report = pool.get('l10n.es.aeat.mod347.report').browse(cr, uid, data['id'], context=context)
105
pool.get('l10n.es.aeat.mod347.report').write(cr, uid, data['id'], {
107
'calc_date': time.strftime('%Y-%m-%d %H:%M:%S')
111
# Delete the previous partner records
113
pool.get('l10n.es.aeat.mod347.partner_record').unlink(cr, uid, [r.id for r in report.partner_records])
115
# Get the cash journals (moves on this journals will be considered cash)
116
cash_journal_ids = pool.get('account.journal').search(cr, uid, [('cash_journal', '=', True)])
118
# Get the fiscal year period ids of the non-special periods
119
# (to ignore closing/opening entries)
120
period_ids = [period.id for period in report.fiscalyear_id.period_ids if not period.special]
123
# We will check every partner with include_in_mod347
125
partner_ids = pool.get('res.partner').search(cr, uid, [('include_in_mod347', '=', True)])
127
total_partners = len(partner_ids)
128
for partner in pool.get('res.partner').browse(cr, uid, partner_ids):
131
# Search for invoices
133
# We will repeat the process for sales and purchases:
134
for invoice_type, refund_type in zip(('out_invoice', 'in_invoice'), ('out_refund', 'in_refund')):
136
# CHECK THE SALE/PURCHASES INVOICE LIMIT -------------------
137
# (A and B operation keys)
141
# Search for invoices to this partner (with account moves).
143
invoice_ids = pool.get('account.invoice').search(cr, uid, [
144
('partner_id', '=', partner.id),
145
('type', '=', invoice_type),
146
('period_id', 'in', period_ids),
147
('move_id', '!=', None),
149
refund_ids = pool.get('account.invoice').search(cr, uid, [
150
('partner_id', '=', partner.id),
151
('type', '=', refund_type),
152
('period_id', 'in', period_ids),
153
('move_id', '!=', None),
155
invoices = pool.get('account.invoice').browse(cr, uid, invoice_ids)
156
refunds = pool.get('account.invoice').browse(cr, uid, refund_ids)
158
# Calculate the invoiced amount
159
invoice_amount = sum([invoice.cc_amount_total for invoice in invoices])
160
refund_amount = sum([invoice.cc_amount_total for invoice in refunds])
161
total_amount = invoice_amount - refund_amount
164
# Search for payments received in cash from this partner.
166
cash_account_move_line_ids = pool.get('account.move.line').search(cr, uid, [
167
('partner_id', '=', partner.id),
168
('account_id', '=', partner.property_account_receivable.id),
169
('journal_id', 'in', cash_journal_ids),
170
('period_id', 'in', period_ids),
172
cash_account_move_lines = pool.get('account.move.line').browse(cr, uid, cash_account_move_line_ids)
174
# Calculate the cash amount
175
received_cash_amount = sum([line.credit for line in cash_account_move_lines])
178
# If the invoiced amount is greater than the limit
179
# we will add an partner record to the report.
181
if total_amount > report.operations_limit:
182
if invoice_type == 'out_invoice':
183
operation_key = 'B' # Note: B = Sale operations
185
assert invoice_type == 'in_invoice'
186
operation_key = 'A' # Note: A = Purchase operations
189
# Get the default invoice address of the partner
192
address_ids = pool.get('res.partner').address_get(cr, uid, [partner.id], ['invoice', 'default'])
193
if address_ids.get('invoice'):
194
address = pool.get('res.partner.address').browse(cr, uid, address_ids.get('invoice'))
195
elif address_ids.get('default'):
196
address = pool.get('res.partner.address').browse(cr, uid, address_ids.get('default'))
199
# Get the partner data
201
partner_vat = partner.vat and re.match(r"([A-Z]{0,2})(.*)", partner.vat).groups()[1]
202
partner_state_code = address.state_id and address.state_id.code or ''
203
partner_country_code = address.country_id and address.country_id.code or ''
205
partner_country_code, partner_vat = re.match("(ES){0,1}(.*)", partner.vat).groups()
207
# Create the partner record
208
partner_record = pool.get('l10n.es.aeat.mod347.partner_record').create(cr, uid, {
209
'report_id': report.id ,
210
'operation_key' : operation_key,
211
'partner_id': partner.id,
212
'partner_vat': partner_vat,
213
'representative_vat': '',
214
'partner_state_code': partner_state_code,
215
'partner_country_code' : partner_country_code,
216
'amount': total_amount,
217
'cash_amount': received_cash_amount > report.received_cash_limit and received_cash_amount or 0,
221
# Add the invoices detail to the partner record
223
for invoice in invoices:
224
pool.get('l10n.es.aeat.mod347.invoice_record').create(cr, uid, {
225
'partner_record_id' : partner_record,
226
'invoice_id': invoice.id,
227
'date': invoice.date_invoice,
228
'amount': invoice.cc_amount_total,
230
for invoice in refunds:
231
pool.get('l10n.es.aeat.mod347.invoice_record').create(cr, uid, {
232
'partner_record_id' : partner_record,
233
'invoice_id': invoice.id,
234
'date': invoice.date_invoice,
235
'amount': -invoice.cc_amount_total,
239
# Add the cash detail to the partner record if over limit
241
if received_cash_amount > report.received_cash_limit:
242
for line in cash_account_move_lines:
243
pool.get('l10n.es.aeat.mod347.cash_record').create(cr, uid, {
244
'partner_record_id' : partner_record,
245
'move_line_id' : line.id,
247
'amount': line.credit,
251
# TODO: Calculate records of operation keys C-D-E-F-G !
255
# Update the progress:
258
data['calculation_progress'] = (partners_done * 100.0) / total_partners
261
# Set the report as calculated
263
wf_service = netsvc.LocalService("workflow")
264
wf_service.trg_validate(uid, 'l10n.es.aeat.mod347.report', report.id, 'calculate', cr)
266
data['calculation_progress'] = 100
268
except Exception, ex:
269
data['calculation_exception'] = ex
274
data['calculation_done'] = True
278
def _calculate_in_background_choice(self, cr, uid, data, context):
280
Choice-like action that runs the calculation on background,
281
waiting for it to end or timeout.
283
if not data.get('calculation_thread'):
284
# Run the calculation in background
285
data['calculation_done'] = False
286
data['calculation_exception'] = None
287
data['calculation_thread'] = threading.Thread(target=self._calculate, args=(cr.dbname, uid, data, context))
288
data['calculation_thread'].start()
290
# Wait up some seconds seconds for the task to end.
293
while not data['calculation_done'] and time_left > 0:
294
time_left = time_left - 1
297
# Check if we are done
299
if data['calculation_done']:
300
if data['calculation_exception']:
301
return 'show_exception'
308
def _progress_action(self, cr, uid, data, context):
310
Action that gets the current progress
312
return { 'progress': data['calculation_progress'] }
314
def _show_exception_action(self, cr, uid, data, context):
316
Action that gets the calculation exception text
319
exception_text = unicode(data.get('process_exception', ''))
320
except UnicodeDecodeError:
321
exception_text = str(data.get('process_exception', ''))
322
return { 'exception_text': exception_text }
324
############################################################################
326
############################################################################
331
'result': {'type':'form', 'arch': _init_form, 'fields': {}, 'state':[('end', 'Cancel', 'gtk-cancel', True), ('calculate_records', 'Calculate', 'gtk-apply', True)]}
333
'calculate_records': {
335
'result': {'type': 'choice', 'next_state': _calculate_in_background_choice}
338
'actions': [_progress_action],
339
'result': {'type': 'form', 'arch': _progress_form, 'fields': _progress_fields, 'state':[('end','Close (continues in background)', 'gtk-cancel', True),('calculate_records','Keep waiting', 'gtk-go-forward', True)]}
343
'result': {'type': 'form', 'arch': _done_form, 'fields': {}, 'state':[('end','Done', 'gtk-ok', True)]}
346
'actions': [_show_exception_action],
347
'result': {'type': 'form', 'arch': _show_exception_form, 'fields': _show_exception_fields, 'state':[('end','Done', 'gtk-ok', True)]}
352
wizard_calculate('l10n_es_aeat_mod347.calculate_wizard')