require "prawn/measurement_extensions" module Dorsale module BillingMachine class InvoiceSingleVatPdf < Prawn::Document include Dorsale::Alexandrie::Prawn include Dorsale::AllHelpers include ActionView::Helpers::NumberHelper DEBUG = false GREY = "808080" LIGHT_GREY = "C0C0C0" WHITE = "FFFFFF" BLACK = "000000" BLUE = "005F9E" def bm_currency(n) currency(n, Dorsale::BillingMachine.default_currency) end def has_advance main_document.try(:advance) && main_document.advance != 0.0 end def has_discount main_document.try(:commercial_discount) && main_document.commercial_discount != 0.0 end attr_reader :main_document def initialize(main_document) super(page_size: 'A4', margin: 1.cm) setup @main_document = main_document @id_card = main_document.id_card end def header_height; 90.mm; end def logo_height; 32.mm; end def logo_width; 50.mm; end def footer_height; 40.mm; end def footer_top_height; 15.mm; end def footer_bottom_height; 15.mm; end def middle_height; 14.cm; end def products_table_height; 90.mm; end def first_column_width; 7.6.cm; end def second_column_width; 2.4.cm; end def third_column_width; 2.5.cm; end def fourth_column_width; 2.9.cm; end def last_column_width bounds.width - first_column_width - second_column_width - third_column_width - fourth_column_width end def build repeat :all do build_header build_footer end build_middle build_page_numbers end def setup font_root = ::Dorsale::Engine.root.join("app/assets/fonts") font_families.update( "BryantPro" => { normal: "#{font_root}/BryantPro-Regular.ttf", bold: "#{font_root}/BryantPro-Bold.ttf", } ) font("BryantPro") font_size 10 end def build_header bounding_box [0, bounds.top], width: bounds.width, height: header_height do draw_bounds_debug build_title build_logo build_contact build_subject build_customer end end def build_title top = bounds.top - 1.5.cm left = bounds.left width = bounds.width height = 1.cm bounding_box [left, top], width: width, height: height do draw_bounds_debug text "#{main_document.t} n° #{main_document.tracking_id}", inline_format: true, size: 20, align: :center end end def logo_path main_document.id_card.logo.path rescue NoMethodError nil end def build_logo return if logo_path.nil? height = logo_height width = logo_width bounding_box [bounds.left, bounds.top], width: width, height: height do draw_bounds_debug image logo_path, fit: [width, height] end end def contact_address_line [ @id_card.address1, @id_card.address2, @id_card.zip, @id_card.city, ].select(&:present?).join(", ") end def contact_content content = [] content << "#{@id_card.entity_name}" if @id_card.entity_name.present? content << "#{contact_address_line}" if contact_address_line.present? content << " " content << "#{main_document.t(:your_contact)} : #{@id_card.contact_full_name}" if @id_card.contact_full_name.present? content << "#{main_document.t(:contact_phone)} : #{@id_card.contact_phone}" if @id_card.contact_phone.present? content << "#{main_document.t(:contact_fax)} : #{@id_card.contact_fax}" if @id_card.contact_fax.present? content << "#{main_document.t(:contact_email)} : #{@id_card.contact_email}" if @id_card.contact_email.present? content.join("\n") end def build_contact top = bounds.top - 4.cm left = bounds.left width = bounds.width / 2 - 1.1.cm height = 2.5.cm bounding_box [left, top], width: width, height: height do draw_bounds_debug text contact_content, inline_format: true, size: 8 end end def build_subject top = bounds.top - 7.5.cm left = bounds.left width = bounds.width / 2 - 1.1.cm height = 15.mm bounding_box [left, top], width: width, height: height do draw_bounds_debug if main_document.label.present? text "#{main_document.t(:label)} : #{main_document.label}", inline_format: true end if main_document.date.present? move_down 3.mm text "#{main_document.t(:date)} : #{date main_document.date}", inline_format: true end end end def customer_content return if main_document.customer.nil? content = [] content << main_document.customer.name content << main_document.customer.address.street content << main_document.customer.address.street_bis content << "#{main_document.customer.address.zip} #{main_document.customer.address.city}" content << main_document.customer.address.country if main_document.customer.try(:european_union_vat_number).present? content << main_document.customer.t(:european_union_vat_number) content << main_document.customer.european_union_vat_number end content.select(&:present?).join("\n") end def build_customer top = bounds.top - 4.cm left = bounds.width / 2 + 1.1.cm width = bounds.width / 2 - 1.1.cm height = 4.5.cm padding = 3.mm bounding_box [left, top], width: width, height: height do draw_bounds_debug stroke do fill_color LIGHT_GREY fill_rounded_rectangle [cursor-bounds.height,cursor], bounds.width, bounds.height, 0 fill_color BLACK end bounding_box [bounds.left + padding, bounds.top - padding], height: bounds.height - padding, width: bounds.width - padding do text customer_content end end end # def build_customer def build_middle left = bounds.left top = bounds.top - header_height width = bounds.width - left height = middle_height bounding_box [left, top], width: width, height: height do build_table build_total build_payment_conditions build_bank_informations build_expiry build_comments end end def build_table left = bounds.left top = bounds.top width = bounds.width - left height = products_table_height # Empty table to draw lines bounding_box [left, top], width: width, height: height do repeat :all do float do table [["","","","",""]], :column_widths => [ first_column_width, second_column_width, third_column_width, fourth_column_width, last_column_width, ], :cell_style => {height: height} \ do row(0).style :text_color => BLACK row(0).style :font_style => :bold column(0).style :align => :left column(1..4).style :align => :right end # table end # float end # repeat all end # bounding_box # products table bounding_box [left, top], width: width, height: height do draw_bounds_debug table_products = [[ main_document.t(:designation).mb_chars.upcase.to_s, main_document.t(:quantity).mb_chars.upcase.to_s, main_document.t(:unit).mb_chars.upcase.to_s, main_document.t(:unit_price).mb_chars.upcase.to_s, main_document.t(:line_total).mb_chars.upcase.to_s, ]] main_document.lines.each do |line| table_products.push [ line.label, number(line.quantity).gsub(",00","").gsub(".00",""), line.unit, bm_currency(line.unit_price), bm_currency(line.total), ] end table table_products, :column_widths => [ first_column_width, second_column_width, third_column_width, fourth_column_width, last_column_width, ], :header => true, :cell_style => {border_width: 0} \ do row(0).font_style = :bold row(0).border_width = 1, cells.style { |c| c.align = c.column == 0 ? :left : :right } end # table end # bounding_box end # build_table def build_total left = bounds.left top = bounds.top - products_table_height - 5.mm width = bounds.width - left height = middle_height - products_table_height bounding_box [left, top], width: width, height: height do draw_bounds_debug table_totals = [[]] if has_discount table_totals.push [ main_document.t(:commercial_discount).mb_chars.upcase.to_s, bm_currency(-main_document.commercial_discount), ] end table_totals.push [ main_document.t(:total_excluding_taxes).mb_chars.upcase.to_s, bm_currency(main_document.total_excluding_taxes), ] vat_rate = number(main_document.vat_rate) table_totals.push [ "#{main_document.t(:vat).mb_chars.upcase.to_s} #{percentage vat_rate}", bm_currency(main_document.vat_amount), ] if has_advance table_totals.push [ main_document.t(:advance).mb_chars.upcase.to_s, bm_currency(main_document.advance), ] table_totals.push [ main_document.t(:total_including_taxes).mb_chars.upcase.to_s, bm_currency(main_document.balance), ] else table_totals.push [ main_document.t(:total_including_taxes).mb_chars.upcase.to_s, bm_currency(main_document.total_including_taxes), ] end table table_totals, :column_widths => [fourth_column_width, last_column_width], :cell_style => {border_width: [0, 1, 0 ,0]}, :position => :right do row(-1).style :font_style => :bold column(0).padding_right = 0.2.cm row(-1).borders = [:top, :right] row(-1).border_width = 1 cells.style do |c| c.align = :right end end stroke do rectangle [(bounds.right - fourth_column_width - last_column_width), bounds.top], (fourth_column_width + last_column_width), (bounds.top-cursor) end end end def build_comments return if main_document.comments.blank? top = bounds.top - products_table_height - 35.mm height = top - bounds.bottom width = 10.cm text_box main_document.comments, :at => [bounds.left, top], :height => height, :width => width, :overflow => :shrink_to_fit, :size => 9 end def build_payment_conditions return if main_document.payment_term.blank? top = bounds.top - products_table_height - 5.mm height = 15.mm width = 7.5.cm txt = "#{main_document.t(:payment_terms)} : #{main_document.payment_term}" text_box txt, :at => [bounds.left, top], :height => height, :width => width, :overflow => :shrink_to_fit, :inline_format => true, :size => 9 end def build_expiry end def build_bank_informations top = bounds.top - products_table_height - 20.mm height = 1.cm width = 7.5.cm bounding_box [bounds.left, top], height: height, width: width do draw_bounds_debug font_size 9 do text "#{main_document.t(:iban)} : #{@id_card.iban}" if @id_card.iban.present? text "#{main_document.t(:bic_swift)} : #{@id_card.bic_swift}" if @id_card.bic_swift.present? end end end def build_footer top = bounds.bottom + footer_height bounding_box [0, top], width: bounds.width, height: footer_height do draw_bounds_debug build_footer_top build_footer_line build_footer_bottom end end def footer_top_content @id_card.custom_info_1.to_s end def build_footer_top top = bounds.top height = footer_top_height width = bounds.width text_box footer_top_content, :at => [bounds.left, top], :height => height, :width => width, :overflow => :shrink_to_fit, :size => 9 end def build_footer_line # Center line between the two footer parts n = footer_top_height + (footer_height - footer_top_height - footer_bottom_height) / 2 move_down n stroke do horizontal_rule line_width 1 end end def footer_bottom_content infos_text = [] infos_text << @id_card.entity_name if @id_card.entity_name.present? infos_text << "#{main_document.t(:info_phone)} : #{@id_card.contact_phone}" if @id_card.contact_phone.present? infos_text << "#{main_document.t(:info_fax)} : #{@id_card.contact_fax}" if @id_card.contact_fax.present? infos_text << "#{@id_card.contact_email}" if @id_card.contact_email.present? infos_text << "#{@id_card.legal_form.to_s}" if @id_card.legal_form.present? infos_text << "#{main_document.t(:capital)} : #{bm_currency @id_card.capital}" if @id_card.capital.present? infos_text << "#{main_document.t(:registration)} : #{@id_card.registration_city} #{@id_card.registration_number}" if @id_card.registration_number.present? infos_text << "#{main_document.t(:siret)} : #{@id_card.siret}" if @id_card.siret.present? infos_text << "#{main_document.t(:intracommunity_vat)} : #{@id_card.intracommunity_vat}" if @id_card.intracommunity_vat.present? infos_text = infos_text.join(" - ") end def build_footer_bottom height = footer_bottom_height top = bounds.bottom + height width = bounds.width text_box footer_bottom_content, :at => [bounds.left, top], :height => height, :width => width, :overflow => :shrink_to_fit, :size => 9 end def build_page_numbers height = 5.mm top = bounds.bottom + height width = bounds.width bounding_box [0, top], height: height, width: bounds.width do float do number_pages "page /", align: :right, size: 9 end end end def draw_bounds_debug transparent(0.5) { stroke_bounds } if DEBUG end end end end