br_danfe 0.15.1 → 0.16.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (57) hide show
  1. checksums.yaml +4 -4
  2. data/.tool-versions +1 -0
  3. data/Gemfile.lock +35 -22
  4. data/Guardfile +2 -2
  5. data/README.md +23 -0
  6. data/br_danfe.gemspec +3 -2
  7. data/lib/br_danfe.rb +1 -1
  8. data/lib/br_danfe/danfe_lib/nfce.rb +1 -1
  9. data/lib/br_danfe/danfe_lib/nfe_lib/dest.rb +1 -1
  10. data/lib/br_danfe/danfe_lib/nfe_lib/emit_header.rb +1 -1
  11. data/lib/br_danfe/document_builder.rb +1 -1
  12. data/lib/br_danfe/helper.rb +7 -9
  13. data/lib/br_danfe/mdfe.rb +55 -0
  14. data/lib/br_danfe/mdfe_lib/authorization_protocol.rb +24 -0
  15. data/lib/br_danfe/mdfe_lib/document.rb +26 -0
  16. data/lib/br_danfe/mdfe_lib/drivers.rb +70 -0
  17. data/lib/br_danfe/mdfe_lib/fisco_control.rb +40 -0
  18. data/lib/br_danfe/mdfe_lib/header.rb +78 -0
  19. data/lib/br_danfe/mdfe_lib/mdfe_identification.rb +69 -0
  20. data/lib/br_danfe/mdfe_lib/notes.rb +94 -0
  21. data/lib/br_danfe/mdfe_lib/totalizer.rb +57 -0
  22. data/lib/br_danfe/mdfe_lib/vehicles.rb +80 -0
  23. data/lib/br_danfe/qr_code.rb +22 -0
  24. data/lib/br_danfe/version.rb +1 -1
  25. data/spec/br_danfe/danfe_lib/nfe_lib/infadic_spec.rb +3 -3
  26. data/spec/br_danfe/helper_spec.rb +12 -0
  27. data/spec/br_danfe/mdfe_lib/authorization_protocol_spec.rb +35 -0
  28. data/spec/br_danfe/mdfe_lib/document_spec.rb +20 -0
  29. data/spec/br_danfe/mdfe_lib/drivers_spec.rb +88 -0
  30. data/spec/br_danfe/mdfe_lib/fisco_control_spec.rb +55 -0
  31. data/spec/br_danfe/mdfe_lib/header_spec.rb +126 -0
  32. data/spec/br_danfe/mdfe_lib/mdfe_identification_spec.rb +104 -0
  33. data/spec/br_danfe/mdfe_lib/notes_spec.rb +97 -0
  34. data/spec/br_danfe/mdfe_lib/totalizer_spec.rb +63 -0
  35. data/spec/br_danfe/mdfe_lib/vehicles_spec.rb +66 -0
  36. data/spec/br_danfe/mdfe_spec.rb +46 -0
  37. data/spec/br_danfe/{danfe_lib/nfce_lib/qr_code_spec.rb → qr_code_spec.rb} +10 -5
  38. data/spec/fixtures/mdfe/lib/document#render.pdf +0 -0
  39. data/spec/fixtures/mdfe/lib/document#text.align.pdf +0 -0
  40. data/spec/fixtures/mdfe/lib/document#text.pad.pdf +0 -0
  41. data/spec/fixtures/mdfe/lib/document#text.simple.pdf +0 -0
  42. data/spec/fixtures/mdfe/lib/document#text.size.pdf +0 -0
  43. data/spec/fixtures/mdfe/lib/document#text.style.pdf +0 -0
  44. data/spec/fixtures/mdfe/lib/fisco_control#barcode.pdf +0 -0
  45. data/spec/fixtures/mdfe/lib/header#render-qr-code.pdf +0 -0
  46. data/spec/fixtures/mdfe/lib/header#render-with_logo.pdf +0 -0
  47. data/spec/fixtures/mdfe/lib/header#render-without_logo.pdf +0 -0
  48. data/spec/fixtures/mdfe/lib/mdfe-with-multiple-pages.pdf +1945 -0
  49. data/spec/fixtures/mdfe/lib/mdfe.fixture.pdf +1286 -0
  50. data/spec/fixtures/mdfe/lib/notes#render-big-aditional-information.pdf +1943 -0
  51. data/spec/fixtures/mdfe/lib/xml-mdfe-multiples-pages.xml +116 -0
  52. data/spec/fixtures/mdfe/lib/xml-mdfe.xml +250 -0
  53. data/spec/fixtures/qr_code/qr_code#render.pdf +0 -0
  54. metadata +83 -34
  55. data/lib/br_danfe/danfe_lib/nfce_lib/qr_code.rb +0 -25
  56. data/lib/br_danfe/danfe_lib/nfe_lib/cep.rb +0 -11
  57. data/spec/br_danfe/danfe_lib/nfe_lib/cep_spec.rb +0 -10
