invoicing 0.2.1 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/LICENSE +1 -0
- data/README.md +57 -0
- data/Rakefile +16 -37
- data/lib/invoicing.rb +20 -10
- data/lib/invoicing/cached_record.rb +9 -6
- data/lib/invoicing/class_info.rb +34 -34
- data/lib/invoicing/connection_adapter_ext.rb +4 -4
- data/lib/invoicing/countries/uk.rb +6 -6
- data/lib/invoicing/currency_value.rb +39 -32
- data/lib/invoicing/find_subclasses.rb +40 -15
- data/lib/invoicing/ledger_item.rb +166 -145
- data/lib/invoicing/ledger_item/pdf_generator.rb +108 -0
- data/lib/invoicing/ledger_item/render_html.rb +76 -73
- data/lib/invoicing/ledger_item/render_ubl.rb +37 -35
- data/lib/invoicing/line_item.rb +43 -38
- data/lib/invoicing/price.rb +1 -1
- data/lib/invoicing/tax_rate.rb +3 -6
- data/lib/invoicing/taxable.rb +37 -32
- data/lib/invoicing/time_dependent.rb +40 -40
- data/lib/invoicing/version.rb +4 -4
- data/lib/rails/generators/invoicing/invoicing_generator.rb +14 -0
- data/lib/rails/generators/invoicing/ledger_item/ledger_item_generator.rb +17 -0
- data/lib/rails/generators/invoicing/ledger_item/templates/migration.rb +25 -0
- data/lib/rails/generators/invoicing/ledger_item/templates/model.rb +5 -0
- data/lib/rails/generators/invoicing/line_item/line_item_generator.rb +17 -0
- data/lib/rails/generators/invoicing/line_item/templates/migration.rb +20 -0
- data/lib/rails/generators/invoicing/line_item/templates/model.rb +5 -0
- data/lib/rails/generators/invoicing/tax_rate/tax_rate_generator.rb +17 -0
- data/lib/rails/generators/invoicing/tax_rate/templates/migration.rb +14 -0
- data/lib/rails/generators/invoicing/tax_rate/templates/model.rb +3 -0
- metadata +110 -153
- data.tar.gz.sig +0 -1
- data/History.txt +0 -31
- data/Manifest.txt +0 -62
- data/PostInstall.txt +0 -10
- data/README.rdoc +0 -58
- data/script/console +0 -10
- data/script/destroy +0 -14
- data/script/generate +0 -14
- data/tasks/rcov.rake +0 -4
- data/test/cached_record_test.rb +0 -100
- data/test/class_info_test.rb +0 -253
- data/test/connection_adapter_ext_test.rb +0 -79
- data/test/currency_value_test.rb +0 -209
- data/test/find_subclasses_test.rb +0 -120
- data/test/fixtures/README +0 -7
- data/test/fixtures/cached_record.sql +0 -22
- data/test/fixtures/class_info.sql +0 -28
- data/test/fixtures/currency_value.sql +0 -29
- data/test/fixtures/find_subclasses.sql +0 -43
- data/test/fixtures/ledger_item.sql +0 -39
- data/test/fixtures/line_item.sql +0 -33
- data/test/fixtures/price.sql +0 -4
- data/test/fixtures/tax_rate.sql +0 -4
- data/test/fixtures/taxable.sql +0 -14
- data/test/fixtures/time_dependent.sql +0 -35
- data/test/ledger_item_test.rb +0 -444
- data/test/line_item_test.rb +0 -139
- data/test/models/README +0 -4
- data/test/models/test_subclass_in_another_file.rb +0 -3
- data/test/models/test_subclass_not_in_database.rb +0 -6
- data/test/price_test.rb +0 -9
- data/test/ref-output/creditnote3.html +0 -82
- data/test/ref-output/creditnote3.xml +0 -89
- data/test/ref-output/invoice1.html +0 -93
- data/test/ref-output/invoice1.xml +0 -111
- data/test/ref-output/invoice2.html +0 -86
- data/test/ref-output/invoice2.xml +0 -98
- data/test/ref-output/invoice_null.html +0 -36
- data/test/render_html_test.rb +0 -70
- data/test/render_ubl_test.rb +0 -44
- data/test/setup.rb +0 -37
- data/test/tax_rate_test.rb +0 -9
- data/test/taxable_test.rb +0 -180
- data/test/test_helper.rb +0 -72
- data/test/time_dependent_test.rb +0 -180
- metadata.gz.sig +0 -4
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 460fa683bd1ba56dede941135a9db41b2b7236cf
|
4
|
+
data.tar.gz: 0ab573110cdf7d5c6bb424b2f69880f2f3a6d347
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: cf54abdd39ade52d2c41ad3757cd4b4b482853bb189e0ac0fe8371a6b5641de5f702557159a61a3d0332a012493b0258420ed7624306b5ff8cb6edb8e306c7b8
|
7
|
+
data.tar.gz: 4a41f9ba2236663d0463348cf5880a89e4d54e4d342e59ba147de4d8d33a4a1ba607422112e6b6db2d18d86d68c7c4eeb124eace3c2c9af7ed81451026466edc
|
data/LICENSE
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
Copyright (c) 2009 Martin Kleppmann
|
2
2
|
Copyright (c) 2009 Ept Computing Limited
|
3
|
+
Copyright (c) 2014 Codemancers Tech Pvt Limited
|
3
4
|
|
4
5
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
5
6
|
this software and associated documentation files (the "Software"), to deal in
|
data/README.md
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
# Ruby Invoicing Framework
|
2
|
+
[](https://travis-ci.org/code-mancers/invoicing)
|
3
|
+
[](https://codeclimate.com/github/code-mancers/invoicing)
|
4
|
+
|
5
|
+
## Description
|
6
|
+
|
7
|
+
This is a framework for generating and displaying invoices (ideal for commercial
|
8
|
+
Rails apps). It allows for flexible business logic; provides tools for tax handling,
|
9
|
+
commission calculation etc. It aims to be both developer-friendly and
|
10
|
+
accountant-friendly. Please visit invoicing [page](http://invoicing.codemancers.com/)
|
11
|
+
for more information about how to install and use.
|
12
|
+
|
13
|
+
## Features
|
14
|
+
|
15
|
+
1. Store any number of different types of invoice, credit note and payment
|
16
|
+
record
|
17
|
+
2. Represent customer accounts, supplier accounts, and even complicated
|
18
|
+
multi-party billing relationships
|
19
|
+
3. Automatically format currency values beautifully
|
20
|
+
4. Automatically round currency values to the customary precision for that
|
21
|
+
particular currency, e.g. based on the smallest coin in circulation
|
22
|
+
5. Support any number of different currencies simultaneously
|
23
|
+
6. Render invoices, account statements etc. into HTML (fully styleable and
|
24
|
+
internationalisable)
|
25
|
+
7. Export into the UBL XML format for sharing data with other systems
|
26
|
+
8. Provide you with a default Value Added Tax (VAT) implementation, but you
|
27
|
+
can also easily plug in your own tax logic
|
28
|
+
9. Dynamically display tax-inclusive or tax-exclusive prices depending on
|
29
|
+
your customer's location and preferences
|
30
|
+
10. Deal with tax rates or prices changing over time, and automatically
|
31
|
+
switch to the new rate at the right moment
|
32
|
+
11. Efficiently summarise account balances, sales statements etc. under
|
33
|
+
arbitrary conditions (e.g. data from one quarter, or payments due at a
|
34
|
+
particular date)
|
35
|
+
|
36
|
+
## TODOs
|
37
|
+
|
38
|
+
1. Slowly move away from `acts-as` model.
|
39
|
+
2. Multiple taxes can be applied on goodies.
|
40
|
+
3. Improve documentation for taxes
|
41
|
+
|
42
|
+
|
43
|
+
## Credits
|
44
|
+
|
45
|
+
The Ruby invoicing framework originated as part of the website
|
46
|
+
[Bid for Wine](http://www.bidforwine.co.uk), developed by Patrick Dietrich,
|
47
|
+
Conrad Irwin, Michael Arnold and Martin Kleppmann for Ept Computing Ltd.
|
48
|
+
It was extracted from the Bid for Wine codebase and substantially extended
|
49
|
+
by Martin Kleppmann.
|
50
|
+
|
51
|
+
## License
|
52
|
+
|
53
|
+
Copyright (c) 2009 Martin Kleppmann, Ept Computing Limited.
|
54
|
+
Copyright (c) 2014 Codemancers Tech Pvt Ltd
|
55
|
+
|
56
|
+
This gem is made publicly available under the terms of the MIT license.
|
57
|
+
See LICENSE and/or COPYING for details.
|
data/Rakefile
CHANGED
@@ -1,43 +1,22 @@
|
|
1
|
-
|
2
|
-
require
|
3
|
-
|
4
|
-
|
5
|
-
# at least) causes a lot of warnings internally, by no fault of our own, which clutters
|
6
|
-
# the output. Comment out the following four lines to see those warnings.
|
7
|
-
class Hoe
|
8
|
-
RUBY_FLAGS = ENV['RUBY_FLAGS'] || "-I#{%w(lib test).join(File::PATH_SEPARATOR)}" +
|
9
|
-
((defined?(RUBY_DEBUG) && RUBY_DEBUG) ? " #{RUBY_DEBUG}" : '')
|
1
|
+
begin
|
2
|
+
require 'bundler/setup'
|
3
|
+
rescue LoadError
|
4
|
+
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
10
5
|
end
|
11
6
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
p.summary = p.paragraphs_of('README.rdoc', 3).join
|
19
|
-
p.description = p.paragraphs_of('README.rdoc', 3..5).join("\n\n")
|
20
|
-
p.changes = p.paragraphs_of("History.txt", 0..1).join("\n\n")
|
21
|
-
p.post_install_message = 'PostInstall.txt'
|
22
|
-
p.rubyforge_name = p.name
|
7
|
+
require 'rubygems'
|
8
|
+
require 'rake'
|
9
|
+
require 'rake/clean'
|
10
|
+
require 'fileutils'
|
11
|
+
require 'rake/testtask'
|
23
12
|
|
24
|
-
|
25
|
-
|
26
|
-
['builder', '>= 2.0']
|
27
|
-
]
|
28
|
-
p.extra_dev_deps = [
|
29
|
-
['newgem', ">= #{::Newgem::VERSION}"]
|
30
|
-
#['invoicing_generator', "= #{Invoicing::VERSION}"] - causes a circular dependency in rubygems < 1.2
|
31
|
-
]
|
13
|
+
# Tasks to run by default
|
14
|
+
task :default => [:test]
|
32
15
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
16
|
+
Rake::TestTask.new do |t|
|
17
|
+
t.libs << "test"
|
18
|
+
t.test_files = FileList['test/**/*_test.rb']
|
19
|
+
t.warning = false
|
37
20
|
end
|
38
21
|
|
39
|
-
|
40
|
-
Dir['tasks/**/*.rake'].each { |t| load t }
|
41
|
-
|
42
|
-
# Tasks to run by default
|
43
|
-
# task :default => [:spec, :features]
|
22
|
+
Bundler::GemHelper.install_tasks
|
data/lib/invoicing.rb
CHANGED
@@ -1,12 +1,22 @@
|
|
1
|
-
|
2
|
-
$:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
|
1
|
+
require "active_record"
|
3
2
|
|
4
|
-
require
|
3
|
+
require "invoicing/class_info" # load first because other modules depend on this
|
4
|
+
require "invoicing/cached_record"
|
5
|
+
require "invoicing/connection_adapter_ext"
|
6
|
+
require "invoicing/currency_value"
|
7
|
+
require "invoicing/find_subclasses"
|
8
|
+
require "invoicing/ledger_item"
|
9
|
+
require "invoicing/line_item"
|
10
|
+
require "invoicing/price"
|
11
|
+
require "invoicing/tax_rate"
|
12
|
+
require "invoicing/taxable"
|
13
|
+
require "invoicing/time_dependent"
|
5
14
|
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
15
|
+
ActiveRecord::Base.send(:extend, Invoicing::CachedRecord::ActMethods)
|
16
|
+
ActiveRecord::Base.send(:extend, Invoicing::CurrencyValue::ActMethods)
|
17
|
+
ActiveRecord::Base.send(:extend, Invoicing::LedgerItem::ActMethods)
|
18
|
+
ActiveRecord::Base.send(:extend, Invoicing::LineItem::ActMethods)
|
19
|
+
ActiveRecord::Base.send(:extend, Invoicing::Price::ActMethods)
|
20
|
+
ActiveRecord::Base.send(:extend, Invoicing::TaxRate::ActMethods)
|
21
|
+
ActiveRecord::Base.send(:extend, Invoicing::Taxable::ActMethods)
|
22
|
+
ActiveRecord::Base.send(:extend, Invoicing::TimeDependent::ActMethods)
|
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'active_support/concern'
|
2
|
+
|
1
3
|
module Invoicing
|
2
4
|
# == Aggressive ActiveRecord cache
|
3
5
|
#
|
@@ -20,6 +22,7 @@ module Invoicing
|
|
20
22
|
# To activate +CachedRecord+, call +acts_as_cached_record+ in the scope of an
|
21
23
|
# <tt>ActiveRecord::Base</tt> class.
|
22
24
|
module CachedRecord
|
25
|
+
extend ActiveSupport::Concern
|
23
26
|
|
24
27
|
module ActMethods
|
25
28
|
# Call +acts_as_cached_record+ on an <tt>ActiveRecord::Base</tt> class to declare
|
@@ -63,7 +66,7 @@ module Invoicing
|
|
63
66
|
def cached_record_list
|
64
67
|
cached_record_class_info.list
|
65
68
|
end
|
66
|
-
|
69
|
+
|
67
70
|
# Reloads the cached objects from the database.
|
68
71
|
def reload_cache
|
69
72
|
cached_record_class_info.reload_cache
|
@@ -78,12 +81,12 @@ module Invoicing
|
|
78
81
|
super
|
79
82
|
reload_cache
|
80
83
|
end
|
81
|
-
|
84
|
+
|
82
85
|
def reload_cache
|
83
86
|
@cache = {}
|
84
|
-
model_class.
|
87
|
+
model_class.all.each {|obj| @cache[get(obj, :id)] = obj }
|
85
88
|
end
|
86
|
-
|
89
|
+
|
87
90
|
# Returns one object from the cache, given its ID.
|
88
91
|
def find_one(id, options)
|
89
92
|
if result = @cache[id]
|
@@ -92,12 +95,12 @@ module Invoicing
|
|
92
95
|
raise ::ActiveRecord::RecordNotFound, "Couldn't find #{model_class.name} with ID=#{id}"
|
93
96
|
end
|
94
97
|
end
|
95
|
-
|
98
|
+
|
96
99
|
# Returns a list of objects from the cache, given a list of IDs.
|
97
100
|
def find_some(ids, options)
|
98
101
|
ids.map{|id| find_one(id, options) }
|
99
102
|
end
|
100
|
-
|
103
|
+
|
101
104
|
# Returns a list of all objects in the cache.
|
102
105
|
def list
|
103
106
|
@cache.values
|
data/lib/invoicing/class_info.rb
CHANGED
@@ -18,19 +18,19 @@ module Invoicing
|
|
18
18
|
# Invoicing::ClassInfo.acts_as(MyNamespace::Teleporter, self, args)
|
19
19
|
# end
|
20
20
|
# end
|
21
|
-
#
|
21
|
+
#
|
22
22
|
# def transmogrify_the_instance # will become an instance method of the class on which the
|
23
23
|
# info = teleporter_class_info # acts_as_ method is called.
|
24
24
|
# info.do_transmogrify
|
25
25
|
# end
|
26
|
-
#
|
26
|
+
#
|
27
27
|
# module ClassMethods
|
28
28
|
# def transmogrify_the_class # will become a class method of the class on which the
|
29
29
|
# info = teleporter_class_info # acts_as_ method is called.
|
30
30
|
# info.do_transmogrify
|
31
31
|
# end
|
32
32
|
# end
|
33
|
-
#
|
33
|
+
#
|
34
34
|
# class ClassInfo < Invoicing::ClassInfo::Base
|
35
35
|
# def do_transmogrify
|
36
36
|
# case all_options[:transmogrification]
|
@@ -40,7 +40,7 @@ module Invoicing
|
|
40
40
|
# end
|
41
41
|
# end
|
42
42
|
# end
|
43
|
-
#
|
43
|
+
#
|
44
44
|
# ActiveRecord::Base.send(:extend, MyNamespace::Teleporter::ActMethods)
|
45
45
|
#
|
46
46
|
#
|
@@ -51,11 +51,11 @@ module Invoicing
|
|
51
51
|
# class Teleporter < ActiveRecord::Base
|
52
52
|
# acts_as_teleporter 'Zoom2020', :transmogrification => :total
|
53
53
|
# end
|
54
|
-
#
|
54
|
+
#
|
55
55
|
# Teleporter.transmogrify_the_class # both return "Transmogrified by Zoom2020"
|
56
56
|
# Teleporter.find(42).transmogrify_the_instance
|
57
57
|
module ClassInfo
|
58
|
-
|
58
|
+
|
59
59
|
# Provides the main implementation pattern for an +acts_as_+ method. See the example above
|
60
60
|
# for usage.
|
61
61
|
# +source_module+:: The module object which is using the +ClassInfo+ pattern
|
@@ -65,28 +65,28 @@ module Invoicing
|
|
65
65
|
# The name by which the particular module using ClassInfo is known
|
66
66
|
module_name = source_module.name.split('::').last.underscore
|
67
67
|
class_info_method = "#{module_name}_class_info"
|
68
|
-
|
69
|
-
previous_info =
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
68
|
+
|
69
|
+
previous_info =
|
70
|
+
if calling_class.respond_to?(class_info_method, true)
|
71
|
+
# acts_as has been called before on the same class, or a superclass
|
72
|
+
calling_class.send(class_info_method) || calling_class.superclass.send(class_info_method)
|
73
|
+
else
|
74
|
+
# acts_as is being called for the first time -- do the mixins!
|
75
|
+
calling_class.send(:include, source_module)
|
76
|
+
nil # no previous_info
|
77
|
+
end
|
78
|
+
|
79
79
|
# Instantiate the ClassInfo::Base subclass and assign it to an instance variable in calling_class
|
80
80
|
class_info_class = source_module.const_get('ClassInfo')
|
81
81
|
class_info = class_info_class.new(calling_class, previous_info, args)
|
82
82
|
calling_class.instance_variable_set("@#{class_info_method}", class_info)
|
83
|
-
|
83
|
+
|
84
84
|
# Define a getter class method on calling_class through which the ClassInfo::Base
|
85
85
|
# instance can be accessed.
|
86
86
|
calling_class.class_eval <<-CLASSEVAL
|
87
87
|
class << self
|
88
88
|
def #{class_info_method}
|
89
|
-
if superclass.
|
89
|
+
if superclass.respond_to?("#{class_info_method}", true)
|
90
90
|
@#{class_info_method} ||= superclass.send("#{class_info_method}")
|
91
91
|
end
|
92
92
|
@#{class_info_method}
|
@@ -94,7 +94,7 @@ module Invoicing
|
|
94
94
|
private "#{class_info_method}"
|
95
95
|
end
|
96
96
|
CLASSEVAL
|
97
|
-
|
97
|
+
|
98
98
|
# For convenience, also define an instance method which does the same as the class method
|
99
99
|
calling_class.class_eval do
|
100
100
|
define_method class_info_method do
|
@@ -103,8 +103,8 @@ module Invoicing
|
|
103
103
|
private class_info_method
|
104
104
|
end
|
105
105
|
end
|
106
|
-
|
107
|
-
|
106
|
+
|
107
|
+
|
108
108
|
# Base class for +ClassInfo+ objects, from which you need to derive a subclass in each module where
|
109
109
|
# you want to use +ClassInfo+. An instance of a <tt>ClassInfo::Base</tt> subclass is created every
|
110
110
|
# time an +acts_as_+ method is called, and that instance can be accessed through the
|
@@ -112,27 +112,27 @@ module Invoicing
|
|
112
112
|
class Base
|
113
113
|
# The class on which the +acts_as_+ method was called
|
114
114
|
attr_reader :model_class
|
115
|
-
|
115
|
+
|
116
116
|
# The <tt>ClassInfo::Base</tt> instance created by the last +acts_as_+ method
|
117
117
|
# call on the same class (or its superclass); +nil+ if this is the first call.
|
118
118
|
attr_reader :previous_info
|
119
|
-
|
119
|
+
|
120
120
|
# The list of arguments passed to the current +acts_as_+ method call (excluding the final options hash)
|
121
121
|
attr_reader :current_args
|
122
|
-
|
122
|
+
|
123
123
|
# Union of +current_args+ and <tt>previous_info.all_args</tt>
|
124
124
|
attr_reader :all_args
|
125
|
-
|
125
|
+
|
126
126
|
# <tt>self.all_args - previous_info.all_args</tt>
|
127
127
|
attr_reader :new_args
|
128
|
-
|
128
|
+
|
129
129
|
# The options hash passed to the current +acts_as_+ method call
|
130
130
|
attr_reader :current_options
|
131
|
-
|
131
|
+
|
132
132
|
# Hash of options with symbolized keys, with +option_defaults+ overridden by +previous_info+ options,
|
133
133
|
# in turn overridden by +current_options+.
|
134
134
|
attr_reader :all_options
|
135
|
-
|
135
|
+
|
136
136
|
# Initialises a <tt>ClassInfo::Base</tt> instance and parses arguments.
|
137
137
|
# If subclasses override +initialize+ they should call +super+.
|
138
138
|
# +model_class+:: The class on which the +acts_as+ method was called
|
@@ -150,26 +150,26 @@ module Invoicing
|
|
150
150
|
@current_options = args.extract_options!.symbolize_keys
|
151
151
|
@all_options = (@previous_info.nil? ? option_defaults : @previous_info.all_options).clone
|
152
152
|
@all_options.update(@current_options)
|
153
|
-
|
153
|
+
|
154
154
|
@all_args = @new_args = @current_args = args.flatten.uniq
|
155
155
|
unless @previous_info.nil?
|
156
156
|
@all_args = (@previous_info.all_args + @all_args).uniq
|
157
157
|
@new_args = @all_args - previous_info.all_args
|
158
158
|
end
|
159
159
|
end
|
160
|
-
|
160
|
+
|
161
161
|
# Override this method to return a hash of default option values.
|
162
162
|
def option_defaults
|
163
163
|
{}
|
164
164
|
end
|
165
|
-
|
165
|
+
|
166
166
|
# If there is an option with the given key, returns the associated value; otherwise returns
|
167
167
|
# the key. This is useful for mapping method names to their renamed equivalents through options.
|
168
168
|
def method(name)
|
169
169
|
name = name.to_sym
|
170
170
|
(all_options[name] || name).to_s
|
171
171
|
end
|
172
|
-
|
172
|
+
|
173
173
|
# Returns the value returned by calling +method_name+ (renamed through options using +method+)
|
174
174
|
# on +object+. Returns +nil+ if +object+ is +nil+ or +object+ does not respond to that method.
|
175
175
|
def get(object, method_name)
|
@@ -184,4 +184,4 @@ module Invoicing
|
|
184
184
|
end
|
185
185
|
end
|
186
186
|
end
|
187
|
-
end
|
187
|
+
end
|
@@ -2,20 +2,20 @@ module Invoicing
|
|
2
2
|
# Extensions specific to certain database adapters. Currently only MySQL and PostgreSQL are
|
3
3
|
# supported.
|
4
4
|
class ConnectionAdapterExt
|
5
|
-
|
5
|
+
|
6
6
|
# Creates a database-specific SQL fragment for evaluating a three-legged conditional function
|
7
7
|
# in a query.
|
8
8
|
def self.conditional_function(condition, value_if_true, value_if_false)
|
9
9
|
case ActiveRecord::Base.connection.adapter_name
|
10
10
|
when "MySQL"
|
11
11
|
"IF(#{condition}, #{value_if_true}, #{value_if_false})"
|
12
|
-
when "PostgreSQL"
|
12
|
+
when "PostgreSQL", "SQLite"
|
13
13
|
"CASE WHEN #{condition} THEN #{value_if_true} ELSE #{value_if_false} END"
|
14
14
|
else
|
15
15
|
raise "Database adapter #{ActiveRecord::Base.connection.adapter_name} not supported by invoicing gem"
|
16
16
|
end
|
17
17
|
end
|
18
|
-
|
18
|
+
|
19
19
|
# Suppose <tt>A has_many B</tt>, and you want to select all As, counting for each A how many
|
20
20
|
# Bs it has. In MySQL you can just say:
|
21
21
|
# SELECT A.*, COUNT(B.id) AS number_of_bs FROM A LEFT JOIN B on A.id = B.a_id GROUP BY A.id
|
@@ -32,7 +32,7 @@ module Invoicing
|
|
32
32
|
when "MySQL"
|
33
33
|
model_class.quoted_table_name + "." +
|
34
34
|
ActiveRecord::Base.connection.quote_column_name(model_class.primary_key)
|
35
|
-
when "PostgreSQL"
|
35
|
+
when "PostgreSQL", "SQLite"
|
36
36
|
model_class.column_names.map{ |column|
|
37
37
|
model_class.quoted_table_name + "." + ActiveRecord::Base.connection.quote_column_name(column)
|
38
38
|
}.join(', ')
|
@@ -6,27 +6,27 @@ module Invoicing
|
|
6
6
|
def tax_rate(params)
|
7
7
|
params[:model_object].send(:tax_rate)
|
8
8
|
end
|
9
|
-
|
9
|
+
|
10
10
|
def tax_factor(params)
|
11
11
|
BigDecimal('1') + tax_rate(params).rate
|
12
12
|
end
|
13
|
-
|
13
|
+
|
14
14
|
def tax_percent(params)
|
15
15
|
BigDecimal('100') * tax_rate(params).rate
|
16
16
|
end
|
17
|
-
|
17
|
+
|
18
18
|
def apply_tax(params)
|
19
19
|
params[:value] * tax_factor(params)
|
20
20
|
end
|
21
|
-
|
21
|
+
|
22
22
|
def remove_tax(params)
|
23
23
|
params[:value] / tax_factor(params)
|
24
24
|
end
|
25
|
-
|
25
|
+
|
26
26
|
def tax_info(params)
|
27
27
|
"(inc. VAT)"
|
28
28
|
end
|
29
|
-
|
29
|
+
|
30
30
|
def tax_details(params)
|
31
31
|
"(including VAT at #{tax_percent(params).to_s}%)"
|
32
32
|
end
|