Class: CFDI::Comprobante

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

Overview

La clase principal para crear Comprobantes

Constant Summary

@@options =
{
  tasa: 0.16,
  defaults: {
    moneda: 'pesos',
    version: '3.2',
    subTotal: 0.0,
    TipoCambio: 1,
    conceptos: [],
    impuestos: [],
    tipoDeComprobante: 'ingreso'
  }
}

Class Method Summary (collapse)

Instance Method Summary (collapse)

Constructor Details

- (CFDI::Comprobante) initialize(data = {}, options = {})

Crear un comprobante nuevo

Parameters:

  • data (Hash) (defaults to: {})

    Los datos de un comprobante

  • options (Hash) (defaults to: {})

    Las opciones para este comprobante

Options Hash (data):

  • :version (String) — default: '3.2'

    La version del CFDI

  • :fecha (String) — default: ''

    La fecha del CFDI

  • :tipoDeComprobante (String) — default: 'ingreso'

    El tipo de Comprobante

  • :formaDePago (String) — default: ''

    La forma de pago (pago en una sóla exhibición?)

  • :condicionesDePago (String) — default: ''

    Las condiciones de pago (Efectos fiscales al pago?)

  • :TipoCambio (String) — default: 1

    El tipo de cambio para la moneda de este CFDI'

  • :moneda (String) — default: 'pesos'

    La moneda de pago

  • :metodoDePago (String) — default: ''

    El método de pago (depósito bancario? efectivo?)

  • :lugarExpedicion (String) — default: ''

    El lugar dónde se expide la factura (Nutopía, México?)

  • :NumCtaPago (String) — default: nil

    El número de cuenta para el pago

See Also:

  • Opciones


57
58
59
60
61
62
63
64
65
66
67
# File 'lib/comprobante.rb', line 57

def initialize (data={}, options={})
  #hack porque dup se caga con instance variables
  opts = Marshal::load(Marshal.dump(@@options))
  data = opts[:defaults].merge data
  @opciones = opts.merge options
  data.each do |k,v|
    method = "#{k}="
    next if !self.respond_to? method
    self.send method, v
  end
end

Class Method Details

+ (Hash) configure(options)

Configurar las opciones default de los comprobantes

Parameters:

options

Las opciones del comprobante: tasa (de impuestos), defaults: un Hash con la moneda (pesos), version (3.2), TipoCambio (1), y tipoDeComprobante (ingreso)

Returns:

  • (Hash)


34
35
36
37
# File 'lib/comprobante.rb', line 34

def self.configure (options)
  @@options = Comprobante.rmerge @@options, options
  @@options
end

Instance Method Details

- (String) cadena_original

La cadena original del CFDI

Returns:

  • (String)

    Separada por pipes, because fuck you that's why



250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
# File 'lib/comprobante.rb', line 250

def cadena_original
  params = []

  @@datosCadena.each {|key| params.push send(key) }      
  params += @emisor.cadena_original
  params << @regimen
  params += @receptor.cadena_original

  @conceptos.each do |concepto|
    params += concepto.cadena_original
  end
    
  @impuestos.each do |traslado|
    params += [traslado[:impuesto], (@opciones[:tasa]*100).to_i, self.subTotal*@opciones[:tasa], self.subTotal*@opciones[:tasa]]
  end

  params.select! { |i| i != nil && i != '' }
  params.map! do |elem|
    if elem.is_a? Float
      elem = sprintf('%.2f', elem)
    else
      elem = elem.to_s
    end
    elem
  end
        
  return "||#{params.join '|'}||"
end

- (CFDI::Complemento) complemento=(complemento)

Asigna un complemento al comprobante

Parameters:

Returns:



136
137
138
139
140
# File 'lib/comprobante.rb', line 136

def complemento= complemento
  complemento = Complemento.new complemento unless complemento.is_a? Complemento
  @complemento = complemento
  complemento
end

- (Array) conceptos=(conceptos)

Agrega uno o varios conceptos En caso de darle un Hash o un CFDI::Concepto, agrega este a los conceptos, de otro modo, sobreescribe los conceptos pre-existentes

Parameters:

Returns:

  • (Array)

    Los conceptos de este comprobante



116
117
118
119
120
121
122
123
124
125
126
127
128
129
# File 'lib/comprobante.rb', line 116

def conceptos= conceptos
  if conceptos.is_a? Array
    conceptos.map! do |concepto|
      concepto = Concepto.new concepto unless concepto.is_a? Concepto
    end
  elsif conceptos.is_a? Hash
    conceptos << Concepto.new(concepto)
  elsif conceptos.is_a? Concepto
    conceptos << conceptos
  end
  
  @conceptos = conceptos
  conceptos
end

- (CFDI::Entidad) emisor=(emisor)

Asigna un emisor de tipo Entidad

Parameters:

Returns:



94
95
96
97
# File 'lib/comprobante.rb', line 94

def emisor= emisor 
  emisor = Entidad.new emisor unless emisor.is_a? Entidad
  @emisor = emisor;
end

- (String) fecha=(fecha)

Asigna una fecha al comprobante

