~openbias/bias-trunk/bias-public-trunk

« back to all changes in this revision

Viewing changes to bias_electronic_invoice_42/invoice.py

  • Committer: Jose Patricio
  • Date: 2011-10-19 03:16:40 UTC
  • Revision ID: josepato@bias.com.mx-20111019031640-05zd7r5lxwx084qu
el push inicial

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# -*- encoding: utf-8 -*-
 
2
##############################################################################
 
3
#
 
4
# Copyright (c) 2004-2006 TINY SPRL. (http://tiny.be) All Rights Reserved.
 
5
#
 
6
# $Id: account.py 1005 2005-07-25 08:41:42Z nicoe $
 
7
#
 
8
# WARNING: This program as such is intended to be used by professional
 
9
# programmers who take the whole responsability of assessing all potential
 
10
# consequences resulting from its eventual inadequacies and bugs
 
11
# End users who are looking for a ready-to-use solution with commercial
 
12
# garantees and support are strongly adviced to contract a Free Software
 
13
# Service Company
 
14
#
 
15
# This program is Free Software; you can redistribute it and/or
 
16
# modify it under the terms of the GNU General Public License
 
17
# as published by the Free Software Foundation; either version 2
 
18
# of the License, or (at your option) any later version.
 
19
#
 
20
# This program is distributed in the hope that it will be useful,
 
21
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
22
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
23
# GNU General Public License for more details.
 
24
#
 
25
# You should have received a copy of the GNU General Public License
 
26
# along with this program; if not, write to the Free Software
 
27
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 
28
#
 
29
##############################################################################
 
30
 
 
31
from osv import fields, osv
 
32
import time
 
33
from mx.DateTime import *
 
34
import tools
 
35
import SOAPpy
 
36
import re
 
37
import StringIO
 
38
from lxml import etree
 
39
import base64
 
40
import codecs, cfdutil
 
41
import os
 
42
 
 
43
 
 
44
#import text
 
45
 
 
46
class account_invoice(osv.osv):
 
47
        _name = "account.invoice"
 
48
        _inherit = 'account.invoice'
 
49
        _description = 'Invoice'
 
50
 
 
51
 
 
52
        _columns = {
 
53
                'sign_date':fields.datetime('Sing Date'),
 
54
                'cancel_date':fields.datetime('Cancel Date'),
 
55
                'approved_year':fields.integer('Approved Year'),
 
56
                'approved_number': fields.char('Approved Number', size=32),
 
57
                'certificate': fields.text('Certificate'),
 
58
                'digital_signature': fields.text('Signature'),
 
59
                'cadena':fields.text('Cadena')
 
60
                #'provider_signature':  fields.text('Signature Provider'),
 
61
                #'invoice_xml':fields.text('Invoice_Xml'),
 
62
                }
 
63
 
 
64
 
 
65
        def copy(self, cr, uid, id, default=None, context=None):
 
66
                if default is None:
 
67
                        default = {}
 
68
                default = default.copy()
 
69
                default.update({'state':'draft', 'number':False, 'move_id':False, 'move_name':False, 'cancel_date':False, 'sign_date':False, 
 
70
                        'approved_year':False, 'approved_number':False, 'certificate':False, 'digital_signature':False, 'cadena':False, 'period_id':False})
 
71
                if 'date_invoice' not in default:
 
72
                        default['date_invoice'] = time.strftime('%Y-%m-%d')
 
73
                if 'date_due' not in default:
 
74
                        default['date_due'] = False
 
75
                if 'cancel_date' not in default:
 
76
                        default['cancel_date'] = False
 
77
                return super(account_invoice, self).copy(cr, uid, id, default, context)
 
78
 
 
79
        def cfdutil_getLineaReporte(self, xml_str_obj, state='False'):
 
80
                return cfdutil.getLineaReporte(xml_str_obj, state)
 
81
 
 
82
        
 
83
        def cfdutil_verifySello(self, cadena, certfname, sello):
 
84
                return cfdutil.verifySello(cadena, certfname, sello)
 
85
 
 
86
        def elimante_double_space(self, data):
 
87
                data = re.sub('[ \t\n\r\f\v]', ' ',data)
 
88
                data = data.strip(' ')
 
89
                while '  ' in data:
 
90
                        data = re.sub('  ', ' ', data)
 
91
                return data
 
92
 
 
93
        def make_utf(self, data, required=''):
 
94
                if not data and required:
 
95
                        raise osv.except_osv(('Error !'), ('Campo %s es Requerido y esta faltando en la captura.'%(required,)))
 
96
                elif data and required:
 
97
                        
 
98
                        return unicode(self.elimante_double_space(data), 'utf-8')
 
99
                elif data:
 
