Class: EcfDgii::Client

Inherits:
Object
  • Object
show all
Defined in:
lib/ecf_dgii/client.rb

Overview

High-level client for the ECF DGII API.

Mirrors the TypeScript EcfClient 1:1 — same method names (snake_case), same validation, same error semantics.

Constant Summary collapse

ENVIRONMENT_URLS =
{
  test: "https://api.test.ecfx.ssd.com.do",
  cert: "https://api.cert.ecfx.ssd.com.do",
  prod: "https://api.prod.ecfx.ssd.com.do"
}.freeze
ECF_TYPE_ROUTE_MAP =
{
  "FacturaDeCreditoFiscalElectronica" => "31",
  "FacturaDeConsumoElectronica"       => "32",
  "NotaDeDebitoElectronica"           => "33",
  "NotaDeCreditoElectronica"          => "34",
  "ComprasElectronico"                => "41",
  "GastosMenoresElectronico"          => "43",
  "RegimenesEspecialesElectronico"    => "44",
  "GubernamentalElectronico"          => "45",
  "ComprobanteDeExportacionesElectronico"  => "46",
  "ComprobanteParaPagosAlExteriorElectronico" => "47"
}.freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(api_key: nil, base_url: nil, environment: :test, timeout: 30) ⇒ Client

Returns a new instance of Client.

Raises:

  • (ArgumentError)


36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
# File 'lib/ecf_dgii/client.rb', line 36

def initialize(api_key: nil, base_url: nil, environment: :test, timeout: 30)
  token = api_key || ENV["ECF_API_KEY"]
  resolved_url = base_url || ENV["ECF_API_URL"] || ENVIRONMENT_URLS[environment.to_sym]

  raise ArgumentError, "Se requiere un api_key o la variable de entorno ECF_API_KEY" if token.nil? || token.empty?
  raise ArgumentError, "El entorno especificado o la URL base no son válidos" if resolved_url.nil? || resolved_url.empty?

  config = EcfDgii::Generated::Configuration.new
  uri = URI.parse(resolved_url)

  config.scheme = uri.scheme
  config.host = uri.host
  config.base_path = uri.path.empty? ? "" : uri.path

  config.access_token = token
  config.timeout = timeout

  @api_client = EcfDgii::Generated::ApiClient.new(config)
  @environment = environment.to_sym
end

Instance Attribute Details

#api_clientEcfDgii::Generated::ApiClient (readonly)

Returns The underlying generated API client.

Returns:



31
32
33
# File 'lib/ecf_dgii/client.rb', line 31

def api_client
  @api_client
end

#environmentSymbol (readonly)

Returns The configured environment (:test, :cert, or :prod).

Returns:

  • (Symbol)

    The configured environment (:test, :cert, or :prod).



34
35
36
# File 'lib/ecf_dgii/client.rb', line 34

def environment
  @environment
end

Instance Method Details

#anulacion_rangos(rnc, body) ⇒ Object

Request range annulment.



268
269
270
# File 'lib/ecf_dgii/client.rb', line 268

def anulacion_rangos(rnc, body)
  ecf_api.anulacion_rangos(rnc, body)
end

#api_key_apiObject



81
82
83
# File 'lib/ecf_dgii/client.rb', line 81

def api_key_api
  @api_key_api ||= EcfDgii::Generated::ApiKeyApi.new(api_client)
end

#aprobacion_comercial(message_id, body) ⇒ Object Also known as: send_aprobacion_comercial

Send aprobación comercial (ACECF) for a given ECF reception messageId.



320
321
322
# File 'lib/ecf_dgii/client.rb', line 320

def aprobacion_comercial(message_id, body)
  recepcion_api.aprobacion_comercial(message_id, body)
end

#aprobacion_comercial_apiObject



77
78
79
# File 'lib/ecf_dgii/client.rb', line 77

def aprobacion_comercial_api
  @aprobacion_comercial_api ||= EcfDgii::Generated::AprobacionComercialApi.new(api_client)
end

#company_apiObject



73
74
75
# File 'lib/ecf_dgii/client.rb', line 73

def company_api
  @company_api ||= EcfDgii::Generated::CompanyApi.new(api_client)
end

#consulta_directorio_listado(rnc) ⇒ Object Also known as: consulta_directorio

Consulta directorio — listado.



344
345
346
# File 'lib/ecf_dgii/client.rb', line 344

def consulta_directorio_listado(rnc)
  dgii_api.consulta_directorio_listado(rnc)
end

#consulta_directorio_por_rnc(rnc, target_rnc) ⇒ Object

Consulta directorio — obtener directorio por RNC.



349
350
351
# File 'lib/ecf_dgii/client.rb', line 349

def consulta_directorio_por_rnc(rnc, target_rnc)
  dgii_api.consulta_directorio_obtener_directorio_por_rnc(rnc, target_rnc)
