diff --git a/M2Crypto-0.37.1-cp39-cp39-win_amd64.whl b/M2Crypto-0.37.1-cp39-cp39-win_amd64.whl new file mode 100644 index 000000000..1977ed27a Binary files /dev/null and b/M2Crypto-0.37.1-cp39-cp39-win_amd64.whl differ diff --git a/cache/24e95ac2a24a18e07d8fca057d7155da.pkl b/cache/24e95ac2a24a18e07d8fca057d7155da.pkl new file mode 100644 index 000000000..93c1b98fc Binary files /dev/null and b/cache/24e95ac2a24a18e07d8fca057d7155da.pkl differ diff --git a/cache/24e95ac2a24a18e07d8fca057d7155da.xml b/cache/24e95ac2a24a18e07d8fca057d7155da.xml new file mode 100644 index 000000000..bc04e814a --- /dev/null +++ b/cache/24e95ac2a24a18e07d8fca057d7155da.xml @@ -0,0 +1,115 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/cache/273d9007b7636f93fada0091c091a5d3.pkl b/cache/273d9007b7636f93fada0091c091a5d3.pkl new file mode 100644 index 000000000..0fe18ba97 Binary files /dev/null and b/cache/273d9007b7636f93fada0091c091a5d3.pkl differ diff --git a/cache/273d9007b7636f93fada0091c091a5d3.xml b/cache/273d9007b7636f93fada0091c091a5d3.xml new file mode 100644 index 000000000..c92bdb6fd --- /dev/null +++ b/cache/273d9007b7636f93fada0091c091a5d3.xml @@ -0,0 +1,1457 @@ + + + Web Service orientado al servicio de Facturacion electronica RG2485 V1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Solicitud de Código de Autorización Electrónico (CAE) + + + + + Retorna la cantidad maxima de registros que puede tener una invocacion al metodo FECAESolicitar / FECAEARegInformativo + + + + + Metodo dummy para verificacion de funcionamiento + + + + + Retorna el ultimo comprobante autorizado para el tipo de comprobante / cuit / punto de venta ingresado / Tipo de Emisión + + + + + Consulta Comprobante emitido y su código. + + + + + Rendición de comprobantes asociados a un CAEA. + + + + + Solicitud de Código de Autorización Electrónico Anticipado (CAEA) + + + + + Consulta CAEA informado como sin movimientos. + + + + + Informa CAEA sin movimientos. + + + + + Consultar CAEA emitidos. + + + + + Recupera la cotizacion de la moneda consultada y su fecha + + + + + Recupera el listado de los diferente tributos que pueden ser utilizados en el servicio de autorizacion + + + + + Recupera el listado de monedas utilizables en servicio de autorización + + + + + Recupera el listado de Tipos de Iva utilizables en servicio de autorización. + + + + + Recupera el listado de identificadores para los campos Opcionales + + + + + Recupera el listado de identificadores para el campo Concepto. + + + + + Recupera el listado de puntos de venta registrados y su estado + + + + + Recupera el listado de Tipos de Comprobantes utilizables en servicio de autorización. + + + + + Recupera el listado de Tipos de Documentos utilizables en servicio de autorización. + + + + + Recupera el listado de los diferente paises que pueden ser utilizados en el servicio de autorizacion + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Web Service orientado al servicio de Facturacion electronica RG2485 V1 + + + + + + + + \ No newline at end of file diff --git a/cache/TA-2b4277f5962bd2cb0265c0fac4b32304.xml b/cache/TA-2b4277f5962bd2cb0265c0fac4b32304.xml new file mode 100644 index 000000000..65127fd62 --- /dev/null +++ b/cache/TA-2b4277f5962bd2cb0265c0fac4b32304.xml @@ -0,0 +1,14 @@ + + +
+ CN=wsaahomo, O=AFIP, C=AR, SERIALNUMBER=CUIT 33693450239 + SERIALNUMBER=CUIT 20267565393, CN=reingart2019pub + 4186086222 + 2021-05-31T18:01:34.899-03:00 + 2021-06-01T06:01:34.899-03:00 +
+ + PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9InllcyI/Pgo8c3NvIHZlcnNpb249IjIuMCI+CiAgICA8aWQgc3JjPSJDTj13c2FhaG9tbywgTz1BRklQLCBDPUFSLCBTRVJJQUxOVU1CRVI9Q1VJVCAzMzY5MzQ1MDIzOSIgZHN0PSJDTj13c2ZlLCBPPUFGSVAsIEM9QVIiIHVuaXF1ZV9pZD0iMzQ3NDE0MzAwMCIgZ2VuX3RpbWU9IjE2MjI0OTQ4MzQiIGV4cF90aW1lPSIxNjIyNTM4MDk0Ii8+CiAgICA8b3BlcmF0aW9uIHR5cGU9ImxvZ2luIiB2YWx1ZT0iZ3JhbnRlZCI+CiAgICAgICAgPGxvZ2luIGVudGl0eT0iMzM2OTM0NTAyMzkiIHNlcnZpY2U9IndzZmUiIHVpZD0iU0VSSUFMTlVNQkVSPUNVSVQgMjAyNjc1NjUzOTMsIENOPXJlaW5nYXJ0MjAxOXB1YiIgYXV0aG1ldGhvZD0iY21zIiByZWdtZXRob2Q9IjIyIj4KICAgICAgICAgICAgPHJlbGF0aW9ucz4KICAgICAgICAgICAgICAgIDxyZWxhdGlvbiBrZXk9IjIwMjY3NTY1MzkzIiByZWx0eXBlPSI0Ii8+CiAgICAgICAgICAgIDwvcmVsYXRpb25zPgogICAgICAgIDwvbG9naW4+CiAgICA8L29wZXJhdGlvbj4KPC9zc28+Cg== + D3xwOIiUOSWxqNk52WrqX2BLMMcvNxT6qVlaDNNO2Z2QvKvdAOckXbo0x70DWkD7C67wKyHB+USbXBFGsZgzmACJwXG32RtC2ryL0KC/FbomfzH4+yakqHze6iar465DIb+eZRs+r+aQG1hz8jbdBWQEwIAX2SY+K7wYlr4YYmU= + +
diff --git a/conf/afip_ca_info.crt b/conf/afip_ca_info.crt index 684776332..e1687d6fb 100644 --- a/conf/afip_ca_info.crt +++ b/conf/afip_ca_info.crt @@ -1,195 +1,26 @@ -----BEGIN CERTIFICATE----- -MIIDVDCCAjygAwIBAgIDAjRWMA0GCSqGSIb3DQEBBQUAMEIxCzAJBgNVBAYTAlVT -MRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMRswGQYDVQQDExJHZW9UcnVzdCBHbG9i -YWwgQ0EwHhcNMDIwNTIxMDQwMDAwWhcNMjIwNTIxMDQwMDAwWjBCMQswCQYDVQQG -EwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEbMBkGA1UEAxMSR2VvVHJ1c3Qg -R2xvYmFsIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2swYYzD9 -9BcjGlZ+W988bDjkcbd4kdS8odhM+KhDtgPpTSEHCIjaWC9mOSm9BXiLnTjoBbdq -fnGk5sRgprDvgOSJKA+eJdbtg/OtppHHmMlCGDUUna2YRpIuT8rxh0PBFpVXLVDv -iS2Aelet8u5fa9IAjbkU+BQVNdnARqN7csiRv8lVK83Qlz6cJmTM386DGXHKTubU -1XupGc1V3sjs0l44U+VcT4wt/lAjNvxm5suOpDkZALeVAjmRCw7+OC7RHQWa9k0+ -bw8HHa8sHo9gOeL6NlMTOdReJivbPagUvTLrGAMoUgRx5aszPeE4uwc2hGKceeoW -MPRfwCvocWvk+QIDAQABo1MwUTAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTA -ephojYn7qwVkDBF9qn1luMrMTjAfBgNVHSMEGDAWgBTAephojYn7qwVkDBF9qn1l -uMrMTjANBgkqhkiG9w0BAQUFAAOCAQEANeMpauUvXVSOKVCUn5kaFOSPeCpilKIn -Z57QzxpeR+nBsqTP3UEaBU6bS+5Kb1VSsyShNwrrZHYqLizz/Tt1kL/6cdjHPTfS -tQWVYrmm3ok9Nns4d0iXrKYgjy6myQzCsplFAMfOEVEiIuCl6rYVSAlk6l5PdPcF -PseKUgzbFbS9bZvlxrFUaKnjaZC2mqUPuLk/IH2uSrW4nOQdtqvmlKXBx4Ot2/Un -hw4EbNX/3aBd7YdStysVAq45pmp06drE57xNNB6pXE0zX5IJL4hmXXeXxx12E6nV -5fEWCRE11azbJHFwLJhWC9kXtNHjUStedejV0NxPNO3CBWaAocvmMw== ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIENjCCAx6gAwIBAgIBATANBgkqhkiG9w0BAQUFADBvMQswCQYDVQQGEwJTRTEU -MBIGA1UEChMLQWRkVHJ1c3QgQUIxJjAkBgNVBAsTHUFkZFRydXN0IEV4dGVybmFs -IFRUUCBOZXR3b3JrMSIwIAYDVQQDExlBZGRUcnVzdCBFeHRlcm5hbCBDQSBSb290 -MB4XDTAwMDUzMDEwNDgzOFoXDTIwMDUzMDEwNDgzOFowbzELMAkGA1UEBhMCU0Ux -FDASBgNVBAoTC0FkZFRydXN0IEFCMSYwJAYDVQQLEx1BZGRUcnVzdCBFeHRlcm5h -bCBUVFAgTmV0d29yazEiMCAGA1UEAxMZQWRkVHJ1c3QgRXh0ZXJuYWwgQ0EgUm9v -dDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALf3GjPm8gAELTngTlvt -H7xsD821+iO2zt6bETOXpClMfZOfvUq8k+0DGuOPz+VtUFrWlymUWoCwSXrbLpX9 -uMq/NzgtHj6RQa1wVsfwTz/oMp50ysiQVOnGXw94nZpAPA6sYapeFI+eh6FqUNzX -mk6vBbOmcZSccbNQYArHE504B4YCqOmoaSYYkKtMsE8jqzpPhNjfzp/haW+710LX -a0Tkx63ubUFfclpxCDezeWWkWaCUN/cALw3CknLa0Dhy2xSoRcRdKn23tNbE7qzN -E0S3ySvdQwAl+mG5aWpYIxG3pzOPVnVZ9c0p10a3CitlttNCbxWyuHv77+ldU9U0 -WicCAwEAAaOB3DCB2TAdBgNVHQ4EFgQUrb2YejS0Jvf6xCZU7wO94CTLVBowCwYD -VR0PBAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wgZkGA1UdIwSBkTCBjoAUrb2YejS0 -Jvf6xCZU7wO94CTLVBqhc6RxMG8xCzAJBgNVBAYTAlNFMRQwEgYDVQQKEwtBZGRU -cnVzdCBBQjEmMCQGA1UECxMdQWRkVHJ1c3QgRXh0ZXJuYWwgVFRQIE5ldHdvcmsx -IjAgBgNVBAMTGUFkZFRydXN0IEV4dGVybmFsIENBIFJvb3SCAQEwDQYJKoZIhvcN -AQEFBQADggEBALCb4IUlwtYj4g+WBpKdQZic2YR5gdkeWxQHIzZlj7DYd7usQWxH -YINRsPkyPef89iYTx4AWpb9a/IfPeHmJIZriTAcKhjW88t5RxNKWt9x+Tu5w/Rw5 -6wwCURQtjr0W4MHfRnXnJK3s9EK0hZNwEGe6nQY1ShjTK3rMUUKhemPR5ruhxSvC -Nr4TDea9Y355e6cJDUCrat2PisP29owaQgVR1EX1n6diIWgVIEM8med8vSTYqZEX -c4g/VhsxOBi0cQ+azcgOno4uG+GMmIPLHzHxREzGBHNJdmAPx/i9F4BrLunMTA5a -mnkPIAou1Z5jJh5VkpTYghdae9C8x49OhgQ= ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIGPzCCBSegAwIBAgIQPnsDxBUUgfhED3t2/785ZjANBgkqhkiG9w0BAQUFADCB -iTELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G -A1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxLzAtBgNV -BAMTJkNPTU9ETyBIaWdoLUFzc3VyYW5jZSBTZWN1cmUgU2VydmVyIENBMB4XDTEz -MDEzMDAwMDAwMFoXDTE1MDEzMDIzNTk1OVowggE7MQswCQYDVQQGEwJBUjENMAsG -A1UEERMEMTA4NjEoMCYGA1UECBMfQ2l1ZGFkIEF1dG9ub21hIGRlIEJ1ZW5vcyBB -aXJlczEYMBYGA1UEBxMPQ2FwaXRhbCBGZWRlcmFsMR8wHQYDVQQJExZIaXBvbGl0 -byBZaXJpZ295ZW4gMzcwMTQwMgYDVQQKEytBRE1JTklTVFJBQ0lPTiBGRURFUkFM -IERFIElOR1JFU09TIFBVQkxJQ09TMUkwRwYDVQQLE0BJc3N1ZWQgdGhyb3VnaCBB -RE1JTklTVFJBQ0lPTiBGRURFUkFMIERFIElOR1JFU09TIFBVQkxJQ09TIEUtUEtJ -MRMwEQYDVQQLEwpJbnN0YW50U1NMMSIwIAYDVQQDExlzZXJ2aWNpb3NqYXZhLmFm -aXAuZ29iLmFyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAoR6MVZFU -lvhL8HyrwkOmAYdYGF+Jmon/Ut6ypr6tA3+v+5PlJ4C95VHEdYzkX4A58HQXdBAi -zFvYqh0VE7rjmLmpuM741ID81tgsEUUAObn532jmklJWC8o+PvGcbzPMQ0HRyTEO -YZUuacywagyl11c1pZTndNMFMlAcxfJq9avq2/22m0rrKo7Qw7xT8EjXU4jzxgqB -g/d3oUUxx2kd1oixfJ0R8o5bhwu/frMZYHsHqjcWqm6QOVCIcC61r1tu+9FmeCVt -YYDREeSJmO1sthfeNJA+j6mp+SmkROmDGFSDsllGYsPdUaXnDV3nCsvHkKvKqVER -1Lz4F8zW6rVHRQIDAQABo4IB7DCCAegwHwYDVR0jBBgwFoAUP9W10NZEeVBKF6Ob -jErcuLAiZGswHQYDVR0OBBYEFHbY+v6k8C06XxyvhWq5zfkaDOZDMA4GA1UdDwEB -/wQEAwIFoDAMBgNVHRMBAf8EAjAAMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEF -BQcDAjBQBgNVHSAESTBHMDsGDCsGAQQBsjEBAgEDBDArMCkGCCsGAQUFBwIBFh1o -dHRwczovL3NlY3VyZS5jb21vZG8uY29tL0NQUzAIBgZngQwBAgIwTwYDVR0fBEgw -RjBEoEKgQIY+aHR0cDovL2NybC5jb21vZG9jYS5jb20vQ09NT0RPSGlnaC1Bc3N1 -cmFuY2VTZWN1cmVTZXJ2ZXJDQS5jcmwwgYAGCCsGAQUFBwEBBHQwcjBKBggrBgEF -BQcwAoY+aHR0cDovL2NydC5jb21vZG9jYS5jb20vQ09NT0RPSGlnaC1Bc3N1cmFu -Y2VTZWN1cmVTZXJ2ZXJDQS5jcnQwJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmNv -bW9kb2NhLmNvbTBDBgNVHREEPDA6ghlzZXJ2aWNpb3NqYXZhLmFmaXAuZ29iLmFy -gh13d3cuc2VydmljaW9zamF2YS5hZmlwLmdvYi5hcjANBgkqhkiG9w0BAQUFAAOC -AQEAq2nMhqVh7UQ/Z4TjZfl1gU3DabY25pbVcDWk94X/xoM/XxZqzgSgiNFhP/al -ztDgsM1Js0WlSj2xX1DwBlGbuer9Asy/JBC2WYfZ/4T5e2h4TUgHahxJJXClsJA8 -UbIWmP1nwVPyDMGQkWoqg54eHgl1QPml5tXtYKgB/Vp0RFxoxB6WzQ2knLebwvab -TU3Ll69Qv5rSUw5bevJ6o3tThOOu7awJCWgD/hayeWFRBR5Txq0wG6p9wcWol2DS -FpmhynLJ7NgcGoTULIo5i+0YdJXAjM5CSLR4V0y96x9NNFaZvXJguZhYiokDVIRh -bvtOXCMfBS/cH3z80XnHX/oDjg== ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIENjCCAx6gAwIBAgIBATANBgkqhkiG9w0BAQUFADBvMQswCQYDVQQGEwJTRTEU -MBIGA1UEChMLQWRkVHJ1c3QgQUIxJjAkBgNVBAsTHUFkZFRydXN0IEV4dGVybmFs -IFRUUCBOZXR3b3JrMSIwIAYDVQQDExlBZGRUcnVzdCBFeHRlcm5hbCBDQSBSb290 -MB4XDTAwMDUzMDEwNDgzOFoXDTIwMDUzMDEwNDgzOFowbzELMAkGA1UEBhMCU0Ux -FDASBgNVBAoTC0FkZFRydXN0IEFCMSYwJAYDVQQLEx1BZGRUcnVzdCBFeHRlcm5h -bCBUVFAgTmV0d29yazEiMCAGA1UEAxMZQWRkVHJ1c3QgRXh0ZXJuYWwgQ0EgUm9v -dDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALf3GjPm8gAELTngTlvt -H7xsD821+iO2zt6bETOXpClMfZOfvUq8k+0DGuOPz+VtUFrWlymUWoCwSXrbLpX9 -uMq/NzgtHj6RQa1wVsfwTz/oMp50ysiQVOnGXw94nZpAPA6sYapeFI+eh6FqUNzX -mk6vBbOmcZSccbNQYArHE504B4YCqOmoaSYYkKtMsE8jqzpPhNjfzp/haW+710LX -a0Tkx63ubUFfclpxCDezeWWkWaCUN/cALw3CknLa0Dhy2xSoRcRdKn23tNbE7qzN -E0S3ySvdQwAl+mG5aWpYIxG3pzOPVnVZ9c0p10a3CitlttNCbxWyuHv77+ldU9U0 -WicCAwEAAaOB3DCB2TAdBgNVHQ4EFgQUrb2YejS0Jvf6xCZU7wO94CTLVBowCwYD -VR0PBAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wgZkGA1UdIwSBkTCBjoAUrb2YejS0 -Jvf6xCZU7wO94CTLVBqhc6RxMG8xCzAJBgNVBAYTAlNFMRQwEgYDVQQKEwtBZGRU -cnVzdCBBQjEmMCQGA1UECxMdQWRkVHJ1c3QgRXh0ZXJuYWwgVFRQIE5ldHdvcmsx -IjAgBgNVBAMTGUFkZFRydXN0IEV4dGVybmFsIENBIFJvb3SCAQEwDQYJKoZIhvcN -AQEFBQADggEBALCb4IUlwtYj4g+WBpKdQZic2YR5gdkeWxQHIzZlj7DYd7usQWxH -YINRsPkyPef89iYTx4AWpb9a/IfPeHmJIZriTAcKhjW88t5RxNKWt9x+Tu5w/Rw5 -6wwCURQtjr0W4MHfRnXnJK3s9EK0hZNwEGe6nQY1ShjTK3rMUUKhemPR5ruhxSvC -Nr4TDea9Y355e6cJDUCrat2PisP29owaQgVR1EX1n6diIWgVIEM8med8vSTYqZEX -c4g/VhsxOBi0cQ+azcgOno4uG+GMmIPLHzHxREzGBHNJdmAPx/i9F4BrLunMTA5a -mnkPIAou1Z5jJh5VkpTYghdae9C8x49OhgQ= ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIFdDCCBFygAwIBAgIQJ2buVutJ846r13Ci/ITeIjANBgkqhkiG9w0BAQwFADBv -MQswCQYDVQQGEwJTRTEUMBIGA1UEChMLQWRkVHJ1c3QgQUIxJjAkBgNVBAsTHUFk -ZFRydXN0IEV4dGVybmFsIFRUUCBOZXR3b3JrMSIwIAYDVQQDExlBZGRUcnVzdCBF -eHRlcm5hbCBDQSBSb290MB4XDTAwMDUzMDEwNDgzOFoXDTIwMDUzMDEwNDgzOFow -gYUxCzAJBgNVBAYTAkdCMRswGQYDVQQIExJHcmVhdGVyIE1hbmNoZXN0ZXIxEDAO -BgNVBAcTB1NhbGZvcmQxGjAYBgNVBAoTEUNPTU9ETyBDQSBMaW1pdGVkMSswKQYD -VQQDEyJDT01PRE8gUlNBIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIICIjANBgkq -hkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAkehUktIKVrGsDSTdxc9EZ3SZKzejfSNw -AHG8U9/E+ioSj0t/EFa9n3Byt2F/yUsPF6c947AEYe7/EZfH9IY+Cvo+XPmT5jR6 -2RRr55yzhaCCenavcZDX7P0N+pxs+t+wgvQUfvm+xKYvT3+Zf7X8Z0NyvQwA1onr -ayzT7Y+YHBSrfuXjbvzYqOSSJNpDa2K4Vf3qwbxstovzDo2a5JtsaZn4eEgwRdWt -4Q08RWD8MpZRJ7xnw8outmvqRsfHIKCxH2XeSAi6pE6p8oNGN4Tr6MyBSENnTnIq -m1y9TBsoilwie7SrmNnu4FGDwwlGTm0+mfqVF9p8M1dBPI1R7Qu2XK8sYxrfV8g/ -vOldxJuvRZnio1oktLqpVj3Pb6r/SVi+8Kj/9Lit6Tf7urj0Czr56ENCHonYhMsT -8dm74YlguIwoVqwUHZwK53Hrzw7dPamWoUi9PPevtQ0iTMARgexWO/bTouJbt7IE -IlKVgJNp6I5MZfGRAy1wdALqi2cVKWlSArvX31BqVUa/oKMoYX9w0MOiqiwhqkfO -KJwGRXa/ghgntNWutMtQ5mv0TIZxMOmm3xaG4Nj/QN370EKIf6MzOi5cHkERgWPO -GHFrK+ymircxXDpqR+DDeVnWIBqv8mqYqnK8V0rSS527EPywTEHl7R09XiidnMy/ -s1Hap0flhFMCAwEAAaOB9DCB8TAfBgNVHSMEGDAWgBStvZh6NLQm9/rEJlTvA73g -JMtUGjAdBgNVHQ4EFgQUu69+Aj36pvE8hI6t7jiY7NkyMtQwDgYDVR0PAQH/BAQD -AgGGMA8GA1UdEwEB/wQFMAMBAf8wEQYDVR0gBAowCDAGBgRVHSAAMEQGA1UdHwQ9 -MDswOaA3oDWGM2h0dHA6Ly9jcmwudXNlcnRydXN0LmNvbS9BZGRUcnVzdEV4dGVy -bmFsQ0FSb290LmNybDA1BggrBgEFBQcBAQQpMCcwJQYIKwYBBQUHMAGGGWh0dHA6 -Ly9vY3NwLnVzZXJ0cnVzdC5jb20wDQYJKoZIhvcNAQEMBQADggEBAGS/g/FfmoXQ -zbihKVcN6Fr30ek+8nYEbvFScLsePP9NDXRqzIGCJdPDoCpdTPW6i6FtxFQJdcfj -Jw5dhHk3QBN39bSsHNA7qxcS1u80GH4r6XnTq1dFDK8o+tDb5VCViLvfhVdpfZLY -Uspzgb8c8+a4bmYRBbMelC1/kZWSWfFMzqORcUx8Rww7Cxn2obFshj5cqsQugsv5 -B5a6SE2Q8pTIqXOi6wZ7I53eovNNVZ96YUWYGGjHXkBrI/V5eu+MtWuLt29G9Hvx -PUsE2JOAWVrgQSQdso8VYFhH2+9uRv0V9dlfmrPb2LjkQLPNlzmuhbsdjrzch5vR -pu/xO28QOG8= ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIGDjCCA/agAwIBAgIQNoJef7WkgZN+9tFza7k8pjANBgkqhkiG9w0BAQwFADCB -hTELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G -A1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNV -BAMTIkNPTU9ETyBSU0EgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTQwMjEy -MDAwMDAwWhcNMjkwMjExMjM1OTU5WjCBljELMAkGA1UEBhMCR0IxGzAZBgNVBAgT -EkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UEChMR -Q09NT0RPIENBIExpbWl0ZWQxPDA6BgNVBAMTM0NPTU9ETyBSU0EgT3JnYW5pemF0 -aW9uIFZhbGlkYXRpb24gU2VjdXJlIFNlcnZlciBDQTCCASIwDQYJKoZIhvcNAQEB -BQADggEPADCCAQoCggEBALkU2YXyQURX/zBEHtw8RKMXuG4B+KNfwqkhHc5Z9Ozz -iKkJMjyxi2OkPic284/5OGYuB5dBj0um3cNfnnM858ogDU98MgXPwS5IZUqF0B9W -MW2O5cYy1Bu8n32W/JjXT/j0WFb440W+kRiC5Iq+r81SN1GHTx6Xweg6rvn/RuRl -Pz/DR4MvzLhCXi1+91porl1LwKY1IfWGo8hJi5hjYA3JIUjCkjBlRrKGNQRCJX6t -p05LEkAAeohoXG+fo6R4ESGuPQsOvkUUI8/rddf2oPG8RWxevKEy7PNYeEIoCzoB -dvDFoJ7BaXDej0umed/ydrbjDxN8GDuxUWxqIDnOnmkCAwEAAaOCAWUwggFhMB8G -A1UdIwQYMBaAFLuvfgI9+qbxPISOre44mOzZMjLUMB0GA1UdDgQWBBSa8yvaz61P -ti+7KkhIKhK3G0LBJDAOBgNVHQ8BAf8EBAMCAYYwEgYDVR0TAQH/BAgwBgEB/wIB -ADAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwGwYDVR0gBBQwEjAGBgRV -HSAAMAgGBmeBDAECAjBMBgNVHR8ERTBDMEGgP6A9hjtodHRwOi8vY3JsLmNvbW9k -b2NhLmNvbS9DT01PRE9SU0FDZXJ0aWZpY2F0aW9uQXV0aG9yaXR5LmNybDBxBggr -BgEFBQcBAQRlMGMwOwYIKwYBBQUHMAKGL2h0dHA6Ly9jcnQuY29tb2RvY2EuY29t -L0NPTU9ET1JTQUFkZFRydXN0Q0EuY3J0MCQGCCsGAQUFBzABhhhodHRwOi8vb2Nz -cC5jb21vZG9jYS5jb20wDQYJKoZIhvcNAQEMBQADggIBAGmKNmiaHjtlC+B8z6ar -cTuvYaQ/5GQBSRDTHY/i1e1n055bl71CHgf50Ltt9zKVWiIpYvgMnFlWJzagIhIR -+kf0UclZeylKpUg1fMWXZuAnJTsVejJ1SpH7pmue4lP6DYwT+yO4CxIsru3bHUeQ -1dCTaXaROBU01xjqfrxrWN4qOZADRARKVtho5fV8aX6efVRL0NiGq2dmE1deiSoX -rS2uvUAOZu2K/1S0wQHLqeBHuhFhj62uI0gqxiV5iRxBBJXAEepXK9a0l/qx6RVi -7Epxd/3zoZza9msAKcUy5/pO6rMqpxiXHFinQjZf7BTP+HsO993MiBWamlzI8SDH -0YZyoRebrrr+bKgy0QB2SXP3PyeHPLbJLfqqkJDJCgmfyWkfBxmpv966+AuIgkQW -EH8HwIAiX3+8MN66zQd5ZFbY//NPnDC7bh5RS+bNvRfExb/IP46xH4pGtwZDb2It -z1GdRcqK6ROLwMeRvlu2+jdKif7wndoTJiIsBpA+ixOYoBnW3dpKSH89D4mdJHJL -DntE/9Q2toN2I1iLFGy4XfdhbTl27d0SPWuHiJeRvsBGAh52HN22r1xP9QDWnE2p -4J6ijvyxFnlcIdNFgZoMOWxtKNcl0rcRkND23m9e9Pqki2Z3ci+bkEAsUhJg+f+1 -cC6JmnkJiYEt7Fx4b4GH8fxV ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIDrzCCApegAwIBAgIQCDvgVpBCRrGhdWrJWZHHSjANBgkqhkiG9w0BAQUFADBh -MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 -d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBD -QTAeFw0wNjExMTAwMDAwMDBaFw0zMTExMTAwMDAwMDBaMGExCzAJBgNVBAYTAlVT -MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j -b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IENBMIIBIjANBgkqhkiG -9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4jvhEXLeqKTTo1eqUKKPC3eQyaKl7hLOllsB -CSDMAZOnTjC3U/dDxGkAV53ijSLdhwZAAIEJzs4bg7/fzTtxRuLWZscFs3YnFo97 -nh6Vfe63SKMI2tavegw5BmV/Sl0fvBf4q77uKNd0f3p4mVmFaG5cIzJLv07A6Fpt -43C/dxC//AH2hdmoRBBYMql1GNXRor5H4idq9Joz+EkIYIvUX7Q6hL+hqkpMfT7P -T19sdl6gSzeRntwi5m3OFBqOasv+zbMUZBfHWymeMr/y7vrTC0LUq7dBMtoM1O/4 -gdW7jVg/tRvoSSiicNoxBN33shbyTApOB6jtSj1etX+jkMOvJwIDAQABo2MwYTAO -BgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUA95QNVbR -TLtm8KPiGxvDl7I90VUwHwYDVR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUw -DQYJKoZIhvcNAQEFBQADggEBAMucN6pIExIK+t1EnE9SsPTfrgT1eXkIoyQY/Esr -hMAtudXH/vTBH1jLuG2cenTnmCmrEbXjcKChzUyImZOMkXDiqw8cvpOp/2PV5Adg -06O/nVsJ8dWO41P0jmP6P6fbtGbfYmbW0W5BjfIttep3Sp+dWOIrWcBAI+0tKIJF -PnlUkiaY4IBIqDfv8NZ5YBberOgOzW6sRBc4L0na4UU+Krk2U886UAb3LujEV0ls -YSEY1QSteDwsOoBrp+uvFRTp2InBuThs4pFsiv9kuXclVzDAGySj4dzp30d8tbQk -CAUw7C29C79Fv1C5qfPrmAESrciIxpg0X40KPMbp1ZWVbd4= +MIIEMjCCAxqgAwIBAgIBATANBgkqhkiG9w0BAQUFADB7MQswCQYDVQQGEwJHQjEb +MBkGA1UECAwSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHDAdTYWxmb3JkMRow +GAYDVQQKDBFDb21vZG8gQ0EgTGltaXRlZDEhMB8GA1UEAwwYQUFBIENlcnRpZmlj +YXRlIFNlcnZpY2VzMB4XDTA0MDEwMTAwMDAwMFoXDTI4MTIzMTIzNTk1OVowezEL +MAkGA1UEBhMCR0IxGzAZBgNVBAgMEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UE +BwwHU2FsZm9yZDEaMBgGA1UECgwRQ29tb2RvIENBIExpbWl0ZWQxITAfBgNVBAMM +GEFBQSBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczCCASIwDQYJKoZIhvcNAQEBBQADggEP +ADCCAQoCggEBAL5AnfRu4ep2hxxNRUSOvkbIgwadwSr+GB+O5AL686tdUIoWMQua +BtDFcCLNSS1UY8y2bmhGC1Pqy0wkwLxyTurxFa70VJoSCsN6sjNg4tqJVfMiWPPe +3M/vg4aijJRPn2jymJBGhCfHdr/jzDUsi14HZGWCwEiwqJH5YZ92IFCokcdmtet4 +YgNW8IoaE+oxox6gmf049vYnMlhvB/VruPsUK6+3qszWY19zjNoFmag4qMsXeDZR +rOme9Hg6jc8P2ULimAyrL58OAd7vn5lJ8S3frHRNG5i1R8XlKdH5kBjHYpy+g8cm +ez6KJcfA3Z3mNWgQIJ2P2N7Sw4ScDV7oL8kCAwEAAaOBwDCBvTAdBgNVHQ4EFgQU +oBEKIz6W8Qfs4q8p74Klf9AwpLQwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQF +MAMBAf8wewYDVR0fBHQwcjA4oDagNIYyaHR0cDovL2NybC5jb21vZG9jYS5jb20v +QUFBQ2VydGlmaWNhdGVTZXJ2aWNlcy5jcmwwNqA0oDKGMGh0dHA6Ly9jcmwuY29t +b2RvLm5ldC9BQUFDZXJ0aWZpY2F0ZVNlcnZpY2VzLmNybDANBgkqhkiG9w0BAQUF +AAOCAQEACFb8AvCb6P+k+tZ7xkSAzk/ExfYAWMymtrwUSWgEdujm7l3sAg9g1o1Q +GE8mTgHj5rCl7r+8dFRBv/38ErjHT1r0iWAFf2C3BUrz9vHCv8S5dIa2LX1rzNLz +Rt0vxuBqw8M0Ayx9lt1awg6nCpnBBYurDC/zXDrPbDdVCYfeU0BsWO/8tqtlbgT2 +G9w84FoVxp7Z8VlIMCFlA2zs6SFz7JsDoeA3raAVGI/6ugLOpyypEBMs1OUIJqsi +l2D4kF501KKaU73yqWjgom7C12yxow+ev+to51byrvLjKzg6CYG1a4XXvi3tPxq3 +smPi9WIsgtRqAEFQ8TmDn5XpNpaYbg== -----END CERTIFICATE----- + diff --git a/cot.py b/cot.py index 5fcba832d..01ea13e9c 100644 --- a/cot.py +++ b/cot.py @@ -45,7 +45,7 @@ class COT: 'Version', 'Excepcion', 'Traceback', 'InstallDir', 'CuitEmpresa', 'NumeroComprobante', 'CodigoIntegridad', 'NombreArchivo', 'TipoError', 'CodigoError', 'MensajeError', - 'NumeroUnico', 'Procesado', + 'NumeroUnico', 'Procesado', 'COT', ] _reg_progid_ = "COT" @@ -68,7 +68,7 @@ def limpiar(self): self.XmlResponse = "" self.Excepcion = self.Traceback = "" self.TipoError = self.CodigoError = self.MensajeError = "" - self.CuitEmpresa = self.NumeroComprobante = "" + self.CuitEmpresa = self.NumeroComprobante = self.COT = "" self.NombreArchivo = self.CodigoIntegridad = "" self.NumeroUnico = self.Procesado = "" @@ -84,10 +84,10 @@ def PresentarRemito(self, filename, testing=""): self.Excepcion = "Archivo no encontrado: %s" % filename return False - archivo = open(filename, "rb") + archivo = open(filename, "r") if not testing: - response = self.client(user=self.Usuario, password=self.Password, - file=archivo) + response = self.client( + user=self.Usuario, password=self.Password, file=archivo) else: response = open(testing).read() self.XmlResponse = response @@ -99,6 +99,8 @@ def PresentarRemito(self, filename, testing=""): if 'cuitEmpresa' in self.xml: self.CuitEmpresa = str(self.xml.cuitEmpresa) self.NumeroComprobante = str(self.xml.numeroComprobante) + if 'cot' in self.xml: + self.COT = str(self.xml.cot) self.NombreArchivo = str(self.xml.nombreArchivo) self.CodigoIntegridad = str(self.xml.codigoIntegridad) if 'validacionesRemitos' in self.xml: @@ -111,7 +113,7 @@ def PresentarRemito(self, filename, testing=""): if 'errores' in remito: for error in remito.errores.error: d['Errores'].append(( - str(error.codigo), + str(error.codigo), str(error.descripcion))) self.remitos.append(d) # establecer valores del primer remito (sin eliminarlo) @@ -238,6 +240,7 @@ def ObtenerTagXml(self, *tags): # datos generales: print("CUIT Empresa:", cot.CuitEmpresa) print("Numero Comprobante:", cot.NumeroComprobante) + print("COT:", cot.COT) print("Nombre Archivo:", cot.NombreArchivo) print("Codigo Integridad:", cot.CodigoIntegridad) diff --git a/factura.pdf b/factura.pdf new file mode 100644 index 000000000..90d4e44d1 Binary files /dev/null and b/factura.pdf differ diff --git a/iibb.py b/iibb.py index c4559359e..c3f511e4e 100644 --- a/iibb.py +++ b/iibb.py @@ -100,7 +100,7 @@ def ConsultarContribuyentes(self, fecha_desde, fecha_hasta, cuit_contribuyente): self.xml.fechaHasta = fecha_hasta self.xml.contribuyentes.contribuyente.cuitContribuyente = cuit_contribuyente - xml = self.xml.as_xml() + xml = self.xml.as_xml().encode('utf8') self.CodigoHash = md5(xml).hexdigest() nombre = "DFEServicioConsulta_%s.xml" % self.CodigoHash diff --git a/pyafipws.iml b/pyafipws.iml new file mode 100644 index 000000000..9e25109ec --- /dev/null +++ b/pyafipws.iml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/pyfepdf.py b/pyfepdf.py index 3d366b06d..8f17ebb0e 100644 --- a/pyfepdf.py +++ b/pyfepdf.py @@ -10,7 +10,7 @@ # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License # for more details. -from . import utils +import utils from fpdf import Template from decimal import Decimal from io import StringIO @@ -20,7 +20,7 @@ import os import decimal import datetime -"Módulo para generar PDF de facturas electrónicas" +"M�dulo para generar PDF de facturas electr�nicas" __author__ = "Mariano Reingart " __copyright__ = "Copyright (C) 2011-2018 Mariano Reingart" @@ -32,14 +32,14 @@ CONFIG_FILE = "rece.ini" LICENCIA = """ -pyfepdf.py: Interfaz para generar Facturas Electrónica en formato PDF +pyfepdf.py: Interfaz para generar Facturas Electr�nica en formato PDF Copyright (C) 2011-2015 Mariano Reingart reingart@gmail.com Este progarma es software libre, se entrega ABSOLUTAMENTE SIN GARANTIA y es bienvenido a redistribuirlo bajo la licencia GPLv3. -Para información adicional sobre garantía, soporte técnico comercial -e incorporación/distribución en programas propietarios ver PyAfipWs: +Para informaci�n adicional sobre garant�a, soporte t�cnico comercial +e incorporaci�n/distribuci�n en programas propietarios ver PyAfipWs: http://www.sistemasagiles.com.ar/trac/wiki/PyAfipWs """ @@ -48,21 +48,21 @@ --ayuda: este mensaje --licencia: muestra la licencia del programa - --debug: modo depuración (detalla y confirma las operaciones) + --debug: modo depuraci�n (detalla y confirma las operaciones) --formato: muestra el formato de los archivos de entrada/salida - --prueba: genera y autoriza una factura de prueba (no usar en producción!) + --prueba: genera y autoriza una factura de prueba (no usar en producci�n!) --cargar: carga un archivo de entrada (txt) a la base de datos --grabar: graba un archivo de salida (txt) con los datos de los comprobantes procesados - --pdf: genera la imágen de factura en PDF + --pdf: genera la im�gen de factura en PDF --dbf: utiliza tablas DBF en lugar del archivo de entrada TXT --json: utiliza el formato JSON para el archivo de entrada -Ver rece.ini para parámetros de configuración " +Ver rece.ini para par�metros de configuraci�n " """ class FEPDF: - "Interfaz para generar PDF de Factura Electrónica" + "Interfaz para generar PDF de Factura Electr�nica" _public_methods_ = ['CrearFactura', 'AgregarDetalleItem', 'AgregarIva', 'AgregarTributo', 'AgregarCmpAsoc', 'AgregarPermiso', @@ -81,17 +81,17 @@ class FEPDF: tipos_doc = {80: 'CUIT', 86: 'CUIL', 96: 'DNI', 99: '', 87: "CDI", 89: "LE", 90: "LC", 91: "CI Extranjera", - 92: "en trámite", 93: "Acta Nacimiento", 94: "Pasaporte", + 92: "en tr�mite", 93: "Acta Nacimiento", 94: "Pasaporte", 95: "CI Bs. As. RNP", - 0: "CI Policía Federal", 1: "CI Buenos Aires", - 2: "CI Catamarca", 3: "CI Córdoba", 4: "CI Corrientes", - 5: "CI Entre Ríos", 6: "CI Jujuy", 7: "CI Mendoza", + 0: "CI Polic�a Federal", 1: "CI Buenos Aires", + 2: "CI Catamarca", 3: "CI C�rdoba", 4: "CI Corrientes", + 5: "CI Entre R�os", 6: "CI Jujuy", 7: "CI Mendoza", 8: "CI La Rioja", 9: "CI Salta", 10: "CI San Juan", 11: "CI San Luis", 12: "CI Santa Fe", - 13: "CI Santiago del Estero", 14: "CI Tucumán", + 13: "CI Santiago del Estero", 14: "CI Tucum�n", 16: "CI Chaco", 17: "CI Chubut", 18: "CI Formosa", - 19: "CI Misiones", 20: "CI Neuquén", 21: "CI La Pampa", - 22: "CI Río Negro", 23: "CI Santa Cruz", + 19: "CI Misiones", 20: "CI Neuqu�n", 21: "CI La Pampa", + 22: "CI R�o Negro", 23: "CI Santa Cruz", 24: "CI Tierra del Fuego", } @@ -105,27 +105,27 @@ class FEPDF: 53: 'kg base', 54: 'gruesa', 61: 'kg bruto', 62: 'uiactant', 63: 'muiactant', 64: 'uiactig', 65: 'muiactig', 66: 'kg activo', 67: 'gramo activo', 68: 'gramo base', 96: 'packs', 97: 'hormas', - 96: 'packs', 97: 'seña/anticipo', + 96: 'packs', 97: 'se�a/anticipo', 99: 'bonificaci\xf3n', 98: 'otras unidades'} ivas_ds = {3: 0, 4: 10.5, 5: 21, 6: 27, 8: 5, 9: 2.5} paises = {512: 'FIJI, ISLAS', 513: 'PAPUA NUEVA GUINEA', 514: 'KIRIBATI, ISLAS', 515: 'MICRONESIA,EST.FEDER', 516: 'PALAU', 517: 'TUVALU', 518: 'SALOMON,ISLAS', 519: 'TONGA', 520: 'MARSHALL,ISLAS', 521: 'MARIANAS,ISLAS', 597: 'RESTO OCEANIA', 598: 'INDET.(OCEANIA)', 101: 'BURKINA FASO', 102: 'ARGELIA', 103: 'BOTSWANA', 104: 'BURUNDI', 105: 'CAMERUN', 107: 'REP. CENTROAFRICANA.', 108: 'CONGO', 109: 'REP.DEMOCRAT.DEL CONGO EX ZAIRE', 110: 'COSTA DE MARFIL', 111: 'CHAD', 112: 'BENIN', 113: 'EGIPTO', 115: 'GABON', 116: 'GAMBIA', 117: 'GHANA', 118: 'GUINEA', 119: 'GUINEA ECUATORIAL', 120: 'KENYA', 121: 'LESOTHO', 122: 'LIBERIA', 123: 'LIBIA', 124: 'MADAGASCAR', 125: 'MALAWI', 126: 'MALI', 127: 'MARRUECOS', 128: 'MAURICIO,ISLAS', 129: 'MAURITANIA', 130: 'NIGER', 131: 'NIGERIA', 132: 'ZIMBABWE', 133: 'RWANDA', 134: 'SENEGAL', 135: 'SIERRA LEONA', 136: 'SOMALIA', 137: 'SWAZILANDIA', 138: 'SUDAN', 139: 'TANZANIA', 140: 'TOGO', 141: 'TUNEZ', 142: 'UGANDA', 144: 'ZAMBIA', 145: 'TERRIT.VINCULADOS AL R UNIDO', 146: 'TERRIT.VINCULADOS A ESPA\xd1A', 147: 'TERRIT.VINCULADOS A FRANCIA', 149: 'ANGOLA', 150: 'CABO VERDE', 151: 'MOZAMBIQUE', 152: 'SEYCHELLES', 153: 'DJIBOUTI', 155: 'COMORAS', 156: 'GUINEA BISSAU', 157: 'STO.TOME Y PRINCIPE', 158: 'NAMIBIA', 159: 'SUDAFRICA', 160: 'ERITREA', 161: 'ETIOPIA', 197: 'RESTO (AFRICA)', 198: 'INDETERMINADO (AFRICA)', 200: 'ARGENTINA', 201: 'BARBADOS', 202: 'BOLIVIA', 203: 'BRASIL', 204: 'CANADA', 205: 'COLOMBIA', 206: 'COSTA RICA', 207: 'CUBA', 208: 'CHILE', 209: 'REP\xdaBLICA DOMINICANA', 210: 'ECUADOR', 211: 'EL SALVADOR', 212: 'ESTADOS UNIDOS', 213: 'GUATEMALA', 214: 'GUYANA', 215: 'HAITI', 216: 'HONDURAS', 217: 'JAMAICA', 218: 'MEXICO', 219: 'NICARAGUA', 220: 'PANAMA', 221: 'PARAGUAY', 222: 'PERU', 223: 'PUERTO RICO', 224: 'TRINIDAD Y TOBAGO', 225: 'URUGUAY', 226: 'VENEZUELA', 227: 'TERRIT.VINCULADO AL R.UNIDO', 228: 'TER.VINCULADOS A DINAMARCA', 229: 'TERRIT.VINCULADOS A FRANCIA AMERIC.', 230: 'TERRIT. HOLANDESES', 231: 'TER.VINCULADOS A ESTADOS UNIDOS', 232: 'SURINAME', 233: 'DOMINICA', 234: 'SANTA LUCIA', 235: 'SAN VICENTE Y LAS GRANADINAS', 236: 'BELICE', 237: 'ANTIGUA Y BARBUDA', 238: 'S.CRISTOBAL Y NEVIS', 239: 'BAHAMAS', 240: 'GRENADA', 241: 'ANTILLAS HOLANDESAS', 250: 'AAE Tierra del Fuego - ARGENTINA', 251: 'ZF La Plata - ARGENTINA', 252: 'ZF Justo Daract - ARGENTINA', 253: 'ZF R\xedo Gallegos - ARGENTINA', 254: 'Islas Malvinas - ARGENTINA', 255: 'ZF Tucum\xe1n - ARGENTINA', 256: 'ZF C\xf3rdoba - ARGENTINA', 257: 'ZF Mendoza - ARGENTINA', 258: 'ZF General Pico - ARGENTINA', 259: 'ZF Comodoro Rivadavia - ARGENTINA', 260: 'ZF Iquique', 261: 'ZF Punta Arenas', 262: 'ZF Salta - ARGENTINA', 263: 'ZF Paso de los Libres - ARGENTINA', 264: 'ZF Puerto Iguaz\xfa - ARGENTINA', 265: 'SECTOR ANTARTICO ARG.', 270: 'ZF Col\xf3n - REP\xdaBLICA DE PANAM\xc1', 271: 'ZF Winner (Sta. C. de la Sierra) - BOLIVIA', 280: 'ZF Colonia - URUGUAY', 281: 'ZF Florida - URUGUAY', 282: 'ZF Libertad - URUGUAY', 283: 'ZF Zonamerica - URUGUAY', 284: 'ZF Nueva Helvecia - URUGUAY', 285: 'ZF Nueva Palmira - URUGUAY', 286: 'ZF R\xedo Negro - URUGUAY', 287: 'ZF Rivera - URUGUAY', 288: 'ZF San Jos\xe9 - URUGUAY', 291: 'ZF Manaos - BRASIL', 295: 'MAR ARG ZONA ECO.EX', 296: 'RIOS ARG NAVEG INTER', 297: 'RESTO AMERICA', 298: 'INDETERMINADO (AMERICA)', 301: 'AFGANISTAN', 302: 'ARABIA SAUDITA', 303: 'BAHREIN', 304: 'MYANMAR (EX-BIRMANIA)', 305: 'BUTAN', 306: 'CAMBODYA (EX-KAMPUCHE)', 307: 'SRI LANKA', 308: 'COREA DEMOCRATICA', 309: 'COREA REPUBLICANA', 310: 'CHINA', 312: 'FILIPINAS', 313: 'TAIWAN', 315: 'INDIA', 316: 'INDONESIA', 317: 'IRAK', 318: 'IRAN', 319: 'ISRAEL', 320: 'JAPON', 321: 'JORDANIA', 322: 'QATAR', 323: 'KUWAIT', 324: 'LAOS', 325: 'LIBANO', 326: 'MALASIA', 327: 'MALDIVAS ISLAS', 328: 'OMAN', 329: 'MONGOLIA', 330: 'NEPAL', 331: 'EMIRATOS ARABES UNIDOS', 332: 'PAKIST\xc1N', 333: 'SINGAPUR', 334: 'SIRIA', 335: 'THAILANDIA', 337: 'VIETNAM', 341: 'HONG KONG', 344: 'MACAO', 345: 'BANGLADESH', 346: 'BRUNEI', 348: 'REPUBLICA DE YEMEN', 349: 'ARMENIA', 350: 'AZERBAIJAN', 351: 'GEORGIA', 352: 'KAZAJSTAN', 353: 'KIRGUIZISTAN', 354: 'TAYIKISTAN', 355: 'TURKMENISTAN', 356: 'UZBEKISTAN', 357: 'TERR. AU. PALESTINOS', 397: 'RESTO DE ASIA', 398: 'INDET.(ASIA)', 401: 'ALBANIA', 404: 'ANDORRA', 405: 'AUSTRIA', 406: 'BELGICA', 407: 'BULGARIA', 409: 'DINAMARCA', 410: 'ESPA\xd1A', 411: 'FINLANDIA', 412: 'FRANCIA', 413: 'GRECIA', 414: 'HUNGRIA', 415: 'IRLANDA', 416: 'ISLANDIA', 417: 'ITALIA', 418: 'LIECHTENSTEIN', 419: 'LUXEMBURGO', 420: 'MALTA', 421: 'MONACO', 422: 'NORUEGA', 423: 'PAISES BAJOS', 424: 'POLONIA', 425: 'PORTUGAL', 426: 'REINO UNIDO', 427: 'RUMANIA', 428: 'SAN MARINO', 429: 'SUECIA', 430: 'SUIZA', 431: 'VATICANO(SANTA SEDE)', 433: 'POS.BRIT.(EUROPA)', 435: 'CHIPRE', 436: 'TURQUIA', 438: 'ALEMANIA,REP.FED.', 439: 'BIELORRUSIA', 440: 'ESTONIA', 441: 'LETONIA', 442: 'LITUANIA', 443: 'MOLDAVIA', 444: 'RUSIA', 445: 'UCRANIA', 446: 'BOSNIA HERZEGOVINA', 447: 'CROACIA', 448: 'ESLOVAQUIA', 449: 'ESLOVENIA', 450: 'MACEDONIA', 451: 'REP. CHECA', 453: 'MONTENEGRO', 454: 'SERBIA', 997: 'RESTO CONTINENTE', 998: 'INDET.(CONTINENTE)', 497: 'RESTO EUROPA', 498: 'INDET.(EUROPA)', 501: 'AUSTRALIA', 503: 'NAURU', 504: 'NUEVA ZELANDIA', 505: 'VANATU', 506: 'SAMOA OCCIDENTAL', 507: 'TERRITORIO VINCULADOS A AUSTRALIA', 508: 'TERRITORIOS VINCULADOS AL R. UNIDO', 509: 'TERRITORIOS VINCULADOS A FRANCIA', 510: 'TER VINCULADOS A NUEVA. ZELANDA', 511: 'TER. VINCULADOS A ESTADOS UNIDOS'} - monedas_ds = {'DOL': 'USD: Dólar', 'PES': 'ARS: Pesos', '010': 'MXN: Pesos Mejicanos', '011': 'UYU: Pesos Uruguayos', '012': 'BRL: Real', '014': 'Coronas Danesas', '015': 'Coronas Noruegas', '016': 'Coronas Suecas', '019': 'JPY: Yens', '018': 'CAD: D\xf3lar Canadiense', '033': 'CLP: Peso Chileno', '056': 'Forint (Hungr\xeda)', '031': 'BOV: Peso Boliviano', '036': 'Sucre Ecuatoriano', '051': 'D\xf3lar de Hong Kong', '034': 'Rand Sudafricano', '053': 'D\xf3lar de Jamaica', '057': 'Baht (Tailandia)', '043': 'Balboas Paname\xf1as', '042': 'Peso Dominicano', '052': 'D\xf3lar de Singapur', '032': 'Peso Colombiano', '035': 'Nuevo Sol Peruano', '061': 'Zloty Polaco', '060': 'EUR: Euro', '063': 'Lempira Hondure\xf1a', '062': 'Rupia Hind\xfa', '064': 'Yuan (Rep. Pop. China)', '009': 'Franco Suizo', '025': 'Dinar Yugoslavo', '002': 'USD: D\xf3lar Libre EEUU', '027': 'Dracma Griego', '026': 'D\xf3lar Australiano', '007': 'Florines Holandeses', '023': 'VEB: Bol\xedvar Venezolano', '047': 'Riyal Saudita', '046': 'Libra Egipcia', '045': 'Dirham Marroqu\xed', '044': 'C\xf3rdoba Nicarag\xfcense', '029': 'G\xfcaran\xed', '028': 'Flor\xedn (Antillas Holandesas)', '054': 'D\xf3lar de Taiwan', '040': 'Lei Rumano', '024': 'Corona Checa', '030': 'Shekel (Israel)', '021': 'Libra Esterlina', '055': 'Quetzal Guatemalteco', '059': 'Dinar Kuwaiti'} + monedas_ds = {'DOL': 'USD: D�lar', 'PES': 'ARS: Pesos', '010': 'MXN: Pesos Mejicanos', '011': 'UYU: Pesos Uruguayos', '012': 'BRL: Real', '014': 'Coronas Danesas', '015': 'Coronas Noruegas', '016': 'Coronas Suecas', '019': 'JPY: Yens', '018': 'CAD: D\xf3lar Canadiense', '033': 'CLP: Peso Chileno', '056': 'Forint (Hungr\xeda)', '031': 'BOV: Peso Boliviano', '036': 'Sucre Ecuatoriano', '051': 'D\xf3lar de Hong Kong', '034': 'Rand Sudafricano', '053': 'D\xf3lar de Jamaica', '057': 'Baht (Tailandia)', '043': 'Balboas Paname\xf1as', '042': 'Peso Dominicano', '052': 'D\xf3lar de Singapur', '032': 'Peso Colombiano', '035': 'Nuevo Sol Peruano', '061': 'Zloty Polaco', '060': 'EUR: Euro', '063': 'Lempira Hondure\xf1a', '062': 'Rupia Hind\xfa', '064': 'Yuan (Rep. Pop. China)', '009': 'Franco Suizo', '025': 'Dinar Yugoslavo', '002': 'USD: D\xf3lar Libre EEUU', '027': 'Dracma Griego', '026': 'D\xf3lar Australiano', '007': 'Florines Holandeses', '023': 'VEB: Bol\xedvar Venezolano', '047': 'Riyal Saudita', '046': 'Libra Egipcia', '045': 'Dirham Marroqu\xed', '044': 'C\xf3rdoba Nicarag\xfcense', '029': 'G\xfcaran\xed', '028': 'Flor\xedn (Antillas Holandesas)', '054': 'D\xf3lar de Taiwan', '040': 'Lei Rumano', '024': 'Corona Checa', '030': 'Shekel (Israel)', '021': 'Libra Esterlina', '055': 'Quetzal Guatemalteco', '059': 'Dinar Kuwaiti'} tributos_ds = {1: 'Impuestos nacionales', 2: 'Impuestos provinciales', 3: 'Impuestos municipales', 4: 'Impuestos Internos', 99: 'Otro'} tipos_fact = { (1, 6, 11, 19, 51): 'Factura', - (2, 7, 12, 20, 52): 'Nota de Débito', - (3, 8, 13, 21, 53): 'Nota de Crédito', + (2, 7, 12, 20, 52): 'Nota de D�bito', + (3, 8, 13, 21, 53): 'Nota de Cr�dito', (4, 9, 15, 54): 'Recibo', (10, 5): 'Nota de Venta al contado', - (60, 61): 'Cuenta de Venta y Líquido producto', - (63, 64): 'Liquidación', + (60, 61): 'Cuenta de Venta y L�quido producto', + (63, 64): 'Liquidaci�n', (91, ): 'Remito', - (39, 40): '???? (R.G. N° 3419)'} + (39, 40): '???? (R.G. N� 3419)'} letras_fact = {(1, 2, 3, 4, 5, 39, 60, 63): 'A', (6, 7, 8, 9, 10, 40, 61, 64): 'B', @@ -159,7 +159,7 @@ def __init__(self): self.LanzarExcepciones = True def DebugLog(self): - "Devolver bitácora de depuración" + "Devolver bit�cora de depuraci�n" msg = self.log.getvalue() return msg @@ -287,7 +287,7 @@ def fmt_date(self, d): return "%s/%s/%s" % (d[6:8], d[4:6], d[0:4]) def fmt_num(self, i, fmt="%0.2f", monetary=True): - "Formatear un número" + "Formatear un n�mero" if i is not None and str(i) and not isinstance(i, bool): loc = self.Locale if loc: @@ -320,7 +320,7 @@ def fmt_cuit(self, c): return '' def fmt_fact(self, tipo_cbte, punto_vta, cbte_nro): - "Formatear tipo, letra y punto de venta y número de factura" + "Formatear tipo, letra y punto de venta y n�mero de factura" n = "%05d-%08d" % (int(punto_vta), int(cbte_nro)) t, l = tipo_cbte, '' for k, v in list(self.tipos_fact.items()): @@ -332,26 +332,26 @@ def fmt_fact(self, tipo_cbte, punto_vta, cbte_nro): return t, l, n def digito_verificador_modulo10(self, codigo): - "Rutina para el cálculo del dígito verificador 'módulo 10'" + "Rutina para el c�lculo del d�gito verificador 'm�dulo 10'" # http://www.consejo.org.ar/Bib_elect/diciembre04_CT/documentos/rafip1702.htm # Etapa 1: comenzar desde la izquierda, sumar todos los caracteres ubicados en las posiciones impares. codigo = codigo.strip() if not codigo or not codigo.isdigit(): return '' etapa1 = sum([int(c) for i, c in enumerate(codigo) if not i % 2]) - # Etapa 2: multiplicar la suma obtenida en la etapa 1 por el número 3 + # Etapa 2: multiplicar la suma obtenida en la etapa 1 por el n�mero 3 etapa2 = etapa1 * 3 - # Etapa 3: comenzar desde la izquierda, sumar todos los caracteres que están ubicados en las posiciones pares. + # Etapa 3: comenzar desde la izquierda, sumar todos los caracteres que est�n ubicados en las posiciones pares. etapa3 = sum([int(c) for i, c in enumerate(codigo) if i % 2]) # Etapa 4: sumar los resultados obtenidos en las etapas 2 y 3. etapa4 = etapa2 + etapa3 - # Etapa 5: buscar el menor número que sumado al resultado obtenido en la etapa 4 dé un número múltiplo de 10. Este será el valor del dígito verificador del módulo 10. + # Etapa 5: buscar el menor n�mero que sumado al resultado obtenido en la etapa 4 d� un n�mero m�ltiplo de 10. Este ser� el valor del d�gito verificador del m�dulo 10. digito = 10 - (etapa4 - (int(etapa4 / 10) * 10)) if digito == 10: digito = 0 return str(digito) - # Funciones públicas: + # Funciones p�blicas: @utils.inicializar_y_capturar_excepciones_simple def CargarFormato(self, archivo="factura.csv"): @@ -408,7 +408,7 @@ def AgregarCampo(self, nombre, tipo, x1, y1, x2, y2, @utils.inicializar_y_capturar_excepciones_simple def CrearPlantilla(self, papel="A4", orientacion="portrait"): - "Iniciar la creación del archivo PDF" + "Iniciar la creaci�n del archivo PDF" fact = self.factura tipo, letra, nro = self.fmt_fact(fact['tipo_cbte'], fact['punto_vta'], fact['cbte_nro']) @@ -421,7 +421,7 @@ def CrearPlantilla(self, papel="A4", orientacion="portrait"): for field in self.elements: # si la imagen no existe, eliminar nombre para que no falle fpdf if field['type'] == 'I' and not os.path.exists(field["text"]): - # ajustar rutas relativas a las imágenes predeterminadas: + # ajustar rutas relativas a las im�genes predeterminadas: if os.path.exists(os.path.join(self.InstallDir, field["text"])): field['text'] = os.path.join(self.InstallDir, field["text"]) else: @@ -436,14 +436,14 @@ def CrearPlantilla(self, papel="A4", orientacion="portrait"): title="%s %s %s" % (tipo.encode("latin1", "ignore"), letra, nro), author="CUIT %s" % self.CUIT, subject="CAE %s" % fact['cae'], - keywords="AFIP Factura Electrónica", + keywords="AFIP Factura Electr�nica", creator='PyFEPDF %s (http://www.PyAfipWs.com.ar)' % __version__,) self.template = t return True @utils.inicializar_y_capturar_excepciones_simple def ProcesarPlantilla(self, num_copias=3, lineas_max=36, qty_pos='izq'): - "Generar el PDF según la factura creada y plantilla cargada" + "Generar el PDF seg�n la factura creada y plantilla cargada" ret = False try: @@ -458,11 +458,11 @@ def ProcesarPlantilla(self, num_copias=3, lineas_max=36, qty_pos='izq'): tipo_fact, letra_fact, numero_fact = self.fmt_fact(fact['tipo_cbte'], fact['punto_vta'], fact['cbte_nro']) fact['_fmt_fact'] = tipo_fact, letra_fact, numero_fact if fact['tipo_cbte'] in (19, 20, 21): - tipo_fact_ex = tipo_fact + " de Exportación" + tipo_fact_ex = tipo_fact + " de Exportaci�n" else: tipo_fact_ex = tipo_fact - # dividir y contar líneas: + # dividir y contar l�neas: lineas = 0 li_items = [] for it in fact['detalles']: @@ -474,14 +474,14 @@ def ProcesarPlantilla(self, num_copias=3, lineas_max=36, qty_pos='izq'): umed = int(umed) ds = it['ds'] or "" if '\x00' in ds: - # limpiar descripción (campos dbf): + # limpiar descripci�n (campos dbf): ds = ds.replace('\x00', '') if '
' in ds: # reemplazar saltos de linea: ds = ds.replace('
', '\n') if DEBUG: print("dividiendo", ds) - # divido la descripción (simil célda múltiple de PDF) + # divido la descripci�n (simil c�lda m�ltiple de PDF) n_li = 0 for ds in f.split_multicell(ds, 'Item.Descripcion01'): if DEBUG: @@ -490,10 +490,10 @@ def ProcesarPlantilla(self, num_copias=3, lineas_max=36, qty_pos='izq'): li_items.append(dict(codigo=codigo, ds=ds, qty=qty, umed=umed if not n_li else None, precio=None, importe=None)) - # limpio cantidad y código (solo en el primero) + # limpio cantidad y c�digo (solo en el primero) qty = codigo = None n_li += 1 - # asigno el precio a la última línea del item + # asigno el precio a la �ltima l�nea del item li_items[-1].update(importe=it['importe'] if float(it['importe'] or 0) or umed else None, despacho=it.get('despacho'), precio=it['precio'] if float(it['precio'] or 0) or umed else None, @@ -531,7 +531,7 @@ def ProcesarPlantilla(self, num_copias=3, lineas_max=36, qty_pos='izq'): li_items.append(dict(codigo=None, ds=ds, qty=None, umed=None, precio=None, importe=None)) # agrego permisos a descripciones (si corresponde) - permisos = ['Codigo de Despacho %s - Destino de la mercadería: %s' % ( + permisos = ['Codigo de Despacho %s - Destino de la mercader�a: %s' % ( p['id_permiso'], self.paises.get(p['dst_merc'], p['dst_merc'])) for p in fact.get('permisos', [])] @@ -555,7 +555,7 @@ def ProcesarPlantilla(self, num_copias=3, lineas_max=36, qty_pos='izq'): li_items.append(dict(codigo=None, ds=ds, qty=None, umed=None, precio=None, importe=None)) cmps_asoc_ds = ', '.join(cmps_asoc) - # calcular cantidad de páginas: + # calcular cantidad de p�ginas: lineas = len(li_items) if lineas_max > 0: hojas = lineas // (lineas_max - 1) @@ -567,7 +567,7 @@ def ProcesarPlantilla(self, num_copias=3, lineas_max=36, qty_pos='izq'): hojas = 1 if HOMO: - self.AgregarDato("homo", "HOMOLOGACIÓN") + self.AgregarDato("homo", "HOMOLOGACI�N") # mostrar las validaciones no excluyentes de AFIP (observaciones) @@ -577,12 +577,12 @@ def ProcesarPlantilla(self, num_copias=3, lineas_max=36, qty_pos='izq'): else: motivos_ds = "%s" % fact['motivos_obs'] elif HOMO: - motivos_ds = "Ejemplo Sin validez fiscal - Homologación - Testing" + motivos_ds = "Ejemplo Sin validez fiscal - Homologaci�n - Testing" else: motivos_ds = "" if letra_fact in ('A', 'M'): - msg_no_iva = "\nEl IVA discriminado no puede computarse como Crédito Fiscal (RG2485/08 Art. 30 inc. c)." + msg_no_iva = "\nEl IVA discriminado no puede computarse como Cr�dito Fiscal (RG2485/08 Art. 30 inc. c)." if not f.has_key('leyenda_credito_fiscal') and motivos_ds: motivos_ds += msg_no_iva @@ -598,14 +598,14 @@ def ProcesarPlantilla(self, num_copias=3, lineas_max=36, qty_pos='izq'): f.set('hojas', str(hojas)) f.set('pagina', 'Pagina %s de %s' % (hoja, hojas)) if hojas > 1 and hoja < hojas: - s = 'Continúa en hoja %s' % (hoja + 1) + s = 'Contin�a en hoja %s' % (hoja + 1) else: s = '' f.set('continua', s) f.set('Item.Descripcion%02d' % (lineas_max + 1), s) if hoja > 1: - s = 'Continúa de hoja %s' % (hoja - 1) + s = 'Contin�a de hoja %s' % (hoja - 1) else: s = '' f.set('continua_de', s) @@ -614,16 +614,16 @@ def ProcesarPlantilla(self, num_copias=3, lineas_max=36, qty_pos='izq'): if DEBUG: print("generando pagina %s de %s" % (hoja, hojas)) - # establezco datos según configuración: + # establezco datos seg�n configuraci�n: for d in self.datos: if d['pagina'] == 'P' and hoja != 1: continue if d['pagina'] == 'U' and hojas != hoja: - # no es la última hoja + # no es la �ltima hoja continue f.set(d['campo'], d['valor']) - # establezco campos según tabla encabezado: + # establezco campos seg�n tabla encabezado: for k, v in list(fact.items()): f.set(k, v) @@ -688,7 +688,7 @@ def ProcesarPlantilla(self, num_copias=3, lineas_max=36, qty_pos='izq'): f.set('Item.Codigo%02d' % li, it['codigo']) if it['umed'] is not None: if it['umed'] and f.has_key("Item.Umed_ds01"): - # recortar descripción: + # recortar descripci�n: umed_ds = self.umeds_ds.get(int(it['umed'])) s = f.split_multicell(umed_ds, 'Item.Umed_ds01') f.set('Item.Umed_ds%02d' % li, s[0]) @@ -723,7 +723,7 @@ def ProcesarPlantilla(self, num_copias=3, lineas_max=36, qty_pos='izq'): f.set('Item.%s%02d' % (adic, li), it[adic]) if hojas == hoja: - # última hoja, imprimo los totales + # �ltima hoja, imprimo los totales li += 1 # agrego otros tributos @@ -777,7 +777,7 @@ def ProcesarPlantilla(self, num_copias=3, lineas_max=36, qty_pos='izq'): f.set('imp_tot_conc', self.fmt_imp(fact['imp_tot_conc'])) f.set('imp_op_ex', self.fmt_imp(fact['imp_op_ex'])) - # campos antiguos (por compatibilidad hacia atrás) + # campos antiguos (por compatibilidad hacia atr�s) f.set('IMPTO_PERC', self.fmt_imp(fact.get('impto_perc'))) f.set('IMP_OP_EX', self.fmt_imp(fact.get('imp_op_ex'))) f.set('IMP_IIBB', self.fmt_imp(fact.get('imp_iibb'))) @@ -811,7 +811,7 @@ def ProcesarPlantilla(self, num_copias=3, lineas_max=36, qty_pos='izq'): f.set('Total.L', 'Total:') f.set('TOTAL', self.fmt_imp(fact['imp_total'])) else: - # limpio todas las etiquetas (no es la última hoja) + # limpio todas las etiquetas (no es la �ltima hoja) for k in ('imp_neto', 'impto_liq', 'imp_total', 'impto_perc', 'imp_iva', 'impto_liq_nri', 'imp_trib', 'imp_op_ex', 'imp_tot_conc', 'imp_op_ex', 'IMP_IIBB', 'imp_iibb', 'impto_perc_mun', 'imp_internos', @@ -875,16 +875,16 @@ def ProcesarPlantilla(self, num_copias=3, lineas_max=36, qty_pos='izq'): ret = True except Exception as e: - # capturar la excepción manualmente, para imprimirla en el PDF: + # capturar la excepci�n manualmente, para imprimirla en el PDF: ex = utils.exception_info() if DEBUG: print(self.Excepcion) print(self.Traceback) - # guardar la traza de la excepción en un archivo temporal: + # guardar la traza de la excepci�n en un archivo temporal: fname = os.path.join(tempfile.gettempdir(), "traceback.txt") self.template.add_page() - # agregar el texto de la excepción y ubicación de la traza al PDF: + # agregar el texto de la excepci�n y ubicaci�n de la traza al PDF: self.AgregarCampo("traceback", 'T', 25, 270, 0, 0, size=10, rotate=0, foreground=0xF00000, priority=-1, text="Traceback %s" % (fname, )) @@ -900,8 +900,8 @@ def ProcesarPlantilla(self, num_copias=3, lineas_max=36, qty_pos='izq'): f.write("imposible grabar") finally: f.close() - # guardar la info de la excepcion a lo último, para que no sea - # limpiada por el decorador de otros métodos (AgregarCampo) ... + # guardar la info de la excepcion a lo �ltimo, para que no sea + # limpiada por el decorador de otros m�todos (AgregarCampo) ... self.Excepcion = ex['msg'] self.Traceback = ex['tb'] finally: @@ -926,7 +926,7 @@ def MostrarPDF(self, archivo, imprimir=False): return True -# busco el directorio de instalación (global para que no cambie si usan otra dll) +# busco el directorio de instalaci�n (global para que no cambie si usan otra dll) if not hasattr(sys, "frozen"): basepath = __file__ elif sys.frozen == 'dll': @@ -954,7 +954,7 @@ def MostrarPDF(self, archivo, imprimir=False): DEBUG = '--debug' in sys.argv utils.safe_console() - # leeo configuración (primer argumento o rece.ini por defecto) + # leeo configuraci�n (primer argumento o rece.ini por defecto) if len(sys.argv) > 1 and not sys.argv[1].startswith("--"): CONFIG_FILE = sys.argv.pop(1) if DEBUG: @@ -987,7 +987,7 @@ def MostrarPDF(self, archivo, imprimir=False): # cargo el formato CSV por defecto (factura.csv) fepdf.CargarFormato(conf_fact.get("formato", "factura.csv")) - # establezco formatos (cantidad de decimales) según configuración: + # establezco formatos (cantidad de decimales) seg�n configuraci�n: fepdf.FmtCantidad = conf_fact.get("fmt_cantidad", "0.2") fepdf.FmtPrecio = conf_fact.get("fmt_precio", "0.2") @@ -1044,13 +1044,13 @@ def MostrarPDF(self, archivo, imprimir=False): imp_subtotal = "105.00" fecha_cbte = fecha fecha_venc_pago = fecha - # Fechas del período del servicio facturado (solo si concepto> 1) + # Fechas del per�odo del servicio facturado (solo si concepto> 1) fecha_serv_desde = fecha fecha_serv_hasta = fecha - # campos p/exportación (ej): DOL para USD, indicando cotización: + # campos p/exportaci�n (ej): DOL para USD, indicando cotizaci�n: moneda_id = 'DOL' if '--expo' in sys.argv else 'PES' moneda_ctz = 1 if moneda_id == 'PES' else 14.90 - incoterms = 'FOB' # solo exportación + incoterms = 'FOB' # solo exportaci�n idioma_cbte = 1 # 1: es, 2: en, 3: pt # datos adicionales del encabezado: @@ -1110,7 +1110,7 @@ def MostrarPDF(self, archivo, imprimir=False): importe = "0.00" fepdf.AgregarTributo(tributo_id, desc, base_imp, alic, importe) - # subtotales por alícuota de IVA: + # subtotales por al�cuota de IVA: iva_id = 5 # 21% base_imp = 100 importe = 21 @@ -1119,7 +1119,7 @@ def MostrarPDF(self, archivo, imprimir=False): for id in (4, 6): fepdf.AgregarIva(iva_id=id, base_imp=0.00, importe=0.00) - # detalle de artículos: + # detalle de art�culos: u_mtx = 123456 cod_mtx = 1234567890123 codigo = "P0001" @@ -1137,14 +1137,14 @@ def MostrarPDF(self, archivo, imprimir=False): bonif = 0.00 iva_id = 5 importe = 133.10 - despacho = 'Nº 123456' + despacho = 'N� 123456' dato_a = "Dato A" fepdf.AgregarDetalleItem(u_mtx, cod_mtx, codigo, ds, qty, umed, precio, bonif, iva_id, imp_iva, importe, despacho, dato_a) # descuento general (a tasa 21%): u_mtx = cod_mtx = codigo = None - ds = "Bonificación/Descuento 10%" + ds = "Bonificaci�n/Descuento 10%" qty = precio = bonif = None umed = 99 iva_id = 5 @@ -1157,18 +1157,18 @@ def MostrarPDF(self, archivo, imprimir=False): fepdf.AgregarDetalleItem(u_mtx, cod_mtx, codigo, ds, qty, umed, precio, bonif, iva_id, imp_iva, importe, "") - # descripción (sin importes ni cantidad): + # descripci�n (sin importes ni cantidad): u_mtx = cod_mtx = codigo = None qty = precio = bonif = iva_id = imp_iva = importe = None umed = 0 - ds = "Descripción Ejemplo" + ds = "Descripci�n Ejemplo" fepdf.AgregarDetalleItem(u_mtx, cod_mtx, codigo, ds, qty, umed, precio, bonif, iva_id, imp_iva, importe, "") # Agrego un permiso (ver manual para el desarrollador WSFEXv1) if '--expo' in sys.argv: id_permiso = "99999AAXX999999A" - dst_merc = 225 # país destino de la mercaderia + dst_merc = 225 # pa�s destino de la mercaderia ok = fepdf.AgregarPermiso(id_permiso, dst_merc) # completo campos personalizados de la plantilla: @@ -1207,7 +1207,7 @@ def MostrarPDF(self, archivo, imprimir=False): for k, v in list(conf_pdf.items()): fepdf.AgregarDato(k, v) if k.upper() == 'CUIT': - fepdf.CUIT = v # CUIT del emisor para código de barras + fepdf.CUIT = v # CUIT del emisor para c�digo de barras fepdf.CrearPlantilla(papel=conf_fact.get("papel", "legal"), orientacion=conf_fact.get("orientacion", "portrait")) @@ -1221,7 +1221,7 @@ def MostrarPDF(self, archivo, imprimir=False): elif 'pdf' in fact and fact['pdf']: salida = fact['pdf'] else: - # genero el nombre de archivo según datos de factura + # genero el nombre de archivo seg�n datos de factura d = conf_fact.get('directorio', ".") clave_subdir = conf_fact.get('subdirectorio', 'fecha_cbte') if clave_subdir: @@ -1235,7 +1235,7 @@ def MostrarPDF(self, archivo, imprimir=False): it['letra'] = letra_fact it['numero'] = numero_fact it['mes'] = fact['fecha_cbte'][4:6] - it['año'] = fact['fecha_cbte'][0:4] + it['a�o'] = fact['fecha_cbte'][0:4] fn = ''.join([str(it.get(ff, ff)) for ff in fs]) fn = fn.encode('ascii', 'replace').replace('?', '_') salida = os.path.join(d, "%s.pdf" % fn) diff --git a/rece.ini b/rece.ini new file mode 100644 index 000000000..c873bae58 --- /dev/null +++ b/rece.ini @@ -0,0 +1,127 @@ +# EJEMPLO de archivo de configuración de la interfaz PyAfipWs +# DEBE CAMBIAR Certificado (CERT) y Clave Privada (PRIVATEKEY) +# Para producción debe descomentar las URL (sacar ##) +# Más información: +# http://www.sistemasagiles.com.ar/trac/wiki/ManualPyAfipWs#Configuración + +[WSAA] +CERT=reingart.crt +PRIVATEKEY=reingart.key +##URL=https://wsaa.afip.gov.ar/ws/services/LoginCms + +[WSFE] +CUIT=20267565393 +ENTRADA=entrada.txt +SALIDA=salida.txt +##URL=https://servicios1.afip.gov.ar/wsfe/service.asmx + +[WSFEv1] +CUIT=20267565393 +CAT_IVA=1 +PTO_VTA=97 +ENTRADA=entrada.txt +SALIDA=salida.txt +##URL=https://servicios1.afip.gov.ar/wsfev1/service.asmx?WSDL + +[WSMTXCA] +CUIT=20267565393 +ENTRADA=entrada.txt +SALIDA=salida.txt +Reprocesar= S +##URL=https://serviciosjava.afip.gob.ar/wsmtxca/services/MTXCAService + +[WSBFE] +CUIT=20267565393 +ENTRADA=entrada.txt +SALIDA=salida.txt +##URL=https://servicios1.afip.gov.ar/wsfe/service.asmx + +[WSFEX] +CUIT=20267565393 +ENTRADA=entrada.txt +SALIDA=salida.txt +##URL=https://servicios1.afip.gov.ar/wsfe/service.asmx + +[WSCT] +CUIT=20267565393 +ENTRADA=entrada.txt +SALIDA=salida.txt +Reprocesar= S +##URL=https://serviciosjava.afip.gob.ar/wsmtxca/services/MTXCAService + +[WSCDC] +CUIT=20267565393 +ENTRADA=entrada.txt +SALIDA=salida.txt +##URL=https://serviciosjava.afip.gob.ar/wsct/CTService?wsdl + +[WS-SR-PADRON-A4] +CUIT=20267565393 +ENTRADA=entrada.txt +SALIDA=salida.txt +##URL=https://aws.afip.gov.ar/sr-padron/webservices/personaServiceA4?wsdl + +[WS-SR-PADRON-A5] +CUIT=20267565393 +ENTRADA=entrada.txt +SALIDA=salida.txt +##URL=https://aws.afip.gov.ar/sr-padron/webservices/personaServiceA5?wsdl + +[FACTURA] +ARCHIVO=tipo,letra,numero +FORMATO=factura.csv +PAPEL=legal +ORIENTACION=portrait +DIRECTORIO=. +SUBDIRECTORIO= +LOCALE=Spanish_Argentina.1252 +FMT_CANTIDAD=0.4 +FMT_PRECIO=0.3 +CANT_POS=izq +ENTRADA=factura.txt +SALIDA=factura.pdf + +[PDF] +LOGO=plantillas/logo.png +EMPRESA=Empresa de Prueba +MEMBRETE1=Direccion de Prueba +MEMBRETE2=Capital Federal +CUIT=CUIT 30-00000000-0 +IIBB=IIBB 30-00000000-0 +IVA=IVA Responsable Inscripto +INICIO=Inicio de Actividad: 01/04/2006 +BORRADOR=HOMOLOGACION + +[MAIL] +SERVIDOR=adan.nsis.com.ar +PUERTO=25 +USUARIO=no.responder@nsis.com.ar +CLAVE=noreplyauto123 +MOTIVO=Factura Electronica Nro. NUMERO +CUERPO=Se adjunta Factura en formato PDF +HTML=Se adjunta factura electronica en formato PDF +REMITENTE=Facturador PyAfipWs + +#[BASE_DATOS] +#DRIVER=PGSQL +#SERVER=localhost +#DATABASE=pyafipws +#UID=pyafipws +#PWD=pyafipws + +[DBF] +Encabezado = encabeza.dbf +Tributo = tributo.dbf +Iva = iva.dbf +Comprobante Asociado = cbteasoc.dbf +Detalle = detalles.dbf +Permiso = permiso.dbf +Dato = dato.dbf +Datos Opcionales = opcional.dbf +Forma Pago = formapago.dbf + +#[PROXY] +#HOST=localhost +#PORT=8000 +#USER=mariano +#PASS=reingart diff --git a/rece1.py b/rece1.py index 4d5342031..cdf4b198a 100644 --- a/rece1.py +++ b/rece1.py @@ -15,7 +15,7 @@ __author__ = "Mariano Reingart (reingart@gmail.com)" __copyright__ = "Copyright (C) 2010-2015 Mariano Reingart" __license__ = "GPL 3.0" -__version__ = "1.37b" +__version__ = "1.37d" import datetime import os @@ -111,6 +111,8 @@ ('tipo_reg', 1, N), # 3: comprobante asociado ('tipo', 3, N), ('pto_vta', 4, N), ('nro', 8, N), + ('fecha', 8, N), + ('cuit', 11, N), ] OPCIONAL = [ @@ -159,7 +161,7 @@ def autorizar(ws, entrada, salida, informar_caea=False): ] dic = leer_dbf(formatos, conf_dbf) - # rearmar estructura asociando id (comparando, si se útiliza) + # rearmar estructura asociando id (comparando, si se utiliza) for encabezado in encabezados: for tributo in tributos: if tributo.get("id") == encabezado.get("id"): @@ -271,6 +273,9 @@ def autorizar(ws, entrada, salida, informar_caea=False): 'motivos_obs': ws.Obs, 'err_code': str(ws.ErrCode), 'err_msg': ws.ErrMsg, + 'cbt_desde': ws.CbtDesde, + 'cbt_hasta': ws.CbtHasta, + 'fecha_cbte': ws.FechaCbte, 'reproceso': ws.Reproceso, 'emision_tipo': ws.EmisionTipo, }) @@ -479,7 +484,7 @@ def depurar_xml(client, ruta="."): if '/prueba' in sys.argv: # generar el archivo de prueba para la próxima factura - tipo_cbte = 1 + tipo_cbte = 3 punto_vta = 4002 cbte_nro = ws.CompUltimoAutorizado(tipo_cbte, punto_vta) if not cbte_nro: @@ -515,7 +520,9 @@ def depurar_xml(client, ruta="."): tipo = 1 pto_vta = 2 nro = 1234 - ws.AgregarCmpAsoc(tipo, pto_vta, nro) + fecha = "20190601" + cuit = "20267565393" + ws.AgregarCmpAsoc(tipo, pto_vta, nro, cuit, fecha) if '--proyectos' in sys.argv: ws.AgregarOpcional(2, "1234") # identificador del proyecto diff --git a/requirements.txt b/requirements.txt index df47d9581..8a43dbb47 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,6 @@ httplib2>=0.12.0 git+https://github.com/pysimplesoap/pysimplesoap.git@stable_py3k#pysimplesoap -m2crypto>=0.18 +# m2crypto>=0.18 fpdf>=1.7.2 dbf>=0.88.019 Pillow>=2.0.0 diff --git a/src/libpyafipws.c b/src/libpyafipws.c index ea0478243..536410fa2 100644 --- a/src/libpyafipws.c +++ b/src/libpyafipws.c @@ -48,16 +48,15 @@ CONSTRUCTOR static void initialize(void) { pName = PyString_FromString(buf); if (PyList_Insert(pSysPath, 0, pName)) MessageBox(NULL, "PyList_Insert", "LibPyAfipWs Initialize", 0); - Py_XDECREF(pName); /* note that pSysPath is a Borrowed reference! */ + Py_XDECREF(pName); MessageBox(NULL, "done!", "LibPyAfipWs Initialize", 0); #else Py_Initialize(); puts(Py_GetPath()); - /* on linux, add the current directory so python can find the modules */ + PyRun_SimpleString("import sys, os"); PyRun_SimpleString("sys.path.append(os.curdir)"); - /* preliminary fix, it could not work on some cases and there could be - some security concerns. It should add the base path of the .so */ + #endif } @@ -117,18 +116,18 @@ BSTR cstr(void *pStr) { char *str; size_t len; - /* get the string val/size, remember to copy '\0' termination character */ + len = PyString_Size((PyObject*) pStr) + 1; str = PyString_AsString((PyObject*) pStr); #ifdef WIN32 - /* on windows, returns a automation string */ + ret = SysAllocStringByteLen(str, len); #else - /* allocate memory for the c string */ + ret = (char *) malloc(len); if (ret) { - /* copy the py string to c (note that it may have \0 characters */ + strncpy(ret, str, len); } #endif @@ -137,7 +136,7 @@ BSTR cstr(void *pStr) { #define FMT "%s: %s - File %s, line %d, in %s" -/* format exception: simplified PyErr_PrintEx (to not write to stdout) */ + BSTR format_ex(void) { char buf[2000]; BSTR ret; @@ -164,10 +163,7 @@ BSTR format_ex(void) { v = PyString_AsString(PyObject_Str(value)); } - /* PyTracebackObject seems defined at frameobject.h, it should be included - to avoid "error: dereferencing pointer to incomplete type" - tb is NULL if the failure is in the c-api (for example in PyImport_Import) - */ + tb1 = (PyTracebackObject *)tb; /* tb_printinternal (traceback.c) */ @@ -200,7 +196,7 @@ BSTR format_ex(void) { return ret; } -/* CreateObject: import the module, instantiate the object and return the ref */ + EXPORT void * STDCALL PYAFIPWS_CreateObject(char *module, char *name) { PyObject *pName, *pModule, *pClass, *pObject=NULL; @@ -225,14 +221,14 @@ EXPORT void * STDCALL PYAFIPWS_CreateObject(char *module, char *name) { } } -/* DestroyObject: decrement the reference to the module */ + EXPORT void STDCALL PYAFIPWS_DestroyObject(void * object) { Py_DECREF((PyObject *) object); } -/* Get: generic method to get an attribute of an object (returns a string) */ + EXPORT BSTR STDCALL PYAFIPWS_Get(void * object, char * name) { PyObject *pValue; BSTR ret=NULL; diff --git a/tests/trazamed.py b/tests/trazamed.py deleted file mode 100644 index cd79d7da3..000000000 --- a/tests/trazamed.py +++ /dev/null @@ -1,221 +0,0 @@ -#!/usr/bin/python -# -*- coding: latin-1 -*- -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by the -# Free Software Foundation; either version 3, or (at your option) any later -# version. -# -# This program is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY -# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -# for more details. - -from pyafipws.trazamed import TrazaMed -"Pruebas para Trazabilidad de Medicamentos ANMAT - PAMI - INSSJP Disp. 3683/11" - -__author__ = "Mariano Reingart " -__copyright__ = "Copyright (C) 2013 Mariano Reingart" -__license__ = "GPL 3.0" - -import unittest -import os -import time -import sys -from decimal import Decimal -import datetime - -sys.path.append("/home/reingart") # TODO: proper packaging - - -WSDL = "https://servicios.pami.org.ar/trazamed.WebService?wsdl" -CACHE = "/home/reingart/pyafipws/cache" - - -class TestTZM(unittest.TestCase): - - def setUp(self): - sys.argv.append("--trace") # TODO: use logging - self.ws = ws = TrazaMed() - - ws.Username = 'testwservice' - ws.Password = 'testwservicepsw' - - ws.Conectar(CACHE, WSDL) - - def test_basico(self): - "Prueba básica para informar un medicamento" - ws = self.ws - ws.SetParametro('nro_asociado', "9999999999999") - ws.SendMedicamentos( - usuario='pruebasws', password='pruebasws', - f_evento=datetime.datetime.now().strftime("%d/%m/%Y"), - h_evento=datetime.datetime.now().strftime("%H:%M"), - gln_origen="9999999999918", gln_destino="glnws", - n_remito="1234", n_factura="1234", - vencimiento=(datetime.datetime.now() + datetime.timedelta(30)).strftime("%d/%m/%Y"), - gtin="GTIN1", lote=datetime.datetime.now().strftime("%Y"), - numero_serial=int(time.time() * 10), - id_obra_social=None, id_evento=134, - cuit_origen="20267565393", cuit_destino="20267565393", - apellido="Reingart", nombres="Mariano", - tipo_documento="96", n_documento="26756539", sexo="M", - direccion="Saraza", numero="1234", piso="", depto="", - localidad="Hurlingham", provincia="Buenos Aires", - n_postal="1688", fecha_nacimiento="01/01/2000", - telefono="5555-5555", - ) - self.assertFalse(ws.Excepcion) - self.assertTrue(ws.Resultado) - self.assertIsInstance(ws.CodigoTransaccion, str) - self.assertEqual(len(ws.CodigoTransaccion), len("23312897")) - - def test_fraccion(self): - "Prueba básica para informar un medicamento fraccionado" - ws = self.ws - ws.SetParametro('nro_asociado', "9999999999999") - ws.SetParametro('cantidad', 5) - ws.SendMedicamentosFraccion( - usuario='pruebasws', password='pruebasws', - f_evento=datetime.datetime.now().strftime("%d/%m/%Y"), - h_evento=datetime.datetime.now().strftime("%H:%M"), - gln_origen="9999999999918", gln_destino="glnws", - n_remito="1234", n_factura="1234", - vencimiento=(datetime.datetime.now() + datetime.timedelta(30)).strftime("%d/%m/%Y"), - gtin="GTIN1", lote=datetime.datetime.now().strftime("%Y"), - numero_serial=int(time.time() * 10), - id_obra_social=None, id_evento=134, - cuit_origen="20267565393", cuit_destino="20267565393", - apellido="Reingart", nombres="Mariano", - tipo_documento="96", n_documento="26756539", sexo="M", - direccion="Saraza", numero="1234", piso="", depto="", - localidad="Hurlingham", provincia="Buenos Aires", - n_postal="1688", fecha_nacimiento="01/01/2000", - telefono="5555-5555",) - self.assertFalse(ws.Resultado) - # verificar error "Su tipo de agente no esta habilitado para fraccionar" - self.assertEqual(ws.Errores[0][:4], "3105") - - def test_dh(self): - "Prueba básica para informar un medicamento desde - hasta" - ws = self.ws - ws.SetParametro('nro_asociado', "1234") - ws.SendMedicamentosDHSerie( - usuario='pruebasws', password='pruebasws', - f_evento=datetime.datetime.now().strftime("%d/%m/%Y"), - h_evento=datetime.datetime.now().strftime("%H:%M"), - gln_origen="9999999999918", gln_destino="glnws", - n_remito="1234", n_factura="1234", - vencimiento=(datetime.datetime.now() + datetime.timedelta(30)).strftime("%d/%m/%Y"), - gtin="GTIN1", lote=datetime.datetime.now().strftime("%Y"), - desde_numero_serial=int(time.time() * 10) - 1, hasta_numero_serial=int(time.time() * 10) + 1, - id_obra_social=None, id_evento=134, - ) - self.assertTrue(ws.Resultado) - self.assertIsInstance(ws.CodigoTransaccion, str) - self.assertEqual(len(ws.CodigoTransaccion), len("23312897")) - - def test_cancela_parcial(self): - "Prueba de cancelación parcial" - ws = self.ws - ws.SendCancelacTransaccParcial( - usuario='pruebasws', password='pruebasws', - codigo_transaccion="23312897", - gtin_medicamento="GTIN1", - numero_serial="13788431940") - # por el momento ANMAT devuelve error en pruebas: - self.assertFalse(ws.Resultado) - # verificar error "3: Transaccion NO encontrada, NO se puede anular." - self.assertEqual(ws.Errores[0][:2], "3:") - - def test_consultar(self): - "Prueba para obtener las transacciones no confirmadas" - ws = self.ws - ws.GetTransaccionesNoConfirmadas( - usuario='pruebasws', password='pruebasws', - id_medicamento="GTIN1", - ) - - self.assertFalse(ws.HayError) - q = 0 - while ws.LeerTransaccion(): - q += 1 - for clave in '_id_transaccion', '_gtin', '_lote', '_numero_serial': - valor = ws.GetParametro(clave) - self.assertIsNot(valor, None) - self.assertTrue(q) - - def test_consultar_alertadas(self): - "Prueba para obtener las transacciones propias alertadas" - ws = self.ws - ws.GetEnviosPropiosAlertados( - usuario='pruebasws', password='pruebasws', - id_medicamento="GTIN1", - ) - - self.assertFalse(ws.HayError) - q = 0 - while ws.LeerTransaccion(): - q += 1 - for clave in '_id_transaccion', '_gtin', '_lote', '_numero_serial': - valor = ws.GetParametro(clave) - self.assertIsNot(valor, None) - self.assertTrue(q) - - def test_confirmar(self): - "Prueba para confirmar las transacciones no confirmadas" - ws = self.ws - # obtengo las transacciones pendientes para confirmar: - ws.GetTransaccionesNoConfirmadas( - usuario='pruebasws', password='pruebasws', - id_medicamento="GTIN1", - ) - # no debería haber error: - self.assertFalse(ws.HayError) - # - while ws.LeerTransaccion(): - _id_transaccion = ws.GetParametro('_id_transaccion') - _f_operacion = datetime.datetime.now().strftime("%d/%m/%Y") - # confirmo la transacción: - ws.SendConfirmaTransacc( - usuario='pruebasws', password='pruebasws', - p_ids_transac=_id_transaccion, - f_operacion=_f_operacion, - ) - # verifico que se haya confirmado correctamente: - self.assertTrue(ws.Resultado) - # verifico que haya devuelto id_transac_asociada: - self.assertIsInstance(ws.CodigoTransaccion, str) - self.assertEqual(len(ws.CodigoTransaccion), len("23312897")) - # salgo del ciclo (solo confirmo una transacción) - break - else: - self.fail("no se devolvieron transacciones para confirmar!") - - def test_alertar(self): - "Prueba para alertar (rechazar) una transaccion no confirmada" - ws = self.ws - # obtengo las transacciones pendientes para confirmar: - ws.GetTransaccionesNoConfirmadas( - usuario='pruebasws', password='pruebasws', - id_medicamento="GTIN1", - ) - # no debería haber error: - self.assertFalse(ws.HayError) - # - while ws.LeerTransaccion(): - _id_transaccion = ws.GetParametro('_id_transaccion') - # alerto la transacción: - ws.SendAlertaTransacc( - usuario='pruebasws', password='pruebasws', - p_ids_transac_ws=_id_transaccion, - ) - # verifico que se haya confirmado correctamente: - self.assertTrue(ws.Resultado) - # salgo del ciclo (solo alerto una transacción) - break - else: - self.fail("no se devolvieron transacciones para alertar!") - - -if __name__ == '__main__': - unittest.main() diff --git a/tests/wscdc.py b/tests/wscdc.py deleted file mode 100644 index 4c4e66ded..000000000 --- a/tests/wscdc.py +++ /dev/null @@ -1,88 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf8 -*- -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by the -# Free Software Foundation; either version 3, or (at your option) any later -# version. -# -# This program is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY -# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -# for more details. - -import pysimplesoap.client -from pyafipws.wscdc import WSCDC -from pyafipws.wsaa import WSAA -from pyafipws import utils -"Pruebas para el servicio web Constatación de Comprobantes de AFIP" - -__author__ = "Mariano Reingart " -__copyright__ = "Copyright (C) 2013 Mariano Reingart" -__license__ = "GPL 3.0" - - -import unittest -import sys -from decimal import Decimal - -sys.path.append("/home/reingart") # TODO: proper packaging - - -print(pysimplesoap.client.__version__) -#assert pysimplesoap.client.__version__ >= "1.08c" - - -WSDL = "https://wswhomo.afip.gov.ar/WSCDC/service.asmx?WSDL" -CUIT = 20267565393 -CERT = "/home/reingart/pyafipws/reingart.crt" -PRIVATEKEY = "/home/reingart/pyafipws/reingart.key" -CACERT = "/home/reingart/pyafipws/afip_root_desa_ca.crt" -CACHE = "/home/reingart/pyafipws/cache" - -# Autenticación: -wsaa = WSAA() -tra = wsaa.CreateTRA(service="wscdc") -cms = wsaa.SignTRA(tra, CERT, PRIVATEKEY) -wsaa.Conectar() -wsaa.LoginCMS(cms) - - -class TestWSCDC(unittest.TestCase): - - def setUp(self): - sys.argv.append("--trace") # TODO: use logging - self.wscdc = wslpg = WSCDC() - wslpg.LanzarExcepciones = True - wslpg.Conectar(wsdl=WSDL, cacert=None, cache=CACHE) - wslpg.Cuit = CUIT - wslpg.Token = wsaa.Token - wslpg.Sign = wsaa.Sign - - def test_constatacion_no(self): - "Prueba de Constatación de Comprobantes (facturas electrónicas)" - wscdc = self.wscdc - cbte_modo = "CAE" - cuit_emisor = "20267565393" - pto_vta = 4002 - cbte_tipo = 1 - cbte_nro = 109 - cbte_fch = "20131227" - imp_total = "121.0" - cod_autorizacion = "63523178385550" - doc_tipo_receptor = 80 - doc_nro_receptor = "30628789661" - ok = wscdc.ConstatarComprobante(cbte_modo, cuit_emisor, pto_vta, cbte_tipo, - cbte_nro, cbte_fch, imp_total, cod_autorizacion, - doc_tipo_receptor, doc_nro_receptor) - self.assertTrue(ok) - self.assertEqual(wscdc.Resultado, "R") # Rechazado - self.assertEqual(wscdc.Obs, "100: El N° de CAI/CAE/CAEA consultado no existe en las bases del organismo.") - self.assertEqual(wscdc.PuntoVenta, pto_vta) - self.assertEqual(wscdc.CbteNro, cbte_nro) - self.assertEqual(wscdc.ImpTotal, imp_total) - self.assertEqual(wscdc.CAE, cod_autorizacion) - self.assertEqual(wscdc.EmisionTipo, "CAE") - - -if __name__ == '__main__': - unittest.main() diff --git a/tests/wsfev1.py b/tests/wsfev1.py deleted file mode 100644 index ead3251b3..000000000 --- a/tests/wsfev1.py +++ /dev/null @@ -1,212 +0,0 @@ -#!/usr/bin/python -# -*- coding: latin-1 -*- -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by the -# Free Software Foundation; either version 3, or (at your option) any later -# version. -# -# This program is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY -# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -# for more details. - -from pyafipws.wsaa import WSAA -from pyafipws.wsfev1 import WSFEv1 -"Pruebas para WSFEv1 de AFIP (Factura Electrónica Mercado Interno sin detalle)" - -__author__ = "Mariano Reingart " -__copyright__ = "Copyright (C) 2010 Mariano Reingart" -__license__ = "GPL 3.0" - -import unittest -import os -import time -import sys -from decimal import Decimal -import datetime - -sys.path.append("/home/reingart") # TODO: proper packaging - - -WSDL = "https://wswhomo.afip.gov.ar/wsfev1/service.asmx?WSDL" -CUIT = 20267565393 -CERT = "/home/reingart/pyafipws/reingart.crt" -PRIVATEKEY = "/home/reingart/pyafipws/reingart.key" -CACERT = "/home/reingart/pyafipws/afip_root_desa_ca.crt" -CACHE = "/home/reingart/pyafipws/cache" - -# Autenticación: -wsaa = WSAA() -tra = wsaa.CreateTRA(service="wsfe") -cms = wsaa.SignTRA(tra, CERT, PRIVATEKEY) -wsaa.Conectar() -wsaa.LoginCMS(cms) - - -class TestFE(unittest.TestCase): - - def setUp(self): - sys.argv.append("--trace") # TODO: use logging - self.wsfev1 = wsfev1 = WSFEv1() - wsfev1.Cuit = CUIT - wsfev1.Token = wsaa.Token - wsfev1.Sign = wsaa.Sign - wsfev1.Conectar(CACHE, WSDL) - - def atest_dummy(self): - print(wsfev1.client.help("dummy")) - wsfev1.Dummy() - print("AppServerStatus", wsfev1.AppServerStatus) - print("DbServerStatus", wsfev1.DbServerStatus) - print("AuthServerStatus", wsfev1.AuthServerStatus) - - def test_autorizar_comprobante(self, tipo_cbte=1, cbte_nro=None, servicios=True): - "Prueba de autorización de un comprobante (obtención de CAE)" - wsfev1 = self.wsfev1 - - # datos generales del comprobante: - punto_vta = 4000 - if not cbte_nro: - # si no me especifícan nro de comprobante, busco el próximo - cbte_nro = wsfev1.CompUltimoAutorizado(tipo_cbte, punto_vta) - cbte_nro = int(cbte_nro) + 1 - fecha = datetime.datetime.now().strftime("%Y%m%d") - tipo_doc = 80 - nro_doc = "30000000007" # "30500010912" # CUIT BNA - cbt_desde = cbte_nro - cbt_hasta = cbt_desde - imp_total = "122.00" - imp_tot_conc = "0.00" - imp_neto = "100.00" - imp_trib = "1.00" - imp_op_ex = "0.00" - imp_iva = "21.00" - fecha_cbte = fecha - # Fechas del período del servicio facturado (solo si concepto = 1?) - if servicios: - concepto = 3 - fecha_venc_pago = fecha - fecha_serv_desde = fecha - fecha_serv_hasta = fecha - else: - concepto = 1 - fecha_venc_pago = fecha_serv_desde = fecha_serv_hasta = None - moneda_id = 'PES' - moneda_ctz = '1.000' - obs = "Observaciones Comerciales, libre" - - wsfev1.CrearFactura(concepto, tipo_doc, nro_doc, tipo_cbte, punto_vta, - cbt_desde, cbt_hasta, imp_total, imp_tot_conc, imp_neto, - imp_iva, imp_trib, imp_op_ex, fecha_cbte, fecha_venc_pago, - fecha_serv_desde, fecha_serv_hasta, # -- - moneda_id, moneda_ctz) - - # agrego un comprobante asociado (solo notas de crédito / débito) - if tipo_cbte in (2, 3): - tipo = 1 - pv = 2 - nro = 1234 - wsfev1.AgregarCmpAsoc(tipo, pv, nro) - - # agrego otros tributos: - tributo_id = 99 - desc = 'Impuesto Municipal Matanza' - base_imp = "100.00" - alic = "1.00" - importe = "1.00" - wsfev1.AgregarTributo(tributo_id, desc, base_imp, alic, importe) - - # agrego el subtotal por tasa de IVA: - iva_id = 5 # 21% - base_im = 100 - importe = 21 - wsfev1.AgregarIva(iva_id, base_imp, importe) - - # llamo al websevice para obtener el CAE: - wsfev1.CAESolicitar() - - self.assertEqual(wsfev1.Resultado, "A") # Aprobado! - self.assertIsInstance(wsfev1.CAE, str) - self.assertEqual(len(wsfev1.CAE), len("63363178822329")) - self.assertEqual(len(wsfev1.Vencimiento), len("20130907")) - wsfev1.AnalizarXml("XmlResponse") - # observación "... no se encuentra registrado en los padrones de AFIP.": - self.assertEqual(wsfev1.ObtenerTagXml('Obs', 0, 'Code'), '10017') - - def test_consulta(self): - "Prueba de obtener los datos de un comprobante autorizado" - wsfev1 = self.wsfev1 - # autorizo un comprobante: - tipo_cbte = 1 - self.test_autorizar_comprobante(tipo_cbte) - # obtengo datos para comprobar - cae = wsfev1.CAE - wsfev1.AnalizarXml("XmlRequest") - imp_total = float(wsfev1.ObtenerTagXml('ImpTotal')) - concepto = int(wsfev1.ObtenerTagXml('Concepto')) - punto_vta = wsfev1.PuntoVenta - cbte_nro = wsfev1.CbteNro - - # llamo al webservice para consultar y validar manualmente el CAE: - wsfev1.CompConsultar(tipo_cbte, punto_vta, cbte_nro) - - self.assertEqual(wsfev1.CAE, cae) - self.assertEqual(wsfev1.CbteNro, cbte_nro) - self.assertEqual(wsfev1.ImpTotal, imp_total) - - wsfev1.AnalizarXml("XmlResponse") - self.assertEqual(wsfev1.ObtenerTagXml('CodAutorizacion'), str(wsfev1.CAE)) - self.assertEqual(wsfev1.ObtenerTagXml('Concepto'), str(concepto)) - - def test_reproceso_servicios(self): - "Prueba de reproceso de un comprobante (recupero de CAE por consulta)" - wsfev1 = self.wsfev1 - # obtengo el próximo número de comprobante - tipo_cbte = 1 - punto_vta = 4000 - nro = wsfev1.CompUltimoAutorizado(tipo_cbte, punto_vta) - cbte_nro = int(nro) + 1 - # obtengo CAE - wsfev1.Reprocesar = True - self.test_autorizar_comprobante(tipo_cbte, cbte_nro) - self.assertEqual(wsfev1.Reproceso, "") - # intento reprocesar: - self.test_autorizar_comprobante(tipo_cbte, cbte_nro) - self.assertEqual(wsfev1.Reproceso, "S") - - def test_reproceso_productos(self): - "Prueba de reproceso de un comprobante (recupero de CAE por consulta)" - wsfev1 = self.wsfev1 - # obtengo el próximo número de comprobante - tipo_cbte = 1 - punto_vta = 4000 - nro = wsfev1.CompUltimoAutorizado(tipo_cbte, punto_vta) - cbte_nro = int(nro) + 1 - # obtengo CAE - wsfev1.Reprocesar = True - self.test_autorizar_comprobante(tipo_cbte, cbte_nro, servicios=False) - self.assertEqual(wsfev1.Reproceso, "") - # intento reprocesar: - self.test_autorizar_comprobante(tipo_cbte, cbte_nro, servicios=False) - self.assertEqual(wsfev1.Reproceso, "S") - - def test_reproceso_nota_debito(self): - "Prueba de reproceso de un comprobante (recupero de CAE por consulta)" - # N/D con comprobantes asociados - wsfev1 = self.wsfev1 - # obtengo el próximo número de comprobante - tipo_cbte = 2 - punto_vta = 4000 - nro = wsfev1.CompUltimoAutorizado(tipo_cbte, punto_vta) - cbte_nro = int(nro) + 1 - # obtengo CAE - wsfev1.Reprocesar = True - self.test_autorizar_comprobante(tipo_cbte, cbte_nro, servicios=False) - self.assertEqual(wsfev1.Reproceso, "") - # intento reprocesar: - self.test_autorizar_comprobante(tipo_cbte, cbte_nro, servicios=False) - self.assertEqual(wsfev1.Reproceso, "S") - - -if __name__ == '__main__': - unittest.main() diff --git a/tests/wslpg.py b/tests/wslpg.py deleted file mode 100644 index 484cb51a4..000000000 --- a/tests/wslpg.py +++ /dev/null @@ -1,544 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf8 -*- -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by the -# Free Software Foundation; either version 3, or (at your option) any later -# version. -# -# This program is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY -# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -# for more details. - -import pysimplesoap.client -from pyafipws.wslpg import WSLPG -from pyafipws.wsaa import WSAA -from pyafipws import utils -"Pruebas Liquidación Primaria Electrónica de Granos web service WSLPG (AFIP)" - -__author__ = "Mariano Reingart " -__copyright__ = "Copyright (C) 2013 Mariano Reingart" -__license__ = "GPL 3.0" - - -import unittest -import sys -from decimal import Decimal - -sys.path.append("/home/reingart") # TODO: proper packaging - - -print(pysimplesoap.client.__version__) -#assert pysimplesoap.client.__version__ >= "1.08c" - - -WSDL = "https://fwshomo.afip.gov.ar/wslpg/LpgService?wsdl" -CUIT = 20267565393 -CERT = "/home/reingart/pyafipws/reingart.crt" -PRIVATEKEY = "/home/reingart/pyafipws/reingart.key" -CACERT = "/home/reingart/pyafipws/afip_root_desa_ca.crt" -CACHE = "/home/reingart/pyafipws/cache" - -# Autenticación: -wsaa = WSAA() -tra = wsaa.CreateTRA(service="wslpg") -cms = wsaa.SignTRA(tra, CERT, PRIVATEKEY) -wsaa.Conectar() -wsaa.LoginCMS(cms) - - -class TestIssues(unittest.TestCase): - - def setUp(self): - sys.argv.append("--trace") # TODO: use logging - self.wslpg = wslpg = WSLPG() - wslpg.LanzarExcepciones = True - wslpg.Conectar(url=WSDL, cacert=None, cache=CACHE) - wslpg.Cuit = CUIT - wslpg.Token = wsaa.Token - wslpg.Sign = wsaa.Sign - - def test_liquidacion(self): - "Prueba de autorización (obtener COE) liquidación electrónica de granos" - wslpg = self.wslpg - pto_emision = 99 - ok = wslpg.ConsultarUltNroOrden(pto_emision) - self.assertTrue(ok) - ok = wslpg.CrearLiquidacion( - pto_emision=pto_emision, - nro_orden=wslpg.NroOrden + 1, - cuit_comprador=wslpg.Cuit, - nro_act_comprador=29, nro_ing_bruto_comprador=wslpg.Cuit, - cod_tipo_operacion=1, - es_liquidacion_propia='N', es_canje='N', - cod_puerto=14, des_puerto_localidad="DETALLE PUERTO", - cod_grano=31, - cuit_vendedor=23000000019, nro_ing_bruto_vendedor=23000000019, - actua_corredor="N", liquida_corredor="N", - cuit_corredor=0, - comision_corredor=0, nro_ing_bruto_corredor=0, - fecha_precio_operacion="2013-02-07", - precio_ref_tn=2000, - cod_grado_ref="G1", - cod_grado_ent="FG", - factor_ent=98, val_grado_ent=1.02, - precio_flete_tn=10, - cont_proteico=20, - alic_iva_operacion=10.5, - campania_ppal=1213, - cod_localidad_procedencia=5544, - cod_prov_procedencia=12, - datos_adicionales="DATOS ADICIONALES", - peso_neto_sin_certificado=10000, - cod_prov_procedencia_sin_certificado=1, - cod_localidad_procedencia_sin_certificado=15124, - ) - - wslpg.AgregarRetencion( - codigo_concepto="RI", - detalle_aclaratorio="DETALLE DE IVA", - base_calculo=1000, - alicuota=10.5, - ) - wslpg.AgregarRetencion( - codigo_concepto="RG", - detalle_aclaratorio="DETALLE DE GANANCIAS", - base_calculo=100, - alicuota=15, - ) - ok = wslpg.AutorizarLiquidacion() - self.assertTrue(ok) - self.assertIsInstance(wslpg.COE, str) - self.assertEqual(len(wslpg.COE), len("330100013142")) - - def test_liquidacion_contrato(self, nro_contrato=26): - "Prueba de obtener COE variante con contrato / corredor (WSLPGv1.4)" - wslpg = self.wslpg - pto_emision = 99 - ok = wslpg.ConsultarUltNroOrden(pto_emision) - self.assertTrue(ok) - nro_orden = wslpg.NroOrden + 1 - - # probar todas las actividades en caso de que devuelva error AFIP: - # 1106: La actividad seleccionada no corresponde al comprador - actividades = (40, 41, 29, 33, 31, 30, 35, 44, 47, 46, 48, 49, 51, 50, - 45, 59, 57, 52, 34, 28, 36, 55, 39, 37) - - for actid in actividades: - ok = wslpg.CrearLiquidacion( - pto_emision=pto_emision, - nro_orden=nro_orden, - nro_contrato=nro_contrato, - cuit_comprador=20400000000, - nro_act_comprador=actid, nro_ing_bruto_comprador=20400000000, - cod_tipo_operacion=1, - es_liquidacion_propia='N', es_canje='N', - cod_puerto=14, des_puerto_localidad="DETALLE PUERTO", - cod_grano=31, - cuit_vendedor=23000000019, nro_ing_bruto_vendedor=23000000019, - actua_corredor="S", liquida_corredor="S", - cuit_corredor=20267565393, - comision_corredor=1, nro_ing_bruto_corredor=20267565393, - fecha_precio_operacion="2013-02-07", - precio_ref_tn=2000, - cod_grado_ref="G1", - cod_grado_ent="FG", - factor_ent=98, val_grado_ent=1.02, - precio_flete_tn=10, - cont_proteico=20, - alic_iva_operacion=10.5, - campania_ppal=1213, - cod_localidad_procedencia=5544, - cod_prov_procedencia=12, - datos_adicionales="DATOS ADICIONALES", - peso_neto_sin_certificado=10000, - cod_prov_procedencia_sin_certificado=1, - cod_localidad_procedencia_sin_certificado=15124, - ) - - wslpg.AgregarRetencion( - codigo_concepto="RI", - detalle_aclaratorio="DETALLE DE IVA", - base_calculo=1000, - alicuota=10.5, - ) - wslpg.AgregarRetencion( - codigo_concepto="RG", - detalle_aclaratorio="DETALLE DE GANANCIAS", - base_calculo=100, - alicuota=15, - ) - ok = wslpg.AutorizarLiquidacion() - if wslpg.COE: - # print "Actividad OK", actid - break - - self.assertTrue(ok) - self.assertIsInstance(wslpg.COE, str) - self.assertEqual(len(wslpg.COE), len("330100013142")) - self.assertEqual(wslpg.NroContrato, nro_contrato) - - def test_anular(self, coe=None): - "Prueba de anulación de una liquidación electrónica de granos" - wslpg = self.wslpg - if not coe: - self.test_liquidacion() # autorizo una nueva liq. - coe = wslpg.COE - ok = wslpg.AnularLiquidacion(coe) # la anulo - self.assertTrue(ok) - self.assertEqual(wslpg.Resultado, "A") - - def test_ajuste_unificado(self): - "Prueba de ajuste unificado de una liquidación de granos (WSLPGv1.4)" - wslpg = self.wslpg - # solicito una liquidación para tener el COE autorizado a ajustar: - self.test_liquidacion() - coe = wslpg.COE - # solicito el último nro de orden para la nueva liquidación de ajuste: - pto_emision = 55 - ok = wslpg.ConsultarUltNroOrden(pto_emision) - self.assertTrue(ok) - nro_orden = wslpg.NroOrden + 1 - # creo el ajuste base y agrego los datos de certificado: - wslpg.CrearAjusteBase(pto_emision=pto_emision, - nro_orden=nro_orden, - coe_ajustado=coe, - cod_provincia=1, - cod_localidad=5, - ) - wslpg.AgregarCertificado(tipo_certificado_deposito=5, - nro_certificado_deposito=555501200729, - peso_neto=10000, - cod_localidad_procedencia=3, - cod_prov_procedencia=1, - campania=1213, - fecha_cierre='2013-01-13', - peso_neto_total_certificado=10000) - # creo el ajuste de crédito (ver documentación AFIP) - wslpg.CrearAjusteCredito( - diferencia_peso_neto=1000, diferencia_precio_operacion=100, - cod_grado="G2", val_grado=1.0, factor=100, - diferencia_precio_flete_tn=10, - datos_adicionales='AJUSTE CRED UNIF', - concepto_importe_iva_0='Alicuota Cero', - importe_ajustar_Iva_0=900, - concepto_importe_iva_105='Alicuota Diez', - importe_ajustar_Iva_105=800, - concepto_importe_iva_21='Alicuota Veintiuno', - importe_ajustar_Iva_21=700, - ) - wslpg.AgregarDeduccion(codigo_concepto="AL", - detalle_aclaratorio="Deduc Alm", - dias_almacenaje="1", - precio_pkg_diario=0.01, - comision_gastos_adm=1.0, - base_calculo=1000.0, - alicuota=10.5, ) - wslpg.AgregarRetencion(codigo_concepto="RI", - detalle_aclaratorio="Ret IVA", - base_calculo=1000, - alicuota=10.5, ) - # creo el ajuste de débito (ver documentación AFIP) - wslpg.CrearAjusteDebito( - diferencia_peso_neto=500, diferencia_precio_operacion=100, - cod_grado="G2", val_grado=1.0, factor=100, - diferencia_precio_flete_tn=0.01, - datos_adicionales='AJUSTE DEB UNIF', - concepto_importe_iva_0='Alic 0', - importe_ajustar_Iva_0=250, - concepto_importe_iva_105='Alic 10.5', - importe_ajustar_Iva_105=200, - concepto_importe_iva_21='Alicuota 21', - importe_ajustar_Iva_21=50, - ) - wslpg.AgregarDeduccion(codigo_concepto="AL", - detalle_aclaratorio="Deduc Alm", - dias_almacenaje="1", - precio_pkg_diario=0.01, - comision_gastos_adm=1.0, - base_calculo=500.0, - alicuota=10.5, ) - wslpg.AgregarRetencion(codigo_concepto="RI", - detalle_aclaratorio="Ret IVA", - base_calculo=100, - alicuota=10.5, ) - # autorizo el ajuste: - ok = wslpg.AjustarLiquidacionUnificado() - self.assertTrue(ok) - # verificar respuesta general: - self.assertIsInstance(wslpg.COE, str) - self.assertEqual(len(wslpg.COE), len("330100013133")) - coe_ajustado = coe - coe = wslpg.COE - try: - self.assertEqual(wslpg.Estado, "AC") - self.assertEqual(wslpg.Subtotal, Decimal("-734.10")) - self.assertEqual(wslpg.TotalIva105, Decimal("-77.61")) - self.assertEqual(wslpg.TotalIva21, Decimal("0")) - self.assertEqual(wslpg.TotalRetencionesGanancias, Decimal("0")) - self.assertEqual(wslpg.TotalRetencionesIVA, Decimal("-94.50")) - self.assertEqual(wslpg.TotalNetoAPagar, Decimal("-716.68")) - self.assertEqual(wslpg.TotalIvaRg2300_07, Decimal("16.89")) - self.assertEqual(wslpg.TotalPagoSegunCondicion, Decimal("-733.57")) - # verificar ajuste credito - ok = wslpg.AnalizarAjusteCredito() - self.assertTrue(ok) - self.assertEqual(wslpg.GetParametro("precio_operacion"), "1.900") - self.assertEqual(wslpg.GetParametro("total_peso_neto"), "1000") - self.assertEqual(wslpg.TotalDeduccion, Decimal("11.05")) - self.assertEqual(wslpg.TotalPagoSegunCondicion, Decimal("2780.95")) - self.assertEqual(wslpg.GetParametro("importe_iva"), "293.16") - self.assertEqual(wslpg.GetParametro("operacion_con_iva"), "3085.16") - self.assertEqual(wslpg.GetParametro("deducciones", 0, "importe_iva"), "1.05") - # verificar ajuste debito - ok = wslpg.AnalizarAjusteDebito() - self.assertTrue(ok) - self.assertEqual(wslpg.GetParametro("precio_operacion"), "2.090") - self.assertEqual(wslpg.GetParametro("total_peso_neto"), "500") - self.assertEqual(wslpg.TotalDeduccion, Decimal("5.52")) - self.assertEqual(wslpg.TotalPagoSegunCondicion, Decimal("2047.38")) - self.assertEqual(wslpg.GetParametro("importe_iva"), "215.55") - self.assertEqual(wslpg.GetParametro("operacion_con_iva"), "2268.45") - self.assertEqual(wslpg.GetParametro("retenciones", 0, "importe_retencion"), "10.50") - - finally: - # anulo el ajuste para evitar subsiguiente validación AFIP: - if coe: - self.test_anular(coe) - if coe_ajustado: - self.test_anular(coe_ajustado) # anulo también la liq. orig. - - def test_ajuste_contrato(self, nro_contrato=27): - "Prueba de ajuste por contrato de una liquidación de granos (WSLPGv1.4)" - wslpg = self.wslpg - # solicito una liquidación para tener el COE autorizado a ajustar: - self.test_liquidacion_contrato(nro_contrato) - coe_ajustado = wslpg.COE - # solicito el último nro de orden para la nueva liquidación de ajuste: - pto_emision = 55 - ok = wslpg.ConsultarUltNroOrden(pto_emision) - self.assertTrue(ok) - nro_orden = wslpg.NroOrden + 1 - wslpg.CrearAjusteBase(pto_emision=55, nro_orden=nro_orden, - nro_contrato=nro_contrato, - coe_ajustado=coe_ajustado, - nro_act_comprador=40, - cod_grano=31, - cuit_vendedor=23000000019, - cuit_comprador=20400000000, - cuit_corredor=20267565393, - precio_ref_tn=100, - cod_grado_ent="G1", - val_grado_ent=1.01, - precio_flete_tn=1000, - cod_puerto=14, - des_puerto_localidad="Desc Puerto", - cod_provincia=1, - cod_localidad=5, - ) - wslpg.CrearAjusteCredito( - concepto_importe_iva_0='Ajuste IVA al 0%', - importe_ajustar_Iva_0=100, - ) - wslpg.CrearAjusteDebito( - concepto_importe_iva_105='Ajuste IVA al 10.5%', - importe_ajustar_Iva_105=100, - ) - wslpg.AgregarDeduccion(codigo_concepto="OD", - detalle_aclaratorio="Otras Deduc", - dias_almacenaje="1", - base_calculo=100.0, - alicuota=10.5, ) - - # autorizo el ajuste: - ok = wslpg.AjustarLiquidacionContrato() - self.assertTrue(ok) - # verificar respuesta general: - coe = wslpg.COE - self.assertIsInstance(wslpg.COE, str) - self.assertEqual(len(wslpg.COE), len("330100013133")) - try: - self.assertEqual(wslpg.Estado, "AC") - self.assertEqual(wslpg.Subtotal, Decimal("-100.00")) - self.assertEqual(wslpg.TotalIva105, Decimal("0")) - self.assertEqual(wslpg.TotalIva21, Decimal("0")) - self.assertEqual(wslpg.TotalRetencionesGanancias, Decimal("0")) - self.assertEqual(wslpg.TotalRetencionesIVA, Decimal("0")) - self.assertEqual(wslpg.TotalNetoAPagar, Decimal("-110.50")) - self.assertEqual(wslpg.TotalIvaRg2300_07, Decimal("0")) - self.assertEqual(wslpg.TotalPagoSegunCondicion, Decimal("-110.50")) - # self.assertEqual(wslpg.NroContrato, nro_contrato) # no devuelto AFIP - # verificar campos globales no documentados (directamente desde el XML): - wslpg.AnalizarXml() - v = wslpg.ObtenerTagXml("totalesUnificados", "subTotalDebCred") - self.assertEqual(v, "0") - v = wslpg.ObtenerTagXml("totalesUnificados", "totalBaseDeducciones") - self.assertEqual(v, "100.0") - v = wslpg.ObtenerTagXml("totalesUnificados", "ivaDeducciones") - self.assertEqual(v, "10.50") - # verificar ajuste credito - ok = wslpg.AnalizarAjusteCredito() - self.assertTrue(ok) - self.assertEqual(wslpg.GetParametro("precio_operacion"), "0.000") - self.assertEqual(wslpg.GetParametro("total_peso_neto"), "0") - self.assertEqual(wslpg.TotalDeduccion, Decimal("0.000")) - self.assertEqual(wslpg.TotalPagoSegunCondicion, Decimal("0.000")) - self.assertEqual(float(wslpg.GetParametro("importe_iva")), 0.00) - self.assertEqual(float(wslpg.GetParametro("operacion_con_iva")), 0.00) - # verificar ajuste debito - ok = wslpg.AnalizarAjusteDebito() - self.assertTrue(ok) - self.assertEqual(float(wslpg.GetParametro("precio_operacion")), 0.00) - self.assertEqual(float(wslpg.GetParametro("total_peso_neto")), 0) - self.assertEqual(wslpg.TotalDeduccion, Decimal("110.50")) - self.assertEqual(wslpg.TotalPagoSegunCondicion, Decimal("-110.50")) - self.assertEqual(float(wslpg.GetParametro("importe_iva")), 0.00) - self.assertEqual(float(wslpg.GetParametro("operacion_con_iva")), 0.00) - self.assertEqual(float(wslpg.GetParametro("deducciones", 0, "importe_iva")), 10.50) - self.assertEqual(float(wslpg.GetParametro("deducciones", 0, "importe_deduccion")), 110.50) - - finally: - # anulo el ajuste para evitar subsiguiente validación AFIP: - # 2105: No puede relacionar la liquidacion con el contrato, porque el contrato tiene un Ajuste realizado. - # 2106: No puede ajustar el contrato, porque tiene liquidaciones relacionadas con ajuste. - # anular primero el ajuste para evitar la validación de AFIP: - # 2108: No puede anular la liquidación porque está relacionada a un contrato con ajuste vigente. - if coe: - self.test_anular(coe) - if coe_ajustado: - self.test_anular(coe_ajustado) # anulo también el COE ajustado - - def atest_ajuste_papel(self): - # deshabilitado ya que el método esta "en estudio" por parte de AFIP - wslpg = self.wslpg - wslpg.CrearAjusteBase(pto_emision=50, - nro_orden=1, - tipo_formulario=6, - nro_formulario="000101800999", - actividad=46, - cuit_comprador=99999999999, - nro_ing_bruto_comprador=99999999999, - tipo_operacion=1, - cod_grano=31, - cuit_vendedor=30000000007, - nro_ing_bruto_vendedor=30000000007, - cod_provincia=1, - cod_localidad=5) - wslpg.AgregarCertificado(tipo_certificado_deposito=5, - nro_certificado_deposito=555501200802, - peso_neto=10000, - cod_localidad_procedencia=5, - cod_prov_procedencia=1, - campania=1213, - fecha_cierre='2013-07-12') - wslpg.CrearAjusteCredito( - concepto_importe_iva_21='IVA al 21%', - importe_ajustar_Iva_21=1500, - ) - wslpg.AgregarRetencion(codigo_concepto="RI", - detalle_aclaratorio="Ret IVA", - base_calculo=1500, - alicuota=8, ) - wslpg.CrearAjusteDebito( - concepto_importe_iva_105='IVA al 0%', - importe_ajustar_Iva_105=100, - ) - - ret = wslpg.AjustarLiquidacionUnificadoPapel() - - def test_asociaciar_coe_contrato(self, nro_contrato=27): - wslpg = self.wslpg - # solicito una liquidación para tener el COE autorizado a asociar: - self.test_liquidacion() - coe = wslpg.COE - try: - # Asocio la liquidación con el contrato: - wslpg.AsociarLiquidacionAContrato(coe=coe, - nro_contrato=nro_contrato, - cuit_comprador="20400000000", - cuit_vendedor="23000000019", - cuit_corredor="20267565393", - cod_grano=31) - self.assertEqual(wslpg.Errores, []) - self.assertIsInstance(wslpg.COE, str) - self.assertEqual(len(wslpg.COE), len("330100013133")) - self.assertEqual(wslpg.Estado, "AC") - finally: - # anulo el ajuste para evitar subsiguiente validación AFIP: - # 2105: No puede relacionar la liquidacion con el contrato, porque el contrato tiene un Ajuste realizado. - # 2112: La liquidacion ya esta relacionada al contrato. - try: - self.test_anular(coe) - except BaseException: - # ignorar error de AFIP (aparentemente problema interno): - self.assertEqual(wslpg.Errores[0], "2100: El contrato ingresado no se encuentra registrado.") - pass - - def test_consultar_liquidaciones_por_contrato(self, nro_contrato=26): - wslpg = self.wslpg - # obtener las liquidaciones relacionadas al contrato: - wslpg.ConsultarLiquidacionesPorContrato( - nro_contrato=nro_contrato, - cuit_comprador="20400000000", - cuit_vendedor="23000000019", - cuit_corredor="20267565393", - cod_grano=31, - ) - self.assertEqual(wslpg.Errores, []) - # verifico COEs previamente relacionados al contrato: - for coe in sorted([330100014020, 330100014022, 330100014023, - 330100014025, 330100014028, 330100014029, - 330100014040, 330100014043, 330100014057, - 330100014061, 330100014450, 330100014454, - 330100014455, 330100014459, 330100014467, - 330100014472, 330100004664]): - self.assertIsInstance(wslpg.COE, str) - self.assertEqual(wslpg.COE, str(coe)) - self.assertEqual(wslpg.Estado, "") # por el momento no lo devuelve - # leo el próximo numero - wslpg.LeerDatosLiquidacion() - - def test_consultar_ajuste_unificado(self): - "Prueba de consulta de un ajuste unificado (WSLPGv1.4)" - wslpg = self.wslpg - # uso datos de un ajuste generado con test_ajuste_unificado: - pto_emision = 55 - nro_orden = 78 - # consulto el ajuste: - ok = wslpg.ConsultarAjuste(pto_emision, nro_orden) - self.assertTrue(ok) - # verificar respuesta general: - self.assertEqual(wslpg.COE, "330100014501") - self.assertEqual(wslpg.Estado, "AN") # anulado! - self.assertEqual(wslpg.Subtotal, Decimal("-734.10")) - self.assertEqual(wslpg.TotalIva105, Decimal("-77.61")) - self.assertEqual(wslpg.TotalIva21, Decimal("0")) - self.assertEqual(wslpg.TotalRetencionesGanancias, Decimal("0")) - self.assertEqual(wslpg.TotalRetencionesIVA, Decimal("-94.50")) - self.assertEqual(wslpg.TotalNetoAPagar, Decimal("-716.68")) - self.assertEqual(wslpg.TotalIvaRg2300_07, Decimal("16.89")) - self.assertEqual(wslpg.TotalPagoSegunCondicion, Decimal("-733.57")) - # verificar ajuste credito - ok = wslpg.AnalizarAjusteCredito() - self.assertTrue(ok) - self.assertEqual(float(wslpg.GetParametro("precio_operacion")), 1.9) - self.assertEqual(wslpg.GetParametro("total_peso_neto"), "1000") - self.assertEqual(wslpg.TotalDeduccion, Decimal("11.05")) - self.assertEqual(wslpg.TotalPagoSegunCondicion, Decimal("2780.95")) - self.assertEqual(float(wslpg.GetParametro("importe_iva")), 293.16) - self.assertEqual(float(wslpg.GetParametro("operacion_con_iva")), 3085.16) - self.assertEqual(float(wslpg.GetParametro("deducciones", 0, "importe_iva")), 1.05) - # verificar ajuste debito - ok = wslpg.AnalizarAjusteDebito() - self.assertTrue(ok) - self.assertEqual(float(wslpg.GetParametro("precio_operacion")), 2.09) - self.assertEqual(wslpg.GetParametro("total_peso_neto"), "500") - self.assertEqual(wslpg.TotalDeduccion, Decimal("5.52")) - self.assertEqual(wslpg.TotalPagoSegunCondicion, Decimal("2047.38")) - self.assertEqual(float(wslpg.GetParametro("importe_iva")), 215.55) - self.assertEqual(float(wslpg.GetParametro("operacion_con_iva")), 2268.45) - self.assertEqual(float(wslpg.GetParametro("retenciones", 0, "importe_retencion")), 10.50) - - -if __name__ == '__main__': - unittest.main() diff --git a/tests/wslpg_cert_autorizar_resp.xml b/tests/wslpg_cert_autorizar_resp.xml deleted file mode 100644 index 59c6b8859..000000000 --- a/tests/wslpg_cert_autorizar_resp.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - - -21 -2 -332000000189 -AC -2015-02-24 - -1500.00 -0.00 -0.00 -0.00 -1500.00 - - -0.00 -0.00 -0.00 -0.00 -0.00 -0.00 - - - - - - diff --git a/tests/wsmtx.py b/tests/wsmtx.py deleted file mode 100644 index 85fb8809b..000000000 --- a/tests/wsmtx.py +++ /dev/null @@ -1,246 +0,0 @@ -#!/usr/bin/python -# -*- coding: latin-1 -*- -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by the -# Free Software Foundation; either version 3, or (at your option) any later -# version. -# -# This program is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY -# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -# for more details. - -from pyafipws.wsaa import WSAA -from pyafipws.wsmtx import WSMTXCA -"Pruebas para WSMTX de AFIP (Factura Electrónica Mercado Interno con detalle)" - -__author__ = "Mariano Reingart " -__copyright__ = "Copyright (C) 2010 Mariano Reingart" -__license__ = "GPL 3.0" - -import unittest -import os -import time -import sys -from decimal import Decimal -import datetime - -sys.path.append("/home/reingart") # TODO: proper packaging - - -WSDL = "https://fwshomo.afip.gov.ar/wsmtxca/services/MTXCAService?wsdl" -CUIT = 20267565393 -CERT = "/home/reingart/pyafipws/reingart.crt" -PRIVATEKEY = "/home/reingart/pyafipws/reingart.key" -CACERT = "/home/reingart/pyafipws/afip_root_desa_ca.crt" -CACHE = "/home/reingart/pyafipws/cache" - -# Autenticación: -wsaa = WSAA() -tra = wsaa.CreateTRA(service="wsmtxca") -cms = wsaa.SignTRA(tra, CERT, PRIVATEKEY) -wsaa.Conectar() -wsaa.LoginCMS(cms) - - -class TestMTX(unittest.TestCase): - - def setUp(self): - sys.argv.append("--trace") # TODO: use logging - self.wsmtxca = wsmtxca = WSMTXCA() - wsmtxca.Cuit = CUIT - wsmtxca.Token = wsaa.Token - wsmtxca.Sign = wsaa.Sign - wsmtxca.Conectar(CACHE, WSDL) - - def atest_dummy(self): - print(wsmtxca.client.help("dummy")) - wsmtxca.Dummy() - print("AppServerStatus", wsmtxca.AppServerStatus) - print("DbServerStatus", wsmtxca.DbServerStatus) - print("AuthServerStatus", wsmtxca.AuthServerStatus) - - def test_autorizar_comprobante(self, tipo_cbte=1, cbte_nro=None, servicios=True, tributos=True): - "Prueba de autorización de un comprobante (obtención de CAE)" - wsmtxca = self.wsmtxca - - # datos generales del comprobante: - punto_vta = 4000 - if not cbte_nro: - # si no me especifícan nro de comprobante, busco el próximo - cbte_nro = wsmtxca.ConsultarUltimoComprobanteAutorizado(tipo_cbte, punto_vta) - cbte_nro = int(cbte_nro) + 1 - fecha = datetime.datetime.now().strftime("%Y-%m-%d") - tipo_doc = 80 - nro_doc = "30000000007" - cbt_desde = cbte_nro - cbt_hasta = cbt_desde - imp_tot_conc = "0.00" - imp_neto = "100.00" - if tributos: - imp_total = "123.00" - imp_trib = "2.00" - else: - imp_total = "121.00" - imp_trib = "0.00" - imp_op_ex = "0.00" - imp_subtotal = "100.00" - fecha_cbte = fecha - # Fechas del período del servicio facturado (solo si concepto = 1?) - if servicios: - concepto = 3 - fecha_venc_pago = fecha - fecha_serv_desde = fecha - fecha_serv_hasta = fecha - else: - concepto = 1 - fecha_venc_pago = fecha_serv_desde = fecha_serv_hasta = None - moneda_id = 'PES' - moneda_ctz = '1.000' - obs = "Observaciones Comerciales, libre" - - wsmtxca.CrearFactura(concepto, tipo_doc, nro_doc, tipo_cbte, punto_vta, - cbt_desde, cbt_hasta, imp_total, imp_tot_conc, imp_neto, - imp_subtotal, imp_trib, imp_op_ex, fecha_cbte, fecha_venc_pago, - fecha_serv_desde, fecha_serv_hasta, # -- - moneda_id, moneda_ctz, obs) - - # agrego un comprobante asociado (solo notas de crédito / débito) - if tipo_cbte in (2, 3): - tipo = 1 - pv = 2 - nro = 1234 - wsmtxca.AgregarCmpAsoc(tipo, pv, nro) - - if tributos: - # agrego otros tributos: - tributo_id = 99 - desc = 'Impuesto Municipal Matanza' - base_imp = "100.00" - alic = "1.00" - importe = "1.00" - wsmtxca.AgregarTributo(tributo_id, desc, base_imp, alic, importe) - - # agrego otros tributos: - tributo_id = 1 - desc = 'Impuestos Internos' - base_imp = "100.00" - alic = "1.00" - importe = "1.00" - wsmtxca.AgregarTributo(tributo_id, desc, base_imp, alic, importe) - - # agrego el subtotal por tasa de IVA: - iva_id = 5 # 21% - base_imp = "100.00" - importe = 21 - wsmtxca.AgregarIva(iva_id, base_imp, importe) - - # agrego un artículo: - u_mtx = 123456 - cod_mtx = 1234567890123 - codigo = "P0001" - ds = "Descripcion del producto P0001" - qty = 2.00 - umed = 7 - precio = 100.00 - bonif = 0.00 - iva_id = 5 - imp_iva = 42.00 - imp_subtotal = 242.00 - wsmtxca.AgregarItem(u_mtx, cod_mtx, codigo, ds, qty, umed, precio, bonif, - iva_id, imp_iva, imp_subtotal) - - # agrego bonificación general - wsmtxca.AgregarItem(None, None, None, 'bonificacion', 0, 99, 1, None, - 5, -21, -121) - - # llamo al websevice para obtener el CAE: - wsmtxca.AutorizarComprobante() - - self.assertEqual(wsmtxca.Resultado, "A") # Aprobado! - self.assertIsInstance(wsmtxca.CAE, str) - self.assertEqual(len(wsmtxca.CAE), len("63363178822329")) - self.assertEqual(len(wsmtxca.Vencimiento), len("2013-09-07")) - - cae = wsmtxca.CAE - - # llamo al webservice para consultar y validar manualmente el CAE: - wsmtxca.ConsultarComprobante(tipo_cbte, punto_vta, cbte_nro) - - self.assertEqual(wsmtxca.CAE, cae) - self.assertEqual(wsmtxca.CbteNro, cbte_nro) - self.assertEqual(wsmtxca.ImpTotal, imp_total) - - wsmtxca.AnalizarXml("XmlResponse") - self.assertEqual(wsmtxca.ObtenerTagXml('codigoAutorizacion'), str(wsmtxca.CAE)) - self.assertEqual(wsmtxca.ObtenerTagXml('codigoConcepto'), str(concepto)) - self.assertEqual(wsmtxca.ObtenerTagXml('arrayItems', 0, 'item', 'unidadesMtx'), '123456') - - def test_reproceso_servicios(self): - "Prueba de reproceso de un comprobante (recupero de CAE por consulta)" - wsmtxca = self.wsmtxca - # obtengo el próximo número de comprobante - tipo_cbte = 1 - punto_vta = 4000 - nro = wsmtxca.ConsultarUltimoComprobanteAutorizado(tipo_cbte, punto_vta) - cbte_nro = int(nro) + 1 - # obtengo CAE - wsmtxca.Reprocesar = True - self.test_autorizar_comprobante(tipo_cbte, cbte_nro) - self.assertEqual(wsmtxca.Reproceso, "") - # intento reprocesar: - self.test_autorizar_comprobante(tipo_cbte, cbte_nro) - self.assertEqual(wsmtxca.Reproceso, "S") - - def test_reproceso_productos(self): - "Prueba de reproceso de un comprobante (recupero de CAE por consulta)" - wsmtxca = self.wsmtxca - # obtengo el próximo número de comprobante - tipo_cbte = 1 - punto_vta = 4000 - nro = wsmtxca.ConsultarUltimoComprobanteAutorizado(tipo_cbte, punto_vta) - cbte_nro = int(nro) + 1 - # obtengo CAE - wsmtxca.Reprocesar = True - self.test_autorizar_comprobante(tipo_cbte, cbte_nro, servicios=False) - self.assertEqual(wsmtxca.Reproceso, "") - # intento reprocesar: - self.test_autorizar_comprobante(tipo_cbte, cbte_nro, servicios=False) - self.assertEqual(wsmtxca.Reproceso, "S") - - def test_reproceso_nota_debito(self): - "Prueba de reproceso de un comprobante (recupero de CAE por consulta)" - # N/D con comprobantes asociados - wsmtxca = self.wsmtxca - # obtengo el próximo número de comprobante - tipo_cbte = 2 - punto_vta = 4000 - nro = wsmtxca.ConsultarUltimoComprobanteAutorizado(tipo_cbte, punto_vta) - cbte_nro = int(nro) + 1 - # obtengo CAE - wsmtxca.Reprocesar = True - self.test_autorizar_comprobante(tipo_cbte, cbte_nro, servicios=False) - self.assertEqual(wsmtxca.Reproceso, "") - # intento reprocesar: - self.test_autorizar_comprobante(tipo_cbte, cbte_nro, servicios=False) - self.assertEqual(wsmtxca.Reproceso, "S") - - def test_reproceso_sin_tributos(self): - "Prueba de reproceso de un comprobante (recupero de CAE por consulta)" - wsmtxca = self.wsmtxca - # obtengo el próximo número de comprobante - tipo_cbte = 1 - punto_vta = 4000 - nro = wsmtxca.ConsultarUltimoComprobanteAutorizado(tipo_cbte, punto_vta) - cbte_nro = int(nro) + 1 - # obtengo CAE (sin tributos) - wsmtxca.Reprocesar = True - self.test_autorizar_comprobante(tipo_cbte, cbte_nro, tributos=False) - self.assertEqual(wsmtxca.Reproceso, "") - # intento reprocesar: - self.test_autorizar_comprobante(tipo_cbte, cbte_nro, tributos=False) - self.assertEqual(wsmtxca.Reproceso, "S") - - -if __name__ == '__main__': - unittest.main() diff --git a/tests/xml/wsltv_aut_test.xml b/tests/xml/wsltv_aut_test.xml deleted file mode 100644 index 6aceb089d..000000000 --- a/tests/xml/wsltv_aut_test.xml +++ /dev/null @@ -1,90 +0,0 @@ - - - - - - - -150 -2016-01-01 -2002 -pto vta al 3500 -1 -av dep acopio 2233 -31 -85523002502850 - - -30000000007 -JOCKER -IVA Responsable Inscripto -Peru 100 -2010-01-01 - - -20111111112 -CUIT PF de Prueba genérica -IVA Responsable Inscripto -Calle 1 -123456 -11223 -22 - - -CPS -BR -22 -6569866 -12 -FFAA -77888 -1 - -1 - - - - -321 -2015-12-10 - -4 -1 -900 -190.0 -171000.0 - - -1 -900 - - -14 -12.0 - - -12 -12.0 - - -2 -Ganancias -15000 -8 -1200.54 - - -171000.0 -21.0 -35910.0 -206910.0 -24.0 -1200.54 -205685.46 - -****** - - - - - diff --git a/utils.py b/utils.py index 719298543..873358398 100644 --- a/utils.py +++ b/utils.py @@ -222,9 +222,13 @@ def Conectar(self, cache=None, wsdl=None, proxy="", wrapper=None, cacert=None, t # deshabilitar verificación cert. servidor si es nulo falso vacio if not cacert: cacert = None - elif cacert is True: + elif cacert is True or cacert.lower() == 'default': # usar certificados predeterminados que vienen en la biblioteca - cacert = os.path.join(httplib2.__path__[0], 'cacerts.txt') + try: + import certifi + cacert = certifi.where() + except ImportError: + cacert = os.path.join(httplib2.__path__[0], 'cacerts.txt') elif cacert.startswith("-----BEGIN CERTIFICATE-----"): pass else: @@ -862,7 +866,7 @@ def norm(x, encoding="latin1"): def date(fmt=None, timestamp=None): "Manejo de fechas (simil PHP)" if fmt == 'U': # return timestamp - t = datetime.datetime.now() + t = datetime.datetime.utcnow() return int(time.mktime(t.timetuple())) if fmt == 'c': # return isoformat d = datetime.datetime.fromtimestamp(timestamp) @@ -883,8 +887,11 @@ def get_install_dir(): if hasattr(sys, "frozen"): # we are running as py2exe-packed executable - import pythoncom - pythoncom.frozen = 1 + try: + import pythoncom + pythoncom.frozen = 1 + except ModuleNotFoundError: + pass sys.argv[0] = sys.executable return os.path.dirname(os.path.abspath(basepath)) diff --git a/wsaa.py b/wsaa.py index 4b33db295..4639e3584 100644 --- a/wsaa.py +++ b/wsaa.py @@ -31,7 +31,7 @@ import warnings import unicodedata from pysimplesoap.client import SimpleXMLElement -from .utils import inicializar_y_capturar_excepciones, BaseWS, get_install_dir, \ +from utils import inicializar_y_capturar_excepciones, BaseWS, get_install_dir, \ exception_info, safe_console, date try: from M2Crypto import BIO, Rand, SMIME, SSL @@ -83,7 +83,7 @@ def create_tra(service=SERVICE, ttl=2400): 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() + return tra.as_xml().encode('utf-8') def sign_tra(tra, cert=CERT, privatekey=PRIVATEKEY, passphrase=""): diff --git a/wsbfev1.py b/wsbfev1.py index 34f6b90fc..6e94b280c 100644 --- a/wsbfev1.py +++ b/wsbfev1.py @@ -33,7 +33,7 @@ class WSBFEv1(BaseWS): "Interfaz para el WebService de Bono Fiscal Electrónico V1 (FE Bs. Capital)" - _public_methods_ = ['CrearFactura', 'AgregarItem', 'Authorize', 'GetCMP', + _public_methods_ = ['CrearFactura', 'AgregarItem', 'Authorize', 'GetCMP', 'AgregarOpcional', 'AgregarCmpAsoc', 'GetParamMon', 'GetParamTipoCbte', 'GetParamUMed', 'GetParamTipoIVA', 'GetParamNCM', 'GetParamZonas', 'GetParamTipoDoc', @@ -91,7 +91,7 @@ def CrearFactura(self, tipo_doc=80, nro_doc=23111111113, imp_total=0.0, imp_neto=0.0, impto_liq=0.0, imp_tot_conc=0.0, impto_liq_rni=0.00, imp_op_ex=0.00, imp_perc=0.00, imp_iibb=0.00, imp_perc_mun=0.00, imp_internos=0.00, - imp_moneda_id=0, imp_moneda_ctz=1.0, **kwargs): + imp_moneda_id=0, imp_moneda_ctz=1.0, fecha_venc_pago=None, **kwargs): "Creo un objeto factura (interna)" # Creo una factura para bonos fiscales electrónicos @@ -104,7 +104,9 @@ def CrearFactura(self, tipo_doc=80, nro_doc=23111111113, 'imp_perc': imp_perc, 'imp_perc_mun': imp_perc_mun, 'imp_iibb': imp_iibb, 'imp_internos': imp_internos, 'imp_moneda_id': imp_moneda_id, 'imp_moneda_ctz': imp_moneda_ctz, + 'fecha_venc_pago': fecha_venc_pago, 'cbtes_asoc': [], + 'opcionales': [], 'iva': [], 'detalles': [], } @@ -127,6 +129,22 @@ def AgregarItem(self, ncm, sec, ds, qty, umed, precio, bonif, iva_id, imp_total, }) return True + def AgregarCmpAsoc(self, tipo=1, pto_vta=0, nro=0, cuit=None, fecha=None, **kwarg): + "Agrego un comprobante asociado a una factura (interna)" + cmp_asoc = {'tipo': tipo, 'pto_vta': pto_vta, 'nro': nro} + if cuit is not None: + cmp_asoc['cuit'] = cuit + if fecha is not None: + cmp_asoc['fecha'] = fecha + self.factura['cbtes_asoc'].append(cmp_asoc) + return True + + def AgregarOpcional(self, opcional_id=0, valor="", **kwarg): + "Agrego un dato opcional a una factura (interna)" + op = { 'opcional_id': opcional_id, 'valor': valor } + self.factura['opcionales'].append(op) + return True + @inicializar_y_capturar_excepciones def Authorize(self, id): "Autoriza la factura cargada en memoria" @@ -150,6 +168,7 @@ def Authorize(self, id): 'Imp_perc': f['imp_perc'], 'Imp_perc_mun': f['imp_perc_mun'], 'Imp_iibb': f['imp_iibb'], 'Imp_internos': f['imp_internos'], + 'Fecha_vto_pago': f['fecha_venc_pago'], 'Items': [ {'Item': { 'Pro_codigo_ncm': d['ncm'], @@ -162,6 +181,20 @@ def Authorize(self, id): 'Imp_total': d['imp_total'], 'Iva_id': d['iva_id'], }} for d in f['detalles']], + 'CbtesAsoc': f['cbtes_asoc'] and [ + {'CbteAsoc': { + 'Tipo_cbte': cbte_asoc['tipo'], + 'Punto_vta': cbte_asoc['pto_vta'], + 'Cbte_nro': cbte_asoc['nro'], + 'Cuit': cbte_asoc.get('cuit'), + 'Fecha_cbte': cbte_asoc.get('fecha'), + }} + for cbte_asoc in f['cbtes_asoc']] or None, + 'Opcionales': [ + {'Opcional': { + 'Id': opcional['opcional_id'], + 'Valor': opcional['valor'], + }} for opcional in f['opcionales']] or None, }) result = ret['BFEAuthorizeResult'] @@ -480,7 +513,7 @@ def p_assert_eq(a, b): if "--prueba" in sys.argv: try: # Establezco los valores de la factura a autorizar: - tipo_cbte = '--nc' in sys.argv and 3 or 1 # FC/NC Expo (ver tabla de parámetros) + tipo_cbte = '--nc' in sys.argv and 3 or 201 # FC/NC Expo (ver tabla de parámetros) punto_vta = 5 tipo_doc = 80 nro_doc = 23111111113 @@ -495,6 +528,7 @@ def p_assert_eq(a, b): impto_liq_rni = imp_tot_conc = imp_op_ex = "0.00" imp_perc = imp_iibb = imp_perc_mun = imp_internos = "0.00" imp_total = "471.90" + fecha_venc_pago = fecha_cbte if "--fce" in sys.argv else None # Creo una factura (internamente, no se llama al WebService): ok = wsbfev1.CrearFactura(tipo_doc, nro_doc, @@ -502,7 +536,7 @@ def p_assert_eq(a, b): imp_total, imp_neto, impto_liq, imp_tot_conc, impto_liq_rni, imp_op_ex, imp_perc, imp_iibb, imp_perc_mun, imp_internos, - imp_moneda_id, imp_moneda_ctz) + imp_moneda_id, imp_moneda_ctz, fecha_venc_pago) # Agrego un item: ncm = '7308.10.00' @@ -528,6 +562,23 @@ def p_assert_eq(a, b): imp_total = "229.90" ok = wsbfev1.AgregarItem(ncm, sec, ds, qty, umed, precio, bonif, iva_id, imp_total) + # comprobantes asociados (notas de crédito / débito) + if True: + tipo = 91 + pto_vta = 4001 + nro = 1 + cuit = "20267565393" + # obligatorio en Factura de Crédito Electrónica MiPyMEs (FCE): + fecha_cbte = fecha_cbte if "--fce" in sys.argv else None + wsbfev1.AgregarCmpAsoc(tipo, pto_vta, nro, cuit, fecha_cbte) + + # datos de Factura de Crédito Electrónica MiPyMEs (FCE): + if '--fce' in sys.argv: + wsbfev1.AgregarOpcional(2101, "2850590940090418135201") # CBU + wsbfev1.AgregarOpcional(2102, "pyafipws") # alias + if tipo_cbte in (203, 208, 213): + wsbfev1.AgregarOpcional(22, "S") # Anulación + # id = "99000000000100" # número propio de transacción # obtengo el último ID y le adiciono 1 # (advertencia: evitar overflow y almacenar!) diff --git a/wsfev1.py b/wsfev1.py index 45e801e54..a90cf505d 100644 --- a/wsfev1.py +++ b/wsfev1.py @@ -10,30 +10,30 @@ # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License # for more details. -"""Módulo para obtener CAE/CAEA, código de autorización electrónico webservice -WSFEv1 de AFIP (Factura Electrónica Nacional - Proyecto Version 1 - 2.10) -Según RG 2485/08, RG 2757/2010, RG 2904/2010 y RG2926/10 (CAE anticipado), +"""M�dulo para obtener CAE/CAEA, c�digo de autorizaci�n electr�nico webservice +WSFEv1 de AFIP (Factura Electr�nica Nacional - Proyecto Version 1 - 2.12) +Seg�n RG 2485/08, RG 2757/2010, RG 2904/2010 y RG2926/10 (CAE anticipado), RG 3067/2011 (RS - Monotributo), RG 3571/2013 (Responsables inscriptos IVA), RG 3668/2014 (Factura A IVA F.8001), RG 3749/2015 (R.I. y exentos) -RG 4004-E Alquiler de inmuebles con destino casa habitación). +RG 4004-E Alquiler de inmuebles con destino casa habitaci�n). RG 4109-E Venta de bienes muebles registrables. -RG 4291/2018 Régimen especial de emisión y almacenamiento electrónico -Más info: http://www.sistemasagiles.com.ar/trac/wiki/ProyectoWSFEv1 +RG 4291/2018 R�gimen especial de emisi�n y almacenamiento electr�nico +RG 4367/2018 R�gimen de Facturas de Cr�dito Electr�nicas MiPyMEs Ley 27.440 +M�s info: http://www.sistemasagiles.com.ar/trac/wiki/ProyectoWSFEv1 """ __author__ = "Mariano Reingart " __copyright__ = "Copyright (C) 2010-2017 Mariano Reingart" __license__ = "GPL 3.0" -__version__ = "1.21a" +__version__ = "1.22b" import datetime -import decimal import os import sys -from .utils import verifica, inicializar_y_capturar_excepciones, BaseWS, get_install_dir +from utils import verifica, inicializar_y_capturar_excepciones, BaseWS, get_install_dir -HOMO = False # solo homologación -TYPELIB = False # usar librería de tipos (TLB) +HOMO = False # solo homologaci�n +TYPELIB = False # usar librer�a de tipos (TLB) LANZAR_EXCEPCIONES = False # valor por defecto: True #WSDL = "https://www.sistemasagiles.com.ar/simulador/wsfev1/call/soap?WSDL=None" @@ -42,7 +42,7 @@ class WSFEv1(BaseWS): - "Interfaz para el WebService de Factura Electrónica Version 1 - 2.10" + "Interfaz para el WebService de Factura Electr�nica Version 1 - 2.12" _public_methods_ = ['CrearFactura', 'AgregarIva', 'CAESolicitar', 'AgregarTributo', 'AgregarCmpAsoc', 'AgregarOpcional', 'AgregarComprador', @@ -73,7 +73,7 @@ class WSFEv1(BaseWS): 'Reprocesar', 'Reproceso', 'EmisionTipo', 'CAEA', 'CbteNro', 'CbtDesde', 'CbtHasta', 'FechaCbte', 'ImpTotal', 'ImpNeto', 'ImptoLiq', - 'ImpIVA', 'ImpOpEx', 'ImpTrib', ] + 'ImpIVA', 'ImpOpEx', 'ImpTrib', 'FchCotiz',] _reg_progid_ = "WSFEv1" _reg_clsid_ = "{CA0E604D-E3D7-493A-8880-F6CDD604185E}" @@ -87,7 +87,7 @@ class WSFEv1(BaseWS): # Variables globales para BaseWS: HOMO = HOMO WSDL = WSDL - Version = "%s %s" % (__version__, HOMO and 'Homologación' or '') + Version = "%s %s" % (__version__, HOMO and 'Homologaci�n' or '') Reprocesar = True # recuperar automaticamente CAE emitidos LanzarExcepciones = LANZAR_EXCEPCIONES factura = None @@ -102,6 +102,7 @@ def inicializar(self): self.ImpTotal = self.ImpIVA = self.ImpOpEx = self.ImpNeto = self.ImptoLiq = self.ImpTrib = None self.EmisionTipo = self.Periodo = self.Orden = "" self.FechaCbte = self.FchVigDesde = self.FchVigHasta = self.FchTopeInf = self.FchProceso = "" + self.FchCotiz = None def __analizar_errores(self, ret): "Comprueba y extrae errores si existen en la respuesta XML" @@ -127,16 +128,17 @@ def Dummy(self): self.AuthServerStatus = result.get('AuthServer') return True - # los siguientes métodos no están decorados para no limpiar propiedades + # los siguientes m�todos no est�n decorados para no limpiar propiedades def CrearFactura(self, concepto=1, tipo_doc=80, nro_doc="", tipo_cbte=1, punto_vta=0, - cbt_desde=0, cbt_hasta=0, imp_total=0.00, imp_tot_conc=0.00, imp_neto=0.00, - imp_iva=0.00, imp_trib=0.00, imp_op_ex=0.00, fecha_cbte="", fecha_venc_pago=None, - fecha_serv_desde=None, fecha_serv_hasta=None, # -- - moneda_id="PES", moneda_ctz="1.0000", caea=None, **kwargs - ): + cbt_desde=0, cbt_hasta=0, imp_total=0.00, imp_tot_conc=0.00, imp_neto=0.00, + imp_iva=0.00, imp_trib=0.00, imp_op_ex=0.00, fecha_cbte="", fecha_venc_pago=None, + fecha_serv_desde=None, fecha_serv_hasta=None, #-- + moneda_id="PES", moneda_ctz="1.0000", caea=None, fecha_hs_gen=None, **kwargs + ): + "Creo un objeto factura (interna)" - # Creo una factura electronica de exportación + # Creo una factura electronica de exportaci�n fact = {'tipo_doc': tipo_doc, 'nro_doc': nro_doc, 'tipo_cbte': tipo_cbte, 'punto_vta': punto_vta, 'cbt_desde': cbt_desde, 'cbt_hasta': cbt_hasta, @@ -146,7 +148,7 @@ def CrearFactura(self, concepto=1, tipo_doc=80, nro_doc="", tipo_cbte=1, punto_v 'fecha_cbte': fecha_cbte, 'fecha_venc_pago': fecha_venc_pago, 'moneda_id': moneda_id, 'moneda_ctz': moneda_ctz, - 'concepto': concepto, + 'concepto': concepto, 'fecha_hs_gen': fecha_hs_gen, 'cbtes_asoc': [], 'tributos': [], 'iva': [], @@ -164,17 +166,19 @@ def CrearFactura(self, concepto=1, tipo_doc=80, nro_doc="", tipo_cbte=1, punto_v return True def EstablecerCampoFactura(self, campo, valor): - if campo in self.factura or campo in ('fecha_serv_desde', 'fecha_serv_hasta', 'caea', 'fch_venc_cae'): + if campo in self.factura or campo in ('fecha_serv_desde', 'fecha_serv_hasta', 'caea', 'fch_venc_cae', 'fecha_hs_gen'): self.factura[campo] = valor return True else: return False - def AgregarCmpAsoc(self, tipo=1, pto_vta=0, nro=0, cuit=None, **kwarg): + def AgregarCmpAsoc(self, tipo=1, pto_vta=0, nro=0, cuit=None, fecha=None, **kwarg): "Agrego un comprobante asociado a una factura (interna)" cmp_asoc = {'tipo': tipo, 'pto_vta': pto_vta, 'nro': nro} if cuit is not None: cmp_asoc['cuit'] = cuit + if fecha is not None: + cmp_asoc['fecha'] = fecha self.factura['cbtes_asoc'].append(cmp_asoc) return True @@ -206,7 +210,7 @@ def AgregarComprador(self, doc_tipo=80, doc_nro=0, porcentaje=100.00, **kwarg): def ObtenerCampoFactura(self, *campos): "Obtener el valor devuelto de AFIP para un campo de factura" - # cada campo puede ser una clave string (dict) o una posición (list) + # cada campo puede ser una clave string (dict) o una posici�n (list) ret = self.factura for campo in campos: if isinstance(ret, dict) and isinstance(campo, str): @@ -238,19 +242,10 @@ def CAESolicitar(self): 'CbteDesde': f['cbt_desde'], 'CbteHasta': f['cbt_hasta'], 'CbteFch': f['fecha_cbte'], - 'ImpTotal': f['imp_total'], - 'ImpTotConc': f['imp_tot_conc'], - 'ImpNeto': f['imp_neto'], - 'ImpOpEx': f['imp_op_ex'], - 'ImpTrib': f['imp_trib'], - 'ImpIVA': f['imp_iva'], # Fechas solo se informan si Concepto in (2,3) 'FchServDesde': f.get('fecha_serv_desde'), 'FchServHasta': f.get('fecha_serv_hasta'), 'FchVtoPago': f.get('fecha_venc_pago'), - 'FchServDesde': f.get('fecha_serv_desde'), - 'FchServHasta': f.get('fecha_serv_hasta'), - 'FchVtoPago': f['fecha_venc_pago'], 'MonId': f['moneda_id'], 'MonCotiz': f['moneda_ctz'], 'CbtesAsoc': f['cbtes_asoc'] and [ @@ -259,6 +254,7 @@ def CAESolicitar(self): 'PtoVta': cbte_asoc['pto_vta'], 'Nro': cbte_asoc['nro'], 'Cuit': cbte_asoc.get('cuit'), + 'CbteFch': cbte_asoc.get('fecha'), }} for cbte_asoc in f['cbtes_asoc']] or None, 'Tributos': f['tributos'] and [ @@ -396,6 +392,7 @@ def CompConsultar(self, tipo_cbte, punto_vta, cbte_nro, reproceso=False): 'PtoVta': cbte_asoc['pto_vta'], 'Nro': cbte_asoc['nro'], 'Cuit': cbte_asoc.get('cuit'), + 'CbteFch': cbte_asoc.get('fecha') or None, }} for cbte_asoc in f['cbtes_asoc']], 'Tributos': [ @@ -457,7 +454,9 @@ def CompConsultar(self, tipo_cbte, punto_vta, cbte_nro, reproceso=False): 'tipo': cbte_asoc['CbteAsoc']['Tipo'], 'pto_vta': cbte_asoc['CbteAsoc']['PtoVta'], 'nro': cbte_asoc['CbteAsoc']['Nro'], - 'cuit': cbte_asoc['CbteAsoc'].get('Cuit')} + 'cuit': cbte_asoc['CbteAsoc'].get('Cuit'), + 'fecha': cbte_asoc['CbteAsoc'].get('CbteFch'), + } for cbte_asoc in resultget.get('CbtesAsoc', [])], 'tributos': [ { @@ -531,7 +530,7 @@ def CompConsultar(self, tipo_cbte, punto_vta, cbte_nro, reproceso=False): @inicializar_y_capturar_excepciones def CAESolicitarX(self): - "Autorizar múltiples facturas (CAE) en una única solicitud" + "Autorizar m�ltiples facturas (CAE) en una �nica solicitud" # Ver CompTotXRequest -> cantidad maxima comprobantes (250) # verificar que hay multiples facturas: if not self.facturas: @@ -557,19 +556,11 @@ def CAESolicitarX(self): 'CbteDesde': f['cbt_desde'], 'CbteHasta': f['cbt_hasta'], 'CbteFch': f['fecha_cbte'], - 'ImpTotal': f['imp_total'], - 'ImpTotConc': f['imp_tot_conc'], - 'ImpNeto': f['imp_neto'], - 'ImpOpEx': f['imp_op_ex'], - 'ImpTrib': f['imp_trib'], - 'ImpIVA': f['imp_iva'], + # Fechas solo se informan si Concepto in (2,3) 'FchServDesde': f.get('fecha_serv_desde'), 'FchServHasta': f.get('fecha_serv_hasta'), 'FchVtoPago': f.get('fecha_venc_pago'), - 'FchServDesde': f.get('fecha_serv_desde'), - 'FchServHasta': f.get('fecha_serv_hasta'), - 'FchVtoPago': f['fecha_venc_pago'], 'MonId': f['moneda_id'], 'MonCotiz': f['moneda_ctz'], 'CbtesAsoc': [ @@ -578,6 +569,7 @@ def CAESolicitarX(self): 'PtoVta': cbte_asoc['pto_vta'], 'Nro': cbte_asoc['nro'], 'Cuit': cbte_asoc.get('cuit'), + 'CbteFch': cbte_asoc.get('fecha'), }} for cbte_asoc in f['cbtes_asoc']] or None, 'Tributos': [ @@ -708,7 +700,7 @@ def CAEASolicitar(self, periodo, orden): @inicializar_y_capturar_excepciones def CAEAConsultar(self, periodo, orden): - "Método de consulta de CAEA" + "M�todo de consulta de CAEA" ret = self.client.FECAEAConsultar( Auth={'Token': self.Token, 'Sign': self.Sign, 'Cuit': self.Cuit}, Periodo=periodo, @@ -733,7 +725,7 @@ def CAEAConsultar(self, periodo, orden): @inicializar_y_capturar_excepciones def CAEARegInformativo(self): - "Método para informar comprobantes emitidos con CAEA" + "M�todo para informar comprobantes emitidos con CAEA" f = self.factura ret = self.client.FECAEARegInformativo( Auth={'Token': self.Token, 'Sign': self.Sign, 'Cuit': self.Cuit}, @@ -758,9 +750,6 @@ def CAEARegInformativo(self): 'FchServDesde': f.get('fecha_serv_desde'), 'FchServHasta': f.get('fecha_serv_hasta'), 'FchVtoPago': f.get('fecha_venc_pago'), - 'FchServDesde': f.get('fecha_serv_desde'), - 'FchServHasta': f.get('fecha_serv_hasta'), - 'FchVtoPago': f['fecha_venc_pago'], 'MonId': f['moneda_id'], 'MonCotiz': f['moneda_ctz'], 'CbtesAsoc': [ @@ -769,6 +758,7 @@ def CAEARegInformativo(self): 'PtoVta': cbte_asoc['pto_vta'], 'Nro': cbte_asoc['nro'], 'Cuit': cbte_asoc.get('cuit'), + 'CbteFch': cbte_asoc.get('fecha'), }} for cbte_asoc in f['cbtes_asoc']] if f['cbtes_asoc'] else None, @@ -790,8 +780,14 @@ def CAEARegInformativo(self): }} for iva in f['iva']] if f['iva'] else None, + 'Opcionales': [ + {'Opcional': { + 'Id': opcional['opcional_id'], + 'Valor': opcional['valor'], + }} for opcional in f['opcionales']] or None, 'CAEA': f['caea'], - } + 'CbteFchHsGen': f.get('fecha_hs_gen'), + } }] }) @@ -828,7 +824,7 @@ def CAEARegInformativo(self): @inicializar_y_capturar_excepciones def CAEASinMovimientoInformar(self, punto_vta, caea): - "Método para informar CAEA sin movimiento" + "M�todo para informar CAEA sin movimiento" ret = self.client.FECAEASinMovimientoInformar( Auth={'Token': self.Token, 'Sign': self.Sign, 'Cuit': self.Cuit}, PtoVta=punto_vta, @@ -850,7 +846,7 @@ def CAEASinMovimientoInformar(self, punto_vta, caea): @inicializar_y_capturar_excepciones def ParamGetTiposCbte(self, sep="|"): - "Recuperador de valores referenciales de códigos de Tipos de Comprobantes" + "Recuperador de valores referenciales de c�digos de Tipos de Comprobantes" ret = self.client.FEParamGetTiposCbte( Auth={'Token': self.Token, 'Sign': self.Sign, 'Cuit': self.Cuit}, ) @@ -860,7 +856,7 @@ def ParamGetTiposCbte(self, sep="|"): @inicializar_y_capturar_excepciones def ParamGetTiposConcepto(self, sep="|"): - "Recuperador de valores referenciales de códigos de Tipos de Conceptos" + "Recuperador de valores referenciales de c�digos de Tipos de Conceptos" ret = self.client.FEParamGetTiposConcepto( Auth={'Token': self.Token, 'Sign': self.Sign, 'Cuit': self.Cuit}, ) @@ -870,7 +866,7 @@ def ParamGetTiposConcepto(self, sep="|"): @inicializar_y_capturar_excepciones def ParamGetTiposDoc(self, sep="|"): - "Recuperador de valores referenciales de códigos de Tipos de Documentos" + "Recuperador de valores referenciales de c�digos de Tipos de Documentos" ret = self.client.FEParamGetTiposDoc( Auth={'Token': self.Token, 'Sign': self.Sign, 'Cuit': self.Cuit}, ) @@ -880,7 +876,7 @@ def ParamGetTiposDoc(self, sep="|"): @inicializar_y_capturar_excepciones def ParamGetTiposIva(self, sep="|"): - "Recuperador de valores referenciales de códigos de Tipos de Alícuotas" + "Recuperador de valores referenciales de c�digos de Tipos de Al�cuotas" ret = self.client.FEParamGetTiposIva( Auth={'Token': self.Token, 'Sign': self.Sign, 'Cuit': self.Cuit}, ) @@ -890,7 +886,7 @@ def ParamGetTiposIva(self, sep="|"): @inicializar_y_capturar_excepciones def ParamGetTiposMonedas(self, sep="|"): - "Recuperador de valores referenciales de códigos de Monedas" + "Recuperador de valores referenciales de c�digos de Monedas" ret = self.client.FEParamGetTiposMonedas( Auth={'Token': self.Token, 'Sign': self.Sign, 'Cuit': self.Cuit}, ) @@ -900,7 +896,7 @@ def ParamGetTiposMonedas(self, sep="|"): @inicializar_y_capturar_excepciones def ParamGetTiposOpcional(self, sep="|"): - "Recuperador de valores referenciales de códigos de Tipos de datos opcionales" + "Recuperador de valores referenciales de c�digos de Tipos de datos opcionales" ret = self.client.FEParamGetTiposOpcional( Auth={'Token': self.Token, 'Sign': self.Sign, 'Cuit': self.Cuit}, ) @@ -910,8 +906,8 @@ def ParamGetTiposOpcional(self, sep="|"): @inicializar_y_capturar_excepciones def ParamGetTiposTributos(self, sep="|"): - "Recuperador de valores referenciales de códigos de Tipos de Tributos" - "Este método permite consultar los tipos de tributos habilitados en este WS" + "Recuperador de valores referenciales de c�digos de Tipos de Tributos" + "Este m�todo permite consultar los tipos de tributos habilitados en este WS" ret = self.client.FEParamGetTiposTributos( Auth={'Token': self.Token, 'Sign': self.Sign, 'Cuit': self.Cuit}, ) @@ -921,8 +917,8 @@ def ParamGetTiposTributos(self, sep="|"): @inicializar_y_capturar_excepciones def ParamGetTiposPaises(self, sep="|"): - "Recuperador de valores referenciales de códigos de Paises" - "Este método permite consultar los tipos de tributos habilitados en este WS" + "Recuperador de valores referenciales de c�digos de Paises" + "Este m�todo permite consultar los tipos de tributos habilitados en este WS" ret = self.client.FEParamGetTiposPaises( Auth={'Token': self.Token, 'Sign': self.Sign, 'Cuit': self.Cuit}, ) @@ -932,13 +928,14 @@ def ParamGetTiposPaises(self, sep="|"): @inicializar_y_capturar_excepciones def ParamGetCotizacion(self, moneda_id): - "Recuperador de cotización de moneda" + "Recuperador de cotizaci�n de moneda" ret = self.client.FEParamGetCotizacion( Auth={'Token': self.Token, 'Sign': self.Sign, 'Cuit': self.Cuit}, MonId=moneda_id, ) self.__analizar_errores(ret) res = ret['FEParamGetCotizacionResult']['ResultGet'] + self.FchCotiz = res.get("FchCotiz") return str(res.get('MonCotiz', "")) @inicializar_y_capturar_excepciones @@ -957,7 +954,7 @@ def p_assert_eq(a, b): def main(): - "Función principal de pruebas (obtener CAE)" + "Funci�n principal de pruebas (obtener CAE)" import os import time @@ -1004,11 +1001,18 @@ def main(): if "--prueba" in sys.argv: print(wsfev1.client.help("FECAESolicitar").encode("latin1")) - tipo_cbte = 6 if '--usados' not in sys.argv else 49 + if '--usados' in sys.argv: + tipo_cbte = 49 + concepto = 1 + elif '--fce' in sys.argv: + tipo_cbte = 203 + concepto = 1 + else: + tipo_cbte = 3 + concepto = 3 if ('--rg4109' not in sys.argv) else 1 punto_vta = 4001 cbte_nro = int(wsfev1.CompUltimoAutorizado(tipo_cbte, punto_vta) or 0) fecha = datetime.datetime.now().strftime("%Y%m%d") - concepto = 2 if ('--usados' not in sys.argv and '--rg4109' not in sys.argv) else 1 tipo_doc = 80 if '--usados' not in sys.argv else 30 nro_doc = "30500010912" cbt_desde = cbte_nro + 1 @@ -1020,15 +1024,15 @@ def main(): imp_trib = "1.00" imp_op_ex = "0.00" fecha_cbte = fecha - # Fechas del período del servicio facturado y vencimiento de pago: + fecha_venc_pago = fecha_serv_desde = fecha_serv_hasta = None + # Fechas del per�odo del servicio facturado y vencimiento de pago: if concepto > 1: fecha_venc_pago = fecha - fecha_serv_desde = fecha - fecha_serv_hasta = fecha - else: - fecha_venc_pago = fecha_serv_desde = fecha_serv_hasta = None - moneda_id = 'PES' - moneda_ctz = '1.000' + fecha_serv_desde = fecha; fecha_serv_hasta = fecha + elif '--fce' in sys.argv: + # obligatorio en Factura de Cr�dito Electr�nica MiPyMEs (FCE): + fecha_venc_pago = fecha + moneda_id = 'PES'; moneda_ctz = '1.000' # inicializar prueba de multiples comprobantes por solicitud if "--multiple" in sys.argv: @@ -1051,14 +1055,17 @@ def main(): orden = 1 if datetime.datetime.today().day < 15 else 2 caea = wsfev1.CAEAConsultar(periodo, orden) wsfev1.EstablecerCampoFactura("caea", caea) + wsfev1.EstablecerCampoFactura("fecha_hs_gen", "yyyymmddhhmiss") - # comprobantes asociados (notas de crédito / débito) - if tipo_cbte in (2, 3, 7, 8, 12, 13): - tipo = 3 - pto_vta = 2 - nro = 1234 + # comprobantes asociados (notas de cr�dito / d�bito) + if tipo_cbte in (2, 3, 7, 8, 12, 13, 203, 208, 213): + tipo = 201 if tipo_cbte in (203, 208, 213) else 3 + pto_vta = 4001 + nro = 1 cuit = "20267565393" - wsfev1.AgregarCmpAsoc(tipo, pto_vta, nro, cuit) + # obligatorio en Factura de Cr�dito Electr�nica MiPyMEs (FCE): + fecha_cbte = fecha if tipo_cbte in (203, 208, 213) else None + wsfev1.AgregarCmpAsoc(tipo, pto_vta, nro, cuit, fecha_cbte) # otros tributos: tributo_id = 99 @@ -1093,22 +1100,28 @@ def main(): wsfev1.AgregarOpcional(5, "02") # IVA Excepciones wsfev1.AgregarOpcional(61, "80") # Firmante Doc Tipo wsfev1.AgregarOpcional(62, "20267565393") # Firmante Doc Nro - wsfev1.AgregarOpcional(7, "01") # Carácter del Firmante + wsfev1.AgregarOpcional(7, "01") # Car�cter del Firmante # datos opcionales para RG 4004-E Alquiler de inmuebles (Ganancias) if '--rg4004' in sys.argv: wsfev1.AgregarOpcional(17, "1") # Intermediario wsfev1.AgregarOpcional(1801, "30500010912") # CUIT Propietario - wsfev1.AgregarOpcional(1802, "BNA") # Nombr e Titular + wsfev1.AgregarOpcional(1802, "BNA") # Nombre Titular # datos de compradores RG 4109-E bienes muebles registrables (%) if '--rg4109' in sys.argv: wsfev1.AgregarComprador(80, "30500010912", 99.99) wsfev1.AgregarComprador(80, "30999032083", 0.01) - # agregar la factura creada internamente para solicitud múltiple: + # datos de Factura de Cr�dito Electr�nica MiPyMEs (FCE): + if '--fce' in sys.argv: + wsfev1.AgregarOpcional(2101, "2850590940090418135201") # CBU + wsfev1.AgregarOpcional(2102, "pyafipws") # alias + if tipo_cbte in (203, 208, 213): + wsfev1.AgregarOpcional(22, "S") # Anulaci�n + + # agregar la factura creada internamente para solicitud m�ltiple: if "--multiple" in sys.argv: wsfev1.AgregarFacturaX() - import time t0 = time.time() if not '--caea' in sys.argv: if not "--multiple" in sys.argv: @@ -1139,7 +1152,7 @@ def main(): open("xmlrequest.xml", "wb").write(wsfev1.XmlRequest) open("xmlresponse.xml", "wb").write(wsfev1.XmlResponse) - if not "--multiple" in sys.argv: + if "--multiple" not in sys.argv: wsfev1.AnalizarXml("XmlResponse") p_assert_eq(wsfev1.ObtenerTagXml('CAE'), str(wsfev1.CAE)) p_assert_eq(wsfev1.ObtenerTagXml('Concepto'), '2') @@ -1289,7 +1302,7 @@ def main(): print(error) -# busco el directorio de instalación (global para que no cambie si usan otra dll) +# busco el directorio de instalaci�n (global para que no cambie si usan otra dll) INSTALL_DIR = WSFEv1.InstallDir = get_install_dir() diff --git a/wsfexv1.py b/wsfexv1.py index 26677d10c..11f5d6ebd 100644 --- a/wsfexv1.py +++ b/wsfexv1.py @@ -39,6 +39,7 @@ class WSFEXv1(BaseWS): 'GetParamIdiomas', 'GetParamUMed', 'GetParamIncoterms', 'GetParamDstPais', 'GetParamDstCUIT', 'GetParamIdiomas', 'GetParamIncoterms', 'GetParamDstCUIT', + 'GetParamMonConCotizacion', 'GetParamPtosVenta', 'GetParamCtz', 'LoadTestXML', 'AnalizarXml', 'ObtenerTagXml', 'DebugLog', 'SetParametros', 'SetTicketAcceso', 'GetParametro', @@ -50,7 +51,7 @@ class WSFEXv1(BaseWS): 'Resultado', 'Obs', 'Reproceso', 'CAE', 'Vencimiento', 'Eventos', 'ErrCode', 'ErrMsg', 'FchVencCAE', 'Excepcion', 'LanzarExcepciones', 'Traceback', "InstallDir", - 'PuntoVenta', 'CbteNro', 'FechaCbte', 'ImpTotal'] + 'PuntoVenta', 'CbteNro', 'FechaCbte', 'ImpTotal', 'FchCotiz'] _reg_progid_ = "WSFEXv1" _reg_clsid_ = "{8106F039-D132-4F87-8AFE-ADE47B5503D4}" @@ -69,6 +70,7 @@ def inicializar(self): self.CbteNro = self.FechaCbte = self.PuntoVenta = self.ImpTotal = None self.InstallDir = INSTALL_DIR self.FchVencCAE = "" # retrocompatibilidad + self.FchCotiz = None def __analizar_errores(self, ret): "Comprueba y extrae errores si existen en la respuesta XML" @@ -90,7 +92,7 @@ def CrearFactura(self, tipo_cbte=19, punto_vta=1, cbte_nro=0, fecha_cbte=None, nombre_cliente="", cuit_pais_cliente="", domicilio_cliente="", id_impositivo="", moneda_id="PES", moneda_ctz=1.0, obs_comerciales="", obs_generales="", forma_pago="", incoterms="", - idioma_cbte=7, incoterms_ds=None, **kwargs): + idioma_cbte=7, incoterms_ds=None, fecha_pago=None, **kwargs): "Creo un objeto factura (interna)" # Creo una factura electronica de exportación @@ -114,6 +116,7 @@ def CrearFactura(self, tipo_cbte=19, punto_vta=1, cbte_nro=0, fecha_cbte=None, 'cbtes_asoc': [], 'permisos': [], 'detalles': [], + 'fecha_pago': fecha_pago, } self.factura = fact @@ -198,6 +201,7 @@ def Authorize(self, id): 'Pro_bonificacion': d['bonif'], 'Pro_total_item': d['importe'], }} for d in f['detalles']], + 'Fecha_pago': f['fecha_pago'], }) result = ret['FEXAuthorizeResult'] @@ -496,10 +500,41 @@ def GetParamCtz(self, moneda_id): res = ret['FEXGetPARAM_CtzResult'].get('FEXResultGet') if res: ctz = str(res.get('Mon_ctz', "")) + self.FchCotiz = res.get("Mon_fecha") else: ctz = '' return ctz + @inicializar_y_capturar_excepciones + def GetParamMonConCotizacion(self, fecha=None, sep="|"): + "Recupera el listado de monedas que tengan cotizacion de ADUANA" + if not fecha: + fecha = datetime.date.today().strftime("%Y%m%d") + + ret = self.client.FEXGetPARAM_MON_CON_COTIZACION( + Auth={'Token': self.Token, 'Sign': self.Sign, 'Cuit': self.Cuit}, + Fecha_CTZ=fecha) + result = ret['FEXGetPARAM_MON_CON_COTIZACIONResult'] + self.__analizar_errores(result) + + mons = [] # monedas + for u in result['FEXResultGet']: + u = u['ClsFEXResponse_Mon_CON_Cotizacion'] + try: + mon = {'id': u.get('Mon_Id'), 'ctz': u.get('Mon_ctz'), + 'fecha': u.get('Fecha_ctz')} + except Exception as e: + print(e) + if u is None: + # WTF! + mon = {'id':'', 'ctz':'','fecha':''} + mons.append(mon) + if sep: + return [("\t%(id)s\t%(ctz)s\t%(fecha)s\t" + % it).replace("\t", sep) for it in mons] + else: + return mons + @inicializar_y_capturar_excepciones def GetParamPtosVenta(self, sep="|"): "Recupera el listado de los puntos de venta para exportacion y estado" @@ -757,5 +792,8 @@ def p_assert_eq(a, b): if "--ctz" in sys.argv: print(wsfexv1.GetParamCtz('DOL')) + if "--monctz" in sys.argv: + print(wsfexv1.GetParamMonConCotizacion()) + if "--ptosventa" in sys.argv: print(wsfexv1.GetParamPtosVenta()) diff --git a/wsmtx.py b/wsmtx.py index d0b457f7b..c36e607d4 100644 --- a/wsmtx.py +++ b/wsmtx.py @@ -18,7 +18,7 @@ __author__ = "Mariano Reingart " __copyright__ = "Copyright (C) 2010-2015 Mariano Reingart" __license__ = "GPL 3.0" -__version__ = "1.13a" +__version__ = "1.14a" import datetime import decimal @@ -34,7 +34,7 @@ class WSMTXCA(BaseWS): "Interfaz para el WebService de Factura Electrónica Mercado Interno WSMTXCA" _public_methods_ = ['CrearFactura', 'EstablecerCampoFactura', 'AgregarIva', 'AgregarItem', - 'AgregarTributo', 'AgregarCmpAsoc', 'EstablecerCampoItem', + 'AgregarTributo', 'AgregarCmpAsoc', 'EstablecerCampoItem', 'AgregarOpcional', 'AutorizarComprobante', 'CAESolicitar', 'AutorizarAjusteIVA', 'SolicitarCAEA', 'ConsultarCAEA', 'ConsultarCAEAEntreFechas', 'InformarComprobanteCAEA', 'InformarAjusteIVACAEA', @@ -48,7 +48,7 @@ class WSMTXCA(BaseWS): 'ConsultarCondicionesIVA', 'ConsultarMonedas', 'ConsultarUnidadesMedida', - 'ConsultarTiposTributo', + 'ConsultarTiposTributo', 'ConsultarTiposDatosAdicionales', 'ConsultarCotizacionMoneda', 'ConsultarPuntosVentaCAE', 'ConsultarPuntosVentaCAEA', @@ -152,7 +152,7 @@ def EstablecerCampoFactura(self, campo, valor): else: return False - def AgregarCmpAsoc(self, tipo=1, pto_vta=0, nro=0, cuit=None, **kwargs): + def AgregarCmpAsoc(self, tipo=1, pto_vta=0, nro=0, cuit=None, fecha=None, **kwargs): "Agrego un comprobante asociado a una factura (interna)" cmp_asoc = { 'tipo': tipo, @@ -160,6 +160,8 @@ def AgregarCmpAsoc(self, tipo=1, pto_vta=0, nro=0, cuit=None, **kwargs): 'nro': nro} if cuit is not None: cmp_asoc['cuit'] = cuit + if fecha is not None: + cmp_asoc['fecha'] = fecha self.factura['cbtes_asoc'].append(cmp_asoc) return True @@ -215,6 +217,16 @@ def EstablecerCampoItem(self, campo, valor): else: return False + def AgregarOpcional(self, opcional_id=0, valor=None, valor2=None, + valor3=None, valor4=None, valor5=None, + valor6=None, **kwarg): + "Agrego un dato adicional a una factura (interna)" + op = { 'opcional_id': opcional_id, 'valor': valor, 'valor2': valor2, + 'valor3': valor3, 'valor4': valor4, 'valor5': valor5, + 'valor6': valor6 } + self.factura['opcionales'].append(op) + return True + @inicializar_y_capturar_excepciones def AutorizarComprobante(self): f = self.factura @@ -240,8 +252,9 @@ def AutorizarComprobante(self): 'numeroPuntoVenta': cbte_asoc['pto_vta'], 'numeroComprobante': cbte_asoc['nro'], 'cuit': cbte_asoc.get('cuit'), - }} for cbte_asoc in f['cbtes_asoc']] or None, - 'arrayOtrosTributos': f['tributos'] and [{'otroTributo': { + 'fechaEmision': cbte_asoc.get('fecha'), + }} for cbte_asoc in f['cbtes_asoc']] or None, + 'arrayOtrosTributos': f['tributos'] and [ {'otroTributo': { 'codigo': tributo['tributo_id'], 'descripcion': tributo['desc'], 'baseImponible': tributo['base_imp'], @@ -264,7 +277,16 @@ def AutorizarComprobante(self): 'importeIVA': it['imp_iva'] if int(f['tipo_cbte']) not in (6, 7, 8) and it['imp_iva'] is not None else None, 'importeItem': it['imp_subtotal'], }} for it in f['detalles']] or None, - } + 'arrayDatosAdicionales': f['opcionales'] and [{'datoAdicional': { + 't': dato['opcional_id'], + 'c1': dato.get('valor'), + 'c2': dato.get('valor2'), + 'c3': dato.get('valor3'), + 'c4': dato.get('valor4'), + 'c5': dato.get('valor5'), + 'c6': dato.get('valor6'), + }} for dato in f['opcionales']] or None, + } ret = self.client.autorizarComprobante( authRequest={'token': self.Token, 'sign': self.Sign, 'cuitRepresentada': self.Cuit}, @@ -370,7 +392,16 @@ def AutorizarAjusteIVA(self): 'importeIVA': it['imp_iva'] if int(f['tipo_cbte']) not in (6, 7, 8) and it['imp_iva'] is not None else None, 'importeItem': it['imp_subtotal'], }} for it in f['detalles']] or None, - } + 'arrayDatosAdicionales': f['opcionales'] and [{'datoAdicional': { + 't': dato['opcional_id'], + 'c1': dato.get('valor'), + 'c2': dato.get('valor2'), + 'c3': dato.get('valor3'), + 'c4': dato.get('valor4'), + 'c5': dato.get('valor5'), + 'c6': dato.get('valor6'), + }} for dato in f['opcionales']] or None, + } ret = self.client.autorizarAjusteIVA( authRequest={'token': self.Token, 'sign': self.Sign, 'cuitRepresentada': self.Cuit}, @@ -876,6 +907,15 @@ def ConsultarTiposTributo(self): return ["%(codigo)s: %(descripcion)s" % p['codigoDescripcion'] for p in ret['arrayTiposTributo']] + @inicializar_y_capturar_excepciones + def ConsultarTiposDatosAdicionales(self): + "Este método permite consultar los tipos de datos adicionales." + ret = self.client.consultarTiposDatosAdicionales( + authRequest={'token': self.Token, 'sign': self.Sign, 'cuitRepresentada': self.Cuit}, + ) + return ["%(codigo)s: %(descripcion)s" % p['codigoDescripcion'] + for p in ret['arrayTiposDatosAdicionales']] + @inicializar_y_capturar_excepciones def ConsultarCotizacionMoneda(self, moneda_id): "Este método permite consultar los tipos de comprobantes habilitados en este WS" @@ -1034,6 +1074,10 @@ def main(): # wsmtxca.AgregarItem(u_mtx, cod_mtx, codigo, ds, 1, umed, # 0, 0, iva_id, 0, 0) + # datos de Factura de Credito Electrónica MiPyMEs (FCE): + if '--fce' in sys.argv: + wsmtxca.AgregarOpcional(21, "2850590940090418135201") # CBU + print(wsmtxca.factura) if '--caea' in sys.argv: @@ -1153,6 +1197,7 @@ def main(): print(wsmtxca.ConsultarMonedas()) print(wsmtxca.ConsultarUnidadesMedida()) print(wsmtxca.ConsultarTiposTributo()) + print(wsmtxca.ConsultarTiposDatosAdicionales()) if "--cotizacion" in sys.argv: print(wsmtxca.ConsultarCotizacionMoneda('DOL'))