~vauxoo/addons-vauxoo/7.0-addons-vauxoo-email_template_followers-dev-julio

« back to all changes in this revision

Viewing changes to ir_values_menu/ir_values.py

  • Committer: Isaac Lopez
  • Date: 2012-07-30 21:33:56 UTC
  • mto: (525.1.14 vaddons)
  • mto: This revision was merged to the branch mainline in revision 527.
  • Revision ID: isaac@vauxoo.com-20120730213356-ky4kcyp426lcpqmb
[ADD][ir_values_menu] Add new module

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# -*- encoding: utf-8 -*-
 
2
###########################################################################
 
3
#    Module Writen to OpenERP, Open Source Management Solution
 
4
#
 
5
#    Copyright (c) 2012 Vauxoo - http://www.vauxoo.com/
 
6
#    All Rights Reserved.
 
7
#    info Vauxoo (info@vauxoo.com)
 
8
############################################################################
 
9
#    Coded by: Isaac Lopez (isaac@vauxoo.com)
 
10
#              moylop260 (moylop260@vauxoo.com)
 
11
############################################################################
 
12
#
 
13
#    This program is free software: you can redistribute it and/or modify
 
14
#    it under the terms of the GNU Affero General Public License as
 
15
#    published by the Free Software Foundation, either version 3 of the
 
16
#    License, or (at your option) any later version.
 
17
#
 
18
#    This program is distributed in the hope that it will be useful,
 
19
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
 
20
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
21
#    GNU Affero General Public License for more details.
 
22
#
 
23
#    You should have received a copy of the GNU Affero General Public License
 
24
#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
25
#
 
26
##############################################################################
 
27
 
 
28
from osv import osv
 
29
from osv import fields
 
30
import tools
 
31
import time
 
32
import base64
 
33
import os
 
34
import netsvc
 
35
from tools.translate import _
 
36
import codecs
 
37
 
 
38
class ir_values
 
39
 
 
40
class account_invoice(osv.osv):
 
41
    _inherit = 'account.invoice'
 
42
 
 
43
    def create_report(self, cr, uid, res_ids, report_name=False, file_name=False):
 
44
        if not report_name or not res_ids:
 
45
            return (False,Exception('Report name and Resources ids are required !!!'))
 
46
        #try:
 
47
        ret_file_name = file_name+'.pdf'
 
48
        service = netsvc.LocalService("report."+report_name);
 
49
        (result,format) = service.create(cr, uid, res_ids, {}, {})
 
50
        fp = open(ret_file_name,'wb+');
 
51
        fp.write(result);
 
52
        fp.close();
 
53
        #except Exception,e:
 
54
            #print 'Exception in create report:',e
 
55
            #return (False,str(e))
 
56
        return (True,ret_file_name)
 
57
    
 
58
    def create_report_pdf(self, cr, uid, ids, context={}):
 
59
        if not context:
 
60
            context = {}
 
61
        id = ids[0]
 
62
        
 
63
        (fileno, fname) = tempfile.mkstemp('.pdf', 'openerp_' + (False or '') + '__facturae__' )
 
64
        os.close( fileno )
 
65
        
 
66
        file = self.create_report(cr, uid, [id], "account.invoice.facturae.pdf", fname)
 
67
        is_file = file[0]
 
68
        fname = file[1]
 
69
        if is_file and os.path.isfile(fname):
 
70
            f = open(fname, "r")
 
71
            data = f.read()
 
72
            f.close()
 
73
            
 
74
            data_attach = {
 
75
                'name': context.get('fname'),
 
76
                'datas': data and base64.encodestring( data ) or None,
 
77
                'datas_fname': context.get('fname'),
 
78
                'description': 'Factura-E PDF',
 
79
                'res_model': self._name,
 
80
                'res_id': id,
 
81
            }
 
82
            self.pool.get('ir.attachment').create(cr, uid, data_attach, context=context)
 
83
        return True
 
84
    
 
85
    def action_make_cfd(self, cr, uid, ids, *args):
 
86
        self._attach_invoice(cr, uid, ids)
 
87
        return True
 
88
    
 
89
    def ________action_number(self, cr, uid, ids, *args):
 
90
        cr.execute('SELECT id, type, number, move_id, reference ' \
 
91
                'FROM account_invoice ' \
 
92
                'WHERE id IN ('+','.join(map(str,ids))+')')
 
93
        obj_inv = self.browse(cr, uid, ids)[0]
 
94
        
 
95
        invoice_id__sequence_id = self._get_sequence(cr, uid, ids)##agregado
 
96
        
 
97
        for (id, invtype, number, move_id, reference) in cr.fetchall():
 
98
            if not number:
 
99
                tmp_context = {
 
100
                    'fiscalyear_id' : obj_inv.period_id.fiscalyear_id.id,
 
101
                }
 
102
                if invoice_id__sequence_id[id]:
 
103
                    sid = invoice_id__sequence_id[id]
 
104
                    number = self.pool.get('ir.sequence').get_id(cr, uid, sid, 'id=%s', context=tmp_context)
 
105
                elif obj_inv.journal_id.invoice_sequence_id:
 
106
                    sid = obj_inv.journal_id.invoice_sequence_id.id
 
107
                    number = self.pool.get('ir.sequence').get_id(cr, uid, sid, 'id=%s', context=tmp_context)
 
108
                else:
 
109
                    number = self.pool.get('ir.sequence').get_id(cr, uid,
 
110
                                                                 'account.invoice.' + invtype,
 
111
                                                                 'code=%s',
 
112
                                                                 context=tmp_context)
 
113
                if not number:
 
114
                    raise osv.except_osv('Warning !', 'No hay una secuencia de folios bien definida. !')
 
115
                if invtype in ('in_invoice', 'in_refund'):
 
116
                    ref = reference
 
117
                else:
 
118
                    ref = self._convert_ref(cr, uid, number)
 
119
                cr.execute('UPDATE account_invoice SET number=%s ' \
 
120
                        'WHERE id=%d', (number, id))
 
121
                cr.execute('UPDATE account_move_line SET ref=%s ' \
 
122
                        'WHERE move_id=%d AND (ref is null OR ref = \'\')',
 
123
                        (ref, move_id))
 
124
                cr.execute('UPDATE account_analytic_line SET ref=%s ' \
 
125
                        'FROM account_move_line ' \
 
126
                        'WHERE account_move_line.move_id = %d ' \
 
127
                            'AND account_analytic_line.move_id = account_move_line.id',
 
128
                            (ref, move_id))
 
129
        return True
 
130
    
 
131
    def _attach_invoice(self, cr, uid, ids, context=None):
 
132
        if not context:
 
133
            context = {}
 
134
        inv_type_facturae = {'out_invoice': True, 'out_refund': True, 'in_invoice': False, 'in_refund': False}
 
135
        for inv in self.browse(cr, uid, ids):
 
136
            if inv_type_facturae.get(inv.type, False):
 
137
                fname, xml_data = self.pool.get('account.invoice')._get_facturae_invoice_xml_data(cr, uid, [inv.id], context=context)
 
138
                data_attach = {
 
139
                        'name': fname,
 
140
                        #'datas':binascii.b2a_base64(str(attachents.get(attactment))),
 
141
                        'datas': xml_data and base64.encodestring( xml_data ) or None,
 
142
                        'datas_fname': fname,
 
143
                        'description': 'Factura-E XML',
 
144
                        'res_model': self._name,
 
145
                        'res_id': inv.id,
 
146
                }
 
