m_cfdi 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +11 -4
- data/lib/m_cfdi/complement.rb +4 -3
- data/lib/m_cfdi/invoice.rb +22 -3
- data/lib/m_cfdi/taxes.rb +2 -1
- data/lib/m_cfdi/version.rb +1 -1
- data/lib/m_cfdi/xml.rb +132 -0
- data/lib/m_cfdi.rb +1 -0
- data/m_cfdi.gemspec +3 -1
- metadata +32 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ae85da182607c7e669c5cf6da65cb1687f1ca92d
|
4
|
+
data.tar.gz: 0c94217b234df9b8e054d006e5aec50d184f8444
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3bac5a4eb45682f0140aa69aeaf1d95536a4d1bb57eaccd93acd5a71415280a587331ef4ad586d05424f26bdbd6dbb6b89c88e5e30ba17e16211c54a64703d25
|
7
|
+
data.tar.gz: 8f1b8a518a5fb0112d2744778d6539294ac020ca874eede131d83c9e4fce71a2d8e73238bc769a938f3a3bce5d702f4af2f1f4bdde02764fc685db3a0920ec1a
|
data/README.md
CHANGED
@@ -6,23 +6,30 @@ CFDI GEM, to generate XML file for invoice, all you have to do, is to send the x
|
|
6
6
|
# Installation:
|
7
7
|
|
8
8
|
In your gem file:
|
9
|
-
```
|
10
|
-
gem 'm_cfdi'
|
9
|
+
```bash
|
10
|
+
gem 'm_cfdi'
|
11
11
|
```
|
12
12
|
|
13
13
|
or
|
14
14
|
|
15
|
-
```
|
15
|
+
```bash
|
16
16
|
gem 'm_cfdi', git: 'https://github.com/MaSys/m_cfdi.git'
|
17
17
|
```
|
18
18
|
|
19
19
|
or install it directly from your terminal:
|
20
|
-
```
|
20
|
+
```bash
|
21
21
|
gem install m_cfdi
|
22
22
|
```
|
23
23
|
|
24
24
|
# Usage:
|
25
25
|
|
26
|
+
Importante:
|
27
|
+
```
|
28
|
+
Dont't forgot to set TimeZone in your app.
|
29
|
+
```
|
30
|
+
You have to set timezone in your app to send the invoice date.
|
31
|
+
if not, it will return an error.
|
32
|
+
|
26
33
|
```ruby
|
27
34
|
|
28
35
|
class Invoice
|
data/lib/m_cfdi/complement.rb
CHANGED
@@ -13,9 +13,10 @@ module MCFDI
|
|
13
13
|
attr_accessor :uuid, :stamp_date, :cfd_stamp, :sat_certificate_num,
|
14
14
|
:sat_stamp, :version
|
15
15
|
|
16
|
-
|
17
|
-
|
18
|
-
|
16
|
+
def initialize(args = {})
|
17
|
+
args.each { |key, value| send("#{key}=", value) }
|
18
|
+
end
|
19
|
+
|
19
20
|
def string
|
20
21
|
a = [@version, @uuid, @stamp_date, @cfd_stamp, @sat_certificate_num]
|
21
22
|
"||#{a.join('|')}||"
|
data/lib/m_cfdi/invoice.rb
CHANGED
@@ -90,15 +90,17 @@ module MCFDI
|
|
90
90
|
# save xml to file and generate original string from schema.
|
91
91
|
def original_string_from_xslt
|
92
92
|
# fail 'You have to specify schema!' unless # TODO: create configuration.
|
93
|
-
save_xml
|
94
93
|
xml = "#{Rails.root}/#{@transmitter.rfc}-#{@series}-#{@folio}.xml"
|
94
|
+
save_xml(xml)
|
95
95
|
sch = "#{Rails.root}/public/sat/schemas/cadenaoriginal_3_2.xslt"
|
96
96
|
@original_string_from_xslt ||= `xsltproc #{sch} #{xml}`
|
97
|
+
File.delete(xml) if File.exist?(xml)
|
98
|
+
@original_string_from_xslt
|
97
99
|
end
|
98
100
|
|
99
101
|
# Save invoice xml to file
|
100
|
-
def save_xml
|
101
|
-
file = File.new(
|
102
|
+
def save_xml(name)
|
103
|
+
file = File.new(name, 'w+')
|
102
104
|
file.write(to_xml)
|
103
105
|
file.close
|
104
106
|
end
|
@@ -300,5 +302,22 @@ module MCFDI
|
|
300
302
|
end
|
301
303
|
errors
|
302
304
|
end # Validate_xml
|
305
|
+
|
306
|
+
# return string with total in words.
|
307
|
+
# Example: ( UN MIL CIENTO SESENTA PESOS 29/100 M.N. )
|
308
|
+
def total_to_words
|
309
|
+
decimal = format('%.2f', @total).split('.')
|
310
|
+
"( #{@total.to_words.upcase} PESOS #{decimal[1]}/100 M.N. )"
|
311
|
+
end
|
312
|
+
|
313
|
+
# return data of qr code image
|
314
|
+
def qr_code
|
315
|
+
t = "?re=#{@transmitter.rfc}"
|
316
|
+
t += "&rr=#{@receptor.rfc}"
|
317
|
+
t += "&tt=#{format('%6f', @total).rjust(17, '0')}"
|
318
|
+
t += "&id=#{@complement.uuid}"
|
319
|
+
img = RQRCode::QRCode.new(t).to_img
|
320
|
+
img.resize(120, 120).to_data_url
|
321
|
+
end
|
303
322
|
end
|
304
323
|
end
|
data/lib/m_cfdi/taxes.rb
CHANGED
@@ -8,6 +8,7 @@
|
|
8
8
|
module MCFDI
|
9
9
|
class Taxes < Base
|
10
10
|
attr_accessor :transferred, :detained
|
11
|
+
|
11
12
|
def initialize
|
12
13
|
@transferred = []
|
13
14
|
@detained = []
|
@@ -33,7 +34,7 @@ module MCFDI
|
|
33
34
|
def transferred=(tax)
|
34
35
|
if data.is_a? Array
|
35
36
|
data.map do |c|
|
36
|
-
c
|
37
|
+
c = Transferred.new(c) unless c.is_a? Tansferred
|
37
38
|
@transferred << c
|
38
39
|
end
|
39
40
|
elsif data.is_a? Hash
|
data/lib/m_cfdi/version.rb
CHANGED
data/lib/m_cfdi/xml.rb
ADDED
@@ -0,0 +1,132 @@
|
|
1
|
+
module MCFDI
|
2
|
+
def self.from_xml(data)
|
3
|
+
xml = Nokogiri::XML(data)
|
4
|
+
xml.remove_namespaces!
|
5
|
+
invoice = Invoice.new
|
6
|
+
invoice.taxes = Taxes.new
|
7
|
+
|
8
|
+
proof = xml.at_xpath('//Comprobante')
|
9
|
+
transmitter = xml.at_xpath('//Emisor')
|
10
|
+
trans_address = transmitter.at_xpath('//DomicilioFiscal')
|
11
|
+
issued_in = transmitter.at_xpath('//ExpedidoEn')
|
12
|
+
receptor = xml.at_xpath('//Receptor')
|
13
|
+
rec_address = receptor.at_xpath('//Domicilio')
|
14
|
+
|
15
|
+
invoice.version = proof.attr('version')
|
16
|
+
invoice.series = proof.attr('serie')
|
17
|
+
invoice.folio = proof.attr('folio')
|
18
|
+
invoice.created_at = Time.parse(proof.attr('fecha'))
|
19
|
+
invoice.certificate_number = proof.attr('noCertificado')
|
20
|
+
invoice.certificate = proof.attr('certificado')
|
21
|
+
invoice.stamp = proof.attr('sello')
|
22
|
+
invoice.payment_way = proof.attr('formaDePago')
|
23
|
+
invoice.payment_conditions = proof.attr('condicionesDePago')
|
24
|
+
invoice.proof_type = proof.attr('tipoDeComprobante')
|
25
|
+
invoice.expedition_place = proof.attr('LugarExpedicion')
|
26
|
+
invoice.payment_method = proof.attr('metodoDePago')
|
27
|
+
invoice.currency = proof.attr('Moneda')
|
28
|
+
invoice.payment_account_num = proof.attr('NumCtaPago')
|
29
|
+
invoice.total = proof.attr('total').to_f
|
30
|
+
invoice.subtotal = proof.attr('subTotal').to_f
|
31
|
+
|
32
|
+
|
33
|
+
fiscal_regime = transmitter.at_xpath('//RegimenFiscal')
|
34
|
+
|
35
|
+
transmitter = {
|
36
|
+
rfc: transmitter.attr('rfc'),
|
37
|
+
business_name: transmitter.attr('nombre'),
|
38
|
+
fiscal_regime: fiscal_regime && fiscal_regime.attr('Regimen'),
|
39
|
+
address: {
|
40
|
+
street: trans_address.attr('calle'),
|
41
|
+
street_number: trans_address.attr('noExterior'),
|
42
|
+
interior_number: trans_address.attr('noInterior'),
|
43
|
+
neighborhood: trans_address.attr('colonia'),
|
44
|
+
location: trans_address.attr('localidad'),
|
45
|
+
reference: trans_address.attr('referencia'),
|
46
|
+
city: trans_address.attr('municipio'),
|
47
|
+
state: trans_address.attr('estado'),
|
48
|
+
country: trans_address.attr('pais'),
|
49
|
+
zip_code: trans_address.attr('codigoPostal')
|
50
|
+
}
|
51
|
+
}
|
52
|
+
|
53
|
+
if issued_in
|
54
|
+
transmitter[:issued_in] = {
|
55
|
+
street: issued_in.attr('calle'),
|
56
|
+
street_number: issued_in.attr('noExterior'),
|
57
|
+
interior_number: issued_in.attr('noInterior'),
|
58
|
+
neighborhood: issued_in.attr('colonia'),
|
59
|
+
location: issued_in.attr('localidad'),
|
60
|
+
reference: issued_in.attr('referencia'),
|
61
|
+
city: issued_in.attr('municipio'),
|
62
|
+
state: issued_in.attr('estado'),
|
63
|
+
country: issued_in.attr('pais'),
|
64
|
+
zip_code: issued_in.attr('codigoPostal')
|
65
|
+
}
|
66
|
+
end
|
67
|
+
|
68
|
+
invoice.transmitter = transmitter
|
69
|
+
|
70
|
+
invoice.receptor = {
|
71
|
+
rfc: receptor.attr('rfc'),
|
72
|
+
business_name: receptor.attr('nombre')
|
73
|
+
}
|
74
|
+
|
75
|
+
if rec_address
|
76
|
+
invoice.receptor.address = {
|
77
|
+
street: rec_address.attr('calle'),
|
78
|
+
street_number: rec_address.attr('noExterior'),
|
79
|
+
interior_number: rec_address.attr('noInterior'),
|
80
|
+
neighborhood: rec_address.attr('colonia'),
|
81
|
+
location: rec_address.attr('localidad'),
|
82
|
+
reference: rec_address.attr('referencia'),
|
83
|
+
city: rec_address.attr('municipio'),
|
84
|
+
state: rec_address.attr('estado'),
|
85
|
+
country: rec_address.attr('pais'),
|
86
|
+
zip_code: rec_address.attr('codigoPostal')
|
87
|
+
}
|
88
|
+
end
|
89
|
+
|
90
|
+
invoice.concepts = []
|
91
|
+
xml.xpath('//Concepto').each do |concept|
|
92
|
+
invoice.concepts << Concept.new(
|
93
|
+
quantity: concept.attr('cantidad').to_f,
|
94
|
+
measure_unit: concept.attr('unidad'),
|
95
|
+
code: concept.attr('noIdentificacion'),
|
96
|
+
name: concept.attr('descripcion'),
|
97
|
+
price: concept.attr('valorUnitario').to_f,
|
98
|
+
import: concept.attr('importe').to_f
|
99
|
+
)
|
100
|
+
end
|
101
|
+
|
102
|
+
complement = xml.at_xpath('//TimbreFiscalDigital')
|
103
|
+
if complement
|
104
|
+
invoice.complement = Complement.new(
|
105
|
+
version: complement.attr('version'),
|
106
|
+
uuid: complement.attr('UUID'),
|
107
|
+
stamp_date: complement.attr('FechaTimbrado'),
|
108
|
+
cfd_stamp: complement.attr('selloCFD'),
|
109
|
+
sat_certificate_num: complement.attr('noCertificadoSAT'),
|
110
|
+
sat_stamp: complement.attr('selloSAT')
|
111
|
+
)
|
112
|
+
end
|
113
|
+
|
114
|
+
xml.xpath('//Traslado').each do |node|
|
115
|
+
invoice.taxes.transferred << Transferred.new(
|
116
|
+
tax: node.attr('impuesto'),
|
117
|
+
rate: node.attr('tasa').to_f,
|
118
|
+
import: node.attr('importe').to_f
|
119
|
+
)
|
120
|
+
end
|
121
|
+
|
122
|
+
xml.xpath('//Retencion').each do |node|
|
123
|
+
invoice.taxes.detained << Detained.new(
|
124
|
+
tax: node.attr('impuesto'),
|
125
|
+
rate: node.attr('tasa').to_f,
|
126
|
+
import: node.attr('importe').to_f
|
127
|
+
)
|
128
|
+
end
|
129
|
+
|
130
|
+
invoice
|
131
|
+
end # from_xml
|
132
|
+
end
|
data/lib/m_cfdi.rb
CHANGED
data/m_cfdi.gemspec
CHANGED
@@ -29,5 +29,7 @@ Gem::Specification.new do |spec|
|
|
29
29
|
spec.add_development_dependency "bundler", "~> 1.10"
|
30
30
|
spec.add_development_dependency "rake", "~> 10.0"
|
31
31
|
spec.add_development_dependency "rspec"
|
32
|
-
spec.
|
32
|
+
spec.add_dependency 'nokogiri', '~> 1.6'
|
33
|
+
spec.add_dependency 'number_to_words', '~> 1.2'
|
34
|
+
spec.add_dependency 'rqrcode_png', '~> 0.1'
|
33
35
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: m_cfdi
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Yaser Almasri
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-12-
|
11
|
+
date: 2015-12-07 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -59,13 +59,41 @@ dependencies:
|
|
59
59
|
- - "~>"
|
60
60
|
- !ruby/object:Gem::Version
|
61
61
|
version: '1.6'
|
62
|
-
type: :
|
62
|
+
type: :runtime
|
63
63
|
prerelease: false
|
64
64
|
version_requirements: !ruby/object:Gem::Requirement
|
65
65
|
requirements:
|
66
66
|
- - "~>"
|
67
67
|
- !ruby/object:Gem::Version
|
68
68
|
version: '1.6'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: number_to_words
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '1.2'
|
76
|
+
type: :runtime
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '1.2'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: rqrcode_png
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0.1'
|
90
|
+
type: :runtime
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0.1'
|
69
97
|
description: CFDI Mexico, To generate XML files from invoices of Mexico, Comprobantes
|
70
98
|
Fiscales Digitales por Internet
|
71
99
|
email:
|
@@ -96,6 +124,7 @@ files:
|
|
96
124
|
- lib/m_cfdi/key.rb
|
97
125
|
- lib/m_cfdi/taxes.rb
|
98
126
|
- lib/m_cfdi/version.rb
|
127
|
+
- lib/m_cfdi/xml.rb
|
99
128
|
- m_cfdi.gemspec
|
100
129
|
homepage: https://github.com/MaSys/cfdi
|
101
130
|
licenses: []
|