Parameters:

  • fecha (Time, String)

    La fecha y hora (YYYY-MM-DDTHH:mm:SS) de la emisión

Returns:

  • (String)

    la fecha en formato '%FT%R:%S'



147
148
149
150
# File 'lib/comprobante.rb', line 147

def fecha= fecha
  fecha = fecha.strftime('%FT%R:%S') unless fecha.is_a? String
  @fecha = fecha
end

- (CFDI::Entidad) receptor=(receptor)

Asigna un receptor

Parameters:

Returns:



104
105
106
107
108
# File 'lib/comprobante.rb', line 104

def receptor= receptor 
  receptor = Entidad.new receptor unless receptor.is_a? Entidad
  @receptor = receptor;
  receptor
end

- (Float) subTotal

Regresa el subtotal de este comprobante, tomando el importe de cada concepto

Returns:

  • (Float)

    El subtotal del comprobante



73
74
75
76
77
78
79
# File 'lib/comprobante.rb', line 73

def subTotal
  ret = 0
  @conceptos.each do |c|
    ret += c.importe
  end
  ret
end

- (Hash) to_h

Un hash con todos los datos del comprobante, listo para Hash.to_json

Returns:

  • (Hash)

    El comprobante como Hash



236
237
238
239
240
241
242
243
244
# File 'lib/comprobante.rb', line 236

def to_h
  hash = {}
  @@data.each do |key|
    data = deep_to_h send(key)
    hash[key] = data
  end
  
  return hash
end

- (String) to_xml

El comprobante como XML

Returns:

  • (String)

    El comprobante namespaceado en versión 3.2 (porque soy un huevón)



156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
# File 'lib/comprobante.rb', line 156

def to_xml
  ns = {
    'xmlns:cfdi' => "http://www.sat.gob.mx/cfd/3",
    'xmlns:xsi' => "http://www.w3.org/2001/XMLSchema-instance",
    'xsi:schemaLocation' => "http://www.sat.gob.mx/cfd/3 http://www.sat.gob.mx/sitio_internet/cfd/3/cfdv32.xsd",
    version: @version,
    folio: @folio,
    fecha: @fecha,
    formaDePago: @formaDePago,
    condicionesDePago: @condicionesDePago,
    subTotal: self.subTotal,
    Moneda: @moneda,
    total: self.total,
    metodoDePago: @metodoDePago,
    tipoDeComprobante: @tipoDeComprobante,
    LugarExpedicion: @lugarExpedicion,
  }
  ns[:serie] = @serie if @serie
  ns[:TipoCambio] = @TipoCambio if @TipoCambio
  ns[:NumCtaPago] = @NumCtaPago if @NumCtaPago

  if @noCertificado
    ns[:noCertificado] = @noCertificado
    ns[:certificado] = @certificado
  end

  if @sello
    ns[:sello] = @sello
  end

  @builder = Nokogiri::XML::Builder.new do |xml|
    xml.Comprobante(ns) do
      ins = xml.doc.root.add_namespace_definition('cfdi', 'http://www.sat.gob.mx/cfd/3')
      xml.doc.root.namespace = ins
    
      xml.Emisor(@emisor.ns)  {
        xml.DomicilioFiscal(@emisor.domicilioFiscal.to_h.reject {|k,v| v == nil})
        xml.ExpedidoEn(@emisor.expedidoEn.to_h.reject {|k,v| v == nil || v == ''})
        xml.RegimenFiscal({Regimen: @emisor.regimenFiscal})
      }
      xml.Receptor(@receptor.ns) {
        xml.Domicilio(@receptor.domicilioFiscal.to_h.reject {|k,v| v == nil || v == ''})
      }
      xml.Conceptos {
        @conceptos.each do |concepto|
          # select porque luego se caga el xml si incluyo noIdentificacion y es empty
          xml.Concepto(concepto.to_h.select {|k,v| v!=nil && v != ''}) {
            xml.ComplementoConcepto
          }
        end
      }
      xml.Impuestos({totalImpuestosTrasladados: self.subTotal*@opciones[:tasa]}) {
        xml.Traslados {
          @impuestos.each do |impuesto|
             xml.Traslado({impuesto: impuesto[:impuesto], tasa:(@opciones[:tasa]*100).to_i, importe: self.subTotal*@opciones[:tasa]})
          end
        }
      }
      xml.Complemento {
        
        if @complemento
          nsTFD = {
            'xsi:schemaLocation' => 'http://www.sat.gob.mx/TimbreFiscalDigital http://www.sat.gob.mx/TimbreFiscalDigital/TimbreFiscalDigital.xsd',
            'xmlns:tfd' => 'http://www.sat.gob.mx/TimbreFiscalDigital',
            'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance'                
          }
          xml['tfd'].TimbreFiscalDigital(@complemento.to_h.merge nsTFD) {
          }
          
        end
      }
    end
  end
  @builder.to_xml
end

- (Float) total

Regresa el total

Returns:

  • (Float)

    El subtotal multiplicado por la tasa



85
86
87
# File 'lib/comprobante.rb', line 85

def total
  self.subTotal+(self.subTotal*@opciones[:tasa])
end