def test_issue46(self): """Example for sending an arbitrary header using SimpleXMLElement""" # fake connection (just to test xml_request): client = SoapClient(location="https://localhost:666/", namespace='http://localhost/api') # Using WSDL, the equivalent is: # client['MyTestHeader'] = {'username': '******', 'password': '******'} headers = SimpleXMLElement("<Headers/>") my_test_header = headers.add_child("MyTestHeader") my_test_header['xmlns'] = "service" my_test_header.marshall('username', 'test') my_test_header.marshall('password', 'password') try: client.methodname(headers=headers) except: open("issue46.xml", "wb").write(client.xml_request) self.assert_( '<soap:Header><MyTestHeader xmlns="service">' '<username>test</username>' '<password>password</password>' '</MyTestHeader></soap:Header>' in client.xml_request.decode(), "header not in request!")
def test_issue78(self): "Example for sending an arbitrary header using SimpleXMLElement and WSDL" # fake connection (just to test xml_request): client = SoapClient(wsdl='http://dczorgwelzijn-test.qmark.nl/qmwise4/qmwise.asmx?wsdl') # Using WSDL, the easier form is but this doesn't allow for namespaces to be used. # If the server requires these (buggy server?) the dictionary method won't work # and marshall will not marshall 'ns:username' style keys # client['MyTestHeader'] = {'username': '******', 'password': '******'} namespace = 'http://questionmark.com/QMWISe/' ns = 'qmw' header = SimpleXMLElement('<Headers/>', namespace=namespace, prefix=ns) security = header.add_child("Security") security['xmlns:qmw']=namespace security.marshall('ClientID','NAME',ns=ns) security.marshall('Checksum','PASSWORD',ns=ns) client['Security']=security try: client.GetParticipantList() except: #open("issue78.xml", "wb").write(client.xml_request) print client.xml_request header="""<soap:Header><qmw:Security xmlns:qmw="http://questionmark.com/QMWISe/"><qmw:ClientID>NAME</qmw:ClientID><qmw:Checksum>PASSWORD</qmw:Checksum></qmw:Security></soap:Header>""" self.assert_(header in client.xml_request, "header not in request!")
def test_issue69(self): """Boolean value not converted correctly during marshall""" span = SimpleXMLElement('<span><name>foo</name></span>') span.marshall('value', True) d = {'span': {'name': str, 'value': bool}} e = {'span': {'name': 'foo', 'value': True}} self.assertEqual(span.unmarshall(d), e)
def test_issue78(self): """Example for sending an arbitrary header using SimpleXMLElement and WSDL""" # fake connection (just to test xml_request): client = SoapClient( wsdl='http://dczorgwelzijn-test.qmark.nl/qmwise4/qmwise.asmx?wsdl') # Using WSDL, the easier form is but this doesn't allow for namespaces to be used. # If the server requires these (buggy server?) the dictionary method won't work # and marshall will not marshall 'ns:username' style keys # client['MyTestHeader'] = {'username': '******', 'password': '******'} namespace = 'http://questionmark.com/QMWISe/' ns = 'qmw' header = SimpleXMLElement('<Headers/>', namespace=namespace, prefix=ns) security = header.add_child("Security") security['xmlns:qmw'] = namespace security.marshall('ClientID', 'NAME', ns=ns) security.marshall('Checksum', 'PASSWORD', ns=ns) client['Security'] = security try: client.GetParticipantList() except: #open("issue78.xml", "wb").write(client.xml_request) #print(client.xml_request) header = '<soap:Header>' \ '<qmw:Security xmlns:qmw="http://questionmark.com/QMWISe/">' \ '<qmw:ClientID>NAME</qmw:ClientID>' \ '<qmw:Checksum>PASSWORD</qmw:Checksum>' \ '</qmw:Security>' \ '</soap:Header>' xml = SimpleXMLElement(client.xml_request) self.assertEquals(str(xml.ClientID), "NAME") self.assertEquals(str(xml.Checksum), "PASSWORD")
def test_issue46(self): """Example for sending an arbitrary header using SimpleXMLElement""" # fake connection (just to test xml_request): client = SoapClient( location="https://localhost:666/", namespace='http://localhost/api' ) # Using WSDL, the equivalent is: # client['MyTestHeader'] = {'username': '******', 'password': '******'} headers = SimpleXMLElement("<Headers/>") my_test_header = headers.add_child("MyTestHeader") my_test_header['xmlns'] = "service" my_test_header.marshall('username', 'test') my_test_header.marshall('password', 'password') try: client.methodname(headers=headers) except: open("issue46.xml", "wb").write(client.xml_request) self.assert_('<soap:Header><MyTestHeader xmlns="service">' '<username>test</username>' '<password>password</password>' '</MyTestHeader></soap:Header>' in client.xml_request.decode(), "header not in request!")
def __init__(self): self.__service = SoapClient(location='https://ws.espol.edu.ec/saac/wsandroid.asmx', namespace='http://tempuri.org/', action='http://tempuri.org/') self.header = SimpleXMLElement('<Header/>') security = self.header.add_child("GTSIAuthSoapHeader") security['xmlns'] = "http://tempuri.org/" security.marshall('usuario', os.environ['SPIDER_USER']) security.marshall('key', os.environ['SPIDER_KEY'])
def createOrUpdateClientEx(target): client = SoapClient(location="http://aboweb.com/aboweb/ClientService?wsdl",trace=False) client['wsse:Security'] = { 'wsse:UsernameToken': { 'wsse:Username': '******', 'wsse:Password': '******', } } target = SimpleXMLElement('<?xml version="1.0" encoding="UTF-8"?><ges:createOrUpdateClientEx xmlns:ges="http://www.gesmag.com/">'+ repr(target) +'</ges:createOrUpdateClientEx>') client.call('createOrUpdateClientEx',target) xml = SimpleXMLElement(client.xml_response) return xml
def getClient(codeClient): client = SoapClient(location="http://aboweb.com/aboweb/ClientService?wsdl",trace=False) client['wsse:Security'] = { 'wsse:UsernameToken': { 'wsse:Username': '******', 'wsse:Password': '******', # 'wsse:Password': '******', } } params = SimpleXMLElement('<?xml version="1.0" encoding="UTF-8"?><ges:getClient xmlns:ges="http://www.gesmag.com/"><codeClient>'+codeClient+'</codeClient></ges:getClient>'); result = client.call("getClient",params) xml = SimpleXMLElement(client.xml_response) return xml.children().children().children()
def authenticateByEmail(username,password): # wsdl = "http://dev.aboweb.com/aboweb/ClientService?wsdl" client = SoapClient(location = "http://aboweb.com/aboweb/ClientService",trace=False) client['wsse:Security'] = { 'wsse:UsernameToken': { 'wsse:Username': '******', # 'wsse:Password': '******', 'wsse:Password': '******', } } params = SimpleXMLElement('<?xml version="1.0" encoding="UTF-8"?><ges:authenticateByEmail xmlns:ges="http://www.gesmag.com/"><email>'+username+'</email><encryptedPassword>'+ b64encode(sha1(password).digest()) +'</encryptedPassword></ges:authenticateByEmail>') response = client.call("authenticateByEmail",params) xml = SimpleXMLElement(client.xml_response) return str(xml('result'))
def getAbonnements(codeClient): # return xml containing a list of user abonnements clientAbonnement = SoapClient(location ="http://aboweb.com/aboweb/AbonnementService",trace=False) clientAbonnement['wsse:Security'] = { 'wsse:UsernameToken': { 'wsse:Username': '******', # 'wsse:Password': '******', 'wsse:Password': '******', } } params = SimpleXMLElement('<?xml version="1.0" encoding="UTF-8"?><ges:getAbonnements xmlns:ges="http://www.gesmag.com/"><codeClient>%s</codeClient><offset>0</offset></ges:getAbonnements>' % codeClient) result = clientAbonnement.call("getAbonnements",params) xml = SimpleXMLElement(clientAbonnement.xml_response) return xml
def ABM_TEST_MAIL(mail): # webservice to check for mail occurence in aboweb DB # Status should be 00 (found) or 01 (not found) clientABM = SoapClient(wsdl="http://aboweb.com/aboweb/abmWeb?wsdl", ns="web", trace=False) clientABM['AuthHeaderElement'] = {'login': '******', 'password': '******'} resultABM = clientABM.ABM_TEST_MAIL(refEditeur='207',refSociete='1',email=mail) xml = SimpleXMLElement(clientABM.xml_response) return xml('status')
def test_issue47_raw(self): "Same example (clarizen), with raw headers (no wsdl)!" client = SoapClient(location="https://api.clarizen.com/v1.0/Clarizen.svc", namespace='http://clarizen.com/api', trace=False) headers = SimpleXMLElement("<Headers/>", namespace="http://clarizen.com/api", prefix="ns1") session = headers.add_child("Session") session['xmlns'] = "http://clarizen.com/api" session.marshall('ID', '1234') client.location = "https://api.clarizen.com/v1.0/Clarizen.svc" client.action = "http://clarizen.com/api/IClarizen/Logout" try: client.call("Logout", headers=headers) except: open("issue47_raw.xml", "wb").write(client.xml_request) self.assert_("""<soap:Header><ns1:Session xmlns="http://clarizen.com/api"><ID>1234</ID></ns1:Session></soap:Header>""" in client.xml_request, "Session header not in request!")
def SetTicketAcceso(self, ta_string): "Establecer el token y sign desde un ticket de acceso XML" if ta_string: ta = SimpleXMLElement(ta_string) self.Token = str(ta.credentials.token) self.Sign = str(ta.credentials.sign) return True else: raise RuntimeError("Ticket de Acceso vacio!")
def LoginCMS(self, cms): "Obtener ticket de autorización (TA)" results = self.client.loginCms(in0=str(cms)) ta_xml = results['loginCmsReturn'].encode("utf-8") self.xml = ta = SimpleXMLElement(ta_xml) self.Token = str(ta.credentials.token) self.Sign = str(ta.credentials.sign) self.ExpirationTime = str(ta.header.expirationTime) return ta_xml
def SetTicketAcceso(self, ta_string): "Establecer el token y sign desde un ticket de acceso XML" if ta_string: ta = SimpleXMLElement(ta_string) self.Token = str(ta.credentials.token) self.Sign = str(ta.credentials.sign) self.ExpiracionTicketAcceso = datetime.datetime.strptime( str(ta.header.expirationTime)[:19], '%Y-%m-%dT%H:%M:%S') return True else: raise RuntimeError("Ticket de Acceso vacio!")
def ABM_ACCES_CLIENT(username, password): clientABM = SoapClient(wsdl="http://aboweb.com/aboweb/abmWeb?wsdl", ns="web", trace=False) clientABM['AuthHeaderElement'] = { 'login': '******', 'password': '******' } resultABM = clientABM.ABM_ACCES_CLIENT('207', username, password) xml = SimpleXMLElement(clientABM.xml_response) return xml('codeClient')
def ABM_MOT_PASSE_OUBLIE(mail): clientABM = SoapClient(wsdl="http://aboweb.com/aboweb/abmWeb?wsdl", ns="web", trace=False) clientABM['AuthHeaderElement'] = { 'login': '******', 'password': '******' } resultABM = clientABM.ABM_MOT_PASSE_OUBLIE(refEditeur='207', email=mail) xml = SimpleXMLElement(clientABM.xml_response) return xml('return')
def LoginCMS(self, cms): "Obtener ticket de autorización (TA)" if not isinstance(cms, str): cms = cms.decode("utf8") results = self.client.loginCms(in0=cms) ta_xml = results["loginCmsReturn"] self.xml = ta = SimpleXMLElement(ta_xml) self.Token = str(ta.credentials.token) self.Sign = str(ta.credentials.sign) self.ExpirationTime = str(ta.header.expirationTime) return ta_xml
def test_consultar(self): "consulta ao resultado do processamento dos arquivos de cupons fiscai" # create the client webservice client = SoapClient(wsdl=WSDL, soap_ns="soap12env") # set user login credentials in the soap header: client['Autenticacao'] = SimpleXMLElement( HEADER_XML % ("user", "password", "fed_tax_num", 1)) # call the webservice response = client.Consultar(Protocolo="") self.assertEqual( response['ConsultarResult'], u'999|O protocolo informado n\xe3o \xe9 um n\xfamero v\xe1lido')
def AnalizarXml(self, xml=""): "Analiza un mensaje XML (por defecto el ticket de acceso)" try: if not xml or xml=='XmlResponse': xml = self.XmlResponse elif xml=='XmlRequest': xml = self.XmlRequest self.xml = SimpleXMLElement(xml) return True except Exception, e: self.Excepcion = traceback.format_exception_only( sys.exc_type, sys.exc_value)[0] return False
def test_issue47_raw(self): "Same example (clarizen), with raw headers (no wsdl)!" client = SoapClient( location="https://api.clarizen.com/v1.0/Clarizen.svc", namespace='http://clarizen.com/api', trace=False) headers = SimpleXMLElement("<Headers/>", namespace="http://clarizen.com/api", prefix="ns1") session = headers.add_child("Session") session['xmlns'] = "http://clarizen.com/api" session.marshall('ID', '1234') client.location = "https://api.clarizen.com/v1.0/Clarizen.svc" client.action = "http://clarizen.com/api/IClarizen/Logout" try: client.call("Logout", headers=headers) except: open("issue47_raw.xml", "wb").write(client.xml_request) self.assertTrue( """<soap:Header><ns1:Session xmlns="http://clarizen.com/api"><ID>1234</ID></ns1:Session></soap:Header>""" in client.xml_request, "Session header not in request!")
def test_issue66(self): """Verify marshaled requests can be sent with no children""" # fake connection (just to test xml_request): client = SoapClient(location="https://localhost:666/", namespace='http://localhost/api') request = SimpleXMLElement("<ChildlessRequest/>") try: client.call('ChildlessRequest', request) except: open("issue66.xml", "wb").write(client.xml_request) self.assert_('<ChildlessRequest' in client.xml_request.decode(), "<ChildlessRequest not in request!") self.assert_('</ChildlessRequest>' in client.xml_request.decode(), "</ChildlessRequest> not in request!")
def test_issue89(self): """Setting attributes for request tag.""" # fake connection (just to test xml_request): client = SoapClient(location="https://localhost:666/", namespace='http://localhost/api') request = SimpleXMLElement( """<?xml version="1.0" encoding="UTF-8"?><test a="b"><a>3</a></test>""" ) # manually make request msg try: client.call('test', request) except: open("issue89.xml", "wb").write(client.xml_request) self.assert_( '<test a="b" xmlns="http://localhost/api">' in client.xml_request.decode(), "attribute not in request!")
def test_retificar(self): "Prueba da retifica de arquivos de cupons fiscais" # create the client webservice client = SoapClient(wsdl=WSDL, soap_ns="soap12env") # set user login credentials in the soap header: client['Autenticacao'] = SimpleXMLElement( HEADER_XML % ("user", "password", "fed_tax_num", 1)) # call the webservice response = client.Retificar(NomeArquivo="file_name", ConteudoArquivo="content", EnvioNormal=True, Observacoes="") self.assertEqual(response['RetificarResult'], u'206|CNPJ informado inv\xe1lido')
def test_issue105(self): """Test target namespace in wsdl (conflicting import)""" WSDL = "https://api.dc2.computing.cloud.it/WsEndUser/v2.4/WsEndUser.svc?wsdl" client = SoapClient(wsdl=WSDL) try: client.SetEnqueueServerStop(serverId=37) except SoapFault as sf: # ignore exception caused by missing credentials sent in this test: if sf.faultstring != "An error occurred when verifying security for the message.": raise # verify the correct namespace: xml = SimpleXMLElement(client.xml_request) ns_uri = xml.SetEnqueueServerStop['xmlns'] self.assertEqual(ns_uri, "https://api.computing.cloud.it/WsEndUser")
def test_issue113(self): """Test target namespace in wsdl import""" WSDL = "https://test.paymentgate.ru/testpayment/webservices/merchant-ws?wsdl" client = SoapClient(wsdl=WSDL) try: client.getOrderStatusExtended(order={'merchantOrderNumber': '1'}) except SoapFault as sf: # ignore exception caused by missing credentials sent in this test: if sf.faultstring != "An error was discovered processing the <wsse:Security> header": raise # verify the correct namespace: xml = SimpleXMLElement(client.xml_request) ns_uri = xml.getOrderStatusExtended['xmlns'] self.assertEqual(ns_uri, "http://engine.paymentgate.ru/webservices/merchant")
def create_tra(service=SERVICE, ttl=2400): "Crear un Ticket de Requerimiento de Acceso (TRA)" tra = SimpleXMLElement('<?xml version="1.0" encoding="UTF-8"?>' '<loginTicketRequest version="1.0">' "</loginTicketRequest>") tra.add_child("header") # El source es opcional. Si falta, toma la firma (recomendado). # tra.header.addChild('source','subject=...') # tra.header.addChild('destination','cn=wsaahomo,o=afip,c=ar,serialNumber=CUIT 33693450239') tra.header.add_child("uniqueId", str(date("U"))) tra.header.add_child("generationTime", str(date("c", date("U") - ttl))) tra.header.add_child("expirationTime", str(date("c", date("U") + ttl))) tra.add_child("service", service) return tra.as_xml()
def get_secondary_amount(cls, invoice, value): Currency = Pool().get('currency.currency') if invoice.pos and invoice.pos.pos_type == 'electronic': afip_tr, = [ tr for tr in invoice.transactions if tr.pyafipws_result == 'A' ] request = SimpleXMLElement(unidecode(afip_tr.pyafipws_xml_request)) if invoice.pos.pyafipws_electronic_invoice_service == 'wsfex': ctz = Decimal(str(request('Moneda_ctz'))) elif invoice.pos.pyafipws_electronic_invoice_service == 'wsfe': ctz = Decimal(str(request('MonCotiz'))) currency_rate = invoice.currency_rate or ctz context = dict(date=invoice.currency_date) if currency_rate: context['currency_rate'] = currency_rate with Transaction().set_context(context): amount = Currency.compute(invoice.currency, value, invoice.company.currency) return amount
def test_issue114(self): """Test no schema in wsdl (Lotus-Domino)""" WSDL = "https://pysimplesoap.googlecode.com/issues/attachment?aid=1140000000&name=WebRequest.xml&token=QVf8DlJ1qmKRH8LAbU4eSe2Ban0%3A1399084258723" # WSDL= "file:WebRequest.xml" try: client = SoapClient(wsdl=WSDL, soap_server="axis") #print client.help("CREATEREQUEST") ret = client.CREATEREQUEST(LOGIN="******", REQUESTTYPE=1, REQUESTCONTENT="test") except ExpatError: # the service seems to be expecting basic auth pass except SoapFault as sf: # todo: check as service is returning DOM failure # verify the correct namespace: xml = SimpleXMLElement(client.xml_request) ns_uri = xml.CREATEREQUEST['xmlns'] self.assertEqual(ns_uri, "http://tps.ru")
def create_tra(service=SERVICE,ttl=2400): "Crear un Ticket de Requerimiento de Acceso (TRA)" tra = SimpleXMLElement( '<?xml version="1.0" encoding="UTF-8"?>' '<loginTicketRequest version="1.0">' '</loginTicketRequest>') tra.add_child('header') # El source es opcional. Si falta, toma la firma (recomendado). #tra.header.addChild('source','subject=...') #tra.header.addChild('destination','cn=wsaahomo,o=afip,c=ar,serialNumber=CUIT 33693450239') tra.header.add_child('uniqueId',str(date('U'))) tra.header.add_child('generationTime',str(date('c',date('U')-ttl))) tra.header.add_child('expirationTime',str(date('c',date('U')+ttl))) tra.add_child('service',service) return tra.as_xml()
def create_tra(service=None, ttl=2400, cert=None): "Create a Access Request Ticket (TRA)" # Base TRA squeleton (Ticket de Requerimiento de Acceso) tra = SimpleXMLElement('<?xml version="1.0" encoding="UTF-8"?>' '<loginTicketRequest version="1.0">' '</loginTicketRequest>') tra.add_child('header') # get the source from the certificate subject, ie "CN=empresa, O=dna, C=py" if cert: crt = xmlsec.x509_parse_cert(cert) tra.header.add_child('source', crt.get_subject().as_text()) tra.header.add_child('destination', 'C=py, O=dna, OU=sofia, CN=wsaatest') d = int(time.mktime(datetime.datetime.now().timetuple())) tra.header.add_child('uniqueId', str(d)) date = lambda ts: datetime.datetime.fromtimestamp(ts).isoformat() tra.header.add_child('generationTime', str(date(d - ttl))) tra.header.add_child('expirationTime', str(date(d + ttl))) tra.add_child('service', service) return tra.as_xml()
def create_tra(service=None, ttl=2400, cert=None): "Create a Access Request Ticket (TRA)" # Base TRA squeleton (Ticket de Requerimiento de Acceso) tra = SimpleXMLElement( '<?xml version="1.0" encoding="UTF-8"?>' '<loginTicketRequest version="1.0">' '</loginTicketRequest>') tra.add_child('header') # get the source from the certificate subject, ie "CN=empresa, O=dna, C=py" if cert: crt = xmlsec.x509_parse_cert(cert) tra.header.add_child('source', crt.get_subject().as_text()) tra.header.add_child('destination', 'C=py, O=dna, OU=sofia, CN=wsaatest') d = int(time.mktime(datetime.datetime.now().timetuple())) tra.header.add_child('uniqueId', str(d)) date = lambda ts: datetime.datetime.fromtimestamp(ts).isoformat() tra.header.add_child('generationTime', str(date(d-ttl))) tra.header.add_child('expirationTime', str(date(d+ttl))) tra.add_child('service', service) return tra.as_xml()
def export_citi_comprobante_ventas(self): logger.info('exportar CITI REG3685 Comprobante Ventas') pool = Pool() Invoice = pool.get('account.invoice') Currency = pool.get('currency.currency') invoices = Invoice.search([ ('type', '=', 'out'), ('pos.pos_do_not_report', '=', False), ['OR', ('state', 'in', ['posted', 'paid']), [('state', '=', 'cancelled'), ('number', '!=', None)]], ('move.period', '=', self.start.period), ], order=[ ('pos', 'ASC'), ('invoice_date', 'ASC'), ('invoice_type', 'ASC'), ('number', 'ASC'), ]) lines = "" for invoice in invoices: alicuotas = { 3: 0, 4: 0, 5: 0, 6: 0, 8: 0, 9: 0, } cant_alicuota = 0 fecha_comprobante = invoice.invoice_date.strftime("%Y%m%d") tipo_comprobante = invoice.invoice_type.invoice_type.rjust(3, '0') punto_de_venta = invoice.number.split('-')[0].rjust(5, '0') if int(tipo_comprobante) in COMPROBANTES_EXCLUIDOS: punto_de_venta = ''.rjust(5, '0') # se informan ceros if ':' in invoice.number: parte_desde = invoice.number.split(':')[0] parte_hasta = invoice.number.split(':')[1] numero_comprobante = parte_desde.split('-')[1].rjust(20, '0') numero_comprobante_hasta = parte_hasta.rjust(20, '0') else: numero_comprobante = invoice.number.split( '-')[1].rjust(20, '0') #if int(punto_de_venta) in [33, 331, 332]: # numero_comprobante = 'COE' numero_comprobante_hasta = invoice.number.split( '-')[1].rjust(20, '0') identificacion_comprador = None codigo_documento_comprador = invoice.party.tipo_documento if invoice.party.vat_number: # Si tenemos vat_number, entonces tenemos CUIT Argentino # use the Argentina AFIP's global CUIT for the country: identificacion_comprador = invoice.party.vat_number codigo_documento_comprador = '80' elif invoice.party.vat_number_afip_foreign: # use the VAT number directly identificacion_comprador = \ invoice.party.vat_number_afip_foreign else: for identifier in invoice.party.identifiers: if identifier.type == 'ar_dni': identificacion_comprador = identifier.code codigo_documento_comprador = '96' break if identificacion_comprador is None: identificacion_comprador = '0' # only "consumidor final" codigo_documento_comprador = '99' # consumidor final identificacion_comprador = identificacion_comprador.strip().rjust( 20, '0') if codigo_documento_comprador == '99': apellido_nombre_comprador = 'VENTA GLOBAL DIARIA'.ljust(30) else: s = self.strip_accents(invoice.party.name[:30]) apellido_nombre_comprador = ''.join( x for x in s if x.isalnum()).ljust(30) importe_total = Currency.round(invoice.currency, abs(invoice.total_amount)).to_eng_string().replace( '.', '').rjust(15, '0') # iterar sobre lineas de facturas importe_total_lineas_sin_impuesto = Decimal('0') # se calcula percepcion_no_categorizados = Decimal('0') # se calcula importe_operaciones_exentas = Decimal('0') # 0 importe_total_percepciones = Decimal('0') # 0 importe_total_impuesto_iibb = Decimal('0') # se calcula importe_total_percepciones_municipales = Decimal('0') # 0 importe_total_impuestos_internos = Decimal('0') # 0 # COMPROBANTES QUE NO CORESPONDE if int(tipo_comprobante) not in [19, 20, 21, 22]: importe_total_lineas_sin_impuesto = invoice.pyafipws_imp_tot_conc importe_operaciones_exentas = invoice.pyafipws_imp_op_ex # calculo total de percepciones for invoice_tax in invoice.taxes: if invoice_tax.tax.group.afip_kind == 'gravado': iva_id = int(invoice_tax.tax.iva_code) alicuotas[iva_id] += 1 elif invoice_tax.tax.group.afip_kind == 'nacional': importe_total_percepciones += invoice.currency.round( abs(invoice_tax.amount)) elif invoice_tax.tax.group.afip_kind == 'provincial': importe_total_impuesto_iibb += abs(invoice_tax.amount) elif invoice_tax.tax.group.afip_kind == 'interno': importe_total_impuestos_internos += abs(invoice_tax.amount) importe_total_lineas_sin_impuesto = Currency.round( invoice.currency, importe_total_lineas_sin_impuesto ).to_eng_string().replace('.', '').rjust(15, '0') percepcion_no_categorizados = Currency.round(invoice.currency, percepcion_no_categorizados).to_eng_string().replace('.', '').rjust(15, '0') # En caso de que en una misma operación se vendan productos # exentos con gravados, la alícuota será la correspondiente a # los productos gravados. En este caso el monto correspondiente a # la parte exenta se consignará en este campo, y la porción # gravada en el campo correspondiente del detalle de alícuotas de # IVA. importe_operaciones_exentas = Currency.round(invoice.currency, importe_operaciones_exentas).to_eng_string().replace('.', '').rjust(15, '0') importe_total_percepciones = Currency.round(invoice.currency, importe_total_percepciones).to_eng_string().replace('.', '').rjust(15, '0') importe_total_impuesto_iibb = Currency.round(invoice.currency, importe_total_impuesto_iibb).to_eng_string().replace('.', '').rjust(15, '0') importe_total_percepciones_municipales = Currency.round( invoice.currency, importe_total_percepciones_municipales ).to_eng_string().replace('.', '').rjust(15, '0') importe_total_impuestos_internos = Currency.round(invoice.currency, importe_total_impuestos_internos).to_eng_string().replace('.', '').rjust(15, '0') codigo_moneda = invoice.currency.afip_code or 'PES' ctz = '1.00' if codigo_moneda != 'PES': for afip_tr in invoice.transactions: if afip_tr.pyafipws_result == 'A': request = SimpleXMLElement(unidecode( afip_tr.pyafipws_xml_request)) ctz = str(request('Moneda_ctz')) break ctz = Currency.round(invoice.currency, Decimal(ctz)) tipo_de_cambio = str("%.6f" % ctz) tipo_de_cambio = tipo_de_cambio.replace('.', '').rjust(10, '0') # recorrer alicuotas y saber cuantos tipos de alicuotas hay for key, value in alicuotas.items(): if value != 0: cant_alicuota += 1 cantidad_alicuotas = str(cant_alicuota) if cant_alicuota == 0: cantidad_alicuotas = '1' # Factura E if int(invoice.invoice_type.invoice_type) in [19, 20, 21, 22]: codigo_operacion = 'X' # Exportaciones del exterior # Clase C elif int(invoice.invoice_type.invoice_type) in [ 11, 12, 13, 15, 211, 212, 213]: codigo_operacion = 'N' # No gravado # Operacion exenta elif invoice.company.party.iva_condition == 'exento': codigo_operacion = 'E' # Operaciones exentas else: # Segun tabla codigo de operaciones codigo_operacion = ' ' otros_atributos = '0'.rjust(15, '0') # Opcional para resto de comprobantes. Obligatorio para liquidacion # servicios clase A y B fecha_venc_pago = '0'.rjust(8, '0') campos = [fecha_comprobante, tipo_comprobante, punto_de_venta, numero_comprobante, numero_comprobante_hasta, codigo_documento_comprador, identificacion_comprador, apellido_nombre_comprador, importe_total, importe_total_lineas_sin_impuesto, percepcion_no_categorizados, importe_operaciones_exentas, importe_total_percepciones, importe_total_impuesto_iibb, importe_total_percepciones_municipales, importe_total_impuestos_internos, codigo_moneda, tipo_de_cambio, cantidad_alicuotas, codigo_operacion, otros_atributos, fecha_venc_pago] separador = self.start.csv_format and _SEPARATOR or '' lines += separador.join(campos) + _EOL logger.info('Comienza attach comprobante de venta') self.exportar.comprobante_ventas = lines.encode('utf-8')
client = SoapClient(LOCATION, ACTION, namespace="http://dgi.gub.uy", ns="dgi", soap_ns="soapenv", trace=True) # si se usa el WSDL, se puede consultar client.help("EFACRECEPCIONSOBRE") # Procedimiento tentativo: # ======================== # leer los datos del comprobante # NOTA: se podría usar más SimpleXMLElement para armar el xml pythonicamente cfe = SimpleXMLElement(open("dgicfe_uy.xml").read()) caratula = cfe("DGICFE:Caratula") # establecer la fecha actual setattr( caratula, "DGICFE:Fecha", datetime.datetime.now().strftime( "%Y-%m-%dT%H:%M:%S-03:00")) #utcnow().strftime("%Y-%m-%dT%H:%M:%S")) # leer el certificado (PEM) del emisor y agregarlo cert_lines = open("certificado.crt").readlines() cert_pem = ''.join([line for line in cert_lines if not line.startswith("---")]) setattr(caratula, "DGICFE:X509Certificate", cert_pem) # preparar la plantilla para la info de firma con los namespaces padres (CFE) plantilla = SimpleXMLElement(xmlsec.SIGN_ENV_TMPL)
def enviar_dte(self): if self.type in ( 'out_invoice', 'out_refund' ) and self.env.user.company_id.invoice_line and self.invoice_line_count > self.env.user.company_id.invoice_line: raise UserError(u'Ha excedido de %d líneas para ésta factura.' % self.env.user.company_id.invoice_line) fmt = "%Y%m%d" ####Variables comunes wsaa_url = self.company_id.ambiente_produccion == 'T' and "https://wsaahomo.afip.gov.ar/ws/services/LoginCms?wsdl" \ or "https://wsaa.afip.gov.ar/ws/services/LoginCms?wsdl" cert = self.company_id.ruta_cert clave = self.company_id.pass_cert self.check_path(cert, 'cert') self.check_path(clave, 'key') companyCuit = self.company_id.cuit.replace("-", '') fecha_cbte = self.date_invoice.strftime("%Y%m%d") tipo_cbte = int(self.tipo_comprobante.codigo) punto_vta = int(self.punto_venta.name) if not self.date_invoice: raise ValidationError("Ingrese la fecha de la factura") moneda_id = "PES" moneda_ctz = '1.000' imp_tot_conc = 0.0 partner_id = self.partner_id.commercial_partner_id if self.currency_id.name == 'USD': moneda_id = "DOL" if self.manual_currency_rate: moneda_ctz = self.currency_rate_invoice else: moneda_ctz = self.currency_id._get_actual_currency_rate( self.date_invoice) if self.currency_id.name == 'EUR': moneda_id = "060" if self.manual_currency_rate: moneda_ctz = self.currency_rate_invoice else: moneda_ctz = self.currency_id._get_actual_currency_rate( self.date_invoice) imp_total = self.amount_total ## Importe Total de la factura wsaa = WSAA() # five hours DEFAULT_TTL = 60 * 60 * 5 tax_map = { '10.5': 4, '21.0': 5, '27.0': 6, '0.0': 3 } # 4: 10.5%, 5: 21%, 6: 27% (no enviar si es otra alicuota) if self.punto_venta.tax_assets: lines = self.invoice_line_ids.filtered( lambda l: not l.product_id.bfe_check or not l.product_id. bfe_ncm) if lines: raise UserError( 'Algunos productos no cuentan con codigo NCM, por favor verificar antes de enviar factura.' ) zona = 1 # Generar un Ticket de Requerimiento de Acceso (TRA) para WSBFE solicitar = True session = self.env['afip.session'].search( [('xml_tag', '=', 'wsbfe'), ('environment', '=', self.company_id.ambiente_produccion), ('company_id', '=', self.company_id.id)], order='id DESC', limit=1) if session and session.expirationTime: expiracion = session.expirationTime solicitar = wsaa.Expirado(expiracion) if solicitar: tra = WSAA().CreateTRA(service="wsbfe", ttl=DEFAULT_TTL) # Generar el mensaje firmado (CMS) cms = WSAA().SignTRA(tra, cert, clave) # Llamar al web service para autenticar wsaa.Conectar(None, wsaa_url, '') ta = wsaa.LoginCMS(cms) if ta: session = self.env['afip.session'].sudo().create({ 'xml_tag': 'wsbfe', 'environment': self.company_id.ambiente_produccion, 'company_id': self.company_id.id, 'sign': wsaa.ObtenerTagXml("sign"), 'token': wsaa.ObtenerTagXml("token"), 'expirationTime': wsaa.ObtenerTagXml("expirationTime") }) self._cr.commit() url = self.company_id.ambiente_produccion == 'T' and "https://wswhomo.afip.gov.ar/" \ or "https://servicios1.afip.gov.ar/" wsdl = "%swsbfev1/service.asmx?WSDL" % url wsbfe = WSBFEv1() ok = wsbfe.Conectar(cache=None, wsdl=wsdl) if not ok: raise ValidationError( "Error conexión con libreria. WSBFEV1: %s" % wsbfe.Excepcion) wsbfe.LanzarExcepciones = True wsbfe.Cuit = companyCuit token = session.token sign = session.sign wsbfe.Token = token wsbfe.Sign = sign try: cbte_nro = wsbfe.GetLastCMP( tipo_cbte, punto_vta) is None and 1 or wsbfe.GetLastCMP( tipo_cbte, punto_vta) + 1 cbt_id = wsbfe.GetLastID() and wsbfe.GetLastID() + 1 or 1 except Exception as e: raise AccessError("Error de AFIP:" + str(e)) if self.company_id.use_afip_rate: moneda_ctz = moneda_id != 'PES' and wsbfe.GetParamCtz( moneda_id) or '1.000' self.env['res.currency.rate'].create({ 'name': datetime.now().strftime('%Y-%m-%d %H:%M:%S'), 'rate': float(moneda_ctz), 'currency_id': self.currency_id.id }) self.currency_rate = moneda_ctz ## Cliente tipo_doc = int(partner_id.documento_id.codigo) nro_doc = partner_id.cuit.replace("-", '') imp_neto = str("%.2f" % abs(self.amount_untaxed)) imp_iva = str("%.2f" % abs(self.amount_iva)) imp_tot_conc = str("%.2f" % abs(self.no_gravado)) impto_liq_rni = 0.0 imp_op_ex = str("%.2f" % abs(self.amount_excempt)) imp_perc = 0.0 imp_iibb = 0.0 imp_perc_mun = 0.0 imp_internos = str("%.2f" % abs(self.amount_other_tax)) fecha_venc_pago = None if self.tipo_comprobante.comprobante_credito and not self.type == 'out_refund': date_due = self.date_due and self.date_due or self.date_invoice if date_due: fecha_venc_pago = date_due.strftime("%Y%m%d") wsbfe.CrearFactura(tipo_doc, nro_doc, zona, tipo_cbte, punto_vta, cbte_nro, fecha_cbte, imp_total, imp_neto, imp_iva, imp_tot_conc, impto_liq_rni, imp_op_ex, imp_perc, imp_iibb, imp_perc_mun, imp_internos, moneda_id, moneda_ctz, fecha_venc_pago) for line in self.invoice_line_ids: codigo = line.product_id.bfe_ncm.name sec = "" ds = line.name qty = line.quantity umed = 1 precio = line.price_unit importe = line.price_subtotal # calculamos bonificacion haciendo teorico menos importe bonif = line.discount and (precio * qty - importe) or None iva_id = 2 imp_iva = 0.0 taxes = line.invoice_line_tax_ids.filtered(lambda t: t.is_iva) if taxes: if tax_map.get(str(taxes[0].amount)): iva_id = tax_map[str(taxes[0].amount)] price_unit = line.price_unit * ( 1 - (line.discount or 0.0) / 100.0) taxesv = line.invoice_line_tax_ids.compute_all( price_unit, self.currency_id, line.quantity, line.product_id, partner_id)['taxes'] for t in taxesv: if t['id'] == taxes[0].id: imp_iva = t['amount'] wsbfe.AgregarItem(codigo, sec, ds, qty, umed, precio, bonif, iva_id, importe + imp_iva) if self.tipo_comprobante.comprobante_credito and self.company_id.cbu and self.type != 'out_refund': wsbfe.AgregarOpcional(2101, self.company_id.cbu) if self.tipo_comprobante.comprobante_credito and self.company_id.cbu: oc = self.client_purchase_ref if not oc: oc = self.refund_invoice_id and self.refund_invoice_id.client_purchase_ref and self.refund_invoice_id.client_purchase_ref or self.refund_invoice_id.name if not oc: oc = 'N/A' wsbfe.AgregarOpcional(23, oc) if self.tipo_comprobante.comprobante_credito and self.type == 'out_refund': wsbfe.AgregarOpcional(22, self.anulacion and 'S' or 'N') wsbfe.Authorize(cbt_id) if not wsbfe.CAE: raise ValidationError("%s .%s" % (wsbfe.ErrMsg, wsbfe.Obs)) else: # datos devueltos por AFIP: self.no_cae = wsbfe.CAE self._cr.commit() vence = wsbfe.Vencimiento self.vence_date = '%s-%s-%s' % (vence[6:10], vence[3:5], vence[0:2]) path1 = "/tmp/xmlrequest.xml" open(path1, "wb").write(wsbfe.XmlRequest) self.requestXml = base64.encodestring( bytes(open(path1, 'r').read(), 'utf8')) self.requestXml_fname = 'peticion%s.xml' % wsbfe.CAE path2 = "/tmp/xmlresponse.xml" responseFile = open(path2, "wb").write(wsbfe.XmlResponse) self.num_comprobante = str(int(cbte_nro)) self.responseXml = base64.encodestring( bytes(open(path2, 'r').read(), 'utf8')) self.responseXml_fname = 'respuesta%s.xml' % wsbfe.CAE digit = self.company_id.cuit.split('-')[1] barcode_str = '%s%s%s%s%s%s' % (companyCuit, str( tipo_cbte).zfill(2), punto_vta, wsbfe.CAE, vence, digit) self.cod_barra = barcode_str self.message_post( body=("Factura Aceptada por la AFIP con No: %s" % str(cbte_nro))) if wsbfe.Resultado == 'A': self.state_dte = 'accepted' self._cr.commit() elif wsbfe.Resultado == 'R': self.state_dte = 'rejected' else: if self.tipo_comprobante.codigo in ['019', '020', '021']: if not partner_id.country_id: raise ValidationError("Debe asociar un país al cliente") aIva = float_is_zero(self.amount_iva, self.currency_id.rounding) aOther = float_is_zero(self.amount_other_tax, self.currency_id.rounding) if not aIva or not aOther: raise ValidationError( "La factura de exportacion es exenta de impuestos. Favor eliminar impuestos de las lineas de factura" ) #Generar un Ticket de Requerimiento de Acceso (TRA) para WSFEX solicitar = True session = self.env['afip.session'].search( [('xml_tag', '=', 'wsfex'), ('environment', '=', self.company_id.ambiente_produccion), ('company_id', '=', self.company_id.id)], order='id DESC', limit=1) if session and session.expirationTime: expiracion = session.expirationTime solicitar = wsaa.Expirado(expiracion) if solicitar: tra = WSAA().CreateTRA(service="wsfex", ttl=DEFAULT_TTL) # Generar el mensaje firmado (CMS) cms = WSAA().SignTRA(tra, cert, clave) # Llamar al web service para autenticar wsaa.Conectar(None, wsaa_url, '') ta = wsaa.LoginCMS(cms) if ta: session = self.env['afip.session'].sudo().create({ 'xml_tag': 'wsfex', 'environment': self.company_id.ambiente_produccion, 'company_id': self.company_id.id, 'sign': wsaa.ObtenerTagXml("sign"), 'token': wsaa.ObtenerTagXml("token"), 'expirationTime': wsaa.ObtenerTagXml("expirationTime") }) self._cr.commit() # autenticarse frente a AFIP (obtención de ticket de acceso): # conectar al webservice de negocio url = self.company_id.ambiente_produccion == 'T' and "https://wswhomo.afip.gov.ar/" \ or "https://servicios1.afip.gov.ar/" wsdl = "%swsfexv1/service.asmx?WSDL" % url wsfex = WSFEX() ok = wsfex.Conectar(wsdl) if not ok: raise ValidationError( "Error conexión con libreria. WSFEX: %s" % wsfex.Excepcion) wsfex.LanzarExcepciones = True #Setear tocken y sing de autorización (pasos previos) #CUIT del emisor (debe estar registrado en la AFIP) wsfex.Cuit = companyCuit token = session.token sign = session.sign wsfex.Token = token wsfex.Sign = sign try: cbte_nro = wsfex.GetLastCMP( tipo_cbte, punto_vta) is None and 1 or wsfex.GetLastCMP( tipo_cbte, punto_vta) + 1 cbt_id = wsfex.GetLastID() and wsfex.GetLastID() + 1 or 1 except Exception as e: raise AccessError("Error de AFIP:" + str(e)) ## Tipo de Exportación tipo_expo = self.tipo_expo or 1 if self.company_id.use_afip_rate: moneda_ctz = moneda_id != 'PES' and wsfex.GetParamCtz( moneda_id) or '1.000' self.env['res.currency.rate'].create({ 'name': datetime.now().strftime('%Y-%m-%d %H:%M:%S'), 'rate': float(moneda_ctz), 'currency_id': self.currency_id.id }) self.currency_rate = moneda_ctz ## Permiso permiso_existente = self.permiso_existente if self.tipo_comprobante.codigo in ['020', '021']: permiso_existente = '' tipo_expo = '1' ## Pais Destino if not partner_id.country_id.cod_nacionalidad: raise ValidationError( "Ingrese el código de la nacionalidad") dst_cmp = partner_id.country_id.cod_nacionalidad ## Cliente cliente = partner_id.name ## Cuit del pais del cliente if partner_id.state_id == self.env.ref( 'base.state_ar_v', raise_if_not_found=False): cuit_pais_cliente = partner_id.cuit.replace('-', '') else: if not partner_id.country_id.cuit_pais: raise ValidationError("Ingrese el cuit del pais") cuit_pais_cliente = partner_id.country_id.cuit_pais.replace( '-', '') ## Direccion Cliente domicilio_cliente = partner_id.street ## id_impositivo id_impositivo = partner_id.cuit ## obs_comerciales obs_generales = self.comment and self.comment or "" forma_pago = self.payment_term_id and self.payment_term_id.name or "" obs_comerciales = forma_pago ## incoterms incoterms = self.incoterms and self.incoterms or "FOB" ## Idioma del Comprobante idioma_cbte = 1 ##Creo una factura (internamente, no se llama al WebService): wsfex.CrearFactura(tipo_cbte, punto_vta, cbte_nro, fecha_cbte, imp_total, tipo_expo, permiso_existente, dst_cmp, cliente, cuit_pais_cliente, domicilio_cliente, id_impositivo, moneda_id, moneda_ctz, obs_comerciales, obs_generales, forma_pago, incoterms, idioma_cbte) #Agregar items for line in self.invoice_line_ids: umed = 1 #Ver tabla de parámetros (unidades de medida) #lo agrego a la factura (internamente, no se llama al WebService): discount = line.quantity * line.price_unit - line.price_subtotal if discount > 0.00: wsfex.AgregarItem(line.product_id.default_code, line.product_id.name, line.quantity, \ umed, line.price_unit, line.price_subtotal, discount) else: wsfex.AgregarItem(line.product_id.default_code, line.product_id.name, line.quantity,\ umed, line.price_unit, line.price_subtotal) #Agrego un permiso (ver manual para el desarrollador) if permiso_existente == "S": permiso_id = "99999AAXX999999A" dst = 225 #país destino de la mercaderia wsfex.AgregarPermiso(permiso_id, dst) #Agrego un comprobante asociado (ver manual para el desarrollador) if tipo_cbte != 19 and self.refund_invoice_id: tipo_cbte_asoc = int( self.refund_invoice_id.tipo_comprobante.codigo ) # tipo de comprobante asociado punto_vta_asoc = int(self.refund_invoice_id.punto_venta. name) # punto de venta cbte_nro_asoc = int(self.refund_invoice_id.num_comprobante ) # nro de comprobante asociado wsfex.AgregarCmpAsoc(tipo_cbte_asoc, punto_vta_asoc, cbte_nro_asoc) wsfex.Authorize(cbt_id) if not wsfex.CAE: raise ValidationError("%s .%s" % (wsfex.ErrMsg, wsfex.Obs)) else: # datos devueltos por AFIP: self.no_cae = wsfex.CAE self._cr.commit() vence = wsfex.Vencimiento self.vence_date = '%s-%s-%s' % (vence[6:10], vence[3:5], vence[0:2]) path1 = "/tmp/xmlrequest.xml" open(path1, "wb").write(wsfex.XmlRequest) self.requestXml = base64.encodestring( bytes(open(path1, 'r').read(), 'utf8')).decode("utf-8") self.requestXml_fname = 'peticion%s.xml' % wsfex.CAE path2 = "/tmp/xmlresponse.xml" responseFile = open(path2, "wb").write(wsfex.XmlResponse) self.num_comprobante = str(int(cbte_nro)) self.responseXml = base64.encodestring( bytes(open(path2, 'r').read(), 'utf8')).decode("utf-8") self.responseXml_fname = 'respuesta%s.xml' % wsfex.CAE digit = self.company_id.cuit.split('-')[1] ve = '%s%s%s' % (vence[6:10], vence[3:5], vence[0:2]) barcode_str = '%s%s%s%s%s%s' % (companyCuit, str( tipo_cbte).zfill(2), punto_vta, wsfex.CAE, ve, digit) self.cod_barra = barcode_str #imgPath = '/tmp/barcode.png' #PyI25().GenerarImagen(barcode_str, imgPath) #imgFile = open(imgPath, "rb") #self.barcode_img = imgFile.read().encode("base64") self.message_post(body=( "Factura de Exportación Aceptada por la AFIP con No: %s" % str(cbte_nro))) if wsfex.Resultado == 'A': self.state_dte = 'accepted' self._cr.commit() elif wsfex.Resultado != 'A': self.state_dte = 'rejected' else: #Factura Electrónica # instanciar el componente para factura electrónica mercado interno wsfev1 = WSFEv1() wsfev1.LanzarExcepciones = True # datos de conexión (cambiar URL para producción) cache = None url = self.company_id.ambiente_produccion == 'T' and "https://wswhomo.afip.gov.ar/" \ or "https://servicios1.afip.gov.ar/" wsdl = "%swsfev1/service.asmx?WSDL" % url # Iniciamos la conexion proxy = "" wrapper = "" # "pycurl" para usar proxy avanzado / propietarios cacert = None # "afip_ca_info.crt" para verificar canal seguro # conectar al webservice de negocio ok = wsfev1.Conectar(cache, wsdl, proxy, wrapper, cacert) if not ok: raise ValidationError( "Error conexión con libreria. WSFEv1: %s" % WSAA.Excepcion) solicitar = True session = self.env['afip.session'].search( [('xml_tag', '=', 'wsfe'), ('environment', '=', self.company_id.ambiente_produccion), ('company_id', '=', self.company_id.id)], order='id DESC', limit=1) if session and session.expirationTime: expiracion = session.expirationTime solicitar = wsaa.Expirado(expiracion) if solicitar: # autenticarse frente a AFIP (obtención de ticket de acceso): ta = WSAA().Autenticar("wsfe", cert, clave, wsaa_url, debug=True) if not ta: raise ValidationError( "Error conexión con libreria. Error WSAA: %s" % WSAA.Excepcion) # establecer credenciales (token y sign) y cuit emisor: wsfev1.SetTicketAcceso(ta) if ta: wsfev1.xml = SimpleXMLElement(ta) session = self.env['afip.session'].sudo().create({ 'xml_tag': 'wsfe', 'environment': self.company_id.ambiente_produccion, 'company_id': self.company_id.id, 'sign': wsfev1.ObtenerTagXml("sign"), 'token': wsfev1.ObtenerTagXml("token"), 'expirationTime': wsfev1.ObtenerTagXml("expirationTime") }) self._cr.commit() token = session.token sign = session.sign wsfev1.Token = token wsfev1.Sign = sign wsfev1.Cuit = companyCuit try: cbte_nro = int( wsfev1.CompUltimoAutorizado(tipo_cbte, punto_vta) or 0) except Exception as e: raise AccessError("Error de AFIP:" + str(e)) if not partner_id.cuit: raise ValidationError("El partner no posee CUIT asociado") concepto = self.get_concept_type(self.invoice_line_ids) tipo_doc = int(partner_id.documento_id.codigo) nro_doc = partner_id.cuit.replace("-", '') cbt_desde = cbte_nro + 1 # usar proximo numero de comprobante cbt_hasta = cbte_nro + 1 # desde y hasta distintos solo lotes factura B imp_tot_conc = str("%.2f" % abs( self.no_gravado)) # importe total conceptos no gravado? imp_neto = str("%.2f" % abs(self.neto_gravado) ) # importe neto gravado (todas las alicuotas) imp_iva = str( "%.2f" % abs(self.amount_iva)) # importe total iva liquidado (idem) imp_trib = self.amount_other_tax + self.amount_perception # importe total otros conceptos? imp_trib = str("%.2f" % abs(imp_trib)) imp_op_ex = str("%.2f" % abs( self.amount_excempt)) # importe total operaciones exentas date_due = self.date_due or '' if date_due < self.date_invoice: date_due = self.date_invoice if date_due: if not self.tipo_comprobante.comprobante_credito: fecha_venc_pago = concepto != 1 and date_due.strftime( "%Y%m%d") or '' else: fecha_venc_pago = date_due.strftime("%Y%m%d") else: fecha_venc_pago = '' if concepto != 1 and not fecha_venc_pago and not self.tipo_comprobante.comprobante_credito: fecha_venc_pago = fecha_cbte if not fecha_venc_pago and self.tipo_comprobante.comprobante_credito: fecha_venc_pago = fecha_cbte if self.tipo_comprobante.comprobante_credito and self.type == 'out_refund': fecha_venc_pago = '' # Fechas del período del servicio facturado (solo si concepto != 1)? fecha_serv_desde = concepto != 1 and fecha_cbte or '' fecha_serv_hasta = concepto != 1 and fecha_venc_pago or '' if concepto != 1 and not fecha_serv_hasta: fecha_serv_hasta = fecha_cbte # inicializar la estructura de factura (interna) wsfev1.CrearFactura(concepto, tipo_doc, nro_doc, tipo_cbte, punto_vta, cbt_desde, cbt_hasta, str(imp_total), str(imp_tot_conc), str(imp_neto), str(imp_iva), str(imp_trib), str(imp_op_ex), fecha_cbte, fecha_venc_pago, fecha_serv_desde, fecha_serv_hasta, moneda_id, moneda_ctz) # agregar comprobantes asociados (solo para Notas de Débito y Cŕedito) if tipo_cbte not in (1, 6, 11) and self.refund_invoice_id: tipo = int(self.refund_invoice_id.tipo_comprobante.codigo ) # tipo de comprobante asociado pto_vta = int(self.refund_invoice_id.punto_venta.name ) # punto de venta nro = int(self.refund_invoice_id.num_comprobante ) # nro de comprobante asociado cuit = self.refund_invoice_id.company_id.cuit.replace( "-", '') fecha_cbte = self.refund_invoice_id.date_invoice.strftime( "%Y%m%d") wsfev1.AgregarCmpAsoc(tipo, pto_vta, nro, cuit, fecha_cbte) for tax in self.tax_line_ids: t = tax.tax_id base_imp = tax.base if tax.base: importe = round( t._compute_amount(tax.base, tax.base, 1), 2) else: importe = round(tax.amount, 2) if t.is_iva: # agregar subtotales por tasa de iva (repetir por cada alicuota): if tax_map.get(str(t.amount)): taxId = tax_map[str(t.amount)] print('%s: base(%s), imp(%s)' % (tax.name, tax.base, importe)) wsfev1.AgregarIva(taxId, base_imp, importe) else: # agregar otros impuestos (repetir por cada tributo diferente) taxId = int( t.tipo_tributo) # tipo de tributo (ver tabla) desc = tax.name # descripción del tributo alic = t.amount # alicuota iva wsfev1.AgregarTributo(taxId, desc, base_imp, alic, importe) # llamar al webservice de AFIP para autorizar la factura y obtener CAE: if self.tipo_comprobante.comprobante_credito and self.company_id.cbu and self.type != 'out_refund': wsfev1.AgregarOpcional(2101, self.company_id.cbu) if self.tipo_comprobante.comprobante_credito and self.company_id.cbu: oc = self.client_purchase_ref if not oc: oc = self.refund_invoice_id and self.refund_invoice_id.client_purchase_ref and self.refund_invoice_id.client_purchase_ref or self.refund_invoice_id.name if not oc: oc = 'N/A' wsfev1.AgregarOpcional(23, oc) if self.tipo_comprobante.comprobante_credito and self.company_id.cbu and self.type == 'out_refund': wsfev1.AgregarOpcional(22, 'N') wsfev1.CAESolicitar() if not wsfev1.CAE: raise ValidationError("%s .%s" % (wsfev1.ErrMsg, wsfev1.Obs)) else: # datos devueltos por AFIP: self.no_cae = wsfev1.CAE self._cr.commit() vence = wsfev1.Vencimiento self.vence_date = '%s-%s-%s' % (vence[0:4], vence[4:6], vence[6:8]) path1 = "/tmp/xmlrequest.xml" open(path1, "wb").write(wsfev1.XmlRequest) self.requestXml = base64.encodestring( bytes(open(path1, 'r').read(), 'utf8')) self.requestXml_fname = 'peticion%s.xml' % wsfev1.CAE path2 = "/tmp/xmlresponse.xml" responseFile = open(path2, "wb").write(wsfev1.XmlResponse) self.num_comprobante = str(cbt_desde) self.responseXml = base64.encodestring( bytes(open(path2, 'r').read(), 'utf8')) self.responseXml_fname = 'respuesta%s.xml' % wsfev1.CAE digit = self.company_id.cuit.split('-')[1] barcode_str = '%s%s%s%s%s%s' % ( companyCuit, str(tipo_cbte).zfill(2), punto_vta, wsfev1.CAE, vence, digit) self.cod_barra = barcode_str self.message_post( body=("Factura Aceptada por la AFIP con No: %s" % str(cbt_desde))) if wsfev1.Resultado == 'A': self.state_dte = 'accepted' self._cr.commit() elif wsfev1.Resultado == 'R': self.state_dte = 'rejected'
# Instancio el cliente para consumir el webservice client = SoapClient(LOCATION, ACTION, namespace="http://dgi.gub.uy", ns="dgi", soap_ns="soapenv", trace=True) # si se usa el WSDL, se puede consultar client.help("EFACRECEPCIONSOBRE") # Procedimiento tentativo: # ======================== # leer los datos del comprobante # NOTA: se podría usar más SimpleXMLElement para armar el xml pythonicamente cfe = SimpleXMLElement(open("dgicfe_uy.xml").read()) caratula = cfe("DGICFE:Caratula") # establecer la fecha actual setattr(caratula, "DGICFE:Fecha", datetime.datetime.now().strftime("%Y-%m-%dT%H:%M:%S-03:00"))#utcnow().strftime("%Y-%m-%dT%H:%M:%S")) # leer el certificado (PEM) del emisor y agregarlo cert_lines = open("certificado.crt").readlines() cert_pem = ''.join([line for line in cert_lines if not line.startswith("---")]) setattr(caratula, "DGICFE:X509Certificate", cert_pem) # preparar la plantilla para la info de firma con los namespaces padres (CFE) plantilla = SimpleXMLElement(xmlsec.SIGN_ENV_TMPL) plantilla["xmlns:DGICFE"] = plantilla["xmlns:ns0"] = "http://cfe.dgi.gub.uy"
xmlsec.lxml = None # deshabilitar lxml y usar c14n.py # Por el momento se utilizan llamadas crudas (RAW) y no se parsea el WSDL ##client = SoapClient(wsdl=wsdl, cache="cache") # Instancio el cliente para consumir el webservice client = SoapClient(LOCATION, ACTION, namespace="DGI_Modernizacion_Consolidado", ns="dgi", soap_ns="soapenv", trace=True) # si se usa el WSDL, se puede consultar client.help("EFACRECEPCIONSOBRE") # construir los parámetros de la llamada al webservice (requerimiento): param = SimpleXMLElement( """<dgi:WS_PersonaGetActEmpresarial.Execute xmlns:dgi="DGI_Modernizacion_Consolidado" />""", namespace="DGI_Modernizacion_Consolidado", prefix="dgi") rut = param.add_child("Rut", "210475730011", ns=True) # agregar seguridad WSSE (mensaje firmado digitalmente para autenticacion) # usar el certificado unico asociado al RUT emisor emitidos por la # Administración Nacional de Correos (CA: autoridad certificadora actual) plugin = BinaryTokenSignature(certificate="certificado.crt", private_key="private.key", password=None, cacert="CorreoUruguayoCA.crt", ) client.plugins += [plugin] # llamar al método remoto
import xml.dom.minidom from pysimplesoap.client import SoapClient, SimpleXMLElement from pysimplesoap.wsse import BinaryTokenSignature from pysimplesoap import xmlsec fechacfe = lambda *x: datetime.datetime.now().strftime("%Y-%m-%dT%H:%M:%S-03:00") LOCATION = "https://efactura.dgi.gub.uy:6443/ePrueba/ws_eprueba?wsdl" ACTION = "http://dgi.gub.uyaction/" with open("documento.xml") as f: dataCFE = f.read() cfe = SimpleXMLElement(dataCFE) caratula = cfe("DGICFE:Caratula") setattr(caratula, "DGICFE:Fecha", fechacfe()) # leer el certificado (PEM) del emisor y agregarlo cert_lines = open("certificado.crt").readlines() cert_fmt = [line for line in cert_lines if not line.startswith("---")] cert_pem = ''.join(cert_fmt) setattr(caratula, "DGICFE:X509Certificate", cert_pem) cfeXML = cfe("ns0:CFE") # preparar la plantilla para la info de firma con los namespaces padres (CFE)