100
                        return unicode(self.elimante_double_space(data), 'utf-8')
 
101
                else:
 
102
                        return ''
 
103
 
 
104
        def _period_get(self, cr, uid, ctx={}):
 
105
                try:
 
106
                        ids = self.pool.get('account.period').find(cr, uid, context=ctx)
 
107
                        return ids[0]
 
108
                except:
 
109
                        return False
 
110
                
 
111
        def action_cancel(self, cr, uid, ids, *args):
 
112
                account_move_obj = self.pool.get('account.move')
 
113
                invoices = self.read(cr, uid, ids, ['move_id', 'payment_ids', 'id'])
 
114
                for i in invoices:
 
115
                        if i['payment_ids']:
 
116
                                account_move_line_obj = self.pool.get('account.move.line')
 
117
                                pay_ids = account_move_line_obj.browse(cr, uid , i['payment_ids'])
 
118
                                for move_line in pay_ids:
 
119
                                    if move_line.reconcile_partial_id and move_line.reconcile_partial_id.line_partial_ids:
 
120
                                        raise osv.except_osv(_('Error !'), _('You cannot cancel the Invoice which is Partially Paid! You need to unreconcile concerned payment entries!'))
 
121
                        if i['move_id']:
 
122
                                reconcile_obj = self.pool.get('account.move.reconcile')
 
123
                                line_obj = self.pool.get('account.move.line')
 
124
                                move_obj = self.pool.get('account.move')
 
125
                                date = time.strftime('%Y-%m-%d')
 
126
                                period = self._period_get(cr, uid)
 
127
                                inv = self.browse(cr, uid, i['id'])
 
128
                                if inv.state in ['draft', 'proforma2', 'cancel']:
 
129
                                        raise wizard.except_wizard(_('Error !'), _('Can not cancel draft/proforma/cancel invoice.'))
 
130
                                if inv.type == 'in_invoice':
 
131
                                        description = 'Fac. '+inv.reference+' Cancelada'
 
132
                                elif inv.type == 'out_invoice':
 
133
                                        description = 'Fac. '+inv.number+' Cancelada'
 
134
                                else:
 
135
                                        description = 'Fac. '+inv.number+' Cancelada'
 
136
                                if not period:
 
137
                                        raise wizard.except_wizard(_('Data Insufficient !'), _('No Period found on Invoice!'))
 
138
                                default={'date':date, 'period_id':period, 'ref':description , 'state':'draft', 'name':'/',}
 
139
                                move_obj.button_cancel(cr, uid, [inv.move_id.id,])
 
140
                                move_id = move_obj.copy(cr, uid, inv.move_id.id, context={}, default={'date':date, 'period_id':period, 'ref':description })
 
141
                                move_obj.post(cr, uid,  [inv.move_id.id,])
 
142
                                move = move_obj.browse(cr, uid, move_id)
 
143
                                move_obj.write(cr, uid, [move_id], {'date':date})
 
144
                                for line in move.line_id:
 
145
                                        line_obj.write(cr, uid, [line.id], {
 
146
                                                'debit': line.credit,
 
147
                                                'credit': line.debit,
 
148
                                                'amount_currency': line.amount_currency and -line.amount_currency or False,
 
149
                                                'date':date
 
150
                                                }, context={'multireconcile':True})
 
151
 
 
152
                                    #move_obj.post(self, cr, uid, [move_id])
 
153
                                move_obj.post(cr, uid, [move_id])
 
154
                                #if inv is paid we unreconcile
 
155
                                movelines = inv.move_id.line_id
 
156
                                #we unreconcile the lines
 
157
                                to_reconcile_ids = {}
 
158
                                for line in movelines :
 
159
                                    #if the account of the line is the as the one in the invoice
 
160
                                    #we reconcile
 
161
                                    if line.account_id.id == inv.account_id.id :
 
162
                                        to_reconcile_ids[line.account_id.id] =[line.id]
 
163
                                    if type(line.reconcile_id) != osv.orm.browse_null :
 
164
                                        reconcile_obj.unlink(cr,uid, line.reconcile_id.id)
 
165
 
 
166
                                #we match the line to reconcile
 
167
                                for tmpline in move.line_id :
 
168
                                    if tmpline.account_id.id == inv.account_id.id :
 
169
                                        to_reconcile_ids[tmpline.account_id.id].append(tmpline.id)
 
170
                                for account in to_reconcile_ids :
 
171
                                    line_obj.reconcile(cr, uid, to_reconcile_ids[account],
 
172
                                        type = 'simple',
 
173
                                        writeoff_period_id=period,
 
174
                                        writeoff_journal_id=inv.journal_id.id,
 
175
                                        writeoff_acc_id=inv.account_id.id
 
176
                                    )
 