147
                self.pool.get('ir.attachment').create(cr, uid, data_attach, context=context)
 
148
                fname = fname.replace('.xml', '.pdf')
 
149
                self.create_report_pdf(cr, uid, ids, context={'fname': fname})
 
150
        return True
 
151
    
 
152
    def _get_fname_invoice(self, cr, uid, ids, field_names=None, arg=False, context={}):
 
153
        if not context:
 
154
            context = {}
 
155
        res = {}
 
156
        sequence_obj = self.pool.get('ir.sequence')
 
157
        
 
158
        invoice_id__sequence_id = self._get_invoice_sequence(cr, uid, ids, context=context)
 
159
        for invoice in self.browse(cr, uid, ids, context=context):
 
160
            sequence_id = invoice_id__sequence_id[invoice.id]
 
161
            sequence = False
 
162
            if sequence_id:
 
163
                sequence = sequence_obj.browse(cr, uid, [sequence_id], context)[0]
 
164
            fname = ""
 
165
            fname += (invoice.company_id.partner_id and (invoice.company_id.partner_id._columns.has_key('vat_split') and invoice.company_id.partner_id.vat_split or invoice.company_id.partner_id.vat) or '')
 
166
            fname += '.'
 
167
            number_work = invoice.number or invoice.internal_number
 
168
            try:
 
169
                context.update({ 'number_work': int( number_work ) or False })
 
170
                fname += sequence and sequence.approval_id and sequence.approval_id.serie or ''
 
171
                fname += '.'
 
172
            except:
 
173
                pass
 
174
            fname += number_work or ''
 
175
            res[invoice.id] = fname
 
176
        return res
 
177
        
 
178
    def action_cancel_draft(self, cr, uid, ids, *args):
 
179
        attachment_obj = self.pool.get('ir.attachment')
 
180
        for invoice in self.browse(cr, uid, ids):
 
181
            try:
 
182
                attachment_xml_id = attachment_obj.search(cr, uid, [
 
183
                    ('name','=',invoice.fname_invoice+'.xml'),
 
184
                    ('datas_fname','=',invoice.fname_invoice+'.xml'),
 
185
                    ('res_model','=','account.invoice'),
 
186
                    ('res_id','=',invoice.id)
 
187
                ], limit=1)
 
188
                attachment_obj.unlink(cr, uid, attachment_xml_id)
 
189
                
 
190
                attachment_pdf_id = attachment_obj.search(cr, uid, [
 
191
                    ('name','=',invoice.fname_invoice),###no se agrega.pdf, porque el generador de reportes, no lo hace asi, actualmente o agrega doble .pdf o nada
 
192
                    #('name','=',invoice.fname_invoice+'.pdf'),
 
193
                    ('datas_fname','=',invoice.fname_invoice+'.pdf'),
 
194
                    ('res_model','=','account.invoice'),
 
195
                    ('res_id','=',invoice.id)
 
196
                ], limit=1)
 
197
                attachment_obj.unlink(cr, uid, attachment_pdf_id)
 
198
            except:
 
199
                pass
 
200
        self.write(cr, uid, ids, {
 
201
            'no_certificado': False,
 
202
            'certificado': False,
 
203
            'sello': False,
 
204
            'cadena_original': False,
 
205
            'date_invoice_cancel': False,
 
206
        })
 
207
        return super(account_invoice, self).action_cancel_draft(cr, uid, ids, args)
 
208
    
 
209
    def action_cancel(self, cr, uid, ids, *args):
 
210
        self.write(cr, uid, ids, {'date_invoice_cancel': time.strftime('%Y-%m-%d %H:%M:%S')})
 
211
        return super(account_invoice, self).action_cancel(cr, uid, ids, args)
 
212
    
 
213
    def _get_cfd_xml_invoice(self, cr, uid, ids, field_name=None, arg=False, context=None):
 
214
        res = {}
 
215
        attachment_obj = self.pool.get('ir.attachment')
 
216
        for invoice in self.browse(cr, uid, ids, context=context):
 
217
            attachment_xml_id = attachment_obj.search(cr, uid, [
 
218
                    ('name','=',invoice.fname_invoice+'.xml'),
 
219
                    ('datas_fname','=',invoice.fname_invoice+'.xml'),
 
220
                    ('res_model','=','account.invoice'),
 
221
                    ('res_id','=',invoice.id),
 
222
                ], limit=1)
 
223
            res[invoice.id] = attachment_xml_id and attachment_xml_id[0] or False
 
224
        return res
 
225
    
 
226
    _columns = {
 
227
        ##Extract date_invoice from original, but add datetime
 
228
        #'date_invoice': fields.datetime('Date Invoiced', states={'open':[('readonly',True)],'close':[('readonly',True)]}, help="Keep empty to use the current date"),
 
229
        #'invoice_sequence_id': fields.function(_get_invoice_sequence, method=True, type='many2one', relation='ir.sequence', string='Invoice Sequence', store=True),
 
230
        #'certificate_id': fields.function(_get_invoice_certificate, method=True, type='many2one', relation='res.company.facturae.certificate', string='Invoice Certificate', store=True),
 
231
        'fname_invoice':  fields.function(_get_fname_invoice, method=True, type='char', size=26, string='File Name Invoice'),
 
232
        #'amount_to_text':  fields.function(_get_amount_to_text, method=True, type='char', size=256, string='Amount to Text', store=True),
 
233
        'no_certificado': fields.char('No. Certificado', size=64),
 
234
        'certificado': fields.text('Certificado', size=64),
 
235
        'sello': fields.text('Sello', size=512),
 
236
        'cadena_original': fields.text('Cadena Original', size=512),
 
237
        'date_invoice_cancel': fields.datetime('Date Invoice Cancelled', readonly=True),
 
238
        'cfd_xml_id': fields.function(_get_cfd_xml_invoice, method=True, type='many2one', relation='ir.attachment', string='XML'),
 
239
    }
 
240
    
 
241
    _defaults = {
 
242
        #'date_invoice': lambda *a: time.strftime('%Y-%m-%d %H:%M:%S'),
 
243
    }
 
244
        
 
245
    def copy(self, cr, uid, id, default={}, context=None):
 
246
        if context is None:
 
247
            context = {}
 
248
        default.update({
 
249
            'invoice_sequence_id': False,
 
250
            'no_certificado': False,
 
251
            'certificado': False,
 
252
            'sello': False,
 
253
            'cadena_original': False,
 
254
        })
 
255
        return super(account_invoice, self).copy(cr, uid, id, default, context=context)
 
256
    
 
257
    def binary2file(self, cr, uid, ids, binary_data, file_prefix="", file_suffix=""):
 
258
        (fileno, fname) = tempfile.mkstemp(file_suffix, file_prefix)
 
259
        f = open( fname, 'wb' )
 
260
        f.write( base64.decodestring( binary_data ) )
 
261
        f.close()
 
262
        os.close( fileno )
 
263
        return fname
 
264
    
 
265
    def _get_file_globals(self, cr, uid, ids, context={}):
 
266
        if not context:
 
267
            context={}
 
268
        id = ids and ids[0] or False
 
269
        file_globals = {}
 
270
        if id:
 
271
            invoice = self.browse(cr, uid, id, context=context)
 
272
            #certificate_id = invoice.company_id.certificate_id
 
