br_danfe 0.15.1 → 0.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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