177
                                date_time = now().Format('%Y-%m-%d %H:%M:%S')
 
178
                                self.write(cr, uid, inv.id, {'cancel_date':date_time, 'state':'cancel'})
 
179
                                #Original
 
180
                                #account_move_obj.button_cancel(cr, uid, [i['move_id'][0]])
 
181
                                # delete the move this invoice was pointing to
 
182
                                # Note that the corresponding move_lines and move_reconciles
 
183
                                # will be automatically deleted too
 
184
                                #account_move_obj.unlink(cr, uid, [i['move_id'][0]])
 
185
 
 
186
 
 
187
                                self.write(cr, uid, ids, {'state':'cancel'})
 
188
                                self._log_event(cr, uid, ids,-1.0, 'Cancel Invoice')
 
189
                                date_time = now().Format('%Y-%m-%d %H:%M:%S')
 
190
                                self.write(cr, uid, ids, {'cancel_date':date_time})
 
191
                return True
 
192
 
 
193
        def action_cancel_draft(self, cr, uid, ids, *args):
 
194
                for invoice in self.browse(cr, uid, ids):
 
195
                        if invoice.type != 'out_invoice':
 
196
                                return super(account_invoice, self).action_cancel_draft( cr, uid, ids, *args)
 
197
                self.write(cr, uid, ids, {'cancel_date':False,
 
198
                                          'sign_date':False,
 
199
                                          'approved_year':'',
 
200
                                          'approved_number':'',
 
201
                                          'certificate':'',
 
202
                                          'digital_signature':'',
 
203
                                          'cadena':''})
 
204
                for inv_brw in self.browse(cr, uid, ids):
 
205
                        if inv_brw.journal_id.code in ('factura_e' ,'nota_credito_e'):
 
206
                                number, prefix = self.get_inv_number(cr, uid, inv_brw)
 
207
                                if inv_brw.journal_id.code == 'factura_e':
 
208
                                        invtype = inv_brw.journal_id.code
 
209
                                else:
 
210
                                        invtype = inv_brw.type
 
211
                                if number:
 
212
                                        if inv_brw.journal_id.invoice_sequence_id:
 
213
                                                sid = inv_brw.journal_id.invoice_sequence_id.id
 
214
                                        else:
 
215
                                                seq_code =  'account.invoice.' + invtype
 
216
                                                cr.execute("SELECT id FROM ir_sequence WHERE code='%s'"%(seq_code))
 
217
                                                sid = cr.fetchone()[0]
 
218
                                        sid_brw = self.pool.get('ir.sequence').browse(cr, uid, sid)
 
219
                                        next_number = sid_brw.number_next
 
220
                                        number_increment = sid_brw.number_increment
 
221
                                        if (number + number_increment) == next_number:
 
222
                                                super(account_invoice, self).action_cancel_draft( cr, uid, ids, *args)
 
223
                                                move_line_brw = inv_brw.move_id.line_id
 
224
                                                move_line_ids = [m_line.id for m_line in move_line_brw]
 
225
                                                query = "SELECT reconcile_id from account_move_line where reconcile_id is not null and id in %s"%(tuple(move_line_ids), )
 
226
                                                cr.execute(query)
 
227
                                                rec_ids = cr.fetchall()
 
228
                                                rec_ids = [x[0] for x in rec_ids]
 
229
                                                if len(rec_ids) == 1:
 
230
                                                        
 
231
                                                        query2 = "SELECT id, move_id from account_move_line where reconcile_id = %s"%rec_ids[0]
 
232
                                                        cr.execute(query2)
 
233
                                                        move_ids = cr.fetchall()
 
234
                                                        move_ids = [x[1] for x in move_ids]
 
235
                                                        self.pool.get('account.move.reconcile').unlink(cr, uid, rec_ids)
 
236
                                                        self.pool.get('account.move').button_cancel(cr, uid, move_ids)
 
237
                                                        self.pool.get('account.move').unlink(cr, uid, move_ids)
 
238
                                                else:
 
239
                                                        #solo cancela y borra el asiento de la factura ya que no esta conciliado o bien se rompio la conciliacion a mano
 
240
                                                        self.pool.get('account.move').button_cancel(cr, uid, inv_brw.move_id.id)
 
241
                                                        self.pool.get('account.move').unlink(cr, uid, inv_brw.move_id.id)
 
242
                                                attachment_ids = self.pool.get('ir.attachment').search(cr, uid, [('res_id','in',ids),('res_model','=', self._name)])
 
243
                                                self.pool.get('ir.attachment').unlink(cr, uid,attachment_ids)
 
244
                                                return True
 
245
                                        else:
 
246
                                                raise osv.except_osv(('Warrnig !'), ('This invoice can not be set to draft.'))
 