273
            context.update( {'date_work': invoice.date_invoice_tz} )
 
274
            certificate_id = self.pool.get('res.company')._get_current_certificate(cr, uid, [invoice.company_id.id], context=context)[invoice.company_id.id]
 
275
            certificate_id = certificate_id and self.pool.get('res.company.facturae.certificate').browse(cr, uid, [certificate_id], context=context)[0] or False
 
276
            
 
277
            if certificate_id:
 
278
                if not certificate_id.certificate_file_pem:
 
279
                    #generate certificate_id.certificate_file_pem, a partir del certificate_id.certificate_file
 
280
                    pass
 
281
                fname_cer_pem = False
 
282
                try:
 
283
                    fname_cer_pem = self.binary2file(cr, uid, ids, certificate_id.certificate_file_pem, 'openerp_' + (certificate_id.serial_number or '') + '__certificate__', '.cer.pem')
 
284
                except:
 
285
                    raise osv.except_osv('Error !', 'No se ha capturado un archivo CERTIFICADO en formato PEM, en la company!')
 
286
                file_globals['fname_cer'] = fname_cer_pem
 
287
                
 
288
                fname_key_pem = False
 
289
                try:
 
290
                    fname_key_pem = self.binary2file(cr, uid, ids, certificate_id.certificate_key_file_pem, 'openerp_' + (certificate_id.serial_number or '') + '__certificate__', '.key.pem')
 
291
                except:
 
292
                    raise osv.except_osv('Error !', 'No se ha capturado un archivo KEY en formato PEM, en la company!')
 
293
                file_globals['fname_key'] = fname_key_pem
 
294
                
 
295
                fname_cer_no_pem = False
 
296
                try:
 
297
                    fname_cer_no_pem = self.binary2file(cr, uid, ids, certificate_id.certificate_file, 'openerp_' + (certificate_id.serial_number or '') + '__certificate__', '.cer')
 
298
                except:
 
299
                    pass
 
300
                file_globals['fname_cer_no_pem'] = fname_cer_no_pem
 
301
                
 
302
                fname_key_no_pem = False
 
303
                try:
 
304
                    fname_key_no_pem = self.binary2file(cr, uid, ids, certificate_id.certificate_key_file, 'openerp_' + (certificate_id.serial_number or '') + '__certificate__', '.key')
 
305
                except:
 
306
                    pass
 
307
                file_globals['fname_key_no_pem'] = fname_key_no_pem
 
308
                
 
309
                file_globals['password'] = certificate_id.certificate_password
 
310
                
 
311
                if certificate_id.fname_xslt:
 
312
                    if ( certificate_id.fname_xslt[0] == os.sep or certificate_id.fname_xslt[1] == ':' ):
 
313
                        file_globals['fname_xslt'] = certificate_id.fname_xslt
 
314
                    else:
 
315
                        file_globals['fname_xslt'] = os.path.join( tools.config["root_path"], certificate_id.fname_xslt )
 
316
                else:
 
317
                    file_globals['fname_xslt'] = os.path.join( tools.config["addons_path"], 'l10n_mx_facturae', 'SAT', 'cadenaoriginal_2_0_l.xslt' )
 
318
                
 
319
                file_globals['fname_repmensual_xslt'] = os.path.join( tools.config["addons_path"], 'l10n_mx_facturae', 'SAT', 'reporte_mensual_2_0.xslt' )
 
320
                
 
321
                if not file_globals.get('fname_xslt', False):
 
322
                    raise osv.except_osv('Warning !', 'No se ha definido fname_xslt. !')
 
323
                
 
324
                if not os.path.isfile(file_globals.get('fname_xslt', ' ')):
 
325
                    raise osv.except_osv('Warning !', 'No existe el archivo [%s]. !'%(file_globals.get('fname_xslt', ' ')))
 
326
                
 
327
                file_globals['serial_number'] = certificate_id.serial_number
 
328
            else:
 
329
                raise osv.except_osv('Warning !', 'Verique la fecha de la factura y la vigencia del certificado, y que el registro del certificado este activo.\n%s!'%(msg2))
 
330
        return file_globals
 
331
    
 
332
    def _____________get_facturae_invoice_txt_data(self, cr, uid, ids, context={}):
 
333
        #TODO: Transform date to fmt %d/%m/%Y %H:%M:%S
 
334
        certificate_lib = self.pool.get('facturae.certificate.library')
 
335
        fname_repmensual_xslt = self._get_file_globals(cr, uid, ids, context=context)['fname_repmensual_xslt']
 
336
        fname_tmp = certificate_lib.b64str_to_tempfile( base64.encodestring(''), file_suffix='.txt', file_prefix='openerp__' + (False or '') + '__repmensual__' )
 
337
        rep_mensual = ''
 
338
        for invoice in self.browse(cr, uid, ids, context=context):
 
339
            xml_b64 = invoice.cfd_xml_id and invoice.cfd_xml_id.datas or False
 
340
            if xml_b64:
 
341
                fname_xml = certificate_lib.b64str_to_tempfile( xml_b64 or '', file_suffix='.xml', file_prefix='openerp__' + (False or '') + '__xml__' )
 
342
                rep_mensual += certificate_lib._transform_xml(fname_xml=fname_xml, fname_xslt=fname_repmensual_xslt, fname_out=fname_tmp)
 
343
                rep_mensual += '\r\n'
 
344
        return rep_mensual, fname_tmp
 
345
        
 
346
    def _get_facturae_invoice_txt_data(self, cr, uid, ids, context={}):
 
347
        facturae_datas = self._get_facturae_invoice_dict_data(cr, uid, ids, context=context)
 
348
        facturae_data_txt_lists = []
 
349
        folio_data = self._get_folio(cr, uid, ids, context=context)
 
350
        facturae_type_dict = {'out_invoice': 'I', 'out_refund': 'E', 'in_invoice': False, 'in_refund': False}
 
351
        fechas = []
 
352
        for facturae_data in facturae_datas:
 
353
            invoice_comprobante_data = facturae_data['Comprobante']
 
354
            fechas.append( invoice_comprobante_data['fecha'] )
 
355
            if facturae_data['state'] in ['open', 'paid']:
 
356
                facturae_state = 1
 
357
            elif facturae_data['state'] in ['cancel']:
 
358
                facturae_state = 0
 
359
            else:
 
360
                continue
 
361
            facturae_type = facturae_type_dict[ facturae_data['type'] ]
 
362
            rate = facturae_data['rate']
 
363
            
 
364
            if not facturae_type:
 
365
                continue
 
366
            #if not invoice_comprobante_data['Receptor']['rfc']:
 
367
                #raise osv.except_osv('Warning !', 'No se tiene definido el RFC de la factura [%s].\n%s !'%(facturae_data['Comprobante']['folio'], msg2))
 
368
            
 
369
            invoice = self.browse(cr, uid, [facturae_data['invoice_id']], context=context)[0]
 
370
            pedimento_numeros = []
 
371
            pedimento_fechas = []
 
372
            pedimento_aduanas = []
 
373
            for line in invoice.invoice_line:
 
374
                try:
 
375
                    pedimento_numeros.append(line.tracking_id.import_id.name or '')
 
376
                    pedimento_fechas.append(line.tracking_id.import_id.date or '')
 
377
                    pedimento_aduanas.append(line.tracking_id.import_id.customs or '')
 
378
                except:
 
379
                    pass
 
