Compare commits

..

4 Commits

Author SHA1 Message Date
Paul van Tilburg e21058c5d8 Add a button to trigger GnuCash CSV generation to the invoice_form view
This button is only there if information has been filled in for the
company and customer.
2016-03-06 15:04:11 +01:00
Paul van Tilburg cd488cbee8 Implement GnuCash CSV generation 2016-03-06 15:04:11 +01:00
Paul van Tilburg fad9076913 Add a config option to map tax rates to GnuCash tax table names 2016-03-06 15:04:11 +01:00
Paul van Tilburg f87954b73d Add GnuCash related fields to the CompanyInfo and Customer model 2016-03-06 15:04:11 +01:00
10 changed files with 106 additions and 166 deletions

21
.gitignore vendored
View File

@ -1,32 +1,11 @@
# Ignore bundler config
/.bundle
# Ignore bundler installation
/vendor/bundle
# Ignore SASS and YARD cache
.sass-cache
.yardoc
# Ignore the local configuration
config.yaml
htpasswd
# Ignore the SQLite databases
db/*
# Ignore generated documentation
doc/*
# Ignore generated invoice assets
public/invoices/*
# Ignore compiled asssets
public/stylesheets/style.css
public/stylesheets/style.css.map
# Ignore custom invoice templates
templates/*_invoice.tex.erb
# Ignore temporary files
tmp/*

View File

@ -1 +0,0 @@
2.5.1

View File

@ -1,24 +1,5 @@
= Stop… Camping Time! release news
== 1.17.1
Bugfixes:
* Fix the number/currency input to allow for all values ≥ 0.00
== 1.17.0
Features:
* Add support for choosing what date/time is used for new entries
* Some textual and style tweaks
Bugfixes:
* Fix crash when showing all entries in the timeline [#89c2a1]
Other bugfixes:
* Fix check that determines if last company info is used
== 1.16.1
Features:

18
Gemfile
View File

@ -1,18 +0,0 @@
source "http://rubygems.org"
git_source(:github) { |repo| "https://github.com/#{repo}.git" }
# This application is build on Camping, use it from our own Git master
gem "camping", github: "paulvt/camping", branch: "master"
# Use ActionView for the templates.
gem "actionview", "~> 4.2.10"
# Use ActiveRecord as the ORM.
gem "activerecord", "~> 4.2.10"
# Use SASS for handling CSS assets/Bootstrap.
gem "sass", "~> 3.4.6"
# Use SQLite as the database.
gem "sqlite3", "~> 1.3.9"

View File

@ -1,74 +0,0 @@
GIT
remote: https://github.com/paulvt/camping.git
revision: e6834563b244e0f24af2034d679cd6e8e9d932c0
branch: master
specs:
camping (2.1.602)
mab (>= 0.0.3)
rack (>= 1.0)
GEM
remote: http://rubygems.org/
specs:
actionview (4.2.11.3)
activesupport (= 4.2.11.3)
builder (~> 3.1)
erubis (~> 2.7.0)
rails-dom-testing (~> 1.0, >= 1.0.5)
rails-html-sanitizer (~> 1.0, >= 1.0.3)
activemodel (4.2.11.3)
activesupport (= 4.2.11.3)
builder (~> 3.1)
activerecord (4.2.11.3)
activemodel (= 4.2.11.3)
activesupport (= 4.2.11.3)
arel (~> 6.0)
activesupport (4.2.11.3)
i18n (~> 0.7)
minitest (~> 5.1)
thread_safe (~> 0.3, >= 0.3.4)
tzinfo (~> 1.1)
arel (6.0.4)
builder (3.2.4)
concurrent-ruby (1.1.9)
crass (1.0.6)
erubis (2.7.0)
i18n (0.9.5)
concurrent-ruby (~> 1.0)
loofah (2.12.0)
crass (~> 1.0.2)
nokogiri (>= 1.5.9)
mab (0.0.3)
mini_portile2 (2.6.1)
minitest (5.14.4)
nokogiri (1.12.5)
mini_portile2 (~> 2.6.1)
racc (~> 1.4)
racc (1.5.2)
rack (2.2.3)
rails-deprecated_sanitizer (1.0.4)
activesupport (>= 4.2.0.alpha)
rails-dom-testing (1.0.9)
activesupport (>= 4.2.0, < 5.0)
nokogiri (~> 1.6)
rails-deprecated_sanitizer (>= 1.0.1)
rails-html-sanitizer (1.4.2)
loofah (~> 2.3)
sass (3.4.25)
sqlite3 (1.3.13)
thread_safe (0.3.6)
tzinfo (1.2.9)
thread_safe (~> 0.1)
PLATFORMS
ruby
DEPENDENCIES
actionview (~> 4.2.10)
activerecord (~> 4.2.10)
camping!
sass (~> 3.4.6)
sqlite3 (~> 1.3.9)
BUNDLED WITH
1.17.3

View File

@ -39,9 +39,6 @@ and the following LaTeX programs:
* isodoc package (>= 1.00)
* rubber
It is also possible to use Bundler (which is the default when using
@config.ru@), in this you only need Ruby and Bundler installed.
== Installation
For now, Stop… Camping Time! is in a developing state and not ready for

View File

@ -1,10 +1,9 @@
#!/usr/bin/env rackup
require "bundler/setup"
require "./stoptime"
StopTime::Models::Base.establish_connection(adapter: "sqlite3",
database: "db/stoptime.db",
timeout: 10000)
StopTime::Models::Base.establish_connection( :adapter => 'sqlite3',
:database => 'db/stoptime.db',
:timeout => 10000 )
StopTime.create
run StopTime

View File

@ -13,6 +13,9 @@
# The VAT rate
#vat_rate: 21.0
# VAT rate to Gnucash tax table name mapping
#gnucash_vat_table: {}
# The invoice ID format (see strftime(3) and %N for the sequence number)
#invoice_id: %Y%N

View File

@ -13,6 +13,7 @@
require "action_view"
require "active_support"
require "csv"
require "camping"
require "camping/mab"
require "camping/ar"
@ -55,7 +56,7 @@ end
module StopTime
# The version of the application
VERSION = '1.17.1'
VERSION = '1.16.1'
puts "Starting Stop… Camping Time! version #{VERSION}"
# @return [Hash{String=>Object}] The parsed configuration.
@ -162,12 +163,13 @@ module StopTime::Models
# The default configuration. Note that the configuration of the root
# will be merged with this configuration.
DefaultConfig = { "invoice_id" => "%Y%N",
"invoice_template" => "invoice",
"hourly_rate" => 20.0,
"time_resolution" => 1,
"date_new_entry" => "today",
"vat_rate" => 21.0 }
DefaultConfig = { "gnucash_vat_table" => {},
"invoice_id" => "%Y%N",
"invoice_template" => "invoice",
"hourly_rate" => 20.0,
"time_resolution" => 1,
"date_new_entry" => "today",
"vat_rate" => 21.0 }
# Creates a new configuration object and loads the configuation.
# by reading the file +config.yaml+ on disk (see {ConfigFile}, parsing
@ -236,6 +238,8 @@ module StopTime::Models
# @!attribute time_specification
# @return [Boolean] flag whether the customer requires time
# specifications
# @!attribute gnucash_customer_owner_id
# @return [String] owner ID used in GnuCash for this customer
# @!attribute created_at
# @return [Time] time of creation
# @!attribute updated_at
@ -630,6 +634,8 @@ module StopTime::Models
# @return [String] number of the bank account
# @!attribute accountiban
# @return [String] international bank account number
# @!attribute gnucash_revenues_account_name
# @return [String] account name used in GnuCash for the revenues account
# @!attribute created_at
# @return [Time] time of creation
# @!attribute updated_at
@ -887,6 +893,19 @@ module StopTime::Models
end
end
# @private
class GnuCashInvoiceExportSupport < V 1.96
def self.up
add_column(CompanyInfo.table_name, :gnucash_revenues_account_name, :string)
add_column(Customer.table_name, :gnucash_customer_owner_id, :string)
end
def self.down
remove_column(CompanyInfo.table_name, :gnucash_revenues_account_name)
remove_column(Customer.table_name, :gnucash_customer_owner_id)
end
end
end # StopTime::Models
# = The Stop… Camping Time! helpers
@ -1085,7 +1104,7 @@ module StopTime::Controllers
elsif @input.has_key? "update"
attrs = ["name", "short_name", "financial_contact",
"address_street", "address_postal_code", "address_city",
"email", "phone", "hourly_rate"]
"email", "phone", "hourly_rate", "gnucash_customer_owner_id"]
attrs.each do |attr|
@customer[attr] = @input[attr]
end
@ -1097,7 +1116,7 @@ module StopTime::Controllers
return render :customer_form
end
end
redirect R(CustomersN, customer_id)
redirect R(Customers)
end
end # class StopTime::Controllers::CustomersN
@ -1368,9 +1387,12 @@ module StopTime::Controllers
tex_file = PUBLIC_DIR + "invoices/#{@number}.tex"
pdf_file = PUBLIC_DIR + "invoices/#{@number}.pdf"
csv_file = PUBLIC_DIR + "invoices/#{@number}.csv"
@csv_enabled = @company.gnucash_revenues_account_name.present? &&
@customer.gnucash_customer_owner_id.present?
if @format == "html"
@input = @invoice.attributes
@invoice_file_present = tex_file.exist?
@invoice_file_present = tex_file.exist? || csv_file.exist?
render :invoice_form
elsif @format == "tex"
_generate_invoice_tex(@number) unless tex_file.exist?
@ -1378,6 +1400,9 @@ module StopTime::Controllers
elsif @format == "pdf"
_generate_invoice_pdf(@number) unless pdf_file.exist?
redirect R(Static, "") + "invoices/#{pdf_file.basename}"
elsif @format == "csv"
_generate_invoice_csv(@number) unless csv_file.exist?
redirect R(Static, "") + "invoices/#{csv_file.basename}"
end
end
@ -1410,6 +1435,9 @@ module StopTime::Controllers
pdf_file = PUBLIC_DIR + "invoices/#{invoice_number}.pdf"
File.unlink(pdf_file) if pdf_file.exist?
csv_file = PUBLIC_DIR + "invoices/#{invoice_number}.csv"
File.unlink(csv_file) if csv_file.exist?
redirect R(CustomersNInvoicesX, customer_id, invoice_number)
end
@ -1467,6 +1495,44 @@ module StopTime::Controllers
system("rubber --pdf --inplace #{tex_file}")
system("rubber --clean --inplace #{tex_file}")
end
# Generates a CSV file for the invoice with the give number
# using {#_generate_invoice_csv}.
#
# @raise if CSV generation is not enabled due to missing
# data
def _generate_invoice_csv(number)
raise "GnuCash CSV is not enabled due to missing data" unless @csv_enabled
csv_file = PUBLIC_DIR + "invoices/#{number}.csv"
CSV.open(csv_file, "wb", col_sep: ";", headers: false) do |csv|
id = @invoice.number
date = @invoice.created_at.to_date
owner_id = @customer.gnucash_customer_owner_id
account = @company.gnucash_revenue_account_name
@tasks.each do |task, line|
desc = task.comment_or_name
tax_table = config["gnucash_vat_table"][task.vat_rate]
taxable = tax_table.present?
if line[1].blank?
# This is a fixed cost task
action = "Project"
quantity = 1
price = number_with_precision(line[2])
else
# This is a task with an hourly rate
action = "Hours"
quantity = number_with_precision(line[0])
price = number_with_precision(line[1])
end
due_date = date + 30.days # FIXME: hardcoded?!
csv_row = [id, date, owner_id, nil, nil, date, desc, action,
account, quantity, price, nil, nil, nil, taxable, nil,
tax_table, due_date, nil, nil, nil, nil]
csv << csv_row
end
end
end
end # class StopTime::Controllers::CustomerNInvoicesX
# == The invoice creating controller for a specifc customer
@ -1520,7 +1586,6 @@ module StopTime::Controllers
.where("stoptime_tasks.invoice_id" => nil)\
.order("start DESC")
end
@time_entries = @time_entries.where.not(task_id: nil)
@time_entries.each do |te|
@input["bill_#{te.id}"] = true if te.bill?
end
@ -1739,7 +1804,8 @@ module StopTime::Controllers
"country", "country_code",
"phone", "cell", "email", "website",
"chamber", "vatno",
"bank_name", "bank_bic", "accountno", "accountiban"]
"bank_name", "bank_bic", "accountno", "accountiban",
"gnucash_revenues_account_name"]
attrs.each do |attr|
@company[attr] = @input[attr]
end
@ -2161,11 +2227,11 @@ module StopTime::Views
control_class: "col-sm-3 col-xs-4")
_form_input_with_label("Financial contact", "financial_contact", :text,
control_class: "col-sm-6 col-xs-8")
_form_input_with_label("Default hourly rate", "hourly_rate", :number,
_form_input_with_label("Default hourly rate", "hourly_rate", :text,
control_class: "col-sm-4 col-xs-5",
input_addon: "€ / h",
min: "0.00",
step: "0.01")
input_addon: "€ / h")
_form_input_with_label("GnuCash owner ID",
"gnucash_customer_owner_id", :text)
div.form_group do
label.control_label.col_sm_3.col_xs_4 "Time specifications?"
div.col_sm_6.col_xs_8 do
@ -2319,16 +2385,14 @@ module StopTime::Views
end if @task.billed?
div.row do
div.col_md_6.col_xs_12 do
form.form_horizontal.form_condensed action: R(*@target),
method: :post do
form.form_horizontal.form_condensed action: R(*@target), method: :post do
div.form_group do
label.control_label.col_sm_3.col_xs_4 "Customer", for: "customer"
div.col_sm_4.col_xs_8 do
_form_select("customer", @customer_list)
end
div.col_sm_offset_2.col_sm_3.hidden_xs do
a.btn.btn_default role: "button",
href: R(CustomersN, @customer.id) do
a.btn.btn_default role: "button", href: R(CustomersN, @customer.id) do
_icon("user")
span "Show customer"
end
@ -2343,8 +2407,7 @@ module StopTime::Views
_form_input_radio("type", "hourly_rate", true)
text!("Hourly rate: ")
div.input_group do
_form_input("hourly_rate", :number, "Hourly rate",
min: "0.00", step: "0.01")
_form_input("hourly_rate", :number, "Hourly rate")
span.input_group_addon "€ / h"
end
end
@ -2354,8 +2417,7 @@ module StopTime::Views
_form_input_radio("type", "fixed_cost")
text!("Fixed cost: ")
div.input_group do
_form_input("fixed_cost", :number, "Fixed cost",
min: "0.00", step: "0.01")
_form_input("fixed_cost", :number, "Fixed cost")
span.input_group_addon ""
end
end
@ -2592,6 +2654,14 @@ module StopTime::Views
_icon("download")
span "Download LaTeX"
end
if @csv_enabled
a.btn.btn_default role: "button",
href: R(CustomersNInvoicesX,
@customer.id, "#{@invoice.number}.csv") do
_icon("download")
span "Download GnuCash CSV"
end
end
a.btn.btn_default role: "button",
href: R(Company, revision: @company.revision) do
_icon("briefcase")
@ -2759,7 +2829,7 @@ module StopTime::Views
button.btn.btn_primary "Create invoice", type: :submit,
name: "create", value: "Create invoice",
disabled: @none_found
button.btn.btn_default "Cancel", type: :submit, name: "cancel",
button.btn.btn_default "Cancel", type: :submit, name: "cancel",
value: "Cancel"
end
end
@ -2860,6 +2930,10 @@ module StopTime::Views
_form_input_with_label("Intl. account number", "accountiban", :text,
control_class: "col-sm-5 col-xs-6")
h3 "GnuCash export information"
_form_input_with_label("Revenues account name",
"gnucash_revenues_account_name", :text)
div.form_group do
div.col_sm_offset_3.col_sm_6.col_xs_offset_4.col_xs_8 do
button.btn.btn_primary "Update", type: "submit",

View File

@ -1,4 +1,4 @@
/* CSS file for Stop... Camping Time! */
/* CSS file for Stop Camping Time! */
/* Main elements */
html