@@ -0,0 +1,40 @@
1
+ module BrDanfe
2
+ module MdfeLib
3
+ class FiscoControl
4
+ def initialize(pdf, xml)
5
+ @pdf = pdf
6
+ @xml = xml
7
+ end
8
+
9
+ def generate
10
+ title
11
+ barcode
12
+ generate_nfe_key
13
+ end
14
+
15
+ private
16
+
17
+ def title
18
+ title = 'CONTROLE DO FISCO'
19
+
20
+ @pdf.text_box(title, size: 9, align: :left, at: [250, 600])
21
+ end
22
+
23
+ def barcode
24
+ barcode = Barby::Code128C.new(nfe_key)
25
+ barcode.annotate_pdf(@pdf, x: 250, y: 530, height: 50)
26
+ end
27
+
28
+ def nfe_key
29
+ @xml['mdfeProc > protMDFe > infProt > chMDFe']
30
+ end
31
+
32
+ def generate_nfe_key
33
+ title = 'Chave de Acesso'
34
+
35
+ @pdf.text_box(title, size: 9, align: :left, style: :bold, at: [250, 510])
36
+ @pdf.text_box(nfe_key, size: 11, align: :left, at: [250, 500])
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,78 @@
1
+ module BrDanfe
2
+ module MdfeLib
3
+ class Header
4
+ def initialize(pdf, xml, logo, logo_dimensions)
5
+ @pdf = pdf
6
+ @xml = xml
7
+ @logo = logo
8
+ @logo_dimensions = logo_dimensions
9
+ end
10
+
11
+ def generate
12
+ generate_emit
13
+ generate_title
14
+ qr_code
15
+ end
16
+
17
+ private
18
+
19
+ def generate_emit
20
+ if @logo.present?
21
+ company(x: 90)
22
+ logo
23
+ else
24
+ company(x: 0)
25
+ end
26
+ end
27
+
28
+ def company(x:)
29
+ @pdf.bounding_box([x, @pdf.cursor], width: 280, height: 80) do
30
+ @pdf.text(@xml['emit/xNome'], size: 12, style: :bold, align: :left)
31
+ @pdf.text(address, align: :left, size: 9)
32
+ @pdf.text(company_informations, size: 9, align: :left, inline_format: true)
33
+ end
34
+ end
35
+
36
+ def address
37
+ formatted = @xml['enderEmit/xLgr'] + ', nº ' + @xml['enderEmit/nro'] + "\n"
38
+ formatted += @xml['enderEmit/xMun'] + ' - ' + @xml['enderEmit/UF'] + ' ' + 'CEP ' + cep + "\n"
39
+ formatted
40
+ end
41
+
42
+ def cep
43
+ Helper.format_cep(@xml['enderEmit/CEP'])
44
+ end
45
+
46
+ def company_informations
47
+ '<b>CNPJ: </b>' + @xml['emit/CNPJ'] + ' ' + '<b>IE: </b>' + @xml['emit/IE']
48
+ end
49
+
50
+ def logo
51
+ bounding_box_size = 80
52
+ logo_options = BrDanfe::Logo::Options.new(bounding_box_size, @logo_dimensions).options
53
+
54
+ @pdf.bounding_box([0, 770], width: bounding_box_size, height: bounding_box_size) do
55
+ @pdf.image @logo, logo_options
56
+ end
57
+ end
58
+
59
+ def generate_title
60
+ title = '<b>DAMDFE</b> - Documento Auxiliar de Manifesto Eletrônico de Documentos Fiscais'
61
+
62
+ @pdf.move_down 15
63
+ @pdf.bounding_box([0, @pdf.cursor], width: 500, height: 20) do
64
+ @pdf.text(title, size: 12, align: :left, inline_format: true)
65
+ end
66
+ end
67
+
68
+ def qr_code
69
+ box_size = 40.mm
70
+ security_margin = box_size + box_size / 10.0
71
+
72
+ @pdf.bounding_box([414, 780], width: security_margin, height: security_margin) do
73
+ BrDanfe::QrCode.new(pdf: @pdf, xml: @xml, qr_code_tag: @xml['qrCodMDFe'], box_size: box_size).render
74
+ end
75
+ end
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,69 @@
1
+ module BrDanfe
2
+ module MdfeLib
3
+ class MdfeIdentification
4
+ def initialize(pdf, xml)
5
+ @pdf = pdf
6
+ @xml = xml
7
+ end
8
+
9
+ def generate(page)
10
+ model
11
+ series
12
+ number
13
+ number_of_pages(page)
14
+ emitted_at
15
+ origin_uf
16
+ destination_uf
17
+ end
18
+
19
+ private
20
+
21
+ def model
22
+ generate_box('Modelo', @xml['ide/mod'], 0)
23
+ end
24
+
25
+ def generate_box(title, text, x_position, width = 40)
26
+ @pdf.move_cursor_to 655
27
+
28
+ @pdf.stroke do
29
+ @pdf.fill_color LIGHT_GRAY_COLOR
30
+ @pdf.fill_rectangle([x_position, @pdf.cursor], width, 35)
31
+ @pdf.fill_color BLACK_COLOR
32
+ end
33
+
34
+ @pdf.bounding_box([x_position, @pdf.cursor], width: width, height: 35) do
35
+ @pdf.move_down 5
36
+ @pdf.text_box(title, size: 10, at: [3, @pdf.cursor])
37
+ @pdf.move_down 15
38
+ @pdf.text_box(text, size: 12, at: [3, @pdf.cursor])
39
+ end
40
+ end
41
+
42
+ def series
43
+ generate_box('Série', @xml['ide/serie'], 40)
44
+ end
45
+
46
+ def number
47
+ generate_box('Número', @xml['ide/nMDF'], 80)
48
+ end
49
+
50
+ def number_of_pages(page)
51
+ text = page.to_s + '/' + @pdf.page_count.to_s
52
+ generate_box('FL', text, 125)
53
+ end
54
+
55
+ def emitted_at
56
+ date = Helper.format_datetime(@xml['dhEmi'])
57
+ generate_box('Data e hora de Emissão', date, 170, 110)
58
+ end
59
+
60
+ def origin_uf
61
+ generate_box('UF Carreg.', @xml['ide/UFIni'], 285, 70)
62
+ end
63
+
64
+ def destination_uf
65
+ generate_box('UF Descarreg.', @xml['ide/UFFim'], 345, 70)
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,94 @@
1
+ module BrDanfe
2
+ module MdfeLib
3
+ class Notes
4
+ MAX_CHARS_ON_LINE = 120
5
+
6
+ def initialize(pdf, xml)
7
+ @pdf = pdf
8
+ @xml = xml
9
+ end
10
+
11
+ def generate
12
+ title
13
+ aditional_information_first_page
14
+ aditional_information_taxpayer
15
+ end
16
+
17
+ private
18
+
19
+ def title
20
+ title = 'Observações'
21
+
22
+ @pdf.move_down 20
23
+ @pdf.bounding_box [0, @pdf.cursor], width: 278, height: 20 do
24
+ @pdf.stroke_color GRAY_COLOR
25
+ @pdf.stroke_horizontal_line(0, 526, at: 6)
26
+ @pdf.text(title, size: 12, align: :left, style: :bold)
27
+ end
28
+ end
29
+
30
+ def aditional_information_first_page
31
+ fisco_information = @xml['infAdic/infAdFisco']
32
+
33
+ fisco_information_title = 'INFORMAÇÕES ADICIONAIS DE INTERESSE DO FISCO'
34
+ taxpayer_information_title = 'INFORMAÇÕES ADICIONAIS DE INTERESSE DO CONTRIBUINTE'
35
+
36
+ @pdf.bounding_box([0, @pdf.cursor], width: 526) do
37
+ if fisco_information.present?
38
+ @pdf.text(fisco_information_title, size: 10)
39
+ @pdf.move_down 5
40
+ @pdf.text(fisco_information, size: 10)
41
+ @pdf.move_down 10
42
+ end
43
+
44
+ if taxpayer_information_xml.present?
45
+ @pdf.text(taxpayer_information_title, size: 10)
46
+ @pdf.move_down 5
47
+ end
48
+ end
49
+ end
50
+
51
+ def taxpayer_information_xml
52
+ @taxpayer_information_xml ||= @xml['infAdic/infCpl']
53
+ end
54
+
55
+ def aditional_information_taxpayer
56
+ first_page, next_page = fill_aditional_information_taxpayer(@pdf.cursor).values_at(:first_page, :next_page)
57
+
58
+ generate_aditional_information_taxpayer(first_page, @pdf.cursor)
59
+
60
+ if next_page.present?
61
+ @pdf.start_new_page
62
+ generate_aditional_information_taxpayer(next_page, y_position_next_pages)
63
+ end
64
+ end
65
+
66
+ def fill_aditional_information_taxpayer(height_on_first_page)
67
+ first_page = ''
68
+ next_page = ''
69
+ total_chars = 0
70
+ total_height = 0
71
+
72
+ taxpayer_information_xml.split.each do |word|
73
+ total_chars += word.split.count + 1
74
+
75
+ total_height += 10 if total_chars >= MAX_CHARS_ON_LINE
76
+
77
+ total_height <= height_on_first_page ? first_page += word + ' ' : next_page += word + ' '
78
+ end
79
+
80
+ { first_page: first_page, next_page: next_page }
81
+ end
82
+
83
+ def generate_aditional_information_taxpayer(data, position)
84
+ @pdf.bounding_box([0, position], width: 526) do
85
+ @pdf.text(data, size: 10)
86
+ end
87
+ end
88
+
89
+ def y_position_next_pages
90
+ 600
91
+ end
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,57 @@
1
+ module BrDanfe
2
+ module MdfeLib
3
+ class Totalizer
4
+ def initialize(pdf, xml)
5
+ @pdf = pdf
6
+ @xml = xml
7
+ end
8
+
9
+ def generate
10
+ generate_title
11
+ nfe_quantity
12
+ cte_quantity
13
+ total_weight
14
+ end
15
+
16
+ private
17
+
18
+ def generate_title
19
+ title = 'Modelo Rodoviário de Carga'
20
+
21
+ @pdf.text_box(title, size: 12, align: :left, style: :bold, at: [0, 600])
22
+ end
23
+
24
+ def nfe_quantity
25
+ generate_box('QTD. NFe', @xml['qNFe'], 65)
26
+ end
27
+
28
+ def generate_box(title, text, x_position, width = 60)
29
+ @pdf.move_cursor_to 580
30
+
31
+ @pdf.stroke do
32
+ @pdf.fill_color LIGHT_GRAY_COLOR
33
+ @pdf.fill_rectangle([x_position, @pdf.cursor], width, 35)
34
+ @pdf.fill_color BLACK_COLOR
35
+ end
36
+
37
+ @pdf.bounding_box([x_position, @pdf.cursor], width: width, height: 35) do
38
+ @pdf.move_down 5
39
+ @pdf.text_box(title, size: 10, at: [3, @pdf.cursor])
40
+ @pdf.move_down 15
41
+ @pdf.text_box(text, size: 12, at: [3, @pdf.cursor])
42
+ end
43
+ end
44
+
45
+ def cte_quantity
46
+ generate_box('QTD. CTe', '', 0)
47
+ end
48
+
49
+ def total_weight
50
+ weight = ActiveSupport::NumberHelper.number_to_rounded(@xml['qCarga'], precision: 2)
51
+ weight = Helper.numerify(weight)
52
+
53
+ generate_box('Peso total (Kg)', weight, 130, 90)
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,80 @@
1
+ module BrDanfe
2
+ module MdfeLib
3
+ class Vehicles
4
+ def initialize(pdf, xml)
5
+ @pdf = pdf
6
+ @xml = xml
7
+ end
8
+
9
+ def generate
10
+ @pdf.move_cursor_to 460
11
+ title
12
+ table_titles
13
+ generate_vehicles
14
+ end
15
+
16
+ private
17
+
18
+ def title
19
+ title = 'Veículo'
20
+
21
+ @pdf.text_box(title, size: 12, align: :left, style: :bold, at: [0, @pdf.cursor])
22
+ end
23
+
24
+ def table_titles
25
+ plate = 'Placa'
26
+ rntrc = 'RNTRC'
27
+
28
+ @pdf.stroke_color GRAY_COLOR
29
+ @pdf.move_down 20
30
+ @pdf.text_box(plate, size: 9, align: :left, at: [0, @pdf.cursor])
31
+ @pdf.text_box(rntrc, size: 9, align: :left, at: [100, @pdf.cursor])
32
+ @pdf.move_down 10
33
+ @pdf.stroke_horizontal_line(0, 190, at: @pdf.cursor)
34
+ end
35
+
36
+ def generate_vehicles
37
+ @pdf.move_cursor_to 425
38
+
39
+ vehicles.each_with_index do |cell, index|
40
+ @pdf.bounding_box [0, @pdf.cursor], width: 190, height: 20 do
41
+ @pdf.stroke_color GRAY_COLOR
42
+ @pdf.dash([2], phase: 6)
43
+ @pdf.stroke_horizontal_line(0, 190, at: 26) unless index.zero?
44
+ @pdf.text cell[:content], cell[:options]
45
+ @pdf.undash
46
+ @pdf.move_down 20
47
+ end
48
+ end
49
+ end
50
+
51
+ def vehicles
52
+ vehicle = []
53
+ vehicle += collect_vehicles('veicReboque')
54
+ vehicle += collect_vehicles('veicTracao')
55
+
56
+ vehicle
57
+ end
58
+
59
+ def collect_vehicles(tag)
60
+ vehicles = []
61
+ @xml.collect('xmlns', tag) { |rodo| vehicles += vehicle(rodo) }
62
+
63
+ vehicles
64
+ end
65
+
66
+ def vehicle(rodo)
67
+ [
68
+ cell_text(rodo.css('placa').text)
69
+ ]
70
+ end
71
+
72
+ def cell_text(text, options = {})
73
+ cell = { content: text, options: { size: 12 } }
74
+ cell[:options].merge!(options)
75
+
76
+ cell
77
+ end
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,22 @@
1
+ module BrDanfe
2
+ class QrCode
3
+ require 'rqrcode'
4
+ require 'chunky_png'
5
+ require 'tempfile'
6
+
7
+ def initialize(pdf:, xml:, qr_code_tag:, box_size:)
8
+ @pdf = pdf
9
+ @xml = xml
10
+ @qr_code_tag = qr_code_tag
11
+ @box_size = box_size
12
+ end
13
+
14
+ def render
15
+ qrcode = RQRCode::QRCode.new(@qr_code_tag)
16
+ image = Tempfile.new(%w[qrcode png], binmode: true)
17
+ image.write(qrcode.as_png(module_px_size: 12).to_s)
18
+
19
+ @pdf.image(image, width: @box_size, height: @box_size, position: :center)
20
+ end
21
+ end
22
+ end