invoicing 0.2.1 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
[![Build Status](https://travis-ci.org/code-mancers/invoicing.svg?branch=master)](https://travis-ci.org/code-mancers/invoicing)
|
3
|
+
[![Code Climate](https://codeclimate.com/github/code-mancers/invoicing.png)](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
|