247
                        else:
 
248
                                super(account_invoice, self).action_cancel_draft( cr, uid, ids, *args)
 
249
 
 
250
 
 
251
 
 
252
        def action_number(self, cr, uid, ids, *args):
 
253
                cr.execute('SELECT id, type, number, move_id, reference ' \
 
254
                                'FROM account_invoice ' \
 
255
                                'WHERE id IN ('+','.join(map(str,ids))+')')
 
256
                for (id, invtype, number, move_id, reference) in cr.fetchall():
 
257
                        if not number:
 
258
                                if self.browse(cr, uid, id).journal_id.code == 'factura_e':
 
259
                                        invtype = 'factura_e'
 
260
                                elif self.browse(cr, uid, id).journal_id.code == 'nota_credito_e':
 
261
                                        invtype = 'nota_credito_e'
 
262
                                number = self.pool.get('ir.sequence').get(cr, uid,
 
263
                                                'account.invoice.' + invtype)
 
264
                                if type in ('in_invoice', 'in_refund'):
 
265
                                        ref = reference
 
266
                                else:
 
267
                                        ref = self._convert_ref(cr, uid, number)
 
268
                                cr.execute('UPDATE account_invoice SET number=%s ' \
 
269
                                                'WHERE id=%d', (number, id))
 
270
                                cr.execute('UPDATE account_move_line SET ref=%s ' \
 
271
                                                'WHERE move_id=%d AND (ref is null OR ref = \'\')',
 
272
                                                (ref, move_id))
 
273
                                cr.execute('UPDATE account_analytic_line SET ref=%s ' \
 
274
                                                'FROM account_move_line ' \
 
275
                                                'WHERE account_move_line.move_id = %d ' \
 
276
                                                        'AND account_analytic_line.move_id = account_move_line.id',
 
277
                                                        (ref, move_id))
 
278
                return True
 
279
 
 
280
        def undo_action_number(self, cr, uid, inv_brw , *args):
 
281
                number = inv_brw.number
 
282
                if not number:
 
283
                        if inv_brw.journal_id.code == 'factura_e':
 
284
                                invtype = 'factura_e'
 
285
                        elif inv_brw.journal_id.code == 'nota_credito_e':
 
286
                                invtype = 'nota_credito_e'
 
287
                        cr.execute('lock table ir_sequence')
 
288
                        cr.execute("select id,number_next,number_increment,prefix,suffix,padding from ir_sequence where code = 'account.invoice." + invtype + "' and active=True")
 
289
                        res = cr.dictfetchone()
 
290
                        if res:
 
291
                                cr.execute('update ir_sequence set number_next=number_next-number_increment where id=%s and active=True', (res['id'],))
 
292
                                cr.commit()
 
293
                return True
 
294
 
 
295
 
 
296
        def test_open(self, cr, uid, ids, *args):
 
297
                #super(account_invoice, self).action_date_assign( cr, uid, ids, *args)
 
298
                for inv_brw in self.browse(cr, uid, ids):
 
299
                        super(account_invoice, self).action_move_create( cr, uid, [inv_brw.id], *args)
 
300
                        #super(account_invoice, self).action_number( cr, uid, [inv_brw.id], *args)
 
301
                        self.action_number( cr, uid, [inv_brw.id], *args)
 
302
                        if (inv_brw.journal_id.code in ('factura_e', 'nota_credito_e')) and (inv_brw.type in ('out_invoice', 'out_refund')):
 
303
                                #try:
 
304
                                #self.action_number( cr, uid, [inv_brw.id], *args)
 
305
                                cfe = self.create_xml(cr, uid, [inv_brw.id], *args)
 
306
                                cfe_str = etree.tostring(cfe, pretty_print=True, encoding='utf-8')
 
307
                                self.attach_xml(cr, uid, inv_brw.id, cfe_str)
 
308
                                #except:
 
309
                                        #self.undo_action_number( cr, uid, inv_brw , *args)
 
310
                                        #raise osv.except_osv(('Error !'), ('Error al crear la Factura.'))
 
311
                return True
 
312
 
 
313
        def attach_xml(self, cr, uid, id, cfe):
 
314
                inv_brw = self.browse(cr, uid, id)
 
315
                self.pool.get('ir.attachment').create(cr, uid, {
 
316
                        'name': 'Fact.-' + inv_brw.number,
 
317
                        'datas': base64.encodestring(cfe),
 
318
                        'datas_fname': inv_brw.number or inv_brw.name + '.xml',
 
319
                        'res_model': self._name,
 
320
                        'res_id': inv_brw.id,
 
321
                        }, )
 
322
                return True
 
323
                                                   
 
324
 
 
325
        def set_cfe_defaults(self):
 
