facturama 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. checksums.yaml +7 -0
  2. data/lib/facturama.rb +56 -0
  3. data/lib/facturama/facturama_api_multi.rb +37 -0
  4. data/lib/facturama/facturama_api_web.rb +55 -0
  5. data/lib/facturama/models/address.rb +16 -0
  6. data/lib/facturama/models/branch_office.rb +14 -0
  7. data/lib/facturama/models/cfdi.rb +80 -0
  8. data/lib/facturama/models/cfdi_relation.rb +10 -0
  9. data/lib/facturama/models/cfdi_relations.rb +12 -0
  10. data/lib/facturama/models/client.rb +19 -0
  11. data/lib/facturama/models/complement.rb +8 -0
  12. data/lib/facturama/models/connection_info.rb +36 -0
  13. data/lib/facturama/models/csd.rb +14 -0
  14. data/lib/facturama/models/exception/facturama_exception.rb +18 -0
  15. data/lib/facturama/models/exception/model_exception.rb +11 -0
  16. data/lib/facturama/models/image.rb +11 -0
  17. data/lib/facturama/models/item.rb +25 -0
  18. data/lib/facturama/models/model.rb +75 -0
  19. data/lib/facturama/models/product.rb +27 -0
  20. data/lib/facturama/models/product_tax.rb +14 -0
  21. data/lib/facturama/models/receiver.rb +15 -0
  22. data/lib/facturama/models/serie.rb +13 -0
  23. data/lib/facturama/models/tax.rb +14 -0
  24. data/lib/facturama/models/tax_entity.rb +21 -0
  25. data/lib/facturama/models/tax_stamp.rb +12 -0
  26. data/lib/facturama/services/branch_office_service.rb +16 -0
  27. data/lib/facturama/services/catalog_service.rb +42 -0
  28. data/lib/facturama/services/cfdi_multi_service.rb +164 -0
  29. data/lib/facturama/services/cfdi_service.rb +172 -0
  30. data/lib/facturama/services/client_service.rb +19 -0
  31. data/lib/facturama/services/crud_service.rb +41 -0
  32. data/lib/facturama/services/csd_service.rb +16 -0
  33. data/lib/facturama/services/http_service.rb +152 -0
  34. data/lib/facturama/services/product_service.rb +16 -0
  35. data/lib/facturama/version.rb +4 -0
  36. data/lib/samples/sample_api.rb +17 -0
  37. data/lib/samples/sample_api_multi.rb +352 -0
  38. data/lib/samples/sample_api_web.rb +454 -0
  39. metadata +94 -0