end

#consulta_estado(rnc, rnc_emisor, ncf_electronico, rnc_comprador, codigo_seguridad) ⇒ Object

Consulta estado.



354
355
356
# File 'lib/ecf_dgii/client.rb', line 354

def consulta_estado(rnc, rnc_emisor, ncf_electronico, rnc_comprador, codigo_seguridad)
  dgii_api.consulta_estado(rnc, rnc_emisor, ncf_electronico, rnc_comprador, codigo_seguridad)
end

#consulta_resultado(rnc, track_id) ⇒ Object

Consulta resultado.



359
360
361
# File 'lib/ecf_dgii/client.rb', line 359

def consulta_resultado(rnc, track_id)
  dgii_api.consulta_resultado(rnc, track_id)
end

#consulta_rfce(rnc, rnc_emisor, encf, codigo_seguridad) ⇒ Object

Consulta RFCE.



364
365
366
# File 'lib/ecf_dgii/client.rb', line 364

def consulta_rfce(rnc, rnc_emisor, encf, codigo_seguridad)
  dgii_api.consulta_rfce(rnc, rnc_emisor, encf, codigo_seguridad)
end

#consulta_timbre(rnc, rnc_emisor, ncf_electronico, rnc_comprador, codigo_seguridad, fecha_emision, monto_total, uid_timbre) ⇒ Object

Consulta timbre.



369
370
371
# File 'lib/ecf_dgii/client.rb', line 369

def consulta_timbre(rnc, rnc_emisor, ncf_electronico, rnc_comprador, codigo_seguridad, fecha_emision, monto_total, uid_timbre)
  dgii_api.consulta_timbre(rnc, rnc_emisor, ncf_electronico, rnc_comprador, codigo_seguridad, fecha_emision, monto_total, uid_timbre)
end

#consulta_timbre_fc(rnc, rnc_emisor, ncf_electronico, rnc_comprador, fecha_emision, monto_total) ⇒ Object

Consulta timbre FC.



374
375
376
# File 'lib/ecf_dgii/client.rb', line 374

def consulta_timbre_fc(rnc, rnc_emisor, ncf_electronico, rnc_comprador, fecha_emision, monto_total)
  dgii_api.consulta_timbre_fc(rnc, rnc_emisor, ncf_electronico, rnc_comprador, fecha_emision, monto_total)
end

#consulta_track_id(rnc, rnc_emisor, encf) ⇒ Object

Consulta track IDs.



379
380
381
# File 'lib/ecf_dgii/client.rb', line 379

def consulta_track_id(rnc, rnc_emisor, encf)
  dgii_api.consulta_track_id(rnc, rnc_emisor, encf)
end

#create_api_key(body) ⇒ Object Also known as: new_company_api_key

Create a new API key (read-only, scoped token for frontend use).



332
333
334
# File 'lib/ecf_dgii/client.rb', line 332

def create_api_key(body)
  api_key_api.new_company_api_key(body)
end

#delete_company(rnc) ⇒ Object

Delete a company by RNC.



211
212
213
# File 'lib/ecf_dgii/client.rb', line 211

def delete_company(rnc)
  company_api.delete_company(rnc)
end

#dgii_apiObject



65
66
67
# File 'lib/ecf_dgii/client.rb', line 65

def dgii_api
  @dgii_api ||= EcfDgii::Generated::DgiiApi.new(api_client)
end

#ecf_apiObject


Base API clients




61
62
63
# File 'lib/ecf_dgii/client.rb', line 61

def ecf_api
  @ecf_api ||= EcfDgii::Generated::EcfApi.new(api_client)
end

#estatus_servicios(rnc) ⇒ Object Also known as: estatus_servicio

Estatus servicios — obtener estatus.



384
385
386
# File 'lib/ecf_dgii/client.rb', line 384

def estatus_servicios(rnc)
  dgii_api.estatus_servicios_obtener_estatus(rnc)
end

#firmar_semilla(rnc, body) ⇒ Object

Sign a seed for a company.



282
283
284
# File 'lib/ecf_dgii/client.rb', line 282

def firmar_semilla(rnc, body)
  ecf_api.firmar_semilla(rnc, body)
end

#get_acecf_reception_request(message_id) ⇒ Object

Get a specific ACECF reception request by messageId.



311
312
313
# File 'lib/ecf_dgii/client.rb', line 311

def get_acecf_reception_request(message_id)
  aprobacion_comercial_api.get_acecf_reception_request(message_id)
end

#get_certificate(rnc) ⇒ Object Also known as: get_current_certificate

Get the current certificate for a company.



220
221
222
# File 'lib/ecf_dgii/client.rb', line 220

def get_certificate(rnc)
  company_api.get_current_certificate(rnc)
end

