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