380
            pedimento_numeros = ','.join(map(lambda x: str(x) or '', pedimento_numeros))
 
381
            pedimento_fechas = ','.join(map(lambda x: str(x) or '', pedimento_fechas))
 
382
            pedimento_aduanas = ','.join(map(lambda x: str(x) or '', pedimento_aduanas))                
 
383
            
 
384
            facturae_data_txt_list = [
 
385
                invoice_comprobante_data['Receptor']['rfc'] or '',
 
386
                invoice_comprobante_data.get('serie', False) or '',
 
387
                invoice_comprobante_data['folio'] or '',
 
388
                str( invoice_comprobante_data['anoAprobacion'] ) + str( invoice_comprobante_data['noAprobacion'] ),
 
389
                time.strftime('%d/%m/%Y %H:%M:%S', time.strptime( facturae_data['date_invoice_tz'], '%Y-%m-%d %H:%M:%S')),#invoice_comprobante_data['fecha'].replace('T', ' '),
 
390
                "%.2f"%( round( float(invoice_comprobante_data['total'] or 0.0) * rate, 2) ),
 
391
                "%.2f"%( round( float(invoice_comprobante_data['Impuestos']['totalImpuestosTrasladados'] or 0.0) * rate, 2) ),
 
392
                facturae_state,
 
393
                facturae_type,
 
394
                pedimento_numeros,
 
395
                pedimento_fechas,
 
396
                pedimento_aduanas,
 
397
            ]
 
398
            facturae_data_txt_lists.append( facturae_data_txt_list )
 
399
        
 
400
        fecha_promedio = time.strftime('%Y-%m-%d')
 
401
        if fechas:
 
402
            fecha_promedio = fechas[ int( len(fechas)/2 )-1 ]
 
403
        
 
404
        cad = ""
 
405
        for facturae_data_txt in facturae_data_txt_lists:
 
406
            cad += '|'
 
407
            cad += '|'.join(map(lambda x: str(x) or '', facturae_data_txt))
 
408
            cad += '|'
 
409
            cad += '\r\n'
 
410
        
 
411
        fname = "1" + invoice_comprobante_data['Emisor']['rfc'] + '-' + time.strftime('%m%Y', time.strptime(fecha_promedio, '%Y-%m-%dT%H:%M:%S')) + '.txt'
 
412
        return cad, fname
 
413
    
 
414
    def _get_folio(self, cr, uid, ids, context={}):
 
415
        folio_data = {}
 
416
        id = ids and ids[0] or False
 
417
        if id:
 
418
            invoice = self.browse(cr, uid, id, context=context)
 
419
            """
 
420
            def get_id(self, cr, uid, sequence_id, test='id=%s', context=None):
 
421
                if test not in ('id=%s', 'code=%s'):
 
422
                    raise ValueError('invalid test')
 
423
                cr.execute('SELECT id, number_next, prefix, suffix, padding FROM ir_sequence WHERE '+test+' AND active=%s FOR UPDATE', (sequence_id, True))
 
424
                res = cr.dictfetchone()
 
425
                if res:
 
426
            """
 
427
            """
 
428
            tmp_context = {
 
429
                'fiscalyear_id' : invoice.period_id.fiscalyear_id.id,
 
430
            }
 
431
            if invoice.journal_id.invoice_sequence_id:
 
432
                sid = invoice.journal_id.invoice_sequence_id.id
 
433
                number = self.pool.get('ir.sequence').get_id(cr, uid, sid, 'id=%s', context=tmp_context)
 
434
            else:
 
435
                number = self.pool.get('ir.sequence').get_id(cr, uid,
 
436
                                                             'account.invoice.' + invtype,
 
437
                                                             'code=%s',
 
438
                                                             context=tmp_context)
 
439
                                                                 
 
440
            if not number:
 
441
                raise osv.except_osv('Warning !', 'There is no active invoice sequence defined for the journal !')
 
442
            """
 
443
            sequence_id = self._get_invoice_sequence(cr, uid, [id])[id]
 
444
            """
 
445
            if invoice.journal_id.invoice_sequence_id or invoice_id__sequence_id[id]:
 
446
                sequence_id = invoice_id__sequence_id[id] or invoice.journal_id.invoice_sequence_id.id
 
447
            else:
 
448
                test = 'code=%s'
 
449
                test_value = 'account.invoice.' + invoice.type
 
450
                test2 = '\n--company_id=%s\n'
 
451
                test2_value = invoice.company_id.id
 
452
                cr.execute('SELECT id, number_next, prefix, suffix, padding FROM ir_sequence WHERE '+test + test2+ ' AND active=%s FOR UPDATE', (test_value, test2_value, True))
 
453
                res = cr.dictfetchone()
 
454
                sequence_id = res and res['id'] or False
 
455
            """
 
456
            if sequence_id:
 
457
                #NO ES COMPATIBLE CON TINYERP approval_id = sequence.approval_id.id
 
458
                number_work = invoice.number or invoice.internal_number
 
459
                if invoice.type in ['out_invoice', 'out_refund']:
 
460
                    try:
 
461
                        if number_work:
 
462
                            int(number_work)
 
463
                    except(ValueError):
 
464
                        raise osv.except_osv(_('Warning !'), _('El folio [%s] tiene que ser un numero entero, sin letras.')%( number_work ) )
 
465
                context.update({ 'number_work': number_work or False })
 
466
                approval_id = self.pool.get('ir.sequence')._get_current_approval(cr, uid, [sequence_id], field_names=None, arg=False, context=context)[sequence_id]
 
467
                approval = approval_id and self.pool.get('ir.sequence.approval').browse(cr, uid, [approval_id], context=context)[0] or False
 
468
                if approval:
 
469
                    folio_data = {
 
470
                        'serie': approval.serie or '',
 
471
                        #'folio': '1',
 
472
                        'noAprobacion': approval.approval_number or '',
 
473
                        'anoAprobacion': approval.approval_year or '',
 
474
                        'desde': approval.number_start or '',
 
475
                        'hasta': approval.number_end or '',
 
476
                        #'noCertificado': "30001000000100000800",
 
477
                    }
 
478
                else:
 
479
                    raise osv.except_osv(u'Warning !', u'La secuencia no tiene datos de facturacion electronica.\nEn la sequence_id [%d].\n %s !'%(sequence_id, msg2))
 
480
            else:
 
481
                raise osv.except_osv(u'Warning !', u'No se encontro un sequence de configuracion. %s !'%(msg2))
 
482
        return folio_data
 
483
    
 
484
    def _dict_iteritems_sort(self, data_dict):#cr=False, uid=False, ids=[], context={}):
 
485
        key_order = [
 
486
            'Emisor',
 
487
            'Receptor',
 
488
            'Conceptos',
 
489
            'Impuestos',
 
490
        ]
 
491
        keys = data_dict.keys()
 
492
        key_item_sort = []
 
493
        for ko in key_order:
 
494
            if ko in keys:
 
495
                key_item_sort.append( [ko, data_dict[ko]] )
 
496
                keys.pop( keys.index( ko ) )
 
497
        for key_too in keys:
 
498
            key_item_sort.append( [key_too, data_dict[key_too]] )
 
499
        return key_item_sort
 
500
    
 
501
    def dict2xml(self, data_dict, node=False, doc=False):
 
502
        parent = False
 
503
        if node:
 
504
            parent = True
 
505
        
 
506
        for element, attribute in self._dict_iteritems_sort( data_dict ):
 
