Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Canceled watermark support #219

Open
wants to merge 10 commits into
base: master
Choose a base branch
from
12 changes: 11 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,16 @@ XML version | Supported?
end
end

#### Usage with cancellation event

xml = File.read("spec/fixtures/nfe/v3.10/nfe_simples_nacional_authorized.xml")
xml_event = File.read("spec/fixtures/nfe/v3.10/nfe_simples_nacional_cancellation_event.xml")

danfe = BrDanfe::Danfe.new([xml, xml_event])
danfe.options.logo = "spec/fixtures/logo.png"
danfe.options.logo_dimensions = { width: 100, height: 100 }
danfe.save_pdf("output.pdf")

### Options

* `logo_path`: Path of sender's logo image.
Expand Down Expand Up @@ -267,4 +277,4 @@ This project is based on [Ruby DANFE gem](http://github.com/taxweb/ruby_danfe).

## License

BrDanfe is released under the [MIT License](http://www.opensource.org/licenses/MIT).
BrDanfe is released under the [MIT License](http://www.opensource.org/licenses/MIT).
1 change: 1 addition & 0 deletions config/locales/pt-BR.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ pt-BR:
infProt: "PROTOCOLO DE AUTORIZAÇÃO DE USO"
others:
has_no_fiscal_value: "SEM VALOR FISCAL"
canceled: "CANCELADA"
page: "FOLHA %{current} de %{total}"
danfe: "DOCUMENTO AUXILIAR DA NOTA FISCAL ELETRÔNICA"
sefaz: "Consulta de autenticidade no portal nacional da NF-e www.nfe.fazenda.gov.br/portal ou no site da Sefaz Autorizadora"
Expand Down
26 changes: 25 additions & 1 deletion lib/br_danfe/danfe_lib/base.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ class Base
attr_reader :options

def initialize(xmls)
@xmls = xmls
@xmls = group_xmls(xmls)
@document = document
@options = BrDanfe::Logo::Config.new

Expand All @@ -28,6 +28,30 @@ def document; end
def create_watermark; end

def generate(footer_info); end

def group_xmls(xmls)
xml_list = {}

xmls.each do |xml|
xml_key = BrDanfe::Helper.xml_key(xml)
is_event = BrDanfe::Helper.event?(xml)

group_xmls_by_key(xml_list, xml, xml_key, is_event)
end

filter_xml_list(xml_list)
end

def group_xmls_by_key(xml_list, xml, xml_key, is_event)
xml_list[xml_key] ||= { xml: nil, events: [] }
xml_list[xml_key][:xml] = xml unless is_event
xml_list[xml_key][:events] << xml if is_event
end

def filter_xml_list(xml_list)
xml_list.reject { |_key, data| data[:xml].nil? }
.map { |_key, data| [data[:xml], data[:events]] }
end
end
end
end
47 changes: 32 additions & 15 deletions lib/br_danfe/danfe_lib/nfce.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,22 +11,14 @@ def document
end

def create_watermark
@document.create_stamp('has_no_fiscal_value') do
@document.fill_color '7d7d7d'
@document.text_box(
I18n.t('danfe.others.has_no_fiscal_value'),
size: 0.8.cm,
width: 10.cm,
height: 1.2.cm,
at: [0, PAGE_HEIGHT - 3.8.cm],
rotate: 45,
rotate_around: :center
)
end
create_stamp('canceled', size: 1.1.cm, width: 12.cm, at: [-0.5.cm, PAGE_HEIGHT - 5.cm])
create_stamp('has_no_fiscal_value', size: 0.8.cm)
end

def generate(footer_info)
@xmls.each do |xml|
@xmls.each do |xmls|
xml, event_xmls = xmls

NfceLib::Header.new(@document, xml, @options.logo, @options.logo_dimensions).render
NfceLib::ProductList.new(@document, xml).render
NfceLib::TotalList.new(@document, xml).render
Expand All @@ -37,16 +29,41 @@ def generate(footer_info)
NfceLib::Footer.new(@document, xml).render(footer_info)

render_no_fiscal_value(xml)
render_canceled(xml, event_xmls)
resize_page_height
end

@document
end

def resize_page_height
@document.page.dictionary.data[:MediaBox] = [0, @document.y - 10, PAGE_WIDTH, PAGE_HEIGHT]
end

def render_no_fiscal_value(xml)
@document.stamp('has_no_fiscal_value') if BrDanfe::Helper.unauthorized?(xml)
end

def resize_page_height
@document.page.dictionary.data[:MediaBox] = [0, @document.y - 10, PAGE_WIDTH, PAGE_HEIGHT]
def render_canceled(xml, event_xmls)
@document.stamp('canceled') if BrDanfe::Helper.canceled?(xml, event_xmls)
end

def create_stamp(name, extra_config = {})
@document.create_stamp(name) do
@document.fill_color '7d7d7d'
@document.transparent(0.5) do
@document.text_box(
I18n.t("danfe.others.#{name}"),
{
width: 10.cm,
height: 1.2.cm,
at: [0, PAGE_HEIGHT - 3.8.cm],
rotate: 45,
rotate_around: :center
}.merge(extra_config)
)
end
end
end
end
end
Expand Down
58 changes: 36 additions & 22 deletions lib/br_danfe/danfe_lib/nfe.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,31 +8,21 @@ def document
end

def create_watermark
@document.create_stamp('has_no_fiscal_value') do
@document.fill_color '7d7d7d'
@document.text_box(
I18n.t('danfe.others.has_no_fiscal_value'),
size: 2.2.cm,
width: @document.bounds.width,
height: @document.bounds.height,
align: :center,
valign: :center,
at: [0, @document.bounds.height],
rotate: 45,
rotate_around: :center
)
end
create_stamp('canceled', size: 3.4.cm)
create_stamp('has_no_fiscal_value', size: 2.2.cm)
end

def generate(footer_info)
last_index = @xmls.size - 1

@xmls.each_with_index do |xml, index|
@xmls.each_with_index do |xmls, index|
xml, event_xmls = xmls

break unless BrDanfe::Helper.nfe?(xml)

initial_number_of_pages = @document.page_count
render_on_first_page(xml)
render_on_each_page(footer_info, xml, initial_number_of_pages)
render_on_each_page(footer_info, xml, event_xmls, initial_number_of_pages)
@document.start_new_page unless index == last_index
end

Expand All @@ -56,26 +46,26 @@ def render_products(has_issqn, xml)
NfeLib::DetBody.new(@document, xml).render(has_issqn)
end

def render_on_each_page(footer_info, xml, initial_number_of_pages)
def render_on_each_page(footer_info, xml, event_xmls, initial_number_of_pages)
total_pages = @document.page_count + 1 - initial_number_of_pages

emitter = NfeLib::EmitHeader.new(@document, xml, @options.logo, @options.logo_dimensions)

total_pages.times do |page_index|
page = page_index + initial_number_of_pages

position = page_index + 1 == 1 ? 3.96 : 1.85
repeated_information(page, position, emitter, footer_info, xml, total_pages, page_index + 1)

@document.go_to_page(page)
repeated_information(position, emitter, footer_info, xml, event_xmls, total_pages, page_index + 1)
end
end

def repeated_information(page, y_position, emitter, footer_info, xml, total_pages, initial_page_of_pdf)
@document.go_to_page(page)

def repeated_information(y_position, emitter, footer_info, xml, event_xmls, total_pages, initial_page_of_pdf)
emitter.render(initial_page_of_pdf, y_position, total_pages)
render_product_table_title initial_page_of_pdf
render_footer_information footer_info
render_no_fiscal_value(xml)
render_canceled(xml, event_xmls)
end

def render_product_table_title(page)
Expand All @@ -92,6 +82,30 @@ def render_footer_information(footer_info)
def render_no_fiscal_value(xml)
@document.stamp('has_no_fiscal_value') if BrDanfe::Helper.no_fiscal_value?(xml)
end

def render_canceled(xml, event_xmls)
@document.stamp('canceled') if BrDanfe::Helper.canceled?(xml, event_xmls)
end

def create_stamp(name, extra_config = {})
@document.create_stamp(name) do
@document.fill_color '7d7d7d'
@document.transparent(0.5) do
@document.text_box(
I18n.t("danfe.others.#{name}"),
{
width: @document.bounds.width,
height: @document.bounds.height,
align: :center,
valign: :center,
at: [0, @document.bounds.height],
rotate: 45,
rotate_around: :center
}.merge(extra_config)
)
end
end
end
end
end
end
24 changes: 24 additions & 0 deletions lib/br_danfe/helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,14 @@ def self.unauthorized?(xml)
xml.css('nfeProc/protNFe/infProt/dhRecbto').empty?
end

def self.authorized?(xml)
!unauthorized?(xml)
end

def self.canceled?(xml, event_xmls)
authorized?(xml) && cancellation_event_any?(event_xmls)
end

def self.numerify(number)
return '' if !number || number == ''

Expand All @@ -34,6 +42,22 @@ def self.nfe?(xml)
xml['ide > mod'] == nfe_code
end

def self.event?(xml)
xml['evento > infEvento'].present?
end

def self.cancellation_event?(xml)
xml['evento > infEvento > tpEvento'] == '110111'
end

def self.cancellation_event_any?(xmls)
xmls.any? { |xml| cancellation_event?(xml) }
end

def self.xml_key(xml)
event?(xml) ? xml['evento > infEvento > chNFe'] : xml.css('infNFe').attr('Id').to_s.gsub(/[^\d]/, '')
end

def self.format_cep(cep)
cep.sub(/(\d{2})(\d{3})(\d{3})/, '\\1.\\2-\\3')
end
Expand Down
18 changes: 17 additions & 1 deletion spec/br_danfe/danfe_lib/nfce_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,20 @@
expect("#{base_dir}saved_nfce.fixture.pdf").to have_same_content_of file: output_pdf
end

context 'when nfc-e is canceled' do
let(:xml) { BrDanfe::XML.new(File.read("#{base_dir}nfce-authorized.xml")) }
let(:xml_event) { BrDanfe::XML.new(File.read("#{base_dir}nfce-authorized-cancellation-event.xml")) }

subject { described_class.new [xml, xml_event] }

it 'saves the NFC-e as pdf' do
expect(File.exist?(output_pdf)).to be_falsey
subject.save_pdf output_pdf

expect("#{base_dir}nfce-authorized.fixture.canceled.pdf").to have_same_content_of file: output_pdf
end
end

context 'when nfc-e is unauthorized' do
context 'when nfc-e is in homologation environment' do
let(:xml) { BrDanfe::XML.new(File.read("#{base_dir}nfce-unauthorized-hom.xml")) }
Expand All @@ -56,8 +70,10 @@
end

context 'when there is more than one xml' do
let(:xml2) { BrDanfe::XML.new(File.read("#{base_dir}nfce-authorized.xml")) }

it 'renders multiple danfes on the same pdf' do
subject = described_class.new [xml, xml]
subject = described_class.new [xml, xml2]

expect(File.exist?(output_pdf)).to be_falsey
subject.save_pdf output_pdf
Expand Down
36 changes: 35 additions & 1 deletion spec/br_danfe/danfe_lib/nfe_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,19 @@
expected = IO.binread("#{base_dir}nfe_simples_nacional.xml.fixture.pdf")
expect(subject.render_pdf).to eq expected
end

context 'with cancellation event' do
let(:xml_file) { File.read("#{base_dir}nfe_simples_nacional_authorized.xml") }
let(:xml_event_file) { File.read("#{base_dir}nfe_simples_nacional_cancellation_event.xml") }
let(:xml_event) { BrDanfe::XML.new(xml_event_file) }

subject { described_class.new [xml, xml_event] }

it 'renders a canceled Simples Nacional NF-e using CSOSN' do
expected = IO.binread("#{base_dir}nfe_simples_nacional_cancellation_event.xml.fixture.pdf")
expect(subject.render_pdf).to eq expected
end
end
end

describe '#save_pdf' do
Expand Down Expand Up @@ -115,6 +128,21 @@

expect("#{base_dir}with_three_pages.fixture.pdf").to have_same_content_of file: output_pdf
end

context 'with cancellation event' do
let(:xml_file) { File.read("#{base_dir}with_three_pages_authorized.xml") }
let(:xml_event_file) { File.read("#{base_dir}with_three_pages_cancellation_event.xml") }
let(:xml_event) { BrDanfe::XML.new(xml_event_file) }

subject { described_class.new [xml, xml_event] }

it 'renders xml to the pdf' do
expect(File.exist?(output_pdf)).to be_falsey
subject.save_pdf output_pdf

expect("#{base_dir}with_three_pages_canceled.fixture.pdf").to have_same_content_of file: output_pdf
end
end
end

context 'when there is ISSQN' do
Expand Down Expand Up @@ -157,11 +185,17 @@
let(:base_dir) { './spec/fixtures/nfe/v3.10/' }
let(:xml_file) { File.read("#{base_dir}nfe_simples_nacional.xml") }

let(:xml_file2) { File.read("#{base_dir}nfe_simples_nacional_authorized.xml") }
let(:xml2) { BrDanfe::XML.new(xml_file2) }

let(:xml_file2_event) { File.read("#{base_dir}nfe_simples_nacional_cancellation_event.xml") }
let(:xml2_event) { BrDanfe::XML.new(xml_file2_event) }

before { File.delete(output_pdf) if File.exist?(output_pdf) }

it 'renders multiple danfes on the same pdf' do
expect(File.exist?(output_pdf)).to be_falsey
subject = described_class.new([xml, xml])
subject = described_class.new([xml, xml2, xml2_event])

subject.save_pdf(output_pdf)

Expand Down
Binary file modified spec/fixtures/nfce/v4.00/multiples_xmls_on_the_same_pdf.pdf
Binary file not shown.
Loading