sat_mx 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 24d8984a200bf9e2d95a2fcb90e10097f54a4ed496e418c3880df1848dd5c302
4
- data.tar.gz: 01a221e8d1bcab95919dc4990677ca9c63bc775e1e1f03281c5c07be2653805c
3
+ metadata.gz: a6f23a9065a4facc1ea12963a7a27587c8055775cbb729e3070c0a5a2a8144cd
4
+ data.tar.gz: c043c861ed94a4cb049b3664f37b6a3b13e502917e28dd7b6f07608f15212485
5
5
  SHA512:
6
- metadata.gz: 3e1905940a68f281204884f848fea09806e6cc7ce242389aba92154e95777d5563b0b400dd731832982f824c626f4353bf204138f75a5ca26b1f4db063adabbe
7
- data.tar.gz: 3535c79dafbb37cc18fc887a3d266a7ce9f1474e4f0d5add1ae6cfae652c88e64dd72b7fc73867c4d9a82b941c5179a2f447943df63170ee8ad01a0c69f1e18c
6
+ metadata.gz: 7a4ca5be6421a81e686b084207556f320fc4294d0d9731d9febd8e4d303a49bf10dcbfde496d86d2a8eef72fb172d69b7948ae2812e79f9c950d6749e038c8d8
7
+ data.tar.gz: 6dfb7a7364d74260cb0eb5e804b19b2e113e3fbfd8a5a61baee671e22cb15a109618decae7c7851b941e07901e61b93ec9ba12ede074b736e08fe355717bc227
data/README.md CHANGED
@@ -33,14 +33,15 @@ raise "Auth failed" unless result.success?
33
33
 
34
34
  token = result.value
35
35
 
36
- # 2. Request download
37
- result = SatMx.download_request(
36
+ # 2. Request download of received CFDI
37
+ result = SatMx.download_request_received(
38
38
  start_date: Time.new(2024, 1, 1),
39
39
  end_date: Time.new(2024, 1, 31),
40
40
  request_type: :cfdi,
41
- issuing_rfc: "ABC010101ABC",
42
- recipient_rfcs: ["XYZ020202XYZ"],
41
+ recipient_rfc: "ABC010101ABC",
43
42
  requester_rfc: "ABC010101ABC",
43
+ issuing_rfc: "XYZ020202XYZ",
44
+ document_status: "Vigente",
44
45
  access_token: token
45
46
  )
46
47
  raise "Request failed" unless result.success?
@@ -84,7 +85,7 @@ end
84
85
  | `SatMx.configure` | Configure certificate and private key |
85
86
  | `SatMx.configuration` | Get current configuration |
86
87
  | `SatMx.authenticate` | Get access token |
87
- | `SatMx.download_request` | Request CFDI download |
88
+ | `SatMx.download_request_received` | Request download of received CFDI |
88
89
  | `SatMx.verify_request` | Check request status |
89
90
  | `SatMx.download_petition` | Download package |
90
91
 
data/lib/sat_mx/client.rb CHANGED
@@ -33,6 +33,17 @@ module SatMx
33
33
  )
34
34
  end
35
35
 
36
+ def download_request_received(payload)
37
+ HTTPX.post(
38
+ "https://cfdidescargamasivasolicitud.clouda.sat.gob.mx/SolicitaDescargaService.svc",
39
+ headers: {
40
+ "SOAPAction" => "http://DescargaMasivaTerceros.sat.gob.mx/ISolicitaDescargaService/SolicitaDescargaRecibidos"
41
+ }.merge(authorization)
42
+ .merge(HEADERS),
43
+ body: sign(payload)
44
+ )
45
+ end
46
+
36
47
  def verify_request(payload)