507
            if not parent:
 
508
                doc = minidom.Document()
 
509
            if isinstance( attribute, dict ):
 
510
                if not parent:
 
511
                    node = doc.createElement( element )
 
512
                    self.dict2xml( attribute, node, doc )
 
513
                else:
 
514
                    child = doc.createElement( element )
 
515
                    self.dict2xml( attribute, child, doc )
 
516
                    node.appendChild(child)
 
517
            elif isinstance( attribute, list):
 
518
                child = doc.createElement( element )
 
519
                for attr in attribute:
 
520
                    if isinstance( attr, dict ):
 
521
                        self.dict2xml( attr, child, doc )
 
522
                node.appendChild(child)
 
523
            else:
 
524
                if isinstance(attribute, str) or isinstance(attribute, unicode) :
 
525
                    attribute = conv_ascii(attribute)
 
526
                else:
 
527
                        attribute = str(attribute)
 
528
                node.setAttribute(element, attribute)
 
529
                #print "attribute",unicode( attribute, 'UTF-8')
 
530
        if not parent:
 
531
            doc.appendChild(node)
 
532
        return doc
 
533
 
 
534
    def _get_facturae_invoice_xml_data(self, cr, uid, ids, context={}):
 
535
        if not context:
 
536
            context = {}
 
537
        data_dict = self._get_facturae_invoice_dict_data(cr, uid, ids, context=context)[0]
 
538
        doc_xml = self.dict2xml( {'Comprobante': data_dict.get('Comprobante') } )
 
539
        invoice_number = "sn"
 
540
        (fileno_xml, fname_xml) = tempfile.mkstemp('.xml', 'openerp_' + (invoice_number or '') + '__facturae__' )
 
541
        fname_txt =  fname_xml + '.txt'
 
542
        f = open(fname_xml, 'w')
 
543
        doc_xml.writexml(f, indent='    ', addindent='    ', newl='\r\n', encoding='UTF-8')
 
544
        f.close()
 
545
        os.close(fileno_xml)
 
546
        
 
547
        (fileno_sign, fname_sign) = tempfile.mkstemp('.txt', 'openerp_' + (invoice_number or '') + '__facturae_txt_md5__' )
 
548
        os.close(fileno_sign)
 
549
        
 
550
        context.update({
 
551
            'fname_xml': fname_xml,
 
552
            'fname_txt': fname_txt,
 
553
            'fname_sign': fname_sign,
 
554
        })
 
555
        context.update( self._get_file_globals(cr, uid, ids, context=context) )
 
556
        fname_txt, txt_str = self._xml2cad_orig(cr=False, uid=False, ids=False, context=context)
 
557
        data_dict['cadena_original'] = txt_str
 
558
        
 
559
        if not txt_str:
 
560
            raise osv.except_osv(_('Error en Cadena original!'), _('No se pudo obtener la cadena original del comprobante.\nVerifique su configuracion.\n%s'%(msg2)) )
 
561
        
 
562
        if not data_dict['Comprobante'].get('folio', ''):
 
563
            raise osv.except_osv(_('Error en Folio!'), _('No se pudo obtener el Folio del comprobante.\nAntes de generar el XML, de clic en el boton, generar factura.\nVerifique su configuracion.\n%s'%(msg2)) )
 
564
            
 
565
        #time.strftime('%Y-%m-%dT%H:%M:%S', time.strptime(invoice.date_invoice, '%Y-%m-%d %H:%M:%S'))
 
566
        context.update( { 'fecha': data_dict['Comprobante']['fecha'] } )
 
567
        sign_str = self._get_sello(cr=False, uid=False, ids=False, context=context)
 
568
        if not sign_str:
 
569
            raise osv.except_osv('Error en Sello !', 'No se pudo generar el sello del comprobante.\nVerifique su configuracion.\ns%s'%(msg2))
 
570
        
 
571
        nodeComprobante = doc_xml.getElementsByTagName("Comprobante")[0]
 
572
        nodeComprobante.setAttribute("sello", sign_str)
 
573
        data_dict['Comprobante']['sello'] = sign_str
 
574
        
 
575
        noCertificado = self._get_noCertificado( context['fname_cer'] )
 
576
        if not noCertificado:
 
577
            raise osv.except_osv('Error en No Certificado !', 'No se pudo obtener el No de Certificado del comprobante.\nVerifique su configuracion.\n%s'%(msg2))
 
578
        nodeComprobante.setAttribute("noCertificado", noCertificado)
 
579
        data_dict['Comprobante']['noCertificado'] = noCertificado
 
580
        
 
581
        cert_str = self._get_certificate_str( context['fname_cer'] )
 
582
        if not cert_str:
 
583
            raise osv.except_osv('Error en Certificado!', 'No se pudo generar el Certificado del comprobante.\nVerifique su configuracion.\n%s'%(msg2))
 
584
        nodeComprobante.setAttribute("certificado", cert_str)
 
585
        data_dict['Comprobante']['certificado'] = cert_str
 
586
        
 
587
        self.write_cfd_data(cr, uid, ids, data_dict, context=context)
 
588
        
 
589
        if context.get('type_data') == 'dict':
 
590
            return data_dict
 
591
        if context.get('type_data') == 'xml_obj':
 
592
            return doc_xml
 
593
        data_xml = doc_xml.toxml('UTF-8')
 
594
        data_xml = codecs.BOM_UTF8 + data_xml
 
595
        fname_xml = (data_dict['Comprobante']['Emisor']['rfc'] or '') + '.' + ( data_dict['Comprobante'].get('serie', '') or '') + '.' + ( data_dict['Comprobante'].get('folio', '') or '') + '.xml'
 
596
        return fname_xml, data_xml
 
597
    
 
598
    def write_cfd_data(self, cr, uid, ids, cfd_datas, context={}):
 
599
        if not cfd_datas:
 
600
            cfd_datas = {}
 
601
        ##obtener cfd_data con varios ids
 
602
        #for id in ids:
 
603
        id = ids[0]
 
604
        if True:
 
605
            data = {}
 
606
            cfd_data = cfd_datas
 
607
            noCertificado = cfd_data.get('Comprobante', {}).get('noCertificado', '')
 
608
            certificado = cfd_data.get('Comprobante', {}).get('certificado', '')
 
609
            sello = cfd_data.get('Comprobante', {}).get('sello', '')
 
610
            cadena_original = cfd_data.get('cadena_original', '')
 
611
            data = {
 
612
                'no_certificado': noCertificado,
 
613
                'certificado': certificado,
 
614
                'sello': sello,
 
615
                'cadena_original': cadena_original,
 
616
            }
 
617
            self.write(cr, uid, [id], data, context=context)
 
618
        return True
 
619
    
 
620
    def _get_noCertificado(self, fname_cer, pem=True):
 
621
        certificate_lib = self.pool.get('facturae.certificate.library')
 
622
        fname_serial = certificate_lib.b64str_to_tempfile( base64.encodestring(''), file_suffix='.txt', file_prefix='openerp__' + (False or '') + '__serial__' )
 
623
        result = certificate_lib._get_param_serial(fname_cer, fname_out=fname_serial, type='PEM')
 
624
        return result
 
625
    
 
626
    def _get_sello(self, cr=False, uid=False, ids=False, context={}):
 
627
        #TODO: Put encrypt date dynamic
 
628
        fecha = context['fecha']
 
629
        year = float( time.strftime('%Y', time.strptime(fecha, '%Y-%m-%dT%H:%M:%S')) )
 
630
        if year >= 2011:
 
631
            encrypt = "sha1"
 
632
        if year <= 2010:
 
633
            encrypt = "md5"
 
634
        certificate_lib = self.pool.get('facturae.certificate.library')
 
635
        fname_sign = certificate_lib.b64str_to_tempfile( base64.encodestring(''), file_suffix='.txt', file_prefix='openerp__' + (False or '') + '__sign__' )
 
636
        result = certificate_lib._sign(fname=context['fname_xml'], fname_xslt=context['fname_xslt'], fname_key=context['fname_key'], fname_out=fname_sign, encrypt=encrypt, type_key='PEM')
 
637
        return result
 
638
    
 
639
    def _xml2cad_orig(self, cr=False, uid=False, ids=False, context={}):
 
640
        certificate_lib = self.pool.get('facturae.certificate.library')
 
641
        fname_tmp = certificate_lib.b64str_to_tempfile( base64.encodestring(''), file_suffix='.txt', file_prefix='openerp__' + (False or '') + '__cadorig__' )
 
642
        cad_orig = certificate_lib._transform_xml(fname_xml=context['fname_xml'], fname_xslt=context['fname_xslt'], fname_out=fname_tmp)
 
643
        return fname_tmp, cad_orig
 
644
 
 
645
#TODO: agregar esta funcionalidad con openssl
 
646
    def _get_certificate_str( self, fname_cer_pem = ""):
 
647
        fcer = open( fname_cer_pem, "r")
 
648
        lines = fcer.readlines()
 
649
        fcer.close()
 
650
        cer_str = ""
 
651
        loading = False
 
652
        for line in lines:
 
653
            if 'END CERTIFICATE' in line:
 
654
                loading = False
 
655
            if loading:
 
656
                cer_str += line
 
657
            if 'BEGIN CERTIFICATE' in line:
 
658
                loading = True
 
659
        return cer_str
 
660
#TODO: agregar esta funcionalidad con openssl
 
661
    def _get_md5_cad_orig(self, cadorig_str, fname_cadorig_digest):
 
662
        cadorig_digest = hashlib.md5(cadorig_str).hexdigest()
 
663
        open(fname_cadorig_digest, "w").write(cadorig_digest)
 
664
        return cadorig_digest, fname_cadorig_digest
 
665
    
 
666
    def _get_facturae_invoice_dict_data(self, cr, uid, ids, context={}):
 
667
        invoices = self.browse(cr, uid, ids, context=context)
 
668
        invoice_tax_obj = self.pool.get("account.invoice.tax")
 
669
        invoice_datas = []
 
670
        invoice_data_parents = []
 
671
        #'type': fields.selection([
 
672
            #('out_invoice','Customer Invoice'),
 
673
            #('in_invoice','Supplier Invoice'),
 
674
            #('out_refund','Customer Refund'),
 
675
            #('in_refund','Supplier Refund'),
 
676
            #],'Type', readonly=True, select=True),
 
677
        for invoice in invoices:
 
678
            invoice_data_parent = {}
 
679
            if invoice.type == 'out_invoice':
 
680
                tipoComprobante = 'ingreso'
 
681
            elif invoice.type == 'out_refund':
 
682
                tipoComprobante = 'egreso'
 
683
            else:
 
684
                raise osv.except_osv('Warning !', 'Solo se puede emitir factura electronica a clientes.!')
 
685
            #Inicia seccion: Comprobante
 
686
            invoice_data_parent['Comprobante'] = {}
 
687
            #default data
 
688
            invoice_data_parent['Comprobante'].update({
 
689
                'xmlns': "http://www.sat.gob.mx/cfd/2",
 
690
                'xmlns:xsi': "http://www.w3.org/2001/XMLSchema-instance",
 
691
                'xsi:schemaLocation': "http://www.sat.gob.mx/cfd/2 http://www.sat.gob.mx/sitio_internet/cfd/2/cfdv2.xsd",
 
692
                'version': "2.0",
 
693
            })
 
694
            number_work = invoice.number or invoice.internal_number
 
695
            invoice_data_parent['Comprobante'].update({
 
696
                'folio': number_work,
 
697
                'fecha': invoice.date_invoice_tz and \
 
698
                    #time.strftime('%d/%m/%y', time.strptime(invoice.date_invoice, '%Y-%m-%d')) \
 
699
                    time.strftime('%Y-%m-%dT%H:%M:%S', time.strptime(invoice.date_invoice_tz, '%Y-%m-%d %H:%M:%S'))
 
700
                    or '',
 
701
                'tipoDeComprobante': tipoComprobante,
 
702
                'formaDePago': u'Pago en una sola exhibición',
 
703
                'noCertificado': '@',
 
704
                'sello': '@',
 
705
                'certificado': '@',
 
706
                'subTotal': "%.2f"%( invoice.amount_untaxed or 0.0),
 
707
                'descuento': "0",#Add field general
 
708
                'total': "%.2f"%( invoice.amount_total or 0.0),
 
709
            })
 
710
            folio_data = self._get_folio(cr, uid, [invoice.id], context=context)
 
711
            invoice_data_parent['Comprobante'].update({
 
712
                'anoAprobacion': folio_data['anoAprobacion'],
 
713
                'noAprobacion': folio_data['noAprobacion'],
 
714
            })
 
715
            serie = folio_data.get('serie', False)
 
716
            if serie:
 
717
                invoice_data_parent['Comprobante'].update({
 
718
                    'serie': serie,
 
719
                })
 
720
            #Termina seccion: Comprobante
 
721
            #Inicia seccion: Emisor
 
722
            partner_obj = self.pool.get('res.partner')
 
723
            partner = invoice.company_id and invoice.company_id.partner_id and invoice.company_id.partner_id or False
 
724
            partner_parent = (invoice.company_id and invoice.company_id.parent_id and invoice.company_id.parent_id.partner_id) or (invoice.company_id.partner_id and invoice.company_id.partner_id) or False
 
725
            
 
726
            address_invoice_id = partner_obj.address_get(cr, uid, [partner.id], ['invoice'])['invoice']
 
727
            address_invoice_parent_id = partner_obj.address_get(cr, uid, [partner_parent.id], ['invoice'])['invoice']
 
728
            
 
729
            if not address_invoice_id:
 
730
                raise osv.except_osv('Warning !', 'No se tiene definido los datos de facturacion del partner [%s].\n%s !'%(partner.name, msg2))
 
731
            
 
732
            address_invoice = self.pool.get('res.partner.address').browse(cr, uid, address_invoice_id, context)
 
733
            address_invoice_parent = self.pool.get('res.partner.address').browse(cr, uid, address_invoice_parent_id, context)
 
734
            
 
735
            if not partner.vat:
 
736
                raise osv.except_osv('Warning !', 'No se tiene definido el RFC del partner [%s].\n%s !'%(partner.name, msg2))
 
737
            
 
738
            invoice_data = invoice_data_parent['Comprobante']
 
739
            invoice_data['Emisor'] = {}
 
