class Afip::Bill
Attributes
body[R]
cant_reg[RW]
cbte_asoc[RW]
cbte_type[RW]
client[R]
concepto[RW]
doc_num[RW]
documento[RW]
due_date[RW]
exento[RW]
fch_emision[RW]
fch_serv_desde[RW]
fch_serv_hasta[RW]
fecha_emision[R]
gravado[RW]
iva_cond[RW]
ivas[RW]
moneda[RW]
net[RW]
no_gravado[RW]
opcionales[RW]
otros_imp[RW]
response[R]
sale_point[RW]
total[R]
tributos[RW]
Public Class Methods
comp_consultar(cbte_tipo, cbte_nro, pto_venta)
click to toggle source
# File lib/Afip/bill.rb, line 273 def self.comp_consultar(cbte_tipo, cbte_nro, pto_venta) client = set_client body = { "Auth" => Afip.auth_hash, "FeCompConsReq" => { "CbteTipo" => cbte_tipo, "CbteNro" => cbte_nro, "PtoVta" => pto_venta } } response = client.call(:fe_comp_consultar, message: body) return response end
get_ptos_vta()
click to toggle source
# File lib/Afip/bill.rb, line 34 def self.get_ptos_vta client = set_client body = { "Auth" => Afip.auth_hash } response = client.call(:fe_param_get_ptos_venta, message: body) if response.body[:fe_param_get_ptos_venta_response][:fe_param_get_ptos_venta_result][:errors].nil? if response.body[:fe_param_get_ptos_venta_response][:fe_param_get_ptos_venta_result][:result_get][:pto_venta].is_a?(Hash) response.body[:fe_param_get_ptos_venta_response][:fe_param_get_ptos_venta_result][:result_get][:pto_venta][:nro] else response.body[:fe_param_get_ptos_venta_response][:fe_param_get_ptos_venta_result][:result_get][:pto_venta].map{|r| r[:nro]} end else [] end end
get_tipos_cbte()
click to toggle source
# File lib/Afip/bill.rb, line 49 def self.get_tipos_cbte client = set_client body = { "Auth" => Afip.auth_hash } response = client.call(:fe_param_get_tipos_cbte, message: body) end
get_tributos()
click to toggle source
# File lib/Afip/bill.rb, line 55 def self.get_tributos client = set_client body = { "Auth" => Afip.auth_hash } response = client.call(:fe_param_get_tipos_tributos, message: body) if response.body[:fe_param_get_tipos_tributos_response][:fe_param_get_tipos_tributos_result][:errors].nil? response.body[:fe_param_get_tipos_tributos_response][:fe_param_get_tipos_tributos_result][:result_get][:tributo_tipo] else [] end end
header(cant_reg, cbte_type, sale_point)
click to toggle source
# File lib/Afip/bill.rb, line 186 def self.header(cant_reg, cbte_type, sale_point) {"CantReg" => cant_reg.to_s, "CbteTipo" => cbte_type, "PtoVta" => sale_point} end
new(attrs={})
click to toggle source
# File lib/Afip/bill.rb, line 8 def initialize(attrs={}) @client = Bill.set_client @sale_point = attrs[:sale_point] @body = { "Auth" => Afip.auth_hash } @net = attrs[:net] || 0.0 @documento = attrs[:documento] || Afip.default_documento @moneda = attrs[:moneda] || Afip.default_moneda @iva_cond = attrs[:iva_cond] @concepto = attrs[:concepto] || Afip.default_concepto @ivas = attrs[:ivas] || Array.new # [ 1, 100.00, 10.50 ], [ 2, 100.00, 21.00 ] @fecha_emision = attrs[:fch_emision] || Time.new @fch_serv_hasta = attrs[:fch_serv_hasta] @fch_serv_desde = attrs[:fch_serv_desde] @due_date = attrs[:due_date] @cbte_type = attrs[:cbte_type] @cant_reg = attrs[:cant_reg] || 1 @no_gravado = attrs[:no_gravado] || 0.0 @gravado = attrs[:gravado] || 0.0 @exento = attrs[:exento] || 0.0 @otros_imp = attrs[:otros_imp] || 0.0 @total = net.to_f + iva_sum.to_f + exento.to_f + no_gravado.to_f + otros_imp.to_f @tributos = attrs[:tributos] || [] @opcionales = attrs[:opcionales] || [] @cbte_asoc = attrs[:cbte_asoc] || [] end
set_client()
click to toggle source
# File lib/Afip/bill.rb, line 66 def self.set_client Afip::AuthData.fetch @client = Savon.client( wsdl: Afip.service_url, namespaces: { "xmlns" => "http://ar.gov.afip.dif.FEV1/" }, log_level: :debug, ssl_cert_key_file: Afip.pkey, ssl_cert_file: Afip.cert, ssl_verify_mode: :none, read_timeout: 90, open_timeout: 90, headers: { "Accept-Encoding" => "gzip, deflate", "Connection" => "Keep-Alive" } ) end
Public Instance Methods
exchange_rate()
click to toggle source
# File lib/Afip/bill.rb, line 190 def exchange_rate return 1 if moneda == :peso response = client.call :fe_param_get_cotizacion do message = body.merge!({"MonId" => Afip::MONEDAS[moneda][:codigo]}) end response.to_hash[:fe_param_get_cotizacion_response][:fe_param_get_cotizacion_result][:result_get][:mon_cotiz].to_f end
iva_sum()
click to toggle source
# File lib/Afip/bill.rb, line 198 def iva_sum iva_sum = 0.0 self.ivas.each{ |i| iva_sum += i[2] } return iva_sum.round(2) end
next_bill_number()
click to toggle source
# File lib/Afip/bill.rb, line 206 def next_bill_number var = {"Auth" => Afip.auth_hash, "PtoVta" => sale_point, "CbteTipo" => cbte_type} resp = client.call :fe_comp_ultimo_autorizado do message(var) end resp.to_hash[:fe_comp_ultimo_autorizado_response][:fe_comp_ultimo_autorizado_result][:cbte_nro].to_i + 1 end
setup_bill()
click to toggle source
# File lib/Afip/bill.rb, line 88 def setup_bill array_ivas = {} array_ivas["AlicIva"] = ivas.map{ |i| { "Id" => i[0], "BaseImp" => i[1].round(2), "Importe" => i[2].round(2)} unless ["01", "02"].include?(i[0])}.compact array_tributos = {} array_tributos["Tributo"] = tributos.map{ |t| if t[1].blank? { "Id" => t[0], "BaseImp" => t[2].to_f.round(2), "Alic" => t[3].to_f.round(2), "Importe" => t[4].to_f.round(2) } else { "Id" => t[0], "Desc" => t[1], "BaseImp" => t[2].to_f.round(2), "Alic" => t[3].to_f.round(2), "Importe" => t[4].to_f.round(2) } end } array_opcionales = {} array_opcionales["Opcional"] = opcionales.map{ |t| { "Id" => t[:id], "Valor" => t[:valor] } } array_associados = {} array_associados["CbteAsoc"] = cbte_asoc.map{ |b| { "Tipo" => b[:cbte_tipo], "PtoVta" => b[:sale_point], "Nro" => b[:cbte_num] } } fecaereq = { "FeCAEReq" => { "FeCabReq" => Afip::Bill.header(cant_reg, cbte_type, sale_point), "FeDetReq" => { "FECAEDetRequest" => { "Concepto" => Afip::CONCEPTOS[concepto], "DocTipo" => Afip::DOCUMENTOS[documento], "DocNro" => doc_num, "CbteFch" => fecha_emision.strftime('%Y%m%d'), "ImpTotConc" => no_gravado, "ImpNeto" => net.to_f, "MonId" => Afip::MONEDAS[moneda][:codigo], "MonCotiz" => exchange_rate, "ImpOpEx" => exento, "ImpTotal" => (Afip.own_iva_cond == :responsable_monotributo ? net : total).to_f.round(2), "CbteDesde" => next_bill_number, "CbteHasta" => next_bill_number } } } } detail = fecaereq["FeCAEReq"]["FeDetReq"]["FECAEDetRequest"] if (Afip.own_iva_cond == :responsable_inscripto) detail["ImpIVA"] = iva_sum detail["Iva"] = array_ivas unless iva_sum == 0 end if !tributos.empty? detail["ImpTrib"] = otros_imp detail["Tributos"] = array_tributos end if !opcionales.empty? detail["Opcionales"] = array_opcionales end if !cbte_asoc.empty? detail["CbtesAsoc"] = array_associados end unless concepto == "Productos" # En "Productos" ("01"), si se mandan estos parámetros la afip rechaza. detail.merge!({"FchServDesde" => fch_serv_desde.strftime("%Y%m%d"), "FchServHasta" => fch_serv_hasta.strftime("%Y%m%d"), "FchVtoPago" => due_date.strftime("%Y%m%d")}) end if ["201", "202", "203", "206", "207", "208", "211", "212", "213"].include?(cbte_type) detail.merge!({"FchVtoPago" => due_date.strftime("%Y%m%d")}) end body.merge!(fecaereq) pp body end
setup_response(response)
click to toggle source
# File lib/Afip/bill.rb, line 215 def setup_response(response) # TODO: turn this into an all-purpose Response class pp response result = response[:fecae_solicitar_response][:fecae_solicitar_result] if not result[:fe_det_resp] or not result[:fe_cab_resp] then # Si no obtuvo respuesta ni cabecera ni detalle, evito hacer '[]' sobre algo indefinido. # Ejemplo: Error con el token-sign de WSAA keys, values = { :errores => result[:errors], :header_result => {:resultado => "X" }, :observaciones => nil }.to_a.transpose @response = (defined?(Struct::ResponseMal) ? Struct::ResponseMal : Struct.new("ResponseMal", *keys)).new(*values) return end response_header = result[:fe_cab_resp] response_detail = result[:fe_det_resp][:fecae_det_response] request_header = body["FeCAEReq"]["FeCabReq"].underscore_keys.symbolize_keys request_detail = body["FeCAEReq"]["FeDetReq"]["FECAEDetRequest"].underscore_keys.symbolize_keys # Esto no funciona desde que se soportan múltiples alícuotas de iva simultáneas # FIX ? TO-DO # iva = request_detail.delete(:iva)["AlicIva"].underscore_keys.symbolize_keys # request_detail.merge!(iva) if result[:errors] then response_detail.merge!( result[:errors] ) end response_hash = { :header_result => response_header.delete(:resultado), :authorized_on => response_header.delete(:fch_proceso), :detail_result => response_detail.delete(:resultado), :cae_due_date => response_detail.delete(:cae_fch_vto), :cae => response_detail.delete(:cae), :iva_id => request_detail.delete(:id), :iva_importe => request_detail.delete(:importe), :moneda => request_detail.delete(:mon_id), :cotizacion => request_detail.delete(:mon_cotiz), :iva_base_imp => request_detail.delete(:base_imp), :doc_num => request_detail.delete(:doc_nro), :observaciones => response_detail.delete(:observaciones), :ivas => response_detail.delete(:iva), :tributos => response_detail.delete(:tributos), :errores => response_detail.delete(:err) }.merge!(request_header).merge!(request_detail) keys, values = response_hash.to_a.transpose pp @response = Struct.new("Response", *keys).new(*values) end