#get_companies(opts = {}) ⇒ Object

List companies with optional filters.



196
197
198
# File 'lib/ecf_dgii/client.rb', line 196

def get_companies(opts = {})
  company_api.get_companies(opts)
end

#get_company_by_rnc(rnc) ⇒ Object

Get a company by RNC.



201
202
203
# File 'lib/ecf_dgii/client.rb', line 201

def get_company_by_rnc(rnc)
  company_api.get_company_by_rnc(rnc)
end

#get_ecf_by_id(rnc, id) ⇒ Object

Get a specific ECF by RNC and message ID.



259
260
261
# File 'lib/ecf_dgii/client.rb', line 259

def get_ecf_by_id(rnc, id)
  ecf_api.get_ecf_by_id(rnc, id)
end

#get_ecf_reception_request(rnc, message_id) ⇒ Object

Get a specific ECF reception request by RNC and messageId.



306
307
308
# File 'lib/ecf_dgii/client.rb', line 306

def get_ecf_reception_request(rnc, message_id)
  recepcion_api.get_ecf_reception_request(rnc, message_id)
end

#list_anulaciones(opts = {}) ⇒ Object

List annulments.



273
274
275
# File 'lib/ecf_dgii/client.rb', line 273

def list_anulaciones(opts = {})
  ecf_api.list_anulaciones(opts)
end

#query_ecf(rnc, encf, opts = {}) ⇒ Object

Query ECFs by RNC and eNCF.



244
245
246
# File 'lib/ecf_dgii/client.rb', line 244

def query_ecf(rnc, encf, opts = {})
  ecf_api.query_ecf(rnc, encf, opts)
end

#recepcion_apiObject



69
70
71
# File 'lib/ecf_dgii/client.rb', line 69

def recepcion_api
  @recepcion_api ||= EcfDgii::Generated::RecepcionApi.new(api_client)
end

#search_acecf_reception_requests(opts = {}) ⇒ Object

Search ACECF reception requests.



296
297
298
# File 'lib/ecf_dgii/client.rb', line 296

def search_acecf_reception_requests(opts = {})
  aprobacion_comercial_api.search_acecf_reception_requests(opts)
end

#search_all_ecfs(opts = {}) ⇒ Object

Search all ECFs across all companies.



254
255
256
# File 'lib/ecf_dgii/client.rb', line 254

def search_all_ecfs(opts = {})
  ecf_api.search_all_ecfs(opts)
end

#search_ecf_reception_requests(opts = {}) ⇒ Object

Search ECF reception requests.



291
292
293
# File 'lib/ecf_dgii/client.rb', line 291

def search_ecf_reception_requests(opts = {})
  recepcion_api.search_ecf_reception_requests(opts)
end

#search_ecf_reception_requests_by_rnc(rnc, opts = {}) ⇒ Object

Search ECF reception requests by RNC.



301
302
303
# File 'lib/ecf_dgii/client.rb', line 301

def search_ecf_reception_requests_by_rnc(rnc, opts = {})
  recepcion_api.search_ecf_reception_requests_by_rnc(rnc, opts)
end

#search_ecfs(rnc, opts = {}) ⇒ Object

Search ECFs for a specific RNC.



249
250
251
# File 'lib/ecf_dgii/client.rb', line 249

def search_ecfs(rnc, opts = {})
  ecf_api.search_ecfs(rnc, opts)
end

#send_ecf(ecf, polling_options = nil) ⇒ Object

Send an ECF and poll until processing completes.

Determines the correct endpoint from ecf.encabezado.idDoc.tipoeCF, posts the ECF, then polls until progress is Finished or Error.

Parameters:

  • ecf (Object)

    Any ECF object (Ecf31ECF … Ecf47ECF) or Hash

  • polling_options (PollingOptions, nil) (defaults to: nil)

    Polling configuration

Returns:

  • (Object)

    The final EcfResponse when processing is complete

Raises:

  • (ArgumentError)

    If required fields (tipoeCF, rncEmisor, encf) are missing

  • (EcfError)

    If the ECF type is unknown

  • (EcfError)

    If processing finishes with progress “Error”

  • (PollingTimeoutError)

    If total timeout is exceeded

  • (PollingMaxRetriesError)

    If max retries is exceeded



102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
# File 'lib/ecf_dgii/client.rb', line 102