740
            invoice_data['Emisor'].update({
 
741
                
 
742
                'rfc': ((partner_parent._columns.has_key('vat_split') and partner_parent.vat_split or partner_parent.vat) or '').replace('-', ' ').replace(' ',''),
 
743
                'nombre': address_invoice_parent.name or partner_parent.name or '',
 
744
                #Obtener domicilio dinamicamente
 
745
                #virtual_invoice.append( (invoice.company_id and invoice.company_id.partner_id and invoice.company_id.partner_id.vat or '').replac
 
746
                
 
747
                'DomicilioFiscal': {
 
748
                    'calle': invoice.company_id.address_invoice_parent_company_id.street and invoice.company_id.address_invoice_parent_company_id.street.replace('\n\r', ' ').replace('\r\n', ' ').replace('\n', ' ').replace('\r', ' ') or '',
 
749
                    'noExterior': invoice.company_id.address_invoice_parent_company_id.street3 and invoice.company_id.address_invoice_parent_company_id.street3.replace('\n\r', ' ').replace('\r\n', ' ').replace('\n', ' ').replace('\r', ' ') or 'N/A', #"Numero Exterior"
 
750
                    'noInterior': invoice.company_id.address_invoice_parent_company_id.street4 and invoice.company_id.address_invoice_parent_company_id.street4.replace('\n\r', ' ').replace('\r\n', ' ').replace('\n', ' ').replace('\r', ' ') or 'N/A', #"Numero Interior"
 
751
                    'colonia':  invoice.company_id.address_invoice_parent_company_id.street2 and invoice.company_id.address_invoice_parent_company_id.street2.replace('\n\r', ' ').replace('\r\n', ' ').replace('\n', ' ').replace('\r', ' ') or 'N/A' ,
 
752
                    'localidad': invoice.company_id.address_invoice_parent_company_id.city2 and invoice.company_id.address_invoice_parent_company_id.city2.replace('\n\r', ' ').replace('\r\n', ' ').replace('\n', ' ').replace('\r', ' ') or 'N/A',
 
753
                    'municipio': invoice.company_id.address_invoice_parent_company_id.city and invoice.company_id.address_invoice_parent_company_id.city.replace('\n\r', ' ').replace('\r\n', ' ').replace('\n', ' ').replace('\r', ' ') or '',
 
754
                    'estado': invoice.company_id.address_invoice_parent_company_id.state_id and invoice.company_id.address_invoice_parent_company_id.state_id.name and invoice.company_id.address_invoice_parent_company_id.state_id.name.replace('\n\r', ' ').replace('\r\n', ' ').replace('\n', ' ').replace('\r', ' ') or '' ,
 
755
                    'pais': invoice.company_id.address_invoice_parent_company_id.country_id and invoice.company_id.address_invoice_parent_company_id.country_id.name and invoice.company_id.address_invoice_parent_company_id.country_id.name.replace('\n\r', ' ').replace('\r\n', ' ').replace('\n', ' ').replace('\r', ' ')or '',
 
756
                    'codigoPostal': invoice.company_id.address_invoice_parent_company_id.zip and invoice.company_id.address_invoice_parent_company_id.zip.replace('\n\r', ' ').replace('\r\n', ' ').replace('\n', ' ').replace('\r', ' ') or '',
 
757
                },
 
758
                'ExpedidoEn': {
 
759
                    'calle': invoice.address_invoice_company_id.street and invoice.address_invoice_company_id.street.replace('\n\r', ' ').replace('\r\n', ' ').replace('\n', ' ').replace('\r', ' ') or '',
 
760
                    'noExterior': invoice.address_invoice_company_id.street3 and invoice.address_invoice_company_id.street3.replace('\n\r', ' ').replace('\r\n', ' ').replace('\n', ' ').replace('\r', ' ') or 'N/A', #"Numero Exterior"
 
761
                    'noInterior': invoice.address_invoice_company_id.street4 and invoice.address_invoice_company_id.street4.replace('\n\r', ' ').replace('\r\n', ' ').replace('\n', ' ').replace('\r', ' ') or 'N/A', #"Numero Interior"
 
762
                    'colonia':  invoice.address_invoice_company_id.street2 and invoice.address_invoice_company_id.street2.replace('\n\r', ' ').replace('\r\n', ' ').replace('\n', ' ').replace('\r', ' ') or 'N/A' ,
 
763
                    'localidad': invoice.address_invoice_company_id.city2 and invoice.address_invoice_company_id.city2.replace('\n\r', ' ').replace('\r\n', ' ').replace('\n', ' ').replace('\r', ' ') or 'N/A',
 
764
                    'municipio': invoice.address_invoice_company_id.city and invoice.address_invoice_company_id.city.replace('\n\r', ' ').replace('\r\n', ' ').replace('\n', ' ').replace('\r', ' ') or '',
 
765
                    'estado': invoice.address_invoice_company_id.state_id and invoice.address_invoice_company_id.state_id.name and invoice.address_invoice_company_id.state_id.name.replace('\n\r', ' ').replace('\r\n', ' ').replace('\n', ' ').replace('\r', ' ') or '' ,
 
766
                    'pais': invoice.address_invoice_company_id.country_id and invoice.address_invoice_company_id.country_id.name and invoice.address_invoice_company_id.country_id.name.replace('\n\r', ' ').replace('\r\n', ' ').replace('\n', ' ').replace('\r', ' ')or '',
 
767
                    'codigoPostal': invoice.address_invoice_company_id.zip and invoice.address_invoice_company_id.zip.replace('\n\r', ' ').replace('\r\n', ' ').replace('\n', ' ').replace('\r', ' ') or '',
 
768
                },
 
769
            })
 
770
            #Termina seccion: Emisor
 
771
            #Inicia seccion: Receptor
 
772
            if not invoice.partner_id.vat:
 
773
                raise osv.except_osv('Warning !', 'No se tiene definido el RFC del partner [%s].\n%s !'%(invoice.partner_id.name, msg2))
 
774
            invoice_data['Receptor'] = {}
 
775
            invoice_data['Receptor'].update({
 
776
                'rfc': ((invoice.partner_id._columns.has_key('vat_split') and invoice.partner_id.vat_split or invoice.partner_id.vat) or '').replace('-', ' ').replace(' ',''),
 
777
                'nombre': (invoice.address_invoice_id.name or invoice.partner_id.name or ''),
 
778
                'Domicilio': {
 
779
                    'calle': invoice.address_invoice_id.street and invoice.address_invoice_id.street.replace('\n\r', ' ').replace('\r\n', ' ').replace('\n', ' ').replace('\r', ' ') or '',
 
780
                    'noExterior': invoice.address_invoice_id.street3 and invoice.address_invoice_id.street3.replace('\n\r', ' ').replace('\r\n', ' ').replace('\n', ' ').replace('\r', ' ') or 'N/A', #"Numero Exterior"
 
781
                    'noInterior': invoice.address_invoice_id.street4 and invoice.address_invoice_id.street4.replace('\n\r', ' ').replace('\r\n', ' ').replace('\n', ' ').replace('\r', ' ') or 'N/A', #"Numero Interior"
 
782
                    'colonia':  invoice.address_invoice_id.street2 and invoice.address_invoice_id.street2.replace('\n\r', ' ').replace('\r\n', ' ').replace('\n', ' ').replace('\r', ' ') or 'N/A' ,
 
783
                    'localidad': invoice.address_invoice_id.city2 and invoice.address_invoice_id.city2.replace('\n\r', ' ').replace('\r\n', ' ').replace('\n', ' ').replace('\r', ' ') or 'N/A',
 
784
                    'municipio': invoice.address_invoice_id.city and invoice.address_invoice_id.city.replace('\n\r', ' ').replace('\r\n', ' ').replace('\n', ' ').replace('\r', ' ') or '',
 
785
                    'estado': invoice.address_invoice_id.state_id and invoice.address_invoice_id.state_id.name and invoice.address_invoice_id.state_id.name.replace('\n\r', ' ').replace('\r\n', ' ').replace('\n', ' ').replace('\r', ' ') or '' ,
 
786
                    'pais': invoice.address_invoice_id.country_id and invoice.address_invoice_id.country_id.name and invoice.address_invoice_id.country_id.name.replace('\n\r', ' ').replace('\r\n', ' ').replace('\n', ' ').replace('\r', ' ')or '',
 
787
                    'codigoPostal': invoice.address_invoice_id.zip and invoice.address_invoice_id.zip.replace('\n\r', ' ').replace('\r\n', ' ').replace('\n', ' ').replace('\r', ' ') or '',
 
788
                },
 
789
            })
 