37
48
  HTTPX.post(
38
49
  "https://cfdidescargamasivasolicitud.clouda.sat.gob.mx/VerificaSolicitudDescargaService.svc",
@@ -0,0 +1,110 @@
1
+ require_relative "body"
2
+
3
+ module SatMx
4
+ # @api private
5
+ class DownloadRequestReceived
6
+ def self.call(start_date:,
7
+ end_date:,
8
+ request_type:,
9
+ recipient_rfc:,
10
+ access_token:,
11
+ certificate:,
12
+ private_key:,
13
+ requester_rfc: nil,
14
+ issuing_rfc: nil,
15
+ complement: nil,
16
+ document_status: nil)
17
+ new(
18
+ download_request_received_body: DownloadRequestReceivedBody.new(
19
+ start_date:,
20
+ end_date:,
21
+ request_type:,
22
+ recipient_rfc:,
23
+ requester_rfc:,
24
+ certificate:,
25
+ issuing_rfc:,
26
+ complement:,
27
+ document_status:
28
+ ),
29
+ client: Client.new(private_key:, access_token:)
30
+ ).call
31
+ end
32
+
33
+ def initialize(download_request_received_body:, client:)
34
+ @download_request_received_body = download_request_received_body
35
+ @client = client
36
+ end
37
+
38
+ def call
39
+ if (validation_error = validate_complement!)
40
+ return validation_error
41
+ end
42
+
43
+ if (validation_error = validate_document_status!)
44
+ return validation_error
45
+ end
46
+
47
+ response = client.download_request_received(download_request_received_body.generate)
48
+
49
+ case response.status
50
+ when 200..299
51
+ check_body_status response.xml
52
+ when 400..599
53
+ Result.new(success?: false, value: nil, xml: response.xml)
54
+ else
55
+ Error
56
+ end
57
+ end
58
+
59
+ private
60
+
61
+ attr_reader :download_request_received_body, :client
62
+
63
+ def validate_complement!
64
+ complement = download_request_received_body.complement
65
+ return unless complement
66
+ return if DownloadRequestReceivedBody::COMPLEMENT_TYPES.include?(complement)
67
+
68
+ Result.new(
69
+ success?: false,
70
+ value: {cod_estatus: "INVALID_COMPLEMENT", mensaje: "Invalid complement type: #{complement}"},
71
+ xml: nil
72
+ )
73
+ end
74
+
75
+ def validate_document_status!
76
+ document_status = download_request_received_body.document_status
77
+ return unless document_status
78
+ return if DownloadRequestReceivedBody::DOCUMENT_STATUS.include?(document_status)
79
+
80
+ Result.new(
81
+ success?: false,
82
+ value: {cod_estatus: "INVALID_DOCUMENT_STATUS", mensaje: "Invalid document status: #{document_status}"},
83
+ xml: nil
84
+ )
85
+ end
86
+
87
+ def check_body_status(xml)
88
+ download_result_tag = xml.xpath(
89
+ "//xmlns:SolicitaDescargaRecibidosResult",
90
+ xmlns: Body::NAMESPACE
91
+ )
92
+ if download_result_tag.attr("CodEstatus").value == "5000"
93
+ Result.new(
94
+ success?: true,
95
+ value: download_result_tag.attr("IdSolicitud").value,
96
+ xml:
97
+ )
98
+ else
99
+ Result.new(
100
+ success?: false,
101
+ value: {
102
+ cod_estatus: download_result_tag.attr("CodEstatus").value,
103
+ mensaje: download_result_tag.attr("Mensaje").value
104
+ },
105
+ xml:
106
+ )
107
+ end
108
+ end
109
+ end
110
+ end
@@ -0,0 +1,112 @@
1
+ require "time"
2
+
3
+ module SatMx
4
+ # @api private
5
+ class DownloadRequestReceivedBody
6
+ include Body
7
+
8
+ REQUEST_TYPES = {
9
+ cfdi: "CFDI",
10
+ metadata: "Metadata"
11
+ }.freeze
12
+
13
+ DOCUMENT_STATUS = %w[
14
+ Todos
15
+ Cancelado
16
+ Vigente
17
+ ].freeze
18
+
19
+ COMPLEMENT_TYPES = %w[
20
+ acreditamientoieps10
21
+ aerolineas
22
+ certificadodedestruccion
23
+ cfdiregistrofiscal
24
+ comercioexterior10
25
+ comercioexterior11
26
+ comprobante
27
+ consumodecombustibles
28
+ consumodecombustibles11
29
+ detallista
30
+ divisas
31
+ donat11
32
+ ecc11
33
+ ecc12
34
+ gastoshidrocarbonos10
35
+ iedu
36
+ implocal
37
+ ine11
38
+ ingresoshidrocarbonos
39
+ leyendasfisc
40
+ nomina11
41
+ nomina12
42
+ notariospublicos
43
+ obrasarteantiguedades
44
+ pagoenespecie
45
+ pagos10
46
+ pfic
47
+ renovacionysustitucionvehiculos
48
+ servicioparcialconstruccion
49
+ spei
50
+ terceros11
51
+ turistapasajeroextranjero
52
+ valesdedespensa
53
+ vehiculousado
54
+ ventavehiculos11
55
+ ].freeze
56
+
57
+ def initialize(
58
+ start_date:,
59
+ end_date:,
60
+ request_type:,
61
+ recipient_rfc:,
62
+ requester_rfc:,
63
+ certificate:,
64
+ issuing_rfc: nil,
65
+ complement: nil,
66
+ document_status: nil
67
+ )
68
+ @start_date = start_date
69
+ @end_date = end_date
70
+ @request_type = request_type
71
+ @recipient_rfc = recipient_rfc
72
+ @requester_rfc = requester_rfc
73
+ @certificate = certificate
74
+ @issuing_rfc = issuing_rfc
75
+ @complement = complement
76
+ @document_status = document_status
77
+ end
78
+
79
+ def generate
80
+ envelope do |xml|
81
+ xml[Body::DES].SolicitaDescargaRecibidos do
82
+ attrs = {
83
+ "FechaInicial" => start_date,
84
+ "FechaFinal" => end_date,
85
+ "TipoSolicitud" => request_type,
86
+ "RfcReceptor" => recipient_rfc
87
+ }
88
+ attrs["RfcEmisor"] = issuing_rfc if issuing_rfc
89
+ attrs["RfcSolicitante"] = requester_rfc if requester_rfc
90
+ attrs["Complemento"] = complement if complement
91
+ attrs["EstadoComprobante"] = document_status if document_status
92
+
93
+ xml[Body::DES].solicitud(attrs) do
94
+ signature(xml)
95
+ end
96
+ end
97
+ end
98
+ end
99
+
100
+ attr_reader :complement, :document_status
101
+
102
+ private
103
+
104
+ attr_reader :recipient_rfc, :requester_rfc, :certificate, :issuing_rfc
105
+
106
+ def start_date = @start_date.strftime("%Y-%m-%dT%H:%M:%S")
107
+
108
+ def end_date = @end_date.strftime("%Y-%m-%dT%H:%M:%S")
109
+
110
+ def request_type = REQUEST_TYPES.fetch(@request_type)
111
+ end
112
+ end
@@ -1,3 +1,3 @@
1
1
  module SatMx
2
- VERSION = "0.3.0"
2
+ VERSION = "0.4.0"
3
3
  end
data/lib/sat_mx.rb CHANGED
@@ -7,6 +7,8 @@ module SatMx
7
7
  autoload(:Authentication, "sat_mx/authentication")
8
8
  autoload(:DownloadRequest, "sat_mx/download_request")
9
9
  autoload(:DownloadRequestBody, "sat_mx/download_request_body")
10
+ autoload(:DownloadRequestReceived, "sat_mx/download_request_received")
11
+ autoload(:DownloadRequestReceivedBody, "sat_mx/download_request_received_body")
10
12
  autoload(:VerifyRequest, "sat_mx/verify_request")
11
13
  autoload(:VerifyRequestBody, "sat_mx/verify_request_body")
12
14
  autoload(:DownloadPetition, "sat_mx/download_petition")
@@ -115,6 +117,71 @@ module SatMx
115
117
  )