def send_ecf(ecf, polling_options = nil)
  # 1. Extract tipoeCF
  tipoe_cf = extract_tipoe_cf(ecf)
  raise ArgumentError, "ECF must have encabezado.idDoc.tipoeCF" if tipoe_cf.nil? || tipoe_cf.to_s.empty?

  # 2. Resolve route
  route = ECF_TYPE_ROUTE_MAP[tipoe_cf.to_s]
  raise ArgumentError, "Unknown tipoeCF: #{tipoe_cf}" if route.nil?

  # 3. Extract rncEmisor (for polling)
  rnc = extract_rncemisor(ecf)
  raise ArgumentError, "ECF must have encabezado.emisor.rncEmisor" if rnc.nil? || rnc.to_s.empty?

  # 4. Extract encf (for polling)
  encf = extract_encf(ecf)
  raise ArgumentError, "ECF must have encabezado.idDoc.encf" if encf.nil? || encf.to_s.empty?

  # 5. POST to the correct endpoint
  response = post_ecf(route, ecf)

  # 6. Poll until complete
  result = poll_until_complete(response, rnc, encf, polling_options)

  # 7. Throw EcfError if progress is Error (matching TS behavior)
  progress = extract_progress_value(result)
  if progress == "Error"
    error_msg = nil
    if result.respond_to?(:errors)
      error_msg = result.errors
    elsif result.respond_to?(:mensaje)
      error_msg = result.mensaje
    end
    error_msg ||= result[:errors] || result[:mensaje] || result["errors"] || result["mensaje"] || "ECF processing failed"
    raise EcfError.new(error_msg, result)
  end

  result
end

#send_ecf31(ecf) ⇒ Object


Individual ECF type send methods (kept for backward compatibility)




151
152
153
# File 'lib/ecf_dgii/client.rb', line 151

def send_ecf31(ecf)
  ecf_api.recepcion_ecf_31(ecf)
end

#send_ecf32(ecf) ⇒ Object



155
156
157
# File 'lib/ecf_dgii/client.rb', line 155

def send_ecf32(ecf)
  ecf_api.recepcion_ecf_32(ecf)
end

#send_ecf33(ecf) ⇒ Object



159
160
161
# File 'lib/ecf_dgii/client.rb', line 159

def send_ecf33(ecf)
  ecf_api.recepcion_ecf_33(ecf)
end

#send_ecf34(ecf) ⇒ Object



163
164
165
# File 'lib/ecf_dgii/client.rb', line 163

def send_ecf34(ecf)
  ecf_api.recepcion_ecf_34(ecf)
end

#send_ecf41(ecf) ⇒ Object



167
168
169
# File 'lib/ecf_dgii/client.rb', line 167

def send_ecf41(ecf)
  ecf_api.recepcion_ecf_41(ecf)
end

#send_ecf43(ecf) ⇒ Object



171
172
173
# File 'lib/ecf_dgii/client.rb', line 171

def send_ecf43(ecf)
  ecf_api.recepcion_ecf_43(ecf)
end

#send_ecf44(ecf) ⇒ Object



175
176
177
# File 'lib/ecf_dgii/client.rb', line 175

def send_ecf44(ecf)
  ecf_api.recepcion_ecf_44(ecf)
end

#send_ecf45(ecf) ⇒ Object



179
180
181
# File 'lib/ecf_dgii/client.rb', line 179

def send_ecf45(ecf)
  ecf_api.recepcion_ecf_45(ecf)
end

#send_ecf46(ecf) ⇒ Object



183
184
185
# File 'lib/ecf_dgii/client.rb', line 183

def send_ecf46(ecf)
  ecf_api.recepcion_ecf_46(ecf)
end

#send_ecf47(ecf) ⇒ Object



187
188
189
# File 'lib/ecf_dgii/client.rb', line 187

def send_ecf47(ecf)
  ecf_api.recepcion_ecf_47(ecf)
end

#send_ecf_and_poll(ecf, options = nil) ⇒ Object

Deprecated.

Use #send_ecf instead (which now includes polling 1:1 with TS).

Convenience alias matching older Ruby SDK API.



143
144
145
# File 'lib/ecf_dgii/client.rb', line 143

def send_ecf_and_poll(ecf, options = nil)
  send_ecf(ecf, options)
end

#update_certificate(rnc, certificate, password) ⇒ Object Also known as: update_certificate_company

Update a company’s certificate.

Parameters:

  • rnc (String)

    Company RNC

  • certificate (String, File)

    Path to the .p12 file or a File object

  • password (String)

    Certificate password



229
230
231
# File 'lib/ecf_dgii/client.rb', line 229

def update_certificate(rnc, certificate, password)
  company_api.update_certificate_company(rnc, certificate, password)
end

#upsert_company(body) ⇒ Object

Create or update a company.



206
207
208
# File 'lib/ecf_dgii/client.rb', line 206

def upsert_company(body)
  company_api.upsert_company(body)
end

#ventanas_mantenimiento(rnc) ⇒ Object

Estatus servicios — obtener ventanas de mantenimiento.



389
390
391
# File 'lib/ecf_dgii/client.rb', line 389

def ventanas_mantenimiento(rnc)
  dgii_api.estatus_servicios_obtener_ventanas_mantenimiento(rnc)
end