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 @@
+
+
+
+
+ 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'))