116
118
  end
117
119
 
120
+ # Requests a download of received CFDI documents from the SAT web service SolicitaDescargaService with SOAPAction SolicitaDescargaRecibidos
121
+ #
122
+ # result = SatMx.download_request_received(
123
+ # start_date: Time.new(2024, 1, 1),
124
+ # end_date: Time.new(2024, 1, 31),
125
+ # request_type: :cfdi,
126
+ # recipient_rfc: "ABC010101ABC",
127
+ # requester_rfc: "ABC010101ABC",
128
+ # access_token: "your_access_token"
129
+ # )
130
+ # if result.success?
131
+ # puts "Request ID: #{result.value}"
132
+ # else
133
+ # puts "Request failed: #{result.value}"
134
+ # end
135
+ #
136
+ # @param start_date [Time] Start date for the search range
137
+ # @param end_date [Time] End date for the search range
138
+ # @param request_type [Symbol] Type of request (:cfdi or :metadata)
139
+ # @param access_token [String] Authentication token from SatMx.authenticate
140
+ # @param recipient_rfc [String] RFC of the receiver (required)
141
+ # @param requester_rfc [String, nil] RFC of the requester (optional)
142
+ # @param issuing_rfc [String, nil] RFC of the issuer (optional)
143
+ # @param complement [String, nil] Complement type to filter (e.g., "nomina12", "pagos10")
144
+ # This value is optional. If omitted, the SAT web service will return all documents.
145
+ # @param document_status [String, nil] Document status filter (Todos, Cancelados, Vigentes) defaults to Todos
146
+ # @param certificate [String, nil] Certificate object (uses configuration if nil)
147
+ # @param private_key [String, nil] Private key object (uses configuration if nil)
148
+ #
149
+ # @return [SatMx::Result] A Result object containing:
150
+ # - success?: [Boolean] whether the request was successful
151
+ # - value: [String, nil] the request ID if successful, or {cod_estatus:, mensaje:} on failure
152
+ # - xml: [Nokogiri::XML::Document] the raw XML response from the service
153
+ #
154
+ # @see SatMx::DownloadRequestReceived
155
+ # @see SatMx::Result
156
+ def download_request_received(
157
+ access_token:,
158
+ start_date:,
159
+ end_date:,
160
+ request_type:,
161
+ recipient_rfc:,
162
+ issuing_rfc: nil,
163
+ requester_rfc: nil,
164
+ document_status: nil,
165
+ complement: nil,
166
+ **options
167
+ )
168
+ certificate = options[:certificate] || configuration.certificate
169
+ private_key = options[:private_key] || configuration.private_key
170
+ DownloadRequestReceived.call(
171
+ start_date:,
172
+ end_date:,
173
+ request_type:,
174
+ recipient_rfc:,
175
+ requester_rfc:,
176
+ issuing_rfc:,
177
+ complement:,
178
+ document_status:,
179
+ access_token:,
180
+ certificate:,
181
+ private_key:
182
+ )
183
+ end
184
+
118
185
  # Verifies the status of a previously submitted download request.
