[FIX] l10n_ch: correct syntax
[odoo/odoo.git] / addons / l10n_ch / wizard / dta_wizard.py
1 # -*- encoding: utf-8 -*-
2 #
3 #  dta_wizard.py
4 #  l10n_ch
5 #
6 #  Created by Nicolas Bessi based on Credric Krier contribution
7 #
8 #  Copyright (c) 2009 CamptoCamp. All rights reserved.
9 ##############################################################################
10 # WARNING: This program as such is intended to be used by professional
11 # programmers who take the whole responsability of assessing all potential
12 # consequences resulting from its eventual inadequacies and bugs
13 # End users who are looking for a ready-to-use solution with commercial
14 # garantees and support are strongly adviced to contract a Free Software
15 # Service Company
16 #
17 # This program is Free Software; you can redistribute it and/or
18 # modify it under the terms of the GNU General Public License
19 # as published by the Free Software Foundation; either version 2
20 # of the License, or (at your option) any later version.
21 #
22 # This program is distributed in the hope that it will be useful,
23 # but WITHOUT ANY WARRANTY; without even the implied warranty of
24 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
25 # GNU General Public License for more details.
26 #
27 # You should have received a copy of the GNU General Public License
28 # along with this program; if not, write to the Free Software
29 # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
30 #
31 ##############################################################################
32 import pooler
33 import wizard
34 import base64
35 import time
36 import pooler
37 import mx.DateTime
38 from tools.translate import _
39
40
41 FORM = """<?xml version="1.0"?>
42 <form string="DTA file creation - Results">
43 <separator colspan="4" string="Clic on 'Save as' to save the DTA file :" />
44         <field name="dta"/>
45 </form>"""
46
47 FIELDS = {
48         'dta': {
49                 'string': 'DTA File',
50                 'type': 'binary',
51                 'readonly': True,
52         },
53 }
54
55 TRANS=[
56         (u'é','e'),
57         (u'è','e'),
58         (u'à','a'),
59         (u'ê','e'),
60         (u'î','i'),
61         (u'ï','i'),
62         (u'â','a'),
63         (u'ä','a'),
64 ]
65
66 def tr(s):
67         s= s.decode('utf-8')
68         for k in TRANS:
69                 s = s.replace(k[0],k[1])
70         try:
71                 res= s.encode('ascii','replace')
72         except:
73                 res = s
74         return res
75
76
77 class record:
78
79         def __init__(self, global_context_dict):
80                 for i in global_context_dict:
81                         global_context_dict[i] = global_context_dict[i] \
82                                         and tr(global_context_dict[i])
83                 self.fields = []
84                 self.global_values = global_context_dict
85                 self.pre = {
86                         'padding': '',
87                         'seg_num1': '01',
88                         'seg_num2': '02',
89                         'seg_num3': '03',
90                         'seg_num4': '04',
91                         'seg_num5': '05',
92                         'flag': '0',
93                         'zero5': '00000'
94                 }
95                 self.post={'date_value_hdr': '000000', 'type_paiement': '0'}
96                 self.init_local_context()
97
98         def init_local_context(self):
99                 """
100                 Must instanciate a fields list, field = (name,size)
101                 and update a local_values dict.
102                 """
103                 raise _('not implemented')
104
105         def generate(self):
106                 res=''
107                 for field in self.fields :
108                         if self.pre.has_key(field[0]):
109                                 value = self.pre[field[0]]
110                         elif self.global_values.has_key(field[0]):
111                                 value = self.global_values[field[0]]
112                         elif self.post.has_key(field[0]):
113                                 value = self.post[field[0]]
114                         else :
115                                 pass
116                                 #raise Exception(field[0]+' not found !')
117                         try:
118                                 res = res + c_ljust(value, field[1])
119                         except :
120                                 pass
121                 return res
122
123
124 class record_gt826(record):
125         """
126         bvr
127         """
128         def init_local_context(self):
129                 self.fields=[
130                         ('seg_num1', 2),
131                         #header
132                         ('date_value_hdr', 6),
133                         ('partner_bank_clearing', 12),
134                         ('zero5', 5),
135                         ('creation_date', 6),
136                         ('comp_bank_clearing', 7),
137                         ('uid', 5),
138                         ('sequence', 5),
139                         ('genre_trans', 3),
140                         ('type_paiement', 1),
141                         ('flag', 1),
142                         #seg1
143                         ('comp_dta', 5),
144                         ('number', 11),
145                         ('comp_bank_iban', 24),
146                         ('date_value', 6),
147                         ('currency', 3),
148                         ('amount_to_pay', 12),
149                         ('padding', 14),
150                         #seg2
151                         ('seg_num2', 2),
152                         ('comp_name', 20),
153                         ('comp_street', 20),
154                         ('comp_zip', 10),
155                         ('comp_city', 10),
156                         ('comp_country', 20),
157                         ('padding', 46),
158                         #seg3
159                         ('seg_num3', 2),
160                         ('partner_bvr', 12),#numero d'adherent bvr
161                         ('partner_name', 20),
162                         ('partner_street', 20),
163                         ('partner_zip', 10),
164                         ('partner_city', 10),
165                         ('partner_country', 20),
166                         ('reference', 27),#communication structuree
167                         ('padding', 2),#cle de controle
168                         ('padding', 5)
169                 ]
170                 self.pre.update({
171                         'date_value_hdr': self.global_values['date_value'],
172                         'date_value': '',
173                         'partner_bank_clearing': '',
174                         'partner_cpt_benef': '',
175                         'genre_trans': '826',
176                         'conv_cours': '',
177                         'option_id_bank': 'D',
178                         'partner_bvr': '/C/'+ self.global_values['partner_bvr'],
179                         'ref2': '',
180                         'ref3': '',
181                         'format': '0',
182                 })
183
184 class record_gt827(record):
185         """
186         interne suisse (bvpost et bvbank)
187         """
188         def init_local_context(self):
189                 self.fields = [
190                         ('seg_num1', 2),
191                         #header
192                         ('date_value_hdr', 6),
193                         ('partner_bank_clearing', 12),
194                         ('zero5', 5),
195                         ('creation_date', 6),
196                         ('comp_bank_clearing', 7),
197                         ('uid', 5),
198                         ('sequence', 5),
199                         ('genre_trans', 3),
200                         ('type_paiement', 1),
201                         ('flag', 1),
202                         #seg1
203                         ('comp_dta', 5),
204                         ('number', 11),
205                         ('comp_bank_iban', 24),
206                         ('date_value', 6),
207                         ('currency', 3),
208                         ('amount_to_pay', 12),
209                         ('padding', 14),
210                         #seg2
211                         ('seg_num2', 2),
212                         ('comp_name', 20),
213                         ('comp_street', 20),
214                         ('comp_zip', 10),
215                         ('comp_city', 10),
216                         ('comp_country', 20),
217                         ('padding', 46),
218                         #seg3
219                         ('seg_num3', 2),
220                         ('partner_bank_number', 30),
221                         ('partner_name', 24),
222                         ('partner_street', 24),
223                         ('partner_zip', 12),
224                         ('partner_city', 12),
225                         ('partner_country', 24),
226                         #seg4
227                         ('seg_num4', 2),
228                         ('reference', 112),
229                         ('padding', 14),
230                         #seg5
231                         #('padding',128)
232                         ]
233
234                 self.pre.update({
235                         'date_value_hdr': self.global_values['date_value'],
236                         'date_value': '',
237                         'partner_cpt_benef': '',
238                         'type_paiement': '0',
239                         'genre_trans': '827',
240                         'conv_cours': '',
241                         'option_id_bank': 'D',
242                         'ref2': '',
243                         'ref3': '',
244                         'format': '0'
245                 })
246
247
248 class record_gt836(record):
249         """
250         iban
251         """
252         def init_local_context(self):
253                 self.fields = [
254                         ('seg_num1', 2),
255                         #header
256                         ('date_value_hdr', 6),
257                         ('partner_bank_clearing', 12),
258                         ('zero5', 5),
259                         ('creation_date', 6),
260                         ('comp_bank_clearing', 7),
261                         ('uid', 5),
262                         ('sequence', 5),
263                         ('genre_trans', 3),
264                         ('type_paiement', 1),
265                         ('flag', 1),
266                         #seg1
267                         ('comp_dta', 5),
268                         ('number', 11),
269                         ('comp_bank_iban', 24),
270                         ('date_value', 6),
271                         ('currency', 3),
272                         ('amount_to_pay', 15),
273                         ('padding', 11),
274                         #seg2
275                         ('seg_num2', 2),
276                         ('conv_cours', 12),
277                         ('comp_name', 35),
278                         ('comp_street', 35),
279                         ('comp_country', 3),
280                         ('comp_zip', 10),
281                         ('comp_city', 22),
282                         ('padding', 9),
283                         #seg3
284                         ('seg_num3', 2),
285                         ('option_id_bank', 1),
286                         ('partner_bank_ident', 70),
287                         ('partner_bank_iban', 34),
288                         ('padding', 21),
289                         #seg4
290                         ('seg_num4', 2),
291                         ('partner_name', 35),
292                         ('partner_street', 35),
293                         ('partner_country', 3),
294                         ('partner_zip', 10),
295                         ('partner_city', 22),
296                         ('padding', 21),
297                         #seg5
298                         ('seg_num5', 2),
299                         ('option_motif', 1),
300                         ('reference', 105),
301                         ('format', 1),
302                         ('padding', 19)
303                 ]
304                 self.pre.update( {
305                         'partner_bank_clearing': '',
306                         'partner_cpt_benef': '',
307                         'type_paiement': '0',
308                         'genre_trans': '836',
309                         'conv_cours': '',
310                         'reference': self.global_values['reference'],
311                         'ref2': '',
312                         'ref3': '',
313                         'format': '2'
314                 })
315                 self.post.update({'option_motif': 'U'})
316
317
318 class record_gt890(record):
319         """
320         Total
321         """
322         def init_local_context(self):
323                 self.fields = [
324                         ('seg_num1', 2),
325                         #header
326                         ('date_value_hdr', 6),
327                         ('partner_bank_clearing', 12),
328                         ('zero5', 5),
329                         ('creation_date', 6),
330                         ('comp_bank_clearing', 7),
331                         ('uid', 5),
332                         ('sequence', 5),
333                         ('genre_trans', 3),
334                         ('type_paiement', 1),
335                         ('flag', 1),
336                         #total
337                         ('amount_total', 16),
338                         ('padding', 59)
339                 ]
340                 self.pre.update({'partner_bank_clearing': '', 'partner_cpt_benef': '',
341                         'company_bank_clearing': '', 'genre_trans': '890'})
342
343 def c_ljust(s, size):
344         """
345         check before calling ljust
346         """
347         s= s or ''
348         if len(s) > size:
349                 s= s[:size]
350         s = s.decode('utf-8').encode('latin1','replace').ljust(size)
351         return s
352
353 def _create_dta(obj, cr, uid, data, context):
354         v={}
355         v['uid'] = str(uid)
356         v['creation_date']= time.strftime('%y%m%d')
357         dta=''
358
359         pool = pooler.get_pool(cr.dbname)
360         payment_obj = pool.get('payment.order')
361         attachment_obj = pool.get('ir.attachment')
362
363         payment = payment_obj.browse(cr, uid, data['id'], context=context)
364
365         if not payment.mode or payment.mode.type.code != 'dta':
366                 raise wizard.except_wizard(_('Error'),
367                                 _('No payment mode or payment type code invalid.'))
368         bank = payment.mode.bank_id
369         if not bank:
370                 raise wizard.except_wizard(_('Error'), _('No bank account for the company.'))
371
372         v['comp_bank_name']= bank.bank and bank.bank.name or False
373         v['comp_bank_clearing'] = bank.bank.clearing
374
375         if not v['comp_bank_clearing']:
376                 raise wizard.except_wizard(_('Error'),
377                                 _('You must provide a Clearing Number for your bank account.'))
378
379         user = pool.get('res.users').browse(cr,uid,[uid])[0]
380         company= user.company_id
381         #XXX dirty code use get_addr
382         co_addr= company.partner_id.address[0]
383         v['comp_country'] = co_addr.country_id and co_addr.country_id.name or ''
384         v['comp_street'] = co_addr.street or ''
385         v['comp_zip'] = co_addr.zip
386         v['comp_city'] = co_addr.city
387         v['comp_name'] = co_addr.name
388         v['comp_dta'] = bank.dta_code or '' #XXX not mandatory in pratice
389
390
391         v['comp_bank_number'] = bank.acc_number or ''
392         if bank.iban:
393                 v['comp_bank_iban'] = bank.iban.replace(' ','') or ''
394         else:
395                 v['comp_bank_iban'] = ''
396         if not v['comp_bank_iban']:
397                 raise wizard.except_wizard(_('Error'),
398                                 _('No IBAN for the company bank account.'))
399
400         dta_line_obj = pool.get('account.dta.line')
401         res_partner_bank_obj = pool.get('res.partner.bank')
402
403         seq= 1
404         amount_tot = 0
405         amount_currency_tot = 0
406
407         for pline in payment.line_ids:
408                 if not pline.bank_id:
409                         raise wizard.except_wizard(_('Error'), _('No bank account defined\n' \
410                                         'on line: %s') % pline.name)
411                 if not pline.bank_id.bank:
412                         raise wizard.except_wizard(_('Error'), _('No bank defined\n' \
413                                         'for the bank account: %s\n' \
414                                         'on the partner: %s\n' \
415                                         'on line: %s') + (pline.bank_id.state, pline.partner_id.name, pline.name))
416
417                 v['sequence'] = str(seq).rjust(5).replace(' ', '0')
418                 v['amount_to_pay']= str(pline.amount_currency).replace('.', ',')
419                 v['number'] = pline.name
420                 v['currency'] = pline.currency.code
421
422                 v['partner_bank_name'] =  pline.bank_id.bank.name or False
423                 v['partner_bank_clearing'] =  pline.bank_id.bank.clearing or False
424                 if not v['partner_bank_name'] :
425                         raise wizard.except_wizard(_('Error'), _('No bank name defined\n' \
426                                         'for the bank account: %s\n' \
427                                         'on the partner: %s\n' \
428                                         'on line: %s') % (pline.bank_id.state, pline.partner_id.name, pline.name))
429
430                 v['partner_bank_iban']=  pline.bank_id.iban or False
431                 v['partner_bank_number']=  pline.bank_id.acc_number  \
432                                 and pline.bank_id.acc_number.replace('.','').replace('-','') \
433                                 or  False
434                 v['partner_post_number']=  pline.bank_id.post_number \
435                                 and pline.bank_id.post_number.replace('.', '').replace('-', '') \
436                                 or  False
437                 v['partner_bvr'] = pline.bank_id.bvr_number or ''
438                 if v['partner_bvr']:
439                         v['partner_bvr'] = v['partner_bvr'].replace('-','')
440                         if len(v['partner_bvr']) < 9:
441                                 v['partner_bvr'] = v['partner_bvr'][:2] + '0' * \
442                                                 (9 - len(v['partner_bvr'])) + v['partner_bvr'][2:]
443
444                 if pline.bank_id.bank:
445                         v['partner_bank_city'] = pline.bank_id.bank.city or False
446                         v['partner_bank_street'] = pline.bank_id.bank.street or ''
447                         v['partner_bank_zip'] = pline.bank_id.bank.zip or ''
448                         v['partner_bank_country'] = pline.bank_id.bank.country and \
449                                         pline.bank_id.bank.country.name or ''
450
451                 v['partner_bank_code'] = pline.bank_id.bank.bic
452                 v['reference'] = pline.move_line_id.ref
453                 v['partner_name'] = pline.partner_id and pline.partner_id.name or ''
454                 if pline.partner_id and pline.partner_id.address \
455                                 and pline.partner_id.address[0]:
456                         v['partner_street'] = pline.partner_id.address[0].street
457                         v['partner_city']= pline.partner_id.address[0].city
458                         v['partner_zip']= pline.partner_id.address[0].zip
459                         # If iban => country=country code for space reason
460                         elec_pay = pline.bank_id.state #Bank type
461                         if elec_pay == 'iban':
462                                 v['partner_country']= pline.partner_id.address[0].country_id \
463                                                 and pline.partner_id.address[0].country_id.code+'-' \
464                                                 or ''
465                         else:
466                                 v['partner_country']= pline.partner_id.address[0].country_id \
467                                                 and pline.partner_id.address[0].country_id.name \
468                                                 or ''
469                 else:
470                         v['partner_street'] =''
471                         v['partner_city']= ''
472                         v['partner_zip']= ''
473                         v['partner_country']= ''
474                         raise wizard.except_wizard(_('Error'), _('No address defined \n' \
475                                         'for the partner: %s\n' \
476                                         'on line: %s') % (pline.partner_id.name, pline.name))
477
478                 if pline.order_id.date_planned :
479                         date_value = mx.DateTime.strptime(pline.order_id.date_planned, '%Y-%m-%d')
480                 elif pline.date :
481                         date_value = mx.DateTime.strptime(pline.date, '%Y-%m-%d')
482                 else :
483                         date_value = mx.DateTime.now()
484                 v['date_value'] = date_value.strftime("%y%m%d")
485
486                 # si compte iban -> iban (836)
487                 # si payment structure  -> bvr (826)
488                 # si non -> (827) 
489
490                 if elec_pay == 'dta_iban':
491                         # If iban => country=country code for space reason
492                         v['comp_country'] = co_addr.country_id and co_addr.country_id.code+'-' or ''
493                         record_type = record_gt836
494                         if not v['partner_bank_iban']:
495                                 raise wizard.except_wizard(_('Error'), _('No IBAN defined \n' \
496                                                 'for the bank account: %s\n' + \
497                                                 'on line: %s') % (res_partner_bank_obj.name_get(cr, uid, [pline.bank_id.id], context)[0][1] , pline.name))
498
499                         if v['partner_bank_code'] : # bank code is swift (BIC address)
500                                 v['option_id_bank']= 'A'
501                                 v['partner_bank_ident']= v['partner_bank_code']
502                         elif v['partner_bank_city']:
503
504                                 v['option_id_bank']= 'D'
505                                 v['partner_bank_ident']= v['partner_bank_name'] \
506                                                 + ' ' + v['partner_bank_street'] \
507                                                 + ' ' + v['partner_bank_zip'] \
508                                                 + ' ' + v['partner_bank_city'] \
509                                                 + ' ' + v['partner_bank_country']
510                         else:
511                                 raise wizard.except_wizard(_('Error'), _('You must provide the bank city '
512                                                 'or the bic code for the partner bank: \n %d\n' + \
513                                                 'on line: %s') %(res_partner_bank_obj.name_get(cr, uid, [pline.bank_id.id], context)[0][1], pline.name))
514
515                 elif elec_pay == 'bvrbank' or elec_pay == 'bvrpost':
516                         from tools import mod10r
517                         if v['reference']:
518                                 v['reference'] = v['reference'].replace(' ',
519                                                 '').rjust(27).replace(' ', '0')
520                         if not v['reference'] \
521                                         or mod10r(v['reference'][:-1]) != v['reference']:
522                                 raise wizard.except_wizard(_('Error'), _('You must provide ' \
523                                                 'a valid BVR reference number \n' \
524                                                 'for the line: %s') % pline.name)
525                         if not v['partner_bvr']:
526                                 raise wizard.except_wizard(_('Error'), _('You must provide a BVR number\n'
527                                         'for the bank account: %s' \
528                                         'on line: %s') % (res_partner_bank_obj.name_get(cr, uid, [pline.bank_id.id],context)[0][1] ,pline.name))
529                         record_type = record_gt826
530
531                 elif elec_pay == 'bvbank':
532                         if not v['partner_bank_number'] :
533                                 if v['partner_bank_iban'] :
534                                         v['partner_bank_number']= v['partner_bank_iban'] 
535                                 else:
536                                         raise wizard.except_wizard(_('Error'), _('You must provide ' \
537                                                         'a bank number \n' \
538                                                         'for the partner bank: %s\n' \
539                                                         'on line: %s') % (res_partner_bank_obj.name_get(cr, uid, [pline.bank_id.id], context)[0][1] , pline.name))
540                         if not  v['partner_bank_clearing']:
541                                 raise wizard.except_wizard(_('Error'), _('You must provide ' \
542                                                 'a Clearing Number\n' \
543                                                 'for the partner bank: %s\n' \
544                                                 'on line %s') % (res_partner_bank_obj.name_get(cr, uid, [pline.bank_id.id], context)[0][1] , pline.name))
545                         v['partner_bank_number'] = '/C/'+v['partner_bank_number']
546                         record_type = record_gt827
547                 elif elec_pay == 'bvpost':
548                         if not v['partner_post_number']:
549                                 raise wizard.except_wizard(_('Error'), _('You must provide ' \
550                                                 'a post number \n' \
551                                                 'for the partner bank: %s\n' \
552                                                 'on line: %s') % (res_partner_bank_obj.name_get(cr, uid, [pline.bank_id.id], context)[0][1] ,pline.name))
553                         v['partner_bank_clearing']= ''
554                         v['partner_bank_number'] = '/C/'+v['partner_post_number']
555                         record_type = record_gt827
556                 else:
557                         raise wizard.except_wizard(_('Error'), _('The Bank type %s of the bank account: %s is not supported') \
558                     % (elec_pay, res_partner_bank_obj.name_get(cr, uid, [pline.bank_id.id], context)[0][1],))
559
560                 dta_line = record_type(v).generate()
561
562                 dta = dta + dta_line
563                 amount_tot += pline.amount
564                 amount_currency_tot += pline.amount_currency
565                 seq += 1
566
567         # segment total
568         v['amount_total'] = str(amount_currency_tot).replace('.',',')
569         v['sequence'] = str(seq).rjust(5).replace(' ','0')      
570         if dta :
571                 dta = dta + record_gt890(v).generate()
572
573         dta_data= base64.encodestring(dta)
574         payment_obj.set_done(cr, uid, data['id'], context)
575         attachment_obj.create(cr, uid, {
576                 'name': 'DTA',
577                 'datas': dta_data,
578                 'datas_fname': 'DTA.txt',
579                 'res_model': 'payment.order',
580                 'res_id': data['id'],
581                 }, context=context)
582         return {'dta': dta_data}
583
584
585 class wizard_dta_create(wizard.interface):
586         states = {
587                 'init' : {
588                         'actions' : [_create_dta],
589                         'result' : {'type' : 'form',
590                                 'arch' : FORM,
591                                 'fields' : FIELDS,
592                                 'state' : [('end', 'OK', 'gtk-ok', True)]
593                         }
594                 },
595         }
596
597 wizard_dta_create('account.dta_create')
598 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: