Module: BillTrap::CLI
- Included in:
- CLI
- Defined in:
- lib/billtrap/cli.rb,
lib/billtrap/cmd/in.rb,
lib/billtrap/cmd/new.rb,
lib/billtrap/cmd/set.rb,
lib/billtrap/cmd/show.rb,
lib/billtrap/cmd/entry.rb,
lib/billtrap/cmd/usage.rb,
lib/billtrap/cmd/client.rb,
lib/billtrap/cmd/export.rb,
lib/billtrap/cmd/import.rb,
lib/billtrap/cmd/payment.rb,
lib/billtrap/cmd/configure.rb
Instance Attribute Summary collapse
-
#args ⇒ Object
Returns the value of attribute args.
Instance Method Summary collapse
- #client ⇒ Object
- #commands ⇒ Object
- #configure ⇒ Object
- #entry ⇒ Object
- #export ⇒ Object
- #import ⇒ Object
- #in ⇒ Object
- #invoke ⇒ Object
- #new ⇒ Object
- #payment ⇒ Object
- #set ⇒ Object
- #show ⇒ Object
- #usage ⇒ Object
Methods included from Helpers
format_date, format_money, is_i?
Instance Attribute Details
#args ⇒ Object
Returns the value of attribute args.
6 7 8 |
# File 'lib/billtrap/cli.rb', line 6 def args @args end |
Instance Method Details
#client ⇒ Object
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
# File 'lib/billtrap/cmd/client.rb', line 3 def client opts = Trollop::(args) do opt :add, "Add a new ID, reading from STDIN", :short => '-a' opt :delete, "Delete the client by ID", :type => Integer, :short => '-d' end if opts[:add] firstname = ask_value "First name" surname = ask_value "Surname" company = ask_value "Company" address = ask_value "Address", true mail = ask_value "Mail" rate = ask_value "Hourly rate" currency = ask_value "Use non-standard Currency? [Leave empty for #{BillTrap::Config['currency']}]" currency = currency.empty? ? BillTrap::Config['currency'] : currency puts "'#{currency}'" client = Client.create( :firstname => firstname, :surname => surname, :company => company, :address => address, :mail => mail, :rate => Money.parse(rate, currency).cents, :currency => currency ) puts "Client #{firstname} #{surname} was created with id #{client.id}" elsif id = opts[:delete] if e = Client.get(id) if confirm "Are you sure you want to delete Client #{e.name} (##{e.id})" begin e.destroy puts "Client has been removed." rescue Sequel::ForeignKeyConstraintViolation warn 'Error: Client is still in use. Refusing to delete client' end else puts "Client has NOT been removed." end else warn "Can't find Client with id '#{id}'" end end end |
#commands ⇒ Object
42 43 44 |
# File 'lib/billtrap/cli.rb', line 42 def commands BillTrap::CLI.usage.scan(/\* \w+/).map{|s| s.gsub(/\* /, '')} end |
#configure ⇒ Object
3 4 5 6 |
# File 'lib/billtrap/cmd/configure.rb', line 3 def configure BillTrap::Config.configure! puts "Config file written to: #{BillTrap::Config::CONFIG_PATH.inspect}" end |
#entry ⇒ Object
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
# File 'lib/billtrap/cmd/entry.rb', line 3 def entry opts = Trollop:: args do opt :add, "Manually add entry to current invoice", :short => '-a' opt :delete, "Delete entry from current invoice by ID", :type => :int, :short => '-d' end current = Invoice.current if opts[:add] title = ask_value 'Entry title' date = ask_value 'Entry date (YYYY-MM-DD)' unit = ask_value "Displayed unit (Defaults to 'h' for hours)" || 'h' count = ask_value "Quantity (Numeric)" price = ask_value "Price in #{current.currency} per unit (Numeric)" notes = ask_value 'Optional Notes', true e = InvoiceEntry.create( :invoice_id => current.id, :title => title, :date => Date.parse(date), :unit => unit, :count => count, :notes => notes, :cents => Money.parse(price).cents ) puts "Added entry (##{e.id}) to current invoice (ID #{current.id})" elsif name = opts[:name] Invoice.current.update(:name => name) puts "Set current invoice (##{Invoice.current.id}) name to: #{name}" elsif id = opts[:delete] if e = InvoiceEntry[id] if confirm "Are you sure you want to delete InvoiceEntry ##{e.id}" e.destroy puts "Entry has been removed." else puts "Entry has NOT been removed." end else warn "Can't find entry with id '#{id}'" end end end |
#export ⇒ Object
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
# File 'lib/billtrap/cmd/export.rb', line 3 def export opts = Trollop:: args do opt :adapter, "Set adapter", :type => :string, :short => '-a' end adapter = opts[:adapter] || 'ooffice' begin # Replace invoice number placeholders arg = { # Unique, auto-incremented invoice id (from database) :invoice_id => Invoice.current.id, # Unique, auto-incremented client id :client_id => Invoice.current.client_id } # Replace above parameters, then strfime parameters invoice_number = Invoice.current.created.strftime(Config['invoice_number_format'].gsub(/%\{(.*?)\}/) { arg[$1.to_sym] }) attributes = { :invoice => Invoice.current, :invoice_number => invoice_number, } BillTrap::Adapters.load_adapter(adapter).new(attributes).generate rescue LoadError warn "Couldn't load adapter named #{adapter}.rb" end end |
#import ⇒ Object
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
# File 'lib/billtrap/cmd/import.rb', line 3 def import opts = Trollop:: args do opt :clear, "Clear entries before import", :short => '-c' opt :entry, "Import entries by ID", :type => :strings, :multi => true, :short => '-e' opt :round, "Round imported entries", :short => '-r' opt :sheet, "Import sheet by name", :type => :string, :short => '-s' end # Clear entries if --clear given if opts[:clear] InvoiceEntry.where(:invoice_id => Invoice.current.id).destroy end entries = if opts[:sheet] Entry.filter(:sheet => opts[:sheet]).all elsif opts[:entry_given] Entry.where(:id => opts[:entry].first).all else [] end unless entries.length > 0 warn "No matching entries found." return end entries.each do |e| Entry.round = opts[:round] # Ignore entry if (rounded) is empty next if e.duration == 0 imported = InvoiceEntry.create( :invoice_id => Invoice.current.id, :title => e.sheet, :date => e.start.to_date, :unit => 'h', :count => (e.duration.to_f / 3600).round(2), :notes => e.note, :cents => Invoice.current.rate.cents ) puts "Imported #{imported.count} hours from sheet #{e.sheet} as entry ##{imported.id}" end end |
#in ⇒ Object
3 4 5 6 7 8 9 10 11 12 13 14 |
# File 'lib/billtrap/cmd/in.rb', line 3 def in key = args.shift || raise('Error: No ID/Name given') invoice = Invoice.get key if invoice puts "Activating invoice ##{invoice.id}" # set current id Invoice.current = invoice.id else puts "No Invoice found for input '#{key}'" end end |
#invoke ⇒ Object
8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
# File 'lib/billtrap/cli.rb', line 8 def invoke require 'cmd/usage' case args.first when '-h', '--help', '--usage', '-?', 'help', nil puts BillTrap::CLI.usage exit 0 when '-v', '--version' puts "BillTrap version #{BillTrap::VERSION}" exit 0 end # Grab global options, then stop flags = Trollop:: args do opt :debug stop_on_unknown end command = args.shift # Complete command available = commands.select{ |key| key.match(/^#{command}/) } if available.size == 1 require "cmd/#{available[0]}" send available[0] elsif available.size > 1 warn "Error: Ambiguous command '#{command}'" warn "Matching commands are: #{available.join(", ")}" else warn "Error: Invalid command #{command.inspect}" end rescue StandardError, LoadError => e raise e if flags && flags[:debug] warn e. end |
#new ⇒ Object
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
# File 'lib/billtrap/cmd/new.rb', line 3 def new opts = Trollop:: args do opt :client, "Optional Client ID", :type => :string, :short => '-c' opt :date, "Optional invoice date", :type => :string, :short => '-d' opt :name, "Optional invoice name", :type => :string, :short => '-n' end date = if opts[:date] Chronic.parse(opts[:date]) else Date.today end invoice = Invoice.create( :name => opts[:name], :created => date, :client => Client.get(opts[:client]) ) # Make active Invoice.current = invoice.id puts "Created invoice ##{invoice.id}" end |
#payment ⇒ Object
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
# File 'lib/billtrap/cmd/payment.rb', line 3 def payment opts = Trollop:: args do opt :add, "Add payment to current invoice", :type => :strings, :multi => true, :short => '-a' opt :delete, "Delete payment by ID from current invoice", :type => :int, :short => '-d' end if opts[:add_given] && opts[:add][0].length > 1 # If the invoice has no total if Invoice.current.total.cents == 0 warn "Can't add payment. Invoice ##{Invoice.current.id} has no total" return end # Test if payment would add more than the remaining amount payment = Money.parse(opts[:add][0].shift, Invoice.current.currency) if (Invoice.current.received_amount + payment > Invoice.current.total) warn 'With this payment, the received amount surpasses its total.' cropped = Invoice.current.total - Invoice.current.received_amount if ask_value "Do you want to add the remaining payment of #{format_money(cropped)}" payment = cropped else puts "Payment has NOT been added." return end end Invoice.current.add_payment(:cents => payment.cents, :note => opts[:add][0].shift) puts "Added #{format_money(payment)} to current invoice" elsif opts[:delete] if e = Payment[opts[:delete]] if confirm "Are you sure you want to delete Payment ##{e.id}" e.destroy puts "Payment has been removed." else puts "Payment has NOT been removed." end else warn "Error: No Payment found for id ##{opts[:delete]}" end else warn "Error: Invalid command" end end |
#set ⇒ Object
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
# File 'lib/billtrap/cmd/set.rb', line 3 def set # Grab subcommand k = args.shift case when k == 'client' id = args.shift if e = Client.get(id) Invoice.current.update(:client_id => e.id) puts "SET client to #{e.name} (##{e.id})" else warn "Error: Can't find Client with id '#{id}'" end when k == 'date' if d = args.shift new_date = Date.parse d else new_date = Date.today end Invoice.current.update(:created => new_date) puts "SET created date to #{format_date(new_date)}" when k == 'name' if n = args.shift Invoice.current.update(:name => n) puts "SET name to '#{n}'" else warn "Error: Missing required attributed for token 'name'" end when k == 'sent' if d = args.shift Invoice.current.update(:sent => Date.parse(d)) puts "SET invoice sent date to #{d}" else Invoice.current.update(:sent => nil) puts "UNSET invoice sent date" end when k.respond_to?(:to_s) Invoice.current.set_attr(k.to_str, args.shift) puts "Setting attribute #{k}" else warn "Error: Missing / unrecognized TOKEN #{k}" end end |
#show ⇒ Object
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
# File 'lib/billtrap/cmd/show.rb', line 3 def show opts = Trollop:: args do opt :completed, "Show only completed (i.e., sent and paid) invoices", :short => '-c' opt :detail, "Show details (including entries) of a particular invoice", :type => :string, :short => '-d' end if opts[:detail] # Display details of invoice with id/name from args if invoice = Invoice.get(opts[:detail]) puts "%-12s%s" % ["Invoice: ", "#{invoice.name || 'unnamed'} (##{invoice.id})"] puts "%-12s%s" % ["Created on: ", format_date(invoice.created)] if invoice.sent puts "%-12s%s" % ["Sent on: ", format_date(invoice.sent)] end puts '-' * 22 if invoice.invoice_entries.size > 0 puts 'Invoice entries' # Determine length of entry titles width = invoice.invoice_entries.sort_by{|inv| inv.title.to_s.length }.last.title.to_s.length + 4 width = 12 if width < 12 puts " %-#{width}s%-12s%-12s%-12s%s" % ["Title", "Date", "Quantity", "Price", "Notes"] invoice.invoice_entries.each do |e| puts " %-#{width}s%-12s%-12s%-12s%s" % [ e.title, e.date, e.typed_amount, format_money(e.total), e.notes ] end else puts 'No InvoiceEntries' end if invoice.payments.size > 0 puts '-' * 22 puts 'Received payments' puts " %12s %s" % ["Payment", "Notes"] invoice.payments.each do |e| puts " %12s %s" % [ format_money(e.amount), e.note ] end else end else puts "No Invoice found for input '#{detail_id}'" end elsif opts[:completed] puts 'Showing only completed invoices' print_invoices Invoice.completed else puts 'Showing open invoices' print_invoices Invoice.open end end |
#usage ⇒ Object
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 |
# File 'lib/billtrap/cmd/usage.rb', line 3 def usage <<-EOF Billtrap - Manage invoices, import time slices from Timetrap Usage: bt COMMAND [OPTIONS] [ARGS...] COMMAND can be abbreviated. For example `bt edit --delete 1` and `bt e -d 1` are equivalent. COMMAND is one of: * configure - Write out a YAML config file to HOME/.billtrap.yml. usage: bt configure * client - Manage clients (adding, deleting and connecting to current invoice) usage: bt client [--add] [--delete [ID]] [--set [ID]] -a, --add Manually add a client, reads from STDIN -d, --delete Delete a client. If no ID is given, prints all clients. Note: Clients cannot be deleted if a non-archived invoiced is linked to it. * entry - Edit the active invoice, adding or deleting invoice entries manually usage: bt edit [--add] [--delete [ID]] -a, --add Manually add an invoice entry (timeslice or product), reads from STDIN -d, --delete Delete an invoice entry. If no ID is given, prints all entries and asks for an ID. * export - Export active invoice, using the default adapter (Serenity) -a, --adapter Override the default adapter * in - Switch to another invoice, making it active for edits usage: bt in [ID | NAME] * import - Import data from Timetrap. Sets invoice title to the sheet name and description to notes usage: bt import [--clear] [--sheet NAME] [--entry ID [ID ..]] -c, --clear Clears ALL invoice entries before import -e, --entry Import the given entries. -s, --sheet Import all entries from the given sheet. * new - Create a new invoice, activating it for edits usage: bt new [--name NAME] [--date DATE] [--client ID | NAME] -c, --client Tie the invoice to a client -d, --date Set to override invoice date (defaults to today) -n, --name Set invoice reference name * payment - add, remove payments to current id usage: bt payment [--add AMOUNT ['NOTES']] [--delete ID] -a, --add Add a payment to current invoice -d, --delete Delete a payment by ID from current invoice * set - Set variables on the current invoice usage: bt set TOKEN [VALUE] Where TOKEN is one of client Set a client by id or surname date Set the `created on` date (YYYY-MM-DD), leave empty for today name Set name of current invoice sent Set the `sent on` date (YYYY-MM-DD), leave empty for today other Add { 'other' => VALUE } to the invoice's custom attributes. Use this to set attributes for populating templates * show - Display a list invoices. Shows pending invoices by default (open or unpaid) usage: bt show [--details ID | NAME] [--completed] -d, --detail Show details (including entries) of a particular invoice -c, --completed Show only completed (i.e., sent and paid) invoices GLOBAL OPTIONS Use global options by prepending them before any command. --debug Display stack traces for errors. usage: bt --debug COMMAND [ARGS] OTHER OPTIONS -h, --help Display this help. EXAMPLES Please submit bugs and feature requests to http://github.com/oliverguenther/billtrap/issues EOF end |