326
                NS='http://www.w3.org/2001/XMLSchema-instance'
 
327
                TREE = '{%s}' % NS
 
328
                NSMAP = {'xsi': NS}
 
329
                schema_location = '{%s}schemaLocation' % NS
 
330
                cfe = etree.Element('Comprobante', attrib={schema_location:"http://www.sat.gob.mx/cfd/2 http://www.sat.gob.mx/sitio_internet/cfd/2/cfdv2.xsd http://www.sat.gob.mx/ecc http://www.sat.gob.mx/sitio_internet/cfd/ecc/ecc.xsd"})
 
331
                cfe.set('xmlns',"http://www.sat.gob.mx/cfd/2")
 
332
                cfe.set('version','2.0')
 
333
                return cfe
 
334
 
 
335
        
 
336
        def create_xml(self,cr, uid, ids, context={}):
 
337
                for inv_brw in self.browse(cr, uid, ids):
 
338
                        cfe = self.set_cfe_defaults()
 
339
                        cfe = self.get_datos_comprobante(cr, uid, inv_brw, cfe, context)
 
340
                        emisor = self.get_emisor(cr, uid, inv_brw)
 
341
                        receptro = self.get_receptor(cr, uid, inv_brw)
 
342
                        conceptos = self.get_conceptos(cr, uid, inv_brw.invoice_line)
 
343
                        impuestos = self.get_impuestos(cr, uid, inv_brw.tax_line)
 
344
                        addenda = self.get_addenda(cr, uid, inv_brw)
 
345
                        #certificado = self.get_certificado(cr, uid, inv_brw)
 
346
                        cfe.append(emisor)
 
347
                        cfe.append(receptro)
 
348
                        cfe.append(conceptos)
 
349
                        cfe.append(impuestos)
 
350
                        cfe.append(addenda)
 
351
                        cadena = cfdutil.getCadenaOriginal(etree.parse(StringIO.StringIO(etree.tostring(cfe))))
 
352
                        keyfname = inv_brw.company_id.key
 
353
                        sello = cfdutil.getSelloSHA1(cadena, keyfname, inv_brw.company_id.key_phrase)
 
354
                        if not sello:
 
355
                                raise osv.except_osv(('Error !'), ('Error al crear el sello.'))
 
356
                        cfe.set('sello', sello)
 
357
                        self.write(cr, uid, inv_brw.id, {'sign_date':cfe.get('fecha'),
 
358
                                                         'approved_year':int(cfe.get('anoAprobacion')),
 
359
                                                         'approved_number':int(cfe.get('noAprobacion')),
 
360
                                                         'certificate':cfe.get('certificado'),
 
361
                                                         'digital_signature':sello,
 
362
                                                         'cadena':cadena,
 
363
                                                         'date_invoice': cfe.get('fecha').split('T')[0]})
 
364
                        #certfname = os.path.join(tools.config['root_path'], 'filestore/%s/%s'%(cr.dbname, inv_brw.company_id.certificate.store_fname))
 
365
                        #certfname = os.path.join(tools.config['root_path'], 'ore/%s/%s'%(cr.dbname, inv_brw.company_id.certificate))
 
366
                        certfname = inv_brw.company_id.certificate
 
367
                        verify = cfdutil.verifySelloSHA1(cadena, certfname, sello)
 
368
                return cfe
 
369
 
 
370
 
 
371
        def get_domicilio_fiscal(self, cr, uid, inv_brw):
 
372
                partner_obj = self.pool.get('res.partner')
 
373
                address_obj = self.pool.get('res.partner.address')
 
374
                partner_brw = inv_brw.company_id.partner_id
 
375
                partner_id = inv_brw.company_id.partner_id.id
 
376
                address = etree.Element('Domicilio')
 
377
                #address_obj = self.pool.get('res.partner.address')
 
378
                address = etree.Element('DomicilioFiscal')
 
379
                inovice_addres_id = partner_obj.address_get(cr, uid, [partner_id], ['invoice'])
 
380
                address_brw = address_obj.browse(cr, uid, inovice_addres_id['invoice'])
 
381
                address.set('calle', self.make_utf(address_brw.street, 'Calle Emisior'))
 
382
                address.set('colonia', self.make_utf(address_brw.street2, 'Colonia Emisior'))
 
383
                address.set('municipio',self.make_utf(address_brw.city,'Ciudad Emisior'))
 
384
                address.set('estado', self.make_utf(address_brw.state_id.name, 'Estado Emisior'))
 
385
                address.set('pais',self.make_utf(address_brw.country_id.name, 'Pais Emisior'))
 
386
                address.set('codigoPostal',self.make_utf(address_brw.zip, 'Codigo Postal Emisior'))
 
387
                return address
 