119
186
  #
120
187
  # result = SatMx.verify_request(
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sat_mx
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Oscar Rivas
8
- autorequire:
9
8
  bindir: exe
10
9
  cert_chain: []
11
- date: 2026-02-23 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
13
  name: xmldsig
@@ -131,6 +130,8 @@ files:
131
130
  - lib/sat_mx/download_petition_body.rb
132
131
  - lib/sat_mx/download_request.rb
133
132
  - lib/sat_mx/download_request_body.rb
133
+ - lib/sat_mx/download_request_received.rb
134
+ - lib/sat_mx/download_request_received_body.rb
134
135
  - lib/sat_mx/result.rb
135
136
  - lib/sat_mx/signer.rb
136
137
  - lib/sat_mx/verify_request.rb
@@ -143,7 +144,6 @@ licenses:
143
144
  metadata:
144
145
  homepage_uri: https://github.com/kadru/sat_mx
145
146
  changelog_uri: https://github.com/kadru/sat_mx/CHANGELOG.md
146
- post_install_message:
147
147
  rdoc_options: []
148
148
  require_paths:
149
149
  - lib
@@ -158,8 +158,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
158
158
  - !ruby/object:Gem::Version
159
159
  version: '0'
160
160
  requirements: []
161
- rubygems_version: 3.5.11
162
- signing_key:
161
+ rubygems_version: 4.0.4
163
162
  specification_version: 4
164
163
  summary: a client to connect to SAT web services
165
164
  test_files: []