Skip to content

Instantly share code, notes, and snippets.

@j3ck
Created July 25, 2017 13:12
Show Gist options
  • Save j3ck/f0314656c15783ae5b736b6ad9844362 to your computer and use it in GitHub Desktop.
Save j3ck/f0314656c15783ae5b736b6ad9844362 to your computer and use it in GitHub Desktop.
module Chesters
class Invoice
attr_accessor :number, :phone, :fax, :account_code,
:date, :payment_due, :sub_total, :total,
:gst, :sales_despatch, :sales_order,
:customer_order, :staff, :gst_no
attr_accessor :line_items
def initialize
@line_items = []
end
end
class LineItem
attr_accessor :item_code, :description, :quantity,
:unit_amount, :price, :line_value, :list_price
end
class << self
def parse(file_path)
pdf_doc = PDF::Reader.new(file_path)
@invoices = []
pdf_doc.pages.each do |page|
@invoice = Chesters::Invoice.new
clean_page_lines = page.text.split("\n").delete_if { |val| val == "" }
@table_start = false
@table_end = false
@table_meta_done = false
clean_page_lines.each do |line|
@invoice.date ||= invoice_date(line)
@invoice.number ||= invoice_number(line, @invoice.number)
@invoice.payment_due ||= payment_date(line)
@invoice.account_code ||= customer(line)
@invoice.gst_no ||= gst_number(line)
@invoice.phone ||= phone(line)
@invoice.fax ||= fax(line)
table_start?(line)
table_end?(line)
if @table_end
@invoice.sub_total ||= sub_total(line)
@invoice.gst ||= gst(line)
@invoice.total ||= total(line)
end
# break if @table_end
unless @invoice.total.nil?
@page_end = true
break
end
@invoice.sales_despatch ||= sales_despatch(line) if @table_start && !@table_meta_done
if @table_start && !@table_meta_done
table_meta(line, @invoice)
next if @table_meta_done
end
if @table_start && @table_meta_done && !@table_end
li = build_line_item(line)
@invoice.line_items << li if li
end
end
@invoices << @invoice
end
@invoices
end
private
def table_start?(line)
if line.include_each?(['Item', 'Description', 'Quantity', 'Unit', 'Unit Price', 'Line valu', 'List Price']) ||
(!@table_start && line.include_each?(['Quantity Unit', 'Unit Price', 'Line value', 'List Price']))
@table_start = true
return true
end
false
end
def table_end?(line)
if line.include? 'For paying by Direct Credit, our bank details are'
@table_end = true
return true
end
false
end
def table_meta(line, invoice)
return nil unless line.include_each? ['Sales order', 'Customer order', 'CreatedBy']
params = line.split(/ {2,}/)
invoice.sales_order = params[1]
invoice.customer_order = params[3]
invoice.staff = params[5]
@table_meta_done = true
end
def compile(long, short)
str = long.dup
vals = short.strip.split(/ {2,}/)
vals.each do |val|
pos = short.index(val)
val.each_char do |chr|
str[pos] = chr
pos += 1
end
end
str
end
def build_line_item(line)
values = line.strip.split(/ {2,}/)
if @tmp_line
new_line = line.size > @tmp_line.size ? compile(line, @tmp_line) : compile(@tmp_line, line)
values = new_line.strip.split(/ {2,}/)
@tmp_line = nil
end
if values.size < 6 && values.size > 1
@tmp_line = line
return nil
end
return nil if values.size < 2
line_item = Chesters::LineItem.new
line_item.item_code = values[0]
line_item.description = values[1]
line_item.quantity = values[2].split(' ').first
line_item.unit_amount = values[2].split(' ').last
line_item.price = values[3]
line_item.line_value = values[4]
line_item.list_price = values[5]
defined?(line_item) ? line_item : nil
end
def invoice_number(line, invoice_number)
line[/[0-9]+/] if line.include?('Tax Invoice Number') ||
(invoice_number.nil? &&
line.split(' ').size == 2 &&
line[/ [A-Z]{2} /] &&
line[/ [0-9]+/])
end
def invoice_date(line)
line[/([0-9]{1,2}\/){2}[0-9]{4}/] if line[/Invoice date/i]
end
def payment_date(line)
line[/([0-9]{1,2}\/){2}[0-9]{4}/] if line.include? 'Payment due'
end
def customer(line)
line.split(' ').last if line.include? 'Customer'
end
def gst_number(line)
line[/[0-9]*-[0-9]*-[0-9]*/] if line.include? 'GST Number'
end
def phone(line)
line[/[0-9]{1} [0-9]{3} [0-9]{4}/] if line.include? 'Phone:'
end
def fax(line)
line[/[0-9]{1} [0-9]{3} [0-9]{4}/] if line.include? 'Fax:'
end
def sales_despatch(line)
line.split(' ').last if line.include? 'Sales despatch'
end
def sub_total(line)
line.split(' ').last(2).first if line.include?('Subtotal')
end
def gst(line)
line.split(' ').last(2).first if line.include?('GST')
end
def total(line)
line.split(' ').last(2).first if line.include?('Total')
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment