Compare commits
4 Commits
developmen
...
feature/75
Author | SHA1 | Date |
---|---|---|
Paul van Tilburg | e21058c5d8 | |
Paul van Tilburg | cd488cbee8 | |
Paul van Tilburg | fad9076913 | |
Paul van Tilburg | f87954b73d |
|
@ -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/*
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
2.5.1
|
|
@ -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
18
Gemfile
|
@ -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"
|
74
Gemfile.lock
74
Gemfile.lock
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
124
stoptime.rb
124
stoptime.rb
|
@ -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",
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* CSS file for Stop... Camping Time! */
|
||||
/* CSS file for Stop… Camping Time! */
|
||||
|
||||
/* Main elements */
|
||||
html
|
||||
|
|
Loading…
Reference in New Issue