790
            #Termina seccion: Receptor
 
791
            #Inicia seccion: Conceptos
 
792
            invoice_data['Conceptos'] = []
 
793
            for line in invoice.invoice_line:
 
794
                #price_type = invoice._columns.has_key('price_type') and invoice.price_type or 'tax_excluded'
 
795
                #if price_type == 'tax_included':
 
796
                price_unit = line.price_subtotal/line.quantity#Agrega compatibilidad con modulo TaxIncluded
 
797
                concepto = {
 
798
                    'cantidad': "%.2f"%( line.quantity or 0.0),
 
799
                    'descripcion': line.name or '',
 
800
                    'valorUnitario': "%.2f"%( price_unit or 0.0),
 
801
                    'importe': "%.2f"%( line.price_subtotal or 0.0),#round(line.price_unit *(1-(line.discount/100)),2) or 0.00),#Calc: iva, disc, qty
 
802
                    ##Falta agregar discount
 
803
                }
 
804
                unidad = line.uos_id and line.uos_id.name or ''
 
805
                if unidad:
 
806
                    concepto.update({'unidad': unidad})
 
807
                product_code = line.product_id and line.product_id.default_code or ''
 
808
                if product_code:
 
809
                    concepto.update({'noIdentificacion': product_code})
 
810
                invoice_data['Conceptos'].append( {'Concepto': concepto} )
 
811
                
 
812
                pedimento = None
 
813
                try:
 
814
                    pedimento = line.tracking_id.import_id
 
815
                except:
 
816
                    pass
 
817
                if pedimento:
 
818
                    informacion_aduanera = {
 
819
                        'numero': pedimento.name or '',
 
820
                        'fecha': pedimento.date or '',
 
821
                        'aduana': pedimento.customs,
 
822
                    }
 
823
                    concepto.update( {'InformacionAduanera': informacion_aduanera} )
 
824
            #Termina seccion: Conceptos
 
825
            #Inicia seccion: impuestos
 
826
            invoice_data['Impuestos'] = {}
 
827
            invoice_data['Impuestos'].update({
 
828
                #'totalImpuestosTrasladados': "%.2f"%( invoice.amount_tax or 0.0),
 
829
            })
 
830
            invoice_data['Impuestos'].update({
 
831
                #'totalImpuestosRetenidos': "%.2f"%( invoice.amount_tax or 0.0 )
 
832
            })
 
833
            
 
834
            invoice_data_impuestos = invoice_data['Impuestos']
 
835
            invoice_data_impuestos['Traslados'] = []
 
836
            #invoice_data_impuestos['Retenciones'] = []
 
837
            
 
838
            tax_names = []
 
839
            totalImpuestosTrasladados = 0
 
840
            totalImpuestosRetenidos = 0
 
841
            for line_tax_id in invoice.tax_line:
 
842
                tax_name = line_tax_id.name2
 
843
                tax_names.append( tax_name )
 
844
                line_tax_id_amount = abs( line_tax_id.amount or 0.0 )
 
845
                if line_tax_id.amount >= 0:
 
846
                    impuesto_list = invoice_data_impuestos['Traslados']
 
847
                    impuesto_str = 'Traslado'
 
848
                    totalImpuestosTrasladados += line_tax_id_amount
 
849
                else:
 
850
                    #impuesto_list = invoice_data_impuestos['Retenciones']
 
851
                    impuesto_list = invoice_data_impuestos.setdefault('Retenciones', [])
 
852
                    impuesto_str = 'Retencion'
 
853
                    totalImpuestosRetenidos += line_tax_id_amount
 
854
                impuesto_dict = {impuesto_str: 
 
855
                    {
 
856
                        'impuesto': tax_name,
 
857
                        'importe': "%.2f"%( line_tax_id_amount ),
 
858
                    }
 
859
                }
 
860
                if line_tax_id.amount >= 0:
 
861
                    impuesto_dict[impuesto_str].update({'tasa': "%.2f"%( abs( line_tax_id.tax_percent ) )})
 
862
                impuesto_list.append( impuesto_dict )
 
863
            
 
864
            invoice_data['Impuestos'].update({
 
865
                'totalImpuestosTrasladados': "%.2f"%( totalImpuestosTrasladados ),
 
866
            })
 
867
            if totalImpuestosRetenidos:
 
868
                invoice_data['Impuestos'].update({
 
869
                    'totalImpuestosRetenidos': "%.2f"%( totalImpuestosRetenidos )
 
870
                })
 
871
                
 
872
            tax_requireds = ['IVA', 'IEPS']
 
873
            for tax_required in tax_requireds:
 
874
                if tax_required in tax_names:
 
875
                    continue
 
876
                invoice_data_impuestos['Traslados'].append( {'Traslado': {
 
877
                    'impuesto': tax_required,
 
878
                    'tasa': "%.2f"%( 0.0 ),
 
879
                    'importe': "%.2f"%( 0.0 ),
 
880
                }} )
 
881
            #Termina seccion: impuestos
 
882
            invoice_data_parents.append( invoice_data_parent )
 
883
            invoice_data_parent['state'] = invoice.state
 
884
            invoice_data_parent['invoice_id'] = invoice.id
 
885
            invoice_data_parent['type'] = invoice.type
 
886
            invoice_data_parent['date_invoice'] = invoice.date_invoice
 
887
            invoice_data_parent['date_invoice_tz'] = invoice.date_invoice_tz
 
888
            invoice_data_parent['currency_id'] = invoice.currency_id.id
 
889
            
 
890
            date_ctx = {'date': invoice.date_invoice_tz and time.strftime('%Y-%m-%d', time.strptime(invoice.date_invoice_tz, '%Y-%m-%d %H:%M:%S')) or False}
 
891
            #rate = self.pool.get('res.currency').compute(cr, uid, invoice.currency_id.id, invoice.company_id.currency_id.id, 1, round=False, context=date_ctx, account=None, account_invert=False)
 
892
            #rate = 1.0/self.pool.get('res.currency')._current_rate(cr, uid, [invoice.currency_id.id], name=False, arg=[], context=date_ctx)[invoice.currency_id.id]
 
893
            currency = self.pool.get('res.currency').browse(cr, uid, [invoice.currency_id.id], context=date_ctx)[0]
 
894
            rate = currency.rate <> 0 and 1.0/currency.rate or 0.0
 
895
            #print "currency.rate",currency.rate
 
896
            
 
897
            invoice_data_parent['rate'] = rate
 
898
        return invoice_data_parents
 
899
account_invoice()