@@ -0,0 +1,19 @@
1
+ require_relative "crud_service"
2
+
3
+ module Facturama
4
+
5
+ module Services
6
+ class ClientService < CrudService
7
+
8
+ def initialize(connection_info )
9
+ super(connection_info, "client")
10
+ end
11
+
12
+
13
+
14
+
15
+ end # class ClientService
16
+
17
+ end # module Services
18
+
19
+ end # module Facturama
@@ -0,0 +1,41 @@
1
+ require_relative "http_service"
2
+
3
+ module Facturama
4
+
5
+ module Services
6
+ class CrudService < HttpService
7
+
8
+ def initialize( connection_info, uri_resource )
9
+ super( connection_info, uri_resource)
10
+ end
11
+
12
+
13
+ def retrieve(message)
14
+ HttpService.instance_method(:get).bind(self).call(message)
15
+ end
16
+
17
+
18
+ def list
19
+ HttpService.instance_method(:get).bind(self).call('')
20
+ end
21
+
22
+
23
+ def create (message, url = "")
24
+ HttpService.instance_method(:post).bind(self).call(message, url)
25
+ end
26
+
27
+
28
+ def remove(message)
29
+ HttpService.instance_method(:delete).bind(self).call(message)
30
+ end
31
+
32
+
33
+ def update(message, url = "" )
34
+ HttpService.instance_method(:put).bind(self).call(message, url)
35
+ end
36
+
37
+ end # class CrudService
38
+
39
+ end # module Services
40
+
41
+ end # module Facturama
@@ -0,0 +1,16 @@
1
+ require_relative "crud_service"
2
+
3
+ module Facturama
4
+
5
+ module Services
6
+ class CsdService < CrudService
7
+
8
+ def initialize( connection_info )
9
+ super(connection_info, "api-lite/csds")
10
+ end
11
+
12
+ end # class CsdService
13
+
14
+ end # module Services
15
+
16
+ end # module Facturama
@@ -0,0 +1,152 @@
1
+
2
+ require_relative "../models/exception/facturama_exception"
3
+ require_relative "../models/connection_info.rb"
4
+
5
+ module Facturama
6
+
7
+ module Services
8
+
9
+ class HttpService
10
+
11
+ def initialize(connection_info, uri_resource )
12
+ @connection_info = connection_info
13
+ @uri_resource = uri_resource
14
+ end
15
+
16
+
17
+
18
+ def get(args='')
19
+ res=RestClient::Request.new(
20
+ :method => :get,
21
+ :url => url(args),
22
+ :user => @connection_info.facturama_user,
23
+ :password => @connection_info.facturama_password ,
24
+ :headers => {:accept => :json,
25
+ :content_type => :json,
26
+ :user_agent => '',
27
+ }
28
+ )
29
+
30
+ executor(res)
31
+ end
32
+
33
+
34
+
35
+
36
+ def post(message, args = "")
37
+ json =message.to_json
38
+
39
+ res= RestClient::Request.new(
40
+ :method => :post,
41
+ :url => url(args),
42
+ :user => @connection_info.facturama_user,
43
+ :password => @connection_info.facturama_password,
44
+ :payload => json,
45
+ :headers => { :content_type => :json }
46
+ )
47
+
48
+ executor(res)
49
+ end
50
+
51
+
52
+
53
+
54
+
55
+ def put(message, args = "")
56
+ json =message.to_json
57
+
58
+ res= RestClient::Request.new(
59
+ :method => :put,
60
+ :url => url(args),
61
+ :user => @connection_info.facturama_user,
62
+ :password => @connection_info.facturama_password ,
63
+ :payload => json,
64
+ :headers => {:accept => :json,
65
+ :content_type => :json}
66
+ )
67
+
68
+ executor(res)
69
+ end
70
+
71
+
72
+
73
+
74
+ def delete(args = "")
75
+ res=RestClient::Request.new(
76
+ :method => :delete,
77
+ :url => url(args),
78
+ :user => @connection_info.facturama_user,
79
+ :password => @connection_info.facturama_password ,
80
+ :headers => {:accept => :json,
81
+ :content_type => :json
82
+ }
83
+ )
84
+
85
+ executor(res)
86
+ end
87
+
88
+
89
+
90
+
91
+ private
92
+
93
+ def url(args = '')
94
+ slash = ""
95
+ args = args.to_s
96
+
97
+ if args.length > 0
98
+ slash = (args =~ /^\?/)? "" : "/"
99
+ end
100
+
101
+ if(@uri_resource.length > 0)
102
+ @uri_resource = "/" + @uri_resource;
103
+ end
104
+
105
+ @connection_info.uri_base + @uri_resource + slash + args
106
+ end
107
+
108
+
109
+ # Executa la peticion y procesa la respuesta decodificando el JSON a un Hash y
110
+ # Conviriendo los errores de la API en FacturamaExceptions
111
+ def executor(request)
112
+
113
+ begin
114
+ response = request.execute
115
+
116
+ if response.code != 204 && response.body != "" # 204 = sin contenido
117
+ JSON[ response]
118
+ end
119
+
120
+
121
+ #exceptions
122
+ rescue Exception => e
123
+ case e.class.name
124
+ when "RestClient::NotFound"
125
+ raise( FacturamaException.new( "404 NotFound: Elemento no encontrado" ) )
126
+ when "RestClient::BadRequest"
127
+ json_response = JSON[e.response]
128
+ model_state = json_response['ModelState']
129
+ if model_state != nil
130
+ model_state = json_response['ModelState'].map{|k,v| [k.to_s, v]}
131
+ fact_exception = FacturamaException.new( json_response['Message'] )
132
+ end
133
+
134
+ fact_exception = FacturamaException.new( json_response['Message'], model_state )
135
+ raise( fact_exception )
136
+ else
137
+ raise( Exception.new( e.response ) )
138
+ end
139
+
140
+ end
141
+
142
+ end
143
+
144
+
145
+
146
+
147
+
148
+ end # class HttpService
149
+
150
+ end # module Services
151
+
152
+ end # module Facturama
@@ -0,0 +1,16 @@
1
+ require_relative "crud_service"
2
+
3
+ module Facturama
4
+
5
+ module Services
6
+ class ProductService < CrudService
7
+
8
+ def initialize( connection_info )
9
+ super(connection_info, "product")
10
+ end
11
+
12
+ end # class ProductService
13
+
14
+ end # module Services
15
+
16
+ end # module Facturama
@@ -0,0 +1,4 @@
1
+ # -*- encoding : utf-8 -*-
2
+ module Facturama
3
+ VERSION = '0.0.1'
4
+ end
@@ -0,0 +1,17 @@
1
+
2
+ module Facturama
3
+
4
+
5
+ module Samples
6
+
7
+ class SampleApi
8
+
9
+ def run
10
+ rasie "Este método debe ser sobrecargado, colocando las pruebas adecuadas, segun el tipo de API (Web, Multiemisor)"
11
+ end
12
+
13
+ end
14
+ end
15
+
16
+
17
+ end
@@ -0,0 +1,352 @@
1
+
2
+ require_relative "../facturama"
3
+ require_relative "sample_api.rb"
4
+ require 'json'
5
+
6
+
7
+ module Facturama
8
+
9
+
10
+ module Samples
11
+
12
+ class SampleApiMulti < SampleApi
13
+
14
+
15
+ def run
16
+ puts "====================================================================="
17
+ puts " FACTURAMA MULTIEMISOR SDK #{Facturama::VERSION}"
18
+ puts "====================================================================="
19
+
20
+
21
+ # Creación de una instacia de la API Multiemisor de Facturama, configurado con los datos del usuario de pruebas
22
+ facturama = create_api_instance
23
+
24
+
25
+ # Invocaciones a los ejemplos de uso de los servicios de Facturama API
26
+ begin
27
+
28
+ #sample_csds(facturama) # Servicio de CSD (certificados)
29
+
30
+ sample_cfdis(facturama) # Servicio de CFDI
31
+
32
+
33
+ rescue FacturamaException => ex
34
+ puts "----------- EXCEPCIONES -----------"
35
+ puts " * " + ex.message
36
+
37
+ if ex.details
38
+ ex.details.each do |item|
39
+ puts "#{item[0]}: " + item[1].join(",")
40
+ end
41
+
42
+ end
43
+
44
+ rescue Exception => ex
45
+ puts "----------- EXCEPCIONES -----------"
46
+ puts " * " + ex.to_s
47
+ end
48
+ end # run
49
+
50
+
51
+
52
+
53
+
54
+ # ---------------------------------------------------------------------------------------------------------------------
55
+ # CONFIGURACION DEL ENTORNO DE LA API
56
+ def create_api_instance
57
+ facturama_user='pruebas'
58
+ facturama_password='pruebas2011'
59
+ is_development = true # true = Modo de pruebas / sandbox, false = Modo de Producción (Timbrado real)
60
+
61
+
62
+ #Creacion de una instancia de FacturamaApiMultiemisor
63
+ Facturama::FacturamaApiMulti.new(facturama_user,facturama_password,is_development)
64
+ end
65
+
66
+
67
+
68
+ # ---------------------------------------------------------------------------------------------------------------------
69
+ # EJEMPLO DEL SERVICIO DE CSDS
70
+ def sample_csds(facturama)
71
+ puts "===== Ejemplo de CSD - Inicio ====="
72
+
73
+ sample_csds_list(facturama) # Listar todos los csd de la cuenta
74
+
75
+ sample_csds_load(facturama) # Cargar un nuevo CSD
76
+
77
+ sample_csds_update(facturama) # Obtiene un CSD mediante el RFC y lo actualiza
78
+
79
+ #sample_csds_remove(facturama) # Elmina el CSD asociado con el RFC
80
+
81
+ puts "===== Ejemplo de CSD - Fin ====="
82
+ end
83
+
84
+ # Ejemplo de obtener el listado de todos los certifiados cargados
85
+ def sample_csds_list(facturama)
86
+ puts "===== Obtener los CSD cargados - Inicio ====="
87
+
88
+ lst_csds = facturama.csds.list # Se obtiene una lista con todos los csds
89
+ lst_csds_count = lst_csds.count # Cantidad de csds
90
+
91
+ puts "Cantidad de CSDs: " + lst_csds_count.to_s
92
+
93
+ puts "===== Obtener los CSD cargados - Fin ====="
94
+ end
95
+
96
+
97
+ # Carga de un nuevo certificado, para un RFC
98
+ # * La API espera que el certificado y la llave sean enviados en base64. Por lo que las cadenas aqui expuestas,
99
+ # representan el contenido de los archivos corresponidientes, en dicho formato
100
+ def sample_csds_load(facturama)
101
+ puts "===== Cargar nuevo CSD - Inicio ====="
102
+
103
+ csd_model = Facturama::Models::Csd.new({
104
+ Rfc: "AAA010101AAA",
105
+ Certificate: "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",
106
+ PrivateKey: "MIIFDjBABgkqhkiG9w0BBQ0wMzAbBgkqhkiG9w0BBQwwDgQIAgEAAoIBAQACAggAMBQGCCqGSIb3DQMHBAgwggS9AgEAMASCBMh4EHl7aNSCaMDA1VlRoXCZ5UUmqErAbucRBAKNQXH8tz2zJ7hdZaOZx7PEfMiWh5Nh6e8G8kxY+GW4YCSbLxslkhBtfTR6v5JYv3vhgH7XzMCwJPOfX6gxeeCYZ4HTdDNAyBVCjTbJpqbo778ri33o+I4yx7zgMqA3mzVE61re6MPrGXh1YT/K9zZeEdmwvXQfPs9VnioKUhiswoMcJ3kc3FxGLrEAsjQqv/ZVOHPY3NrbcfpQUyprsCKv3rRdxkIRdMPY4eiA720mffzvDqyzeQ8xfwHTE8Xjunja4KXvW/mV7ItTH0vRXHc3HJQ0dNnyawXmbC1FiYbCVdswoYuVQmslvq3QEXUGwP3KYfxQzKatnU7nprkmsipPqPBqDrzqc6NSN/8rxIc5zTAL4bFul+CEKz9VybwdavgewEy7u3fPnKPN+y4HilNgmlbtS7seWpbIgVPA+woG2Ph5hsgREXZCjGKSRuI77/FLcI5CMrZR+FvbnaqG+gXDBTz2lWhK9pmWlVawT2pvfiHOLzYRf2YyuVbJ79D2EgbUKyp3kCQ6fddMzspPhD/pvLQizExeyIxImb/kQXs2mmtDnyFIsj4Hcn5wCcs+SDIj+FJnwRiKB6YfdzjIig/ZMfpgMpl0u69LX649uL318o+Hy3d5t3wxgSkTaJ5McKhWyh9x9vlHZhYyM6HArBNfP9cGF86M3GwAMHAiJQl9UevyKe6rlvAIDlop6l3M02m5hHUXUpPjz4j7inFXZzvSv0tFoSbEqGgno0Pa+0gWHqRwBEGLGEwHVfyEy+Of8g4+0jzo0jNPIcurA5xRh9HSRSAd3kdEhx75eeVL7lBdLjRUkbtRtg7nelSjqAX7tQZK6Awp5C/17W96+f/vtjB+Y+ZgrSUjnQDADnZCnapIrzHgE3ZanhGAtnMMl+o4aLd1+74inG4jht/GJB60raSQfYrDrM3kBs0oyfpbEk5TI8ISzRlRmejv+mqpTogJaAqhnLP7rAli3d4pRhUjbACn/xQSFKxl2OURdmnMlvlbb6pleXviJHRxzPPQ25NVdWvmCYWrDfAZYn8X1sABOdyrth38BfmAVsyyPATYFB+5cXuNIZkPz1swz3859iZWTn5JRfPEAGICu5G6w6nrgOLYM9UqOPmxofzEdiEPafLQ5orMxdSWF6+3mD2Yw/VP+B43B/oYehgfrYjBUJt2D04VU/v8XK1ZUVgX/Co0odcdcszAP+ljQ7UVhW+uxVMd2sEprwepPPjYT3HvdI6RBB94yYBWfkoCSo/jsrrRpw2DVEyvoDp/hOXKyt8Y/8UGLCxJUhhv5fEiezYnlUAmwAGjgZfzfAErx0gkQFBgNKglEA7jz0Dqc2Z92pGVGTyPtXqRsqX3IYX5WsZVUoJim0wI7+LNmKpu147ePC0G4Sf4AGoZyPWVXq2SZSPpN261pIKSoLEDeA8WIKj2U5JG2DMMYokV0bZ1TsabrwHvwsp3muLnaP8L+n2fBplbhAEE2buBXvsATixMGu57ZI5WKFLnHn4KIBrZzALCtGehfFbCsdf1nBR6aAt+BpWhhZki54fZTurgMr6zuC5hAaP4rExW+LCc3upHMW7R9DcHWaZuZIfwnVDImnAQ9UOsz+A=",
107
+ PrivateKeyPassword:"12345678a"
108
+ })
109
+
110
+ facturama.csds.create(csd_model)
111
+ puts "Se ha cargado un nuevo CSD para el Rfc: #{csd_model.Rfc}"
112
+
113
+ puts "===== Cargar nuevo CSD - Fin ====="
114
+
115
+ end
116
+
117
+
118
+
119
+ # Ejemplo de eliminacion del un CSD asociado como RFC
120
+ def sample_csds_remove(facturama)
121
+ puts "===== Eliminar CSD asociado con un RFC- Inicio ====="
122
+
123
+ facturama.csds.remove("AAA010101AAA")
124
+ puts "Se ha eliminado el CSD asociado al Rfc"
125
+
126
+ puts "===== Eliminar CSD asociado con un RFC- Inicio ====="
127
+ end
128
+
129
+
130
+ # Ejemplo de actualización de CSD asociado con un RFC
131
+ def sample_csds_update(facturama)
132
+ puts "===== Actualiza el CSD asociado con un RFC- Inicio ====="
133
+
134
+ csd = facturama.csds.retrieve("AAA010101AAA")
135
+
136
+ csd['Certificate'] = "MIIF+TCCA+GgAwIBAgIUMzAwMDEwMDAwMDAzMDAwMjM3MDEwDQYJKoZIhvcNAQELBQAwggFmMSAwHgYDVQQDDBdBLkMuIDIgZGUgcHJ1ZWJhcyg0MDk2KTEvMC0GA1UECgwmU2VydmljaW8gZGUgQWRtaW5pc3RyYWNpw7NuIFRyaWJ1dGFyaWExODA2BgNVBAsML0FkbWluaXN0cmFjacOzbiBkZSBTZWd1cmlkYWQgZGUgbGEgSW5mb3JtYWNpw7NuMSkwJwYJKoZIhvcNAQkBFhphc2lzbmV0QHBydWViYXMuc2F0LmdvYi5teDEmMCQGA1UECQwdQXYuIEhpZGFsZ28gNzcsIENvbC4gR3VlcnJlcm8xDjAMBgNVBBEMBTA2MzAwMQswCQYDVQQGEwJNWDEZMBcGA1UECAwQRGlzdHJpdG8gRmVkZXJhbDESMBAGA1UEBwwJQ295b2Fjw6FuMRUwEwYDVQQtEwxTQVQ5NzA3MDFOTjMxITAfBgkqhkiG9w0BCQIMElJlc3BvbnNhYmxlOiBBQ0RNQTAeFw0xNzA1MTgwMzU0NTFaFw0yMTA1MTgwMzU0NTFaMIHlMSkwJwYDVQQDEyBBQ0NFTSBTRVJWSUNJT1MgRU1QUkVTQVJJQUxFUyBTQzEpMCcGA1UEKRMgQUNDRU0gU0VSVklDSU9TIEVNUFJFU0FSSUFMRVMgU0MxKTAnBgNVBAoTIEFDQ0VNIFNFUlZJQ0lPUyBFTVBSRVNBUklBTEVTIFNDMSUwIwYDVQQtExxBQUEwMTAxMDFBQUEgLyBIRUdUNzYxMDAzNFMyMR4wHAYDVQQFExUgLyBIRUdUNzYxMDAzTURGUk5OMDkxGzAZBgNVBAsUEkNTRDEwX0FBQTAxMDEwMUFBQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAIiV+76Q7p9i5Bj4G1YuYuPtf/cO/dyNX19o6y57CiKcgGYEqPqb88cJ/IPPyFPIFtBdxYJmqikxMwxDHTIsolI0GMvqEO1BsokcDOL4UfMZt7NmYaH1P8Nj/fO5xn0b1qSnSfQHGdPLMgXsLPhaR69HREsVEIowEMM5ucoNArSNzel4XJU8X/dnoumZvaOyCdvEC076NzB3UJA53ZD1xvvPEedUfAfj2eaUCQJYPnToyf7TAOGzzGkX5EGcjxC3YfcXGwG2eNdbSbxSiADPx6QACgslCu1vzmCzwQAmfeHWQvirpZccJyD/8shd7z7fv5A/G0g3aDloM5AXwA3nDVsCAwEAAaMdMBswDAYDVR0TAQH/BAIwADALBgNVHQ8EBAMCBsAwDQYJKoZIhvcNAQELBQADggIBAJepSmoMRmasH1IyLe68oM6+Qpm/kXjwQw8ALMkhHTI3XmxjUVqpJ6k9zZQfwyTLc2UZIo8jdO4WH3bcRBDcYOkciW3KxhKAbLgJPHAieVOyObXViET0ktLL6xeDHnf5Au4LOi0m01E8IPFbxYKb+RU1xpOKqJuRHH5dfRBg4HV8y+OTa5lVZil+sAhwdyXFsPf9FqN1SNn9EuKjYc9+lkRiGcHPNb1ZAtDsaQdGzoAbR+Z6m9FdZB/XU+Huls+ePdkw1t2/37AJZkYqr3wVNKrrpQkax9DrnFT8E+7xKXLcbpw3YOYBoENj2+NuMn29sn3U97wKlpyn/GeMwbkCmOGBAMtK9O6+wRrcEmu9Js68asHd5JQSzA39BRAUjb/9aefmWTb6DNm22IUUSSOT9MK5yWGncdWxKrNtMvx7OyYlYV2/qG4p/rMlj6nZcIpwONhyLUwxr74kO0Jo3zus81t9S/J91jumiwyNVqJZ77vmAy6lQnr8Og9/YaIzDH5L/byJQJquDKEmLvuya4sQ2iJj+p282RNpBscO/iyma8T+bZjG2CFYUTwGtOEZ2aLqApJ4cCBW7Ip569B+g7mgG8fdij6E1OlJ8Y3+ovBMak8LtnFVxsfthdWOK+AU2hWGU88rfZkLJ0RJn8oAq/6ri0iJNCKym/mc9g0JpNw+asMM"
137
+ csd['PrivateKey'] = "MIIFDjBABgkqhkiG9w0BBQ0wMzAbBgkqhkiG9w0BBQwwDgQIAgEAAoIBAQACAggAMBQGCCqGSIb3DQMHBAgwggS9AgEAMASCBMh4EHl7aNSCaMDA1VlRoXCZ5UUmqErAbucRBAKNQXH8tz2zJ7hdZaOZx7PEfMiWh5Nh6e8G8kxY+GW4YCSbLxslkhBtfTR6v5JYv3vhgH7XzMCwJPOfX6gxeeCYZ4HTdDNAyBVCjTbJpqbo778ri33o+I4yx7zgMqA3mzVE61re6MPrGXh1YT/K9zZeEdmwvXQfPs9VnioKUhiswoMcJ3kc3FxGLrEAsjQqv/ZVOHPY3NrbcfpQUyprsCKv3rRdxkIRdMPY4eiA720mffzvDqyzeQ8xfwHTE8Xjunja4KXvW/mV7ItTH0vRXHc3HJQ0dNnyawXmbC1FiYbCVdswoYuVQmslvq3QEXUGwP3KYfxQzKatnU7nprkmsipPqPBqDrzqc6NSN/8rxIc5zTAL4bFul+CEKz9VybwdavgewEy7u3fPnKPN+y4HilNgmlbtS7seWpbIgVPA+woG2Ph5hsgREXZCjGKSRuI77/FLcI5CMrZR+FvbnaqG+gXDBTz2lWhK9pmWlVawT2pvfiHOLzYRf2YyuVbJ79D2EgbUKyp3kCQ6fddMzspPhD/pvLQizExeyIxImb/kQXs2mmtDnyFIsj4Hcn5wCcs+SDIj+FJnwRiKB6YfdzjIig/ZMfpgMpl0u69LX649uL318o+Hy3d5t3wxgSkTaJ5McKhWyh9x9vlHZhYyM6HArBNfP9cGF86M3GwAMHAiJQl9UevyKe6rlvAIDlop6l3M02m5hHUXUpPjz4j7inFXZzvSv0tFoSbEqGgno0Pa+0gWHqRwBEGLGEwHVfyEy+Of8g4+0jzo0jNPIcurA5xRh9HSRSAd3kdEhx75eeVL7lBdLjRUkbtRtg7nelSjqAX7tQZK6Awp5C/17W96+f/vtjB+Y+ZgrSUjnQDADnZCnapIrzHgE3ZanhGAtnMMl+o4aLd1+74inG4jht/GJB60raSQfYrDrM3kBs0oyfpbEk5TI8ISzRlRmejv+mqpTogJaAqhnLP7rAli3d4pRhUjbACn/xQSFKxl2OURdmnMlvlbb6pleXviJHRxzPPQ25NVdWvmCYWrDfAZYn8X1sABOdyrth38BfmAVsyyPATYFB+5cXuNIZkPz1swz3859iZWTn5JRfPEAGICu5G6w6nrgOLYM9UqOPmxofzEdiEPafLQ5orMxdSWF6+3mD2Yw/VP+B43B/oYehgfrYjBUJt2D04VU/v8XK1ZUVgX/Co0odcdcszAP+ljQ7UVhW+uxVMd2sEprwepPPjYT3HvdI6RBB94yYBWfkoCSo/jsrrRpw2DVEyvoDp/hOXKyt8Y/8UGLCxJUhhv5fEiezYnlUAmwAGjgZfzfAErx0gkQFBgNKglEA7jz0Dqc2Z92pGVGTyPtXqRsqX3IYX5WsZVUoJim0wI7+LNmKpu147ePC0G4Sf4AGoZyPWVXq2SZSPpN261pIKSoLEDeA8WIKj2U5JG2DMMYokV0bZ1TsabrwHvwsp3muLnaP8L+n2fBplbhAEE2buBXvsATixMGu57ZI5WKFLnHn4KIBrZzALCtGehfFbCsdf1nBR6aAt+BpWhhZki54fZTurgMr6zuC5hAaP4rExW+LCc3upHMW7R9DcHWaZuZIfwnVDImnAQ9UOsz+A="
138
+ csd['PrivateKeyPassword'] = "12345678a"
139
+
140
+ facturama.csds.update(csd, "AAA010101AAA")
141
+
142
+ puts "===== Actualiza CSD asociado con un RFC - Inicio ====="
143
+ end
144
+
145
+
146
+
147
+ # ---------------------------------------------------------------------------------------------------------------------
148
+ # EJEMPLO DEL SERVICIO DE CFDI
149
+ def sample_cfdis( facturama )
150
+ puts "===== Ejemplo de CFDI - Inicio ====="
151
+
152
+ # Creacion del cfdi en su forma general (sin items / productos) asociados
153
+ cfdi = sample_cfdis_create(facturama)
154
+
155
+ cfdi_uuid = cfdi['Complement']['TaxStamp']['Uuid']
156
+ puts "Se creó exitosamente el cfdi con el folio fiscal: " + cfdi_uuid
157
+
158
+ # Descarga de los arvhivos PDF y XML del cfdi recien creado
159
+ file_path = "factura" + cfdi_uuid
160
+ facturama.cfdis.save_pdf( file_path + ".pdf", cfdi['Id'])
161
+ facturama.cfdis.save_xml( file_path + ".xml", cfdi['Id'])
162
+
163
+ # Envio del cfdi por correo
164
+ if facturama.cfdis.send_by_mail(cfdi['Id'], "chucho@facturama.mx", "Factura del servicio" )
165
+ puts "Se envió por correo exitosamente el cfdi con el folio fiscal: " + cfdi_uuid
166
+ end
167
+
168
+ # Se elmina el cfdi recien creado
169
+ facturama.cfdis.remove(cfdi['Id'])
170
+ puts "Se elminó exitosamente el cfdi con el folio fiscal: " + cfdi_uuid
171
+
172
+
173
+ # Consulta de cfdi por palabra clave o Rfc
174
+ lst_by_rfc = facturama.cfdis.list_by_rfc("ESO1202108R2")
175
+ lst_by_keyword = facturama.cfdis.list_by_keyword("Software")
176
+
177
+ puts "Se obtiene la lista de facturas por RFC: #{lst_by_rfc.length}"
178
+ puts "Se obtiene la lista de facturas por KEYWORD: #{lst_by_keyword.length}"
179
+
180
+
181
+ puts "===== Ejemplo de CFDI - Fin ====="
182
+ end
183
+
184
+
185
+
186
+ # Ejemplo de creación de un CFDI
187
+ # En la API MULTIMEMISOR Se puede cambiar el campo de "Issuer" y emitir una factura,
188
+ # siempre y cuando previamente cargáramos los Certificados CSD
189
+ # * Nota:
190
+ # La creación de una factura muy similar a la forma de crearla en API WEB.
191
+ # Y se diferencía en que aquí se tiene la sección de:
192
+ # "Issuer": {
193
+ # "FiscalRegime": "601",
194
+ # "Rfc": "AAA010101AAA",
195
+ # "Name": "EXPRESION EN SOFTWARE"
196
+ # },
197
+ def sample_cfdis_create(facturama)
198
+
199
+ cfdi = {
200
+ "Serie": "R",
201
+ "Currency": "MXN",
202
+ "ExpeditionPlace": "78116",
203
+ "PaymentConditions": "CREDITO A SIETE DIAS",
204
+ "Folio": "100",
205
+ "CfdiType": "I",
206
+ "PaymentForm": "03",
207
+ "PaymentMethod": "PUE",
208
+
209
+
210
+ "Issuer": {
211
+ "FiscalRegime": "601",
212
+ "Rfc": "AAA010101AAA",
213
+ "Name": "EXPRESION EN SOFTWARE"
214
+ },
215
+
216
+
217
+ "Receiver": {
218
+ "Rfc": "XAXX010101000",
219
+ "Name": "RADIAL SOFTWARE SOLUTIONS",
220
+ "CfdiUse": "P01"
221
+ },
222
+ "Items": [
223
+ {
224
+ "ProductCode": "10101504",
225
+ "IdentificationNumber": "EDL",
226
+ "Description": "Estudios de viabilidad",
227
+ "Unit": "NO APLICA",
228
+ "UnitCode": "MTS",
229
+ "UnitPrice": 50.0,
230
+ "Quantity": 2.0,
231
+ "Subtotal": 100.0,
232
+ "Taxes": [{
233
+ "Total": 16.0,
234
+ "Name": "IVA",
235
+ "Base": 100.0,
236
+ "Rate": 0.16,
237
+ "IsRetention": false
238
+ }],
239
+ "Total": 116.0
240
+ },
241
+ {
242
+ "ProductCode": "10101504",
243
+ "IdentificationNumber": "001",
244
+ "Description": "SERVICIO DE COLOCACION",
245
+ "Unit": "NO APLICA",
246
+ "UnitCode": "E49",
247
+ "UnitPrice": 100.0,
248
+ "Quantity": 15.0,
249
+ "Subtotal": 1500.0,
250
+ "Discount": 0.0,
251
+ "Taxes": [{
252
+ "Total": 240.0,
253
+ "Name": "IVA",
254
+ "Base": 1500.0,
255
+ "Rate": 0.16,
256
+ "IsRetention": false
257
+ }],
258
+ "Total": 1740.0
259
+ }
260
+ ]
261
+ }
262
+
263
+
264
+ # Creación del CFDI mediante la API, para su creación
265
+ facturama.cfdis.create(cfdi)
266
+ end
267
+
268
+
269
+ def add_items_to_cfdi(facturama, currency, cfdi)
270
+
271
+ lst_products = facturama.products.list
272
+ lst_products_size = lst_products.length
273
+
274
+ n_items = (rand( lst_products.length ) % 10) + 1
275
+
276
+ decimals = currency['Decimals'].to_i
277
+
278
+ # Lista de conceptos para el CFDI
279
+ lst_items = Array.new
280
+
281
+
282
+ n_begin = lst_products_size - 1 - n_items
283
+
284
+ for index in n_begin..lst_products_size
285
+
286
+ product = lst_products[index] # Un producto cualquiera
287
+
288
+ if( product.nil? )
289
+ break
290
+
291
+ end
292
+
293
+ quantity = rand(5) + 1 # una cantidad aleatoria de elementos de este producto
294
+
295
+ discount = product['Price'] % ( product['Price']) == 0 ? 1 : rand( (product['Price'].to_i ) )
296
+ subtotal = ( product['Price'] * quantity).round(decimals) # Redondeo de acuerdo a la moneda
297
+
298
+
299
+
300
+ item = Facturama::Models::Item.new({
301
+ ProductCode: product['CodeProdServ'],
302
+ UnitCode: product['UnitCode'],
303
+ Unit: product['Unit'],
304
+ Description: product['Description'],
305
+ IdentificationNumber: product['IdentificationNumber'],
306
+ Quantity: quantity,
307
+ Discount: discount.round(decimals),
308
+ UnitPrice: product['Price'].round(decimals),
309
+ Subtotal: subtotal,
310
+ Taxes: nil
311
+
312
+ })
313
+
314
+
315
+ base_amount = (subtotal - discount).round(decimals)
316
+ taxes = product['Taxes'].map { |t|
317
+ Facturama::Models::Tax.new(
318
+ Name: t['Name'],
319
+ IsQuota: t['IsQuota'],
320
+ IsRetention: t['IsRetention'],
321
+ Rate: t['Rate'].to_f.round(decimals),
322
+ Base: base_amount,
323
+ Total: (base_amount * t['Rate'].to_f).round(decimals)
324
+ )
325
+ }
326
+
327
+ retentions_amount = 0
328
+ transfers_amount = 0
329
+ if taxes.length > 0
330
+ item.Taxes = taxes
331
+ # Calculo del monto total del concepto, tomando en cuenta los impuestos
332
+ retentions_amount = item.Taxes.select { |tax| tax.IsRetention }.sum(&:Total)
333
+ transfers_amount = item.Taxes.select { |tax| ! tax.IsRetention }.sum(&:Total)
334
+
335
+ end
336
+
337
+ item.Total = (item.Subtotal - item.Discount + transfers_amount - retentions_amount).round(decimals)
338
+
339
+ lst_items.push(item)
340
+
341
+ end
342
+
343
+ cfdi.Items = lst_items
344
+
345
+ end
346
+
347
+
348
+
349
+ end # class SampleApiWeb
350
+ end
351
+
352
+ end