388
 
 
389
        def get_domicilio_ubicacion(self, cr, uid, inv_brw):
 
390
                partner_obj = self.pool.get('res.partner')
 
391
                address_obj = self.pool.get('res.partner.address')
 
392
                partner_id = inv_brw.partner_id.id
 
393
                address = etree.Element('Domicilio')
 
394
                inovice_addres_id = partner_obj.address_get(cr, uid, [partner_id], ['invoice'])
 
395
                address_brw = address_obj.browse(cr, uid, inovice_addres_id['invoice'])
 
396
                address_brw.street and address.set('calle', self.make_utf(address_brw.street or ''))
 
397
                address_brw.street2 and address.set('colonia', self.make_utf(address_brw.street2 or ''))
 
398
                address_brw.city and address.set('municipio',self.make_utf(address_brw.city or ''))
 
399
                address_brw.state_id and address.set('estado', self.make_utf(address_brw.state_id.name or ''))
 
400
                address_brw.country_id and address.set('pais',self.make_utf(address_brw.country_id.name or 'Pais Emisor'))
 
401
                address_brw.zip and address.set('codigoPostal',self.make_utf(address_brw.zip or ''))
 
402
                return address
 
403
        
 
404
        def get_emisor(self, cr, uid, inv_brw):
 
405
                partner_brw = inv_brw.company_id.partner_id
 
406
                emisor = etree.Element('Emisor')
 
407
                emisor.append(self.get_domicilio_fiscal(cr, uid, inv_brw))
 
408
                if not partner_brw.vat:
 
409
                        raise osv.except_osv(_('No RFC Defined on Emisor Partner!'),_("You must define a RFC for the company !") )
 
410
                emisor.set('rfc',re.sub('[-,._ \t\n\r\f\v]','',partner_brw.vat))
 
411
                emisor.set('nombre',self.make_utf(inv_brw.company_id.name, 'Company Name'))
 
412
                return emisor
 
413
        
 
414
        def get_receptor(self,cr, uid, inv_brw):
 
415
                partner_brw = inv_brw.partner_id
 
416
                receptor = etree.Element('Receptor')
 
417
                receptor.append(self.get_domicilio_ubicacion(cr, uid, inv_brw))
 
418
                if not partner_brw.vat:
 
419
                        raise osv.except_osv(_('No RFC Defined on Receptor Partner!'),_("You must define a RFC for the Client !") )
 
420
                receptor.set('rfc',re.sub('[-,._ \t\n\r\f\v]','',partner_brw.vat))
 
421
                receptor.set('nombre',self.make_utf(inv_brw.partner_id.name or 'Empresa Receptora'))
 
422
                return receptor
 
423
 
 
424
        def get_conceptos(self, cr, uid, lines_brw_lst):
 
425
                conceptos = etree.Element('Conceptos') 
 
426
                for line in lines_brw_lst:
 
427
                        concept = etree.Element('Concepto')
 
428
                        concept.set('cantidad', '%.2f'%(line.quantity))
 
429
                        concept.set('descripcion',self.make_utf(line.name, 'Descripcion de Producto'))
 
430
                        concept.set('valorUnitario','%.2f'%(line.price_unit))
 
431
                        concept.set('importe','%.2f'%(line.price_subtotal))
 
432
                        uos = line.uos_id.name
 
433
                        if uos:
 
434
                                concept.set('unidad', uos)
 
435
                        conceptos.append(concept)
 
436
                return conceptos
 
437
 
 
438
 
 
439
 
 
440
        def get_tax(self, cr, uid, tax, tax_type, tax_name):
 
441
                retencion = etree.Element(tax_name)
 
442
                if tax_type == 'retain_iva':
 
443
                        retencion.set('impuesto','IVA')
 
444
                        tax_pct = round(float(tax.amount) / float(tax.base), 2)
 
445
                        retencion.set('tasa', '%.2f'%(tax_pct * 100))
 
446
                elif tax_type == 'retain_isr':
 
447
                        retencion.set('impuesto','ISR')
 
448
                        tax_pct = round(float(tax.amount) / float(tax.base), 2)
 
449
                        retencion.set('tasa', '%.2f'%(tax_pct * 100))
 
450
                if tax_type == 'tax_ieps':
 
451
                        retencion.set('impuesto','IEPS')
 
452
                        tax_pct = round(float(tax.amount) / float(tax.base), 2)
 
453
                        retencion.set('tasa', '%.2f'%(tax_pct * 100))
 
454
                elif tax_type == 'tax_iva':
 
455
                        retencion.set('impuesto','IVA')
 
456
                        tax_pct = round(float(tax.amount) / float(tax.base), 2)
 
457
                        retencion.set('tasa', '%.2f'%(tax_pct * 100))
 
458
                retencion.set('importe', '%.2f'%(abs(tax.amount)))
 
459
                
 
460
                return retencion
 
461
 
 
462
        
 
463
        def get_impuestos(self, cr, uid, tax_line):
 
464
                impuestos = etree.Element('Impuestos')
 
465
                traslados = etree.Element('Traslados')
 
466
                retenciones = etree.Element('Retenciones')
 
467
                total_retenciones = 0
 
468
                total_traslados = 0 
 
469
                for tax in tax_line:
 
470
                        tax_type = tax.tax_code_id.code
 
471
 
 
472
                        if (tax_type == 'retain_iva') or (tax_type == 'retain_isr'):
 
473
                                retenciones.append(self.get_tax(cr, uid, tax, tax_type, 'Retencion' ))
 
474
                                total_retenciones += tax.amount
 
475
                        elif (tax_type == 'tax_iva') or (tax_type == 'tax_ieps'):
 
476
                                traslados.append(self.get_tax(cr, uid, tax, tax_type, 'Traslado' ))
 
477
                                total_traslados += tax.amount
 
478
                if total_retenciones:
 
479
                        impuestos.set('totalImpuestosRetenidos', '%.2f'%(abs(total_retenciones)))
 
480
                        impuestos.append(retenciones)
 
481
                if total_traslados:
 
482
                        impuestos.set('totalImpuestosTrasladados', '%.2f'%(abs(total_traslados)))
 
483
                        impuestos.append(traslados)
 
484
                return impuestos
 
485
 
 
486
 
 
487
 
 
488
        def get_addenda(self,cr, uid, inv_brw):
 
489
                addenda = etree.Element('Addenda') 
 
490
                partner_brw = inv_brw.partner_id
 
491
                if not partner_brw.addenda:
 
492
                        return addenda
 
493
                addenda_brw = partner_brw.addenda
 
494
                addenda_name = etree.Element(addenda_brw.name)
 
495
                for line_brw in addenda_brw.line_ids:
 
496
                        value = self.make_utf(str(eval('inv_brw.' + line_brw.default)))
 
497
                        addenda_line = etree.Element(line_brw.tag)
 
498
                        addenda_line.set(line_brw.tag,value)
 
499
                        addenda_name.append(addenda_line)
 
500
                addenda.append(addenda_name)
 
501
                return addenda
 
502
 
 
503
        def get_discount(self, cr, uid, inv_brw):
 
504
                return 0
 
505
 
 
506
        def get_payment_form(self, cr, uid, inv_brw):
 
507
                return False
 
508
 
 
509
        def get_inv_number(self, cr, uid, inv_brw):
 
510
                prefix = ''
 
511
                if inv_brw.journal_id.invoice_sequence_id:
 
512
                        prefix = inv_brw.journal_id.invoice_sequence_id.prefix
 
513
                else:
 
514
                        prefix = inv_brw.journal_id.sequence_id.prefix
 
515
                if prefix:
 
516
                        number = int(inv_brw.number[len(prefix):])
 
517
                else:
 
518
                        #number = int(inv_brw.number)
 
519
                        try:
 
520
                                number = int(inv_brw.number)
 
521
                        except ValueError:
 
522
                                sequence_type = 'account.invoice.' + inv_brw.journal_id.code
 
523
                                cr.execute("SELECT prefix from ir_sequence where code ='%s'"%(sequence_type))
 
524
                                
 
525
                                prefix = cr.fetchone()
 
526
                                if prefix:
 
527
                                        prefix = prefix[0]
 
528
                                number = int(inv_brw.number.strip(prefix))
 
529
                return number, prefix
 
530
 
 
531
 
 
532
        def get_datos_comprobante(self, cr, uid, inv_brw, cfe, context={}):
 
533
                company_obj = self.pool.get('res.company.folios')
 
534
                if inv_brw.journal_id.invoice_sequence_id:
 
535
                        prefix = inv_brw.journal_id.invoice_sequence_id.prefix
 
536
                else:
 
537
                        prefix = inv_brw.journal_id.sequence_id.prefix
 
538
                if prefix:
 
539
                        number = int(inv_brw.number[len(prefix):])
 
540
                        cfe.set('serie',prefix)
 
541
                else:
 
542
                        try:
 
543
                                number = int(inv_brw.number)
 
544
                        except ValueError:
 
545
                                sequence_type = 'account.invoice.' + inv_brw.journal_id.code
 
546
                                cr.execute("SELECT prefix from ir_sequence where code ='%s'"%(sequence_type))
 
547
                                
 
548
                                prefix = cr.fetchone()
 
549
                                if prefix:
 
550
                                        prefix = prefix[0]
 
551
                                        cfe.set('serie',prefix)
 
552
                                number = int(inv_brw.number.strip(prefix))
 
553
                                #raise osv.except_osv(_('Error !'),_('The invoice number has to be an integer. Wrong configuratin, check your secuenceses.'))
 
554
 
 
555
                cfe.set('folio',str(int(number)))
 
556
                if context.has_key('date'):
 
557
                        cfe.set('fecha',context['date'])
 
558
                else:
 
559
                        cfe.set('fecha',now().strftime('%Y-%m-%dT%H:%M:%S'))
 
560
                cfe.set('anoAprobacion',company_obj.get_folio_info(cr, uid, inv_brw.company_id.id, number, 'approved_year', prefix))
 
561
                cfe.set('formaDePago','Pago en una sola exhibicion')###ahy que poner una forma de pago valida
 
562
                cfe.set('noAprobacion',company_obj.get_folio_info(cr, uid, inv_brw.company_id.id, number, 'approved_number', prefix))
 
563
                cfe.set('condicionesDePago',self.make_utf(inv_brw.payment_term.name, 'Condiciones de Pago'))
 
564
                #### El certificado
 
565
                certfname = inv_brw.company_id.certificate
 
566
                nocert = cfdutil.getNoSerie(certfname)
 
567
                certstr = cfdutil.getCertString(certfname)
 
568
                cfe.set('noCertificado', nocert)
 
569
                cfe.set('certificado', certstr)
 
570
                #cfe.set('noCertificado',inv_brw.company_id.certificate_no)
 
571
                cfe.set('subTotal','%.2f'%(inv_brw.amount_untaxed))
 
572
                cfe.set('total','%.2f'%(inv_brw.amount_total))
 
573
                if inv_brw.type == 'out_invoice':
 
574
                        cfe.set('tipoDeComprobante','ingreso')
 
575
                elif  inv_brw.type == 'out_refund':
 
576
                        cfe.set('tipoDeComprobante','egreso')
 
577
                elif inv_brw.company_id.partner_id.vat == inv_brw.partner_id.vat:
 
578
                        cfe.set('tipoDeComprobante','traslado')
 
579
                discount = self.get_discount(cr, uid, inv_brw)
 
580
                #Optinal Stuff
 
581
                if discount:
 
582
                        cfe.set('descuento',str(discount))
 
583
                payment_form = self.get_payment_form(cr, uid, inv_brw)
 
584
                if payment_form:
 
585
                        cfe.set('metodoDePago',payment_form)
 
586
                
 
587
                return cfe
 
588
 
 
589
 
 
590
 
 
591
account_invoice()
 
592
 
 
593
 
 
594
 
 
595
class account_invoice_addenda(osv.osv):
 
596
    _name = "account.invoice.addenda"
 
597
    _description = "Addenda for the Electronic Invoice"
 
598
    _columns = {
 
599
        'name': fields.char('Addenda', size=64, required=True),
 
600
        'active': fields.boolean('Active'),
 
601
        'note': fields.text('Description', translate=True),
 
602
        'line_ids': fields.one2many('account.invoice.addenda.lines', 'addenda_id', 'Terms'),
 
603
    }
 
604
    _defaults = {
 
605
        'active': lambda *a: 1,
 
606
    }
 
607
    _order = "name"
 
608
 
 
609
 
 
610
account_invoice_addenda()
 
611
 
 
612
class account_invoice_addenda_lines(osv.osv):
 
613
    _name = "account.invoice.addenda.lines"
 
614
    _description = "Lines of the Electronic Invoice Addenda"
 
615
 
 
616
    def _col_get(self, cr, user, context={}):
 
617
        result = []
 
618
        cols = self.pool.get('account.invoice')._columns
 
619
        for col in cols:
 
620
            result.append( (col, cols[col].string) )
 
621
 
 
622
        result.sort()
 
623
        return result
 
624
 
 
625
    
 
626
    _columns = {
 
627
            'name': fields.char('Field Name', size=64, required=True),
 
628
            'code': fields.char('Code', size=64, required=True),
 
629
            'sequence': fields.integer('Sequence', required=True, help="The sequence field is used to order fields"),
 
630
            'required': fields.boolean('Required'),
 
631
            'relation': fields.many2one('ir.model', 'Relation'),
 
632
            'default': fields.char('Default', size=64),
 
633
            'field': fields.selection(_col_get, 'Field Name', method=True, size=64),
 
634
            'addenda_id': fields.many2one('account.invoice.addenda', 'Addenda', required=True, select=True),
 
635
            }
 
636
    _defaults = {
 
637
        'sequence': lambda *a: 5,
 
638
 
 
639
    }
 
640
    _order = "sequence"
 
641
 
 
642
account_invoice_addenda_lines()
 
643
 
 
644
 
 
645
 
 
646