invoicing_generator 0.2.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.
- data.tar.gz.sig +0 -0
- data/History.txt +21 -0
- data/LICENSE +20 -0
- data/Manifest.txt +29 -0
- data/PostInstall.txt +10 -0
- data/README.rdoc +55 -0
- data/Rakefile +25 -0
- data/lib/invoicing_generator.rb +18 -0
- data/lib/invoicing_generator/generator_extensions.rb +55 -0
- data/lib/invoicing_generator/name_tools.rb +83 -0
- data/lib/invoicing_generator/option_tools.rb +34 -0
- data/rails_generators/invoicing_ledger/USAGE +20 -0
- data/rails_generators/invoicing_ledger/invoicing_ledger_generator.rb +107 -0
- data/rails_generators/invoicing_ledger/templates/controller.rb +40 -0
- data/rails_generators/invoicing_ledger/templates/credit_note.rb +1 -0
- data/rails_generators/invoicing_ledger/templates/initializer.rb +2 -0
- data/rails_generators/invoicing_ledger/templates/invoice.rb +1 -0
- data/rails_generators/invoicing_ledger/templates/ledger_item.rb +49 -0
- data/rails_generators/invoicing_ledger/templates/ledger_view.html +26 -0
- data/rails_generators/invoicing_ledger/templates/line_item.rb +7 -0
- data/rails_generators/invoicing_ledger/templates/migration.rb +63 -0
- data/rails_generators/invoicing_ledger/templates/payment.rb +1 -0
- data/rails_generators/invoicing_ledger/templates/statement_view.html +48 -0
- data/rails_generators/invoicing_ledger/templates/stylesheet.css +127 -0
- data/script/console +10 -0
- data/script/destroy +14 -0
- data/script/generate +14 -0
- data/test/test_generator_helper.rb +36 -0
- data/test/test_helper.rb +3 -0
- data/test/test_invoicing_ledger_generator.rb +46 -0
- metadata +138 -0
- metadata.gz.sig +0 -0
data.tar.gz.sig
ADDED
Binary file
|
data/History.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
== 0.2.0 2009-04-20
|
2
|
+
|
3
|
+
* 4 major enhancements:
|
4
|
+
* New associated gem invoicing_generator for generating an invoicing component in a Rails project
|
5
|
+
(script/generate invoicing_ledger)
|
6
|
+
* Generated controller and views for rendering statements and ledger
|
7
|
+
* Comes with a nice default stylesheet out of the box
|
8
|
+
* More flexible formatting of currency values
|
9
|
+
* 1 bugfix:
|
10
|
+
* Accidental overwriting of total_amount value on payment LedgerItems without LineItems
|
11
|
+
|
12
|
+
== 0.1.0 2009-02-10
|
13
|
+
|
14
|
+
* 2 major enhancements:
|
15
|
+
* Core API is now usable
|
16
|
+
* RCov reports 100% test coverage
|
17
|
+
|
18
|
+
== 0.0.1 2009-01-05
|
19
|
+
|
20
|
+
* 1 major enhancement:
|
21
|
+
* Initial public release
|
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2009 Martin Kleppmann
|
2
|
+
Copyright (c) 2009 Ept Computing Limited
|
3
|
+
|
4
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
5
|
+
this software and associated documentation files (the "Software"), to deal in
|
6
|
+
the Software without restriction, including without limitation the rights to
|
7
|
+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
8
|
+
of the Software, and to permit persons to whom the Software is furnished to do
|
9
|
+
so, subject to the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be included in all
|
12
|
+
copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
15
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
16
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
17
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
18
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
19
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
20
|
+
SOFTWARE.
|
data/Manifest.txt
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
History.txt
|
2
|
+
LICENSE
|
3
|
+
Manifest.txt
|
4
|
+
PostInstall.txt
|
5
|
+
README.rdoc
|
6
|
+
Rakefile
|
7
|
+
lib/invoicing_generator.rb
|
8
|
+
lib/invoicing_generator/generator_extensions.rb
|
9
|
+
lib/invoicing_generator/name_tools.rb
|
10
|
+
lib/invoicing_generator/option_tools.rb
|
11
|
+
rails_generators/invoicing_ledger/USAGE
|
12
|
+
rails_generators/invoicing_ledger/invoicing_ledger_generator.rb
|
13
|
+
rails_generators/invoicing_ledger/templates/controller.rb
|
14
|
+
rails_generators/invoicing_ledger/templates/credit_note.rb
|
15
|
+
rails_generators/invoicing_ledger/templates/initializer.rb
|
16
|
+
rails_generators/invoicing_ledger/templates/invoice.rb
|
17
|
+
rails_generators/invoicing_ledger/templates/ledger_item.rb
|
18
|
+
rails_generators/invoicing_ledger/templates/ledger_view.html
|
19
|
+
rails_generators/invoicing_ledger/templates/line_item.rb
|
20
|
+
rails_generators/invoicing_ledger/templates/migration.rb
|
21
|
+
rails_generators/invoicing_ledger/templates/payment.rb
|
22
|
+
rails_generators/invoicing_ledger/templates/statement_view.html
|
23
|
+
rails_generators/invoicing_ledger/templates/stylesheet.css
|
24
|
+
script/console
|
25
|
+
script/destroy
|
26
|
+
script/generate
|
27
|
+
test/test_generator_helper.rb
|
28
|
+
test/test_helper.rb
|
29
|
+
test/test_invoicing_ledger_generator.rb
|
data/PostInstall.txt
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
|
2
|
+
Thanks for trying the Ruby Invoicing Gem. Hope you find it useful.
|
3
|
+
Do please blog and tweet about it (tag your posts with #invgem).
|
4
|
+
|
5
|
+
Now please go to the root of your Rails project and type:
|
6
|
+
script/generate invoicing_ledger billing --currency=GBP
|
7
|
+
(replace GBP with your currency if you do not use Pounds Sterling).
|
8
|
+
For more information please go to http://ept.github.com/invoicing/
|
9
|
+
|
10
|
+
|
data/README.rdoc
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
= Ruby Invoicing Framework
|
2
|
+
|
3
|
+
* {Ruby Invoicing Framework website}[http://ept.github.com/invoicing/]
|
4
|
+
* {API Reference Docs}[http://invoicing.rubyforge.org/doc/]
|
5
|
+
* {Browse the code on GitHub}[http://github.com/ept/invoicing/]
|
6
|
+
* {RubyForge project}[http://rubyforge.org/projects/invoicing/]
|
7
|
+
* Email: Martin Kleppmann <ept@rubyforge.org>
|
8
|
+
|
9
|
+
== DESCRIPTION
|
10
|
+
|
11
|
+
This is a framework for generating and displaying invoices (ideal for
|
12
|
+
commercial Rails apps). It allows for flexible business logic; provides tools
|
13
|
+
for tax handling, commission calculation etc. It aims to be both
|
14
|
+
developer-friendly and accountant-friendly.
|
15
|
+
|
16
|
+
The Ruby Invoicing Framework is based on
|
17
|
+
{ActiveRecord}[http://api.rubyonrails.org/classes/ActiveRecord/Base.html].
|
18
|
+
|
19
|
+
Please see {the website}[http://ept.github.com/invoicing/] for an introduction
|
20
|
+
to using Invoicing, and check the
|
21
|
+
{API reference}[http://invoicing.rubyforge.org/doc/] for in-depth details.
|
22
|
+
|
23
|
+
== FEATURES
|
24
|
+
|
25
|
+
* TODO
|
26
|
+
|
27
|
+
== REQUIREMENTS
|
28
|
+
|
29
|
+
* ActiveRecord >= 2.1
|
30
|
+
* Only MySQL and PostgreSQL databases are currently supported
|
31
|
+
|
32
|
+
== INSTALL
|
33
|
+
|
34
|
+
sudo gem install invoicing
|
35
|
+
|
36
|
+
== STATUS
|
37
|
+
|
38
|
+
So far, the Ruby Invoicing Framework has been tested with ActiveRecord 2.2.2,
|
39
|
+
MySQL 5.0.67 and PostgreSQL 8.3.5. We will be testing it across a wider
|
40
|
+
variety of versions soon.
|
41
|
+
|
42
|
+
== CREDITS
|
43
|
+
|
44
|
+
The Ruby invoicing framework originated as part of the website
|
45
|
+
{Bid for Wine}[http://www.bidforwine.co.uk], developed by Patrick Dietrich,
|
46
|
+
Conrad Irwin, Michael Arnold and Martin Kleppmann for Ept Computing Ltd.
|
47
|
+
It was extracted from the Bid for Wine codebase and substantially extended
|
48
|
+
by Martin Kleppmann.
|
49
|
+
|
50
|
+
== LICENSE
|
51
|
+
|
52
|
+
Copyright (c) 2009 Martin Kleppmann, Ept Computing Limited.
|
53
|
+
|
54
|
+
This gem is made publicly available under the terms of the MIT license.
|
55
|
+
See LICENSE and/or COPYING for details.
|
data/Rakefile
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
%w[rubygems rake rake/clean fileutils newgem rubigen].each { |f| require f }
|
2
|
+
require File.dirname(__FILE__) + '/lib/invoicing_generator'
|
3
|
+
|
4
|
+
# Generate all the Rake tasks
|
5
|
+
# Run 'rake -T' to see list of generated tasks (from gem root directory)
|
6
|
+
$hoe = Hoe.new('invoicing_generator', InvoicingGenerator::VERSION) do |p|
|
7
|
+
p.developer('Martin Kleppmann', 'rubyforge@eptcomputing.com')
|
8
|
+
p.changes = p.paragraphs_of("History.txt", 0..1).join("\n\n")
|
9
|
+
p.post_install_message = 'PostInstall.txt'
|
10
|
+
p.rubyforge_name = 'invoicing'
|
11
|
+
p.extra_deps = [
|
12
|
+
['invoicing', "= #{InvoicingGenerator::VERSION}"],
|
13
|
+
]
|
14
|
+
p.extra_dev_deps = [
|
15
|
+
['newgem', ">= #{::Newgem::VERSION}"]
|
16
|
+
]
|
17
|
+
|
18
|
+
p.clean_globs |= %w[**/.DS_Store tmp *.log]
|
19
|
+
path = (p.rubyforge_name == p.name) ? p.rubyforge_name : "\#{p.rubyforge_name}/\#{p.name}"
|
20
|
+
p.remote_rdoc_dir = File.join(path.gsub(/^#{p.rubyforge_name}\/?/,''), 'rdoc')
|
21
|
+
p.rsync_args = '-av --delete --ignore-errors'
|
22
|
+
end
|
23
|
+
|
24
|
+
require 'newgem/tasks' # load /tasks/*.rake
|
25
|
+
Dir['tasks/**/*.rake'].each { |t| load t }
|
@@ -0,0 +1,18 @@
|
|
1
|
+
[
|
2
|
+
File.dirname(__FILE__),
|
3
|
+
File.join(File.dirname(__FILE__), '..', '..', 'invoicing', 'lib')
|
4
|
+
].each do |dir|
|
5
|
+
unless !File.exists?(dir) || $:.include?(dir) || $:.include?(File.expand_path(dir))
|
6
|
+
$:.unshift dir
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
require 'invoicing'
|
11
|
+
|
12
|
+
require 'invoicing_generator/generator_extensions'
|
13
|
+
require 'invoicing_generator/name_tools'
|
14
|
+
require 'invoicing_generator/option_tools'
|
15
|
+
|
16
|
+
module InvoicingGenerator
|
17
|
+
VERSION = Invoicing::VERSION
|
18
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
# Inject a custom command into the rails generator -- useful for rendering classes
|
2
|
+
# nested inside modules.
|
3
|
+
module Rails #:nodoc:
|
4
|
+
module Generator
|
5
|
+
module Commands
|
6
|
+
class Create
|
7
|
+
# A bit like the 'template' method, but wraps the rendered template output in a Ruby
|
8
|
+
# class definition, potentially nested in one or more module definitions.
|
9
|
+
# +class_details+ should be a hash in the form returned by
|
10
|
+
# InvoicingGenerator::NameTools#extract_name_details, detailing information about
|
11
|
+
# the class and to which file it should be written.
|
12
|
+
def nested_class_template(relative_source, class_details, template_options = {})
|
13
|
+
# Render the relative_source template
|
14
|
+
inside_template = render_file(source_path(relative_source), template_options) do |file|
|
15
|
+
vars = template_options[:assigns] || {}
|
16
|
+
b = binding
|
17
|
+
vars.each { |k,v| eval "#{k} = vars[:#{k}] || vars['#{k}']", b }
|
18
|
+
# Render the source file with the temporary binding
|
19
|
+
ERB.new(file.read, nil, '-').result(b)
|
20
|
+
end
|
21
|
+
|
22
|
+
# Prepare class and module definitions
|
23
|
+
nesting = class_details[:class_nesting_array]
|
24
|
+
index = -1
|
25
|
+
header = nesting.map{|mod| index += 1; (' ' * index) + "module #{mod}\n"}.join
|
26
|
+
header << (' ' * nesting.size) + "class #{class_details[:class_name_base]}"
|
27
|
+
header << " < #{class_details[:superclass]}" unless [nil, ''].include? class_details[:superclass]
|
28
|
+
header << "\n"
|
29
|
+
footer = (0..nesting.size).to_a.reverse.map{|n| (' ' * n) + "end\n"}.join
|
30
|
+
indent = ' ' * (nesting.size + 1)
|
31
|
+
|
32
|
+
# Write everything to file
|
33
|
+
file(relative_source, class_details[:file_path_full], template_options) do
|
34
|
+
header + inside_template.split("\n").map{|line| "#{indent}#{line}"}.join("\n") + "\n" + footer
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
# Based on the 'route_resources' method, but less restrictive. Adds arbitrary lines to
|
39
|
+
# the config/routes.rb file.
|
40
|
+
def add_routes(*lines)
|
41
|
+
text = (lines.flatten.map do |line|
|
42
|
+
line.strip!
|
43
|
+
logger.route line
|
44
|
+
" #{line}\n"
|
45
|
+
end).join
|
46
|
+
|
47
|
+
sentinel = 'ActionController::Routing::Routes.draw do |map|'
|
48
|
+
unless options[:pretend]
|
49
|
+
gsub_file('config/routes.rb', /(#{Regexp.escape(sentinel)})/mi) {|match| "#{match}\n#{text}" }
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
# Tools for dealing with names of controllers or models, passed to us by the user
|
2
|
+
# from the command line when invoking the generator. Designed to be included into
|
3
|
+
# a subclass of Rails::Generator::NamedBase.
|
4
|
+
#
|
5
|
+
# This code is inspired by the generator in restful_authentication.
|
6
|
+
module InvoicingGenerator
|
7
|
+
module NameTools
|
8
|
+
|
9
|
+
# Analyses a name provided by the user on the command line, and returns a hash
|
10
|
+
# of useful bits of string based on that name:
|
11
|
+
# extract_name_details 'MegaBlurb/foo/BAR_BLOBS', :kind => :controller, :extension => '.rb'
|
12
|
+
# => {
|
13
|
+
# :underscore_base => 'bar_blobs', # last part of the name, underscored
|
14
|
+
# :camel_base => 'BarBlobs', # last part of the name, camelized
|
15
|
+
# :underscore_singular => 'bar_blob', # underscore_base forced to singular form
|
16
|
+
# :camel_singular => 'BarBlob', # camel_base forced to singular form
|
17
|
+
# :underscore_plural => 'bar_blobs', # underscore_base forced to plural form
|
18
|
+
# :camel_plural => 'BarBlobs', # camel_base forced to plural form
|
19
|
+
# :class_path_array => ['mega_blurb', 'foo'], # array of lowercase, underscored directory names
|
20
|
+
# :class_path => 'mega_blurb/foo', # class_path_array joined by filesystem separator
|
21
|
+
# :class_nesting_array => ['MegaBlurb', 'Foo'], # array of camelized module names
|
22
|
+
# :class_nesting => 'MegaBlurb::Foo', # class_nesting_array joined by double colon
|
23
|
+
# :nesting_depth => 2, # length of class_path array
|
24
|
+
#
|
25
|
+
# # The following depend on the given :kind
|
26
|
+
# :file_path_base => 'bar_blobs_controller.rb' # based on underscore_*
|
27
|
+
# :file_path_full => 'app/controllers/mega_blurb/foo/bar_blobs_controller.rb', # full file path
|
28
|
+
# :class_name_base => 'BarBlobsController', # file_path_base.camelize
|
29
|
+
# :class_name_full => 'MegaBlurb::Foo::BarBlobsController', # = class_nesting + class_name_base
|
30
|
+
# }
|
31
|
+
#
|
32
|
+
# Recognised options:
|
33
|
+
# :kind => :model -- use conventions for creating a model object from the name
|
34
|
+
# :kind => :controller -- use conventions for creating a controller from the name
|
35
|
+
def extract_name_details(user_specified_name, options={})
|
36
|
+
result = {}
|
37
|
+
|
38
|
+
# See Rails::Generator::NamedBase#extract_modules
|
39
|
+
modules = extract_modules(user_specified_name)
|
40
|
+
base_name = modules.shift
|
41
|
+
result[:class_path_array] = modules.shift
|
42
|
+
file_path = modules.shift
|
43
|
+
result[:class_nesting] = modules.shift
|
44
|
+
result[:nesting_depth] = modules.shift
|
45
|
+
result[:class_path] = File.join(result[:class_path_array])
|
46
|
+
result[:class_nesting_array] = result[:class_nesting].split('::')
|
47
|
+
|
48
|
+
result[:underscore_base] = base_name.underscore
|
49
|
+
result[:camel_base] = result[:underscore_base].camelize
|
50
|
+
result[:underscore_singular] = result[:underscore_base].singularize
|
51
|
+
result[:camel_singular] = result[:underscore_singular].camelize
|
52
|
+
result[:underscore_plural] = result[:underscore_singular].pluralize
|
53
|
+
result[:camel_plural] = result[:underscore_plural].camelize
|
54
|
+
|
55
|
+
if options[:kind] == :controller
|
56
|
+
result[:file_path_base] = "#{result[:underscore_base]}_controller"
|
57
|
+
path_prefix = File.join('app', 'controllers')
|
58
|
+
elsif options[:kind] == :model
|
59
|
+
result[:file_path_base] = result[:underscore_singular]
|
60
|
+
path_prefix = File.join('app', 'models')
|
61
|
+
else
|
62
|
+
raise 'unknown kind of name'
|
63
|
+
end
|
64
|
+
|
65
|
+
result[:class_name_base] = result[:file_path_base].camelize
|
66
|
+
result[:file_path_base] += (options[:extension] || ".rb")
|
67
|
+
|
68
|
+
if result[:nesting_depth] == 0
|
69
|
+
result[:file_path_full] = File.join(path_prefix, result[:file_path_base])
|
70
|
+
result[:class_name_full] = result[:class_name_base]
|
71
|
+
else
|
72
|
+
result[:file_path_full] = File.join(path_prefix, result[:class_path], result[:file_path_base])
|
73
|
+
result[:class_name_full] = "#{result[:class_nesting]}::#{result[:class_name_base]}"
|
74
|
+
end
|
75
|
+
|
76
|
+
#result[:routing_name] = result[:singular_name]
|
77
|
+
#result[:routing_path] = result[:file_path].singularize
|
78
|
+
#result[:controller_name] = result[:plural_name]
|
79
|
+
|
80
|
+
result
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# Tools for parsing command line options. Designed to be included into
|
2
|
+
# a subclass of Rails::Generator::NamedBase.
|
3
|
+
module InvoicingGenerator
|
4
|
+
module OptionTools
|
5
|
+
protected
|
6
|
+
|
7
|
+
# Overrides Rails::Generator::Options#add_options!
|
8
|
+
# Expects with_or_without_options to be defined in the importing class.
|
9
|
+
def add_options!(opt)
|
10
|
+
opt.separator ''
|
11
|
+
opt.separator 'Optional flags:'
|
12
|
+
with_or_without_options.each_pair do |key, val|
|
13
|
+
opt.on "--with-#{key}", val + (options[key] ? " (default)" : "") do
|
14
|
+
options[key] = true
|
15
|
+
end
|
16
|
+
opt.on "--without-#{key}", "don't #{val}" + (options[key] ? " (default)" : "") do
|
17
|
+
options[key] = false
|
18
|
+
end
|
19
|
+
end
|
20
|
+
opt.on("--debug", "print debugging output") { options[:debug] = true }
|
21
|
+
end
|
22
|
+
|
23
|
+
# Output debugging info
|
24
|
+
def dump_details
|
25
|
+
name_details.each_pair do |key1, val1|
|
26
|
+
puts "#{key1}:"
|
27
|
+
val1.each_pair do |key2, val2|
|
28
|
+
puts " %-40s %s" % ["#{key2}:", val2.inspect]
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
Description:
|
2
|
+
Generates several model classes, a database migration, a controller and a
|
3
|
+
few views in a Rails project, constituting the core structure you need in
|
4
|
+
order to make use of the invoicing gem in your application.
|
5
|
+
|
6
|
+
Once you have this structure in place you can start creating your own
|
7
|
+
Invoice and LineItem subclasses and store them in the database; the
|
8
|
+
generated controller/views will pick these up and present them nicely
|
9
|
+
out of the box.
|
10
|
+
|
11
|
+
You can use the option flags to configure which fields you would like to
|
12
|
+
include in your database tables.
|
13
|
+
|
14
|
+
After running this generator you should take a look at the generated
|
15
|
+
controller and model classes and search for occurrences of 'FIXME', since
|
16
|
+
a few manual tweaks will be required to integrate the generated code into
|
17
|
+
your project.
|
18
|
+
|
19
|
+
Examples:
|
20
|
+
`./script/generate invoicing_ledger billing --currency=GBP`
|
@@ -0,0 +1,107 @@
|
|
1
|
+
require 'invoicing_generator'
|
2
|
+
|
3
|
+
# Rails generator which creates the migration, models and a controller to support a basic ledger.
|
4
|
+
class InvoicingLedgerGenerator < Rails::Generator::NamedBase
|
5
|
+
|
6
|
+
include InvoicingGenerator::NameTools
|
7
|
+
include InvoicingGenerator::OptionTools
|
8
|
+
|
9
|
+
default_options :description => true, :period => true, :uuid => true, :due_date => true,
|
10
|
+
:tax_point => true, :quantity => true, :creator => true, :identifier => false,
|
11
|
+
:timestamps => true, :debug => false
|
12
|
+
|
13
|
+
attr_reader :name_details
|
14
|
+
|
15
|
+
def initialize(runtime_args, runtime_options = {})
|
16
|
+
super
|
17
|
+
@name_details = {
|
18
|
+
:controller => extract_name_details(@name, :kind => :controller),
|
19
|
+
:ledger_item => extract_name_details(args.shift || 'Billing::LedgerItem', :kind => :model),
|
20
|
+
:line_item => extract_name_details(args.shift || 'Billing::LineItem', :kind => :model)
|
21
|
+
}
|
22
|
+
subclass_nesting = name_details[:ledger_item][:class_nesting]
|
23
|
+
subclass_nesting << '::' unless subclass_nesting == ''
|
24
|
+
name_details[:invoice] = extract_name_details("#{subclass_nesting}Invoice", :kind => :model)
|
25
|
+
name_details[:credit_note] = extract_name_details("#{subclass_nesting}CreditNote", :kind => :model)
|
26
|
+
name_details[:payment] = extract_name_details("#{subclass_nesting}Payment", :kind => :model)
|
27
|
+
|
28
|
+
name_details[:controller ][:superclass] = 'ApplicationController'
|
29
|
+
name_details[:ledger_item][:superclass] = 'ActiveRecord::Base'
|
30
|
+
name_details[:invoice ][:superclass] = name_details[:ledger_item][:class_name_base]
|
31
|
+
name_details[:credit_note][:superclass] = name_details[:ledger_item][:class_name_base]
|
32
|
+
name_details[:payment ][:superclass] = name_details[:ledger_item][:class_name_base]
|
33
|
+
name_details[:line_item ][:superclass] = 'ActiveRecord::Base'
|
34
|
+
|
35
|
+
dump_details if options[:debug]
|
36
|
+
end
|
37
|
+
|
38
|
+
def manifest
|
39
|
+
record do |m|
|
40
|
+
name_details.each_pair do |key, details|
|
41
|
+
# Check for class naming collisions.
|
42
|
+
m.class_collisions details[:class_path_array], details[:class_name_base]
|
43
|
+
|
44
|
+
# Create directories
|
45
|
+
m.directory File.dirname(details[:file_path_full])
|
46
|
+
|
47
|
+
# Create classes
|
48
|
+
m.nested_class_template "#{key}.rb", details, :assigns => { :name_details => name_details }
|
49
|
+
end
|
50
|
+
|
51
|
+
# Migration
|
52
|
+
m.migration_template 'migration.rb', 'db/migrate', :migration_file_name => 'create_invoicing_ledger'
|
53
|
+
|
54
|
+
# Static files
|
55
|
+
m.directory 'config/initializers'
|
56
|
+
m.file 'initializer.rb', 'config/initializers/invoicing.rb'
|
57
|
+
view_directory = File.join('app/views', name_details[:controller][:underscore_base])
|
58
|
+
m.directory view_directory
|
59
|
+
m.file 'statement_view.html', File.join(view_directory, 'statement.html.erb')
|
60
|
+
m.file 'ledger_view.html', File.join(view_directory, 'ledger.html.erb')
|
61
|
+
m.file 'stylesheet.css', File.join('public/stylesheets', 'invoicing_ledger.css')
|
62
|
+
|
63
|
+
# Routes
|
64
|
+
ctrl = name_details[:controller][:underscore_base]
|
65
|
+
m.add_routes(
|
66
|
+
"map.ledger '#{ctrl}/:id/ledger', :controller => '#{ctrl}', :action => 'ledger'",
|
67
|
+
"map.statement '#{ctrl}/:id/:other_id', :controller => '#{ctrl}', :action => 'statement', :id => /\\d+/, :other_id => nil",
|
68
|
+
"map.document '#{ctrl}/document/:id', :controller => '#{ctrl}', :action => 'document'"
|
69
|
+
)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
protected
|
74
|
+
def banner
|
75
|
+
<<-EOS
|
76
|
+
Creates the model classes and migration for a ledger of accounting data.
|
77
|
+
|
78
|
+
USAGE: #{$0} invoicing_ledger ControllerName [LedgerItemsModelName] [LineItemsModelName] [options]
|
79
|
+
|
80
|
+
The recommended ControllerName is 'Billing'.
|
81
|
+
The default model names are 'Billing::LedgerItem' and 'Billing::LineItem', respectively.
|
82
|
+
EOS
|
83
|
+
end
|
84
|
+
|
85
|
+
def with_or_without_options
|
86
|
+
{
|
87
|
+
:description => "create a description column for ledger and line items",
|
88
|
+
:period => "create start_period/end_period columns for ledger items",
|
89
|
+
:uuid => "create uuid columns for ledger and line items",
|
90
|
+
:due_date => "create a due_date column for ledger items",
|
91
|
+
:tax_point => "create a tax_point column for line items",
|
92
|
+
:quantity => "create a quantity column for line items",
|
93
|
+
:creator => "create a creator_id column for line items",
|
94
|
+
:identifier => "create an identifier column for ledger items",
|
95
|
+
:timestamps => "create created_at/updated_at columns"
|
96
|
+
}
|
97
|
+
end
|
98
|
+
|
99
|
+
def add_options!(opt)
|
100
|
+
super
|
101
|
+
opt.separator ''
|
102
|
+
opt.separator 'Optional configuration values:'
|
103
|
+
opt.on "--currency=CODE", "set a default currency (3-letter code, e.g. USD or GBP)" do |currency|
|
104
|
+
options[:currency] = currency.nil? ? nil : currency.upcase
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# GET /<%= name_details[:controller][:underscore_base] %>
|
2
|
+
def index
|
3
|
+
# We suggest that you make this a redirect to the ledger or statement of the currently
|
4
|
+
# logged-in party, e.g.
|
5
|
+
#redirect_to ledger_url(current_user.company)
|
6
|
+
end
|
7
|
+
|
8
|
+
# Display a summary of sales, purchases, payments and receipts on accounts.
|
9
|
+
# GET /<%= name_details[:controller][:underscore_base] %>/1/ledger => From the point of view of party 1
|
10
|
+
def ledger
|
11
|
+
# FIXME check if the current user is allowed to access this ledger
|
12
|
+
@self_id = params[:id].to_i
|
13
|
+
@summaries = <%= name_details[:ledger_item][:class_name_full] %>.account_summaries(@self_id)
|
14
|
+
@names = <%= name_details[:ledger_item][:class_name_full] %>.sender_recipient_name_map(@self_id, @summaries.keys)
|
15
|
+
end
|
16
|
+
|
17
|
+
# GET /<%= name_details[:controller][:underscore_base] %>/1 => Show list of transactions where 1 is sender_id or recipient_id
|
18
|
+
# GET /<%= name_details[:controller][:underscore_base] %>/1/2 => Show list of transactions between parties 1 and 2
|
19
|
+
def statement
|
20
|
+
# FIXME check if the current user is allowed to access this account statement
|
21
|
+
@self_id = params[:id].to_i
|
22
|
+
scope = <%= name_details[:ledger_item][:class_name_full] %>.exclude_empty_invoices.sent_or_received_by(@self_id).sorted(:issue_date)
|
23
|
+
scope = scope.sent_or_received_by(params[:other_id]) if params[:other_id]
|
24
|
+
@in_effect = scope.in_effect.all
|
25
|
+
@open_or_pending = scope.open_or_pending.all
|
26
|
+
@summary = <%= name_details[:ledger_item][:class_name_full] %>.account_summary(@self_id, params[:other_id])
|
27
|
+
end
|
28
|
+
|
29
|
+
# Display an invoice or credit note.
|
30
|
+
# GET /<%= name_details[:controller][:underscore_base] %>/document/1
|
31
|
+
# GET /<%= name_details[:controller][:underscore_base] %>/document/1.xml
|
32
|
+
def document
|
33
|
+
# FIXME check if the current user is allowed to access this ledger item
|
34
|
+
@<%= name_details[:ledger_item][:underscore_singular] %> = <%= name_details[:ledger_item][:class_name_full] %>.find(params[:id])
|
35
|
+
|
36
|
+
respond_to do |format|
|
37
|
+
format.html { render :text => @<%= name_details[:ledger_item][:underscore_singular] %>.render_html, :layout => true }
|
38
|
+
format.xml { render :xml => @<%= name_details[:ledger_item][:underscore_singular] %>.render_ubl }
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
acts_as_credit_note
|
@@ -0,0 +1 @@
|
|
1
|
+
acts_as_invoice
|
@@ -0,0 +1,49 @@
|
|
1
|
+
<% if name_details[:line_item][:underscore_plural] != 'line_items' -%>
|
2
|
+
acts_as_ledger_item :line_items => :<%= name_details[:line_item][:underscore_plural] %>
|
3
|
+
<% else -%>
|
4
|
+
acts_as_ledger_item
|
5
|
+
<% end -%>
|
6
|
+
|
7
|
+
has_many :<%= name_details[:line_item][:underscore_plural] %>, :class_name => '<%= name_details[:line_item][:class_name_full] %>'
|
8
|
+
|
9
|
+
# Change the following associations to refer to your customer or supplier model class
|
10
|
+
#belongs_to :sender, :class_name => 'FIXME'
|
11
|
+
#belongs_to :recipient, :class_name => 'FIXME'
|
12
|
+
|
13
|
+
# Returns a hash containing details about the sender of this invoice, credit note or payment. This allows
|
14
|
+
# you to integrate the invoicing gem with your existing model objects for users and customers/suppliers.
|
15
|
+
#
|
16
|
+
# The returned hash should have the following keys:
|
17
|
+
# <tt>:is_self</tt>:: +true+ if these details refer to yourself, i.e. the person or organsiation who owns/operates
|
18
|
+
# this application. +false+ if these details refer to any other party.
|
19
|
+
# <tt>:name</tt>:: The name of the person or organisation whose billing address is defined below.
|
20
|
+
# <tt>:contact_name</tt>:: The name of a person/department within the organisation named by <tt>:name</tt>.
|
21
|
+
# <tt>:address</tt>:: The body of the billing address (not including city, postcode, state and country); may be
|
22
|
+
# a multi-line string, with lines separated by '\n' line breaks.
|
23
|
+
# <tt>:city</tt>:: The name of the city or town in the billing address.
|
24
|
+
# <tt>:state</tt>:: The state/region/province/county of the billing address as appropriate.
|
25
|
+
# <tt>:postal_code</tt>:: The postal code of the billing address (e.g. ZIP code in the US).
|
26
|
+
# <tt>:country</tt>:: The billing address country (human-readable).
|
27
|
+
# <tt>:country_code</tt>:: The two-letter country code of the billing address, according to
|
28
|
+
# ISO-3166-1[http://en.wikipedia.org/wiki/ISO_3166-1_alpha-2].
|
29
|
+
# <tt>:tax_number</tt>:: The Value Added Tax registration code of this person or organisation, if they have
|
30
|
+
# one, preferably including the country identifier at the beginning. This is important for
|
31
|
+
# transactions within the European Union.
|
32
|
+
def sender_details
|
33
|
+
raise 'FIXME: you must implement <%= name_details[:ledger_item][:class_name_full] %>#sender_details'
|
34
|
+
end
|
35
|
+
|
36
|
+
# Returns a hash containing details about the recipient of this invoice, credit note or payment,
|
37
|
+
# in the same format as returned by +sender_details+.
|
38
|
+
def recipient_details
|
39
|
+
raise 'FIXME: you must implement <%= name_details[:ledger_item][:class_name_full] %>#recipient_details'
|
40
|
+
end
|
41
|
+
|
42
|
+
<% if !options[:identifier] -%>
|
43
|
+
# Returns the user-visible identifier for a document, e.g. the invoice number. The default is to simply
|
44
|
+
# use the primary key generated by the database, but if you want to customise invoice numbers, you can
|
45
|
+
# also create a database column called 'identifier'.
|
46
|
+
def identifier
|
47
|
+
id
|
48
|
+
end
|
49
|
+
<% end -%>
|
@@ -0,0 +1,26 @@
|
|
1
|
+
<h1 class="ledger">Sales and purchase ledger</h1>
|
2
|
+
|
3
|
+
<table class="ledger">
|
4
|
+
<tr>
|
5
|
+
<th class="name">Name</th>
|
6
|
+
<th class="currency">Currency</th>
|
7
|
+
<th class="sales">Sales</th>
|
8
|
+
<th class="purchases">Purchases</th>
|
9
|
+
<th class="sale-receipts">Sale Receipts</th>
|
10
|
+
<th class="purchase-payments">Purchase Payments</th>
|
11
|
+
<th class="balance">Balance</th>
|
12
|
+
</tr>
|
13
|
+
<% @summaries.keys.sort.each do |id| -%>
|
14
|
+
<% @summaries[id].keys.sort.each do |currency| -%>
|
15
|
+
<tr>
|
16
|
+
<td class="name"><%= link_to h(@names[id]), statement_path(:id => @self_id, :other_id => id) %></td>
|
17
|
+
<td class="currency"><%=h currency %></td>
|
18
|
+
<td class="sales"><%=h @summaries[id][currency].sales_formatted %></td>
|
19
|
+
<td class="purchases"><%=h @summaries[id][currency].purchases_formatted %></td>
|
20
|
+
<td class="sale-receipts"><%=h @summaries[id][currency].sale_receipts_formatted %></td>
|
21
|
+
<td class="purchase-payments"><%=h @summaries[id][currency].purchase_payments_formatted %></td>
|
22
|
+
<td class="balance"><%=h @summaries[id][currency].balance_formatted %></td>
|
23
|
+
</tr>
|
24
|
+
<% end -%>
|
25
|
+
<% end -%>
|
26
|
+
</table>
|
@@ -0,0 +1,7 @@
|
|
1
|
+
<% if name_details[:ledger_item][:underscore_singular] != 'ledger_item' -%>
|
2
|
+
acts_as_line_item :ledger_item => :<%= name_details[:ledger_item][:underscore_singular] %>
|
3
|
+
<% else -%>
|
4
|
+
acts_as_line_item
|
5
|
+
<% end -%>
|
6
|
+
|
7
|
+
belongs_to :<%= name_details[:ledger_item][:underscore_singular] %>, :class_name => '<%= name_details[:ledger_item][:class_name_full] %>'
|
@@ -0,0 +1,63 @@
|
|
1
|
+
class CreateInvoicingLedger < ActiveRecord::Migration
|
2
|
+
def self.up
|
3
|
+
create_table :<%= name_details[:ledger_item][:underscore_plural] %> do |t|
|
4
|
+
t.string :type
|
5
|
+
t.integer :sender_id
|
6
|
+
t.integer :recipient_id
|
7
|
+
t.datetime :issue_date
|
8
|
+
t.string :currency, :limit => 3, :null => false<%= options[:currency] ? ", :default => '#{options[:currency]}'" : '' %>
|
9
|
+
t.decimal :total_amount, :precision => 20, :scale => 4
|
10
|
+
t.decimal :tax_amount, :precision => 20, :scale => 4
|
11
|
+
t.string :status, :limit => 20
|
12
|
+
<% if options[:identifier] -%>
|
13
|
+
t.string :identifier, :limit => 50
|
14
|
+
<% end -%>
|
15
|
+
<% if options[:description] -%>
|
16
|
+
t.string :description
|
17
|
+
<% end -%>
|
18
|
+
<% if options[:period] -%>
|
19
|
+
t.datetime :period_start
|
20
|
+
t.datetime :period_end
|
21
|
+
<% end -%>
|
22
|
+
<% if options[:uuid] -%>
|
23
|
+
t.string :uuid, :limit => 40
|
24
|
+
<% end -%>
|
25
|
+
<% if options[:due_date] -%>
|
26
|
+
t.datetime :due_date
|
27
|
+
<% end -%>
|
28
|
+
<% if options[:timestamps] -%>
|
29
|
+
t.timestamps
|
30
|
+
<% end -%>
|
31
|
+
end
|
32
|
+
|
33
|
+
create_table :<%= name_details[:line_item][:underscore_plural] %> do |t|
|
34
|
+
t.string :type
|
35
|
+
t.references :<%= name_details[:ledger_item][:underscore_singular] %>
|
36
|
+
t.decimal :net_amount, :precision => 20, :scale => 4
|
37
|
+
t.decimal :tax_amount, :precision => 20, :scale => 4
|
38
|
+
<% if options[:description] -%>
|
39
|
+
t.string :description
|
40
|
+
<% end -%>
|
41
|
+
<% if options[:uuid] -%>
|
42
|
+
t.string :uuid, :limit => 40
|
43
|
+
<% end -%>
|
44
|
+
<% if options[:tax_point] -%>
|
45
|
+
t.datetime :tax_point
|
46
|
+
<% end -%>
|
47
|
+
<% if options[:quantity] -%>
|
48
|
+
t.decimal :quantity, :precision => 20, :scale => 4
|
49
|
+
<% end -%>
|
50
|
+
<% if options[:creator] -%>
|
51
|
+
t.integer :creator_id
|
52
|
+
<% end -%>
|
53
|
+
<% if options[:timestamps] -%>
|
54
|
+
t.timestamps
|
55
|
+
<% end -%>
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def self.down
|
60
|
+
drop_table :<%= name_details[:line_item][:underscore_plural] %>
|
61
|
+
drop_table :<%= name_details[:ledger_item][:underscore_plural] %>
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
acts_as_payment
|
@@ -0,0 +1,48 @@
|
|
1
|
+
<h1 class="statement">Account statement</h1>
|
2
|
+
|
3
|
+
<% unless @in_effect.empty? %>
|
4
|
+
<table class="statement in_effect">
|
5
|
+
<tr>
|
6
|
+
<th class="number">No.</th>
|
7
|
+
<th class="date">Date</th>
|
8
|
+
<th class="description">Description</th>
|
9
|
+
<th class="amount">Amount</th>
|
10
|
+
</tr>
|
11
|
+
<% for document in @in_effect %>
|
12
|
+
<tr>
|
13
|
+
<td class="number"><%= h document.identifier %></td>
|
14
|
+
<td class="date"><%= h (document.issue_date || document.updated_at).strftime('%Y-%m-%d') %></td>
|
15
|
+
<td class="description">
|
16
|
+
<% if document.class.is_payment -%>
|
17
|
+
<%= h document.description %>
|
18
|
+
<% else -%>
|
19
|
+
<%= link_to h(document.description), document_path(document) %>
|
20
|
+
<% end -%>
|
21
|
+
</td>
|
22
|
+
<td class="amount"><%= h document.total_amount_formatted(:debit => :negative, :self_id => @self_id) %></td>
|
23
|
+
</tr>
|
24
|
+
<% end %>
|
25
|
+
<% @summary.each_pair do |currency, balances| %>
|
26
|
+
<tr class="balance">
|
27
|
+
<th colspan="3">Balance due (<%= h currency %>)</th>
|
28
|
+
<td class="amount"><%= balances.balance_formatted %></td>
|
29
|
+
</tr>
|
30
|
+
<% end %>
|
31
|
+
</table>
|
32
|
+
<% end %>
|
33
|
+
|
34
|
+
<% unless @open_or_pending.empty? %>
|
35
|
+
<h2 class="statement open_or_pending">Charges not yet invoiced</h2>
|
36
|
+
<table class="statement open_or_pending">
|
37
|
+
<tr>
|
38
|
+
<th class="description">Description</th>
|
39
|
+
<th class="amount">Amount</th>
|
40
|
+
</tr>
|
41
|
+
<% for document in @open_or_pending %>
|
42
|
+
<tr>
|
43
|
+
<td class="description"><%= link_to h(document.description), document_path(document) %></td>
|
44
|
+
<td class="amount"><%= h document.total_amount_formatted(:debit => :negative, :self_id => @self_id) %></td>
|
45
|
+
</tr>
|
46
|
+
<% end %>
|
47
|
+
</table>
|
48
|
+
<% end %>
|
@@ -0,0 +1,127 @@
|
|
1
|
+
table.invoice {
|
2
|
+
width: 800px;
|
3
|
+
}
|
4
|
+
|
5
|
+
table.invoice td, table.invoice th {
|
6
|
+
vertical-align: top;
|
7
|
+
}
|
8
|
+
|
9
|
+
table.invoice.addresses {
|
10
|
+
table-layout: fixed;
|
11
|
+
text-align: left;
|
12
|
+
}
|
13
|
+
|
14
|
+
table.invoice.addresses td, table.invoice.addresses th {
|
15
|
+
padding: 0.5em;
|
16
|
+
}
|
17
|
+
|
18
|
+
table.invoice.addresses td.vcard {
|
19
|
+
border: 1px solid black;
|
20
|
+
}
|
21
|
+
|
22
|
+
table.invoice.metadata {
|
23
|
+
table-layout: fixed;
|
24
|
+
margin: 2em 0 1em 0;
|
25
|
+
}
|
26
|
+
|
27
|
+
table.invoice.metadata tr {
|
28
|
+
text-align: right;
|
29
|
+
}
|
30
|
+
|
31
|
+
table.invoice.metadata td {
|
32
|
+
width: 20%;
|
33
|
+
text-align: left;
|
34
|
+
}
|
35
|
+
|
36
|
+
p.invoice.description {
|
37
|
+
font-weight: bold;
|
38
|
+
background-color: #eee;
|
39
|
+
width: 800px;
|
40
|
+
padding: 0.5em;
|
41
|
+
}
|
42
|
+
|
43
|
+
table.invoice.line-items {
|
44
|
+
border-collapse: collapse;
|
45
|
+
empty-cells: show;
|
46
|
+
margin-bottom: 5em;
|
47
|
+
}
|
48
|
+
|
49
|
+
table.invoice.line-items td, table.invoice.line-items th {
|
50
|
+
border: 1px solid black;
|
51
|
+
padding: 0.5em;
|
52
|
+
text-align: left;
|
53
|
+
}
|
54
|
+
|
55
|
+
table.invoice.line-items td.net-amount, table.invoice.line-items th.net-amount,
|
56
|
+
table.invoice.line-items td.tax-amount, table.invoice.line-items th.tax-amount {
|
57
|
+
text-align: right;
|
58
|
+
}
|
59
|
+
|
60
|
+
table.invoice.line-items tr.subtotal td, table.invoice.line-items tr.subtotal th {
|
61
|
+
padding-top: 3em;
|
62
|
+
text-align: right;
|
63
|
+
}
|
64
|
+
|
65
|
+
table.invoice.line-items tr.total td, table.invoice.line-items tr.total th {
|
66
|
+
padding-top: 2em;
|
67
|
+
text-align: right;
|
68
|
+
font-weight: bold;
|
69
|
+
}
|
70
|
+
|
71
|
+
|
72
|
+
|
73
|
+
|
74
|
+
table.ledger {
|
75
|
+
border-collapse: collapse;
|
76
|
+
empty-cells: show;
|
77
|
+
margin: 2em 0 5em;
|
78
|
+
table-layout: fixed;
|
79
|
+
width: 900px;
|
80
|
+
}
|
81
|
+
|
82
|
+
table.ledger td, table.ledger th {
|
83
|
+
border: 1px solid black;
|
84
|
+
padding: 0.5em;
|
85
|
+
text-align: right;
|
86
|
+
vertical-align: top;
|
87
|
+
}
|
88
|
+
|
89
|
+
table.ledger td.name, table.ledger th.name {
|
90
|
+
text-align: left;
|
91
|
+
width: 25%;
|
92
|
+
}
|
93
|
+
|
94
|
+
|
95
|
+
|
96
|
+
table.statement {
|
97
|
+
border-collapse: collapse;
|
98
|
+
empty-cells: show;
|
99
|
+
margin: 2em 0 5em;
|
100
|
+
table-layout: fixed;
|
101
|
+
width: 800px;
|
102
|
+
}
|
103
|
+
|
104
|
+
table.statement td, table.statement th {
|
105
|
+
border: 1px solid black;
|
106
|
+
padding: 0.5em;
|
107
|
+
text-align: left;
|
108
|
+
vertical-align: top;
|
109
|
+
}
|
110
|
+
|
111
|
+
table.statement td.number, table.statement th.number {
|
112
|
+
width: 5%;
|
113
|
+
}
|
114
|
+
|
115
|
+
table.statement td.date, table.statement th.date,
|
116
|
+
table.statement td.amount, table.statement th.amount {
|
117
|
+
width: 15%;
|
118
|
+
}
|
119
|
+
|
120
|
+
table.statement td.amount, table.statement th.amount {
|
121
|
+
text-align: right;
|
122
|
+
}
|
123
|
+
|
124
|
+
table.statement tr.balance td, table.statement tr.balance th {
|
125
|
+
text-align: right;
|
126
|
+
font-weight: bold;
|
127
|
+
}
|
data/script/console
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# File: script/console
|
3
|
+
irb = RUBY_PLATFORM =~ /(:?mswin|mingw)/ ? 'irb.bat' : 'irb'
|
4
|
+
|
5
|
+
libs = " -r irb/completion"
|
6
|
+
# Perhaps use a console_lib to store any extra methods I may want available in the cosole
|
7
|
+
# libs << " -r #{File.dirname(__FILE__) + '/../lib/console_lib/console_logger.rb'}"
|
8
|
+
libs << " -r #{File.dirname(__FILE__) + '/../lib/invoicing_generator.rb'}"
|
9
|
+
puts "Loading invoicing_generator gem"
|
10
|
+
exec "#{irb} #{libs} --simple-prompt"
|
data/script/destroy
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
APP_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..'))
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'rubigen'
|
6
|
+
rescue LoadError
|
7
|
+
require 'rubygems'
|
8
|
+
require 'rubigen'
|
9
|
+
end
|
10
|
+
require 'rubigen/scripts/destroy'
|
11
|
+
|
12
|
+
ARGV.shift if ['--help', '-h'].include?(ARGV[0])
|
13
|
+
RubiGen::Base.use_component_sources! [:rubygems, :newgem, :newgem_theme, :test_unit]
|
14
|
+
RubiGen::Scripts::Destroy.new.run(ARGV)
|
data/script/generate
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
APP_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..'))
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'rubigen'
|
6
|
+
rescue LoadError
|
7
|
+
require 'rubygems'
|
8
|
+
require 'rubigen'
|
9
|
+
end
|
10
|
+
require 'rubigen/scripts/generate'
|
11
|
+
|
12
|
+
ARGV.shift if ['--help', '-h'].include?(ARGV[0])
|
13
|
+
RubiGen::Base.use_component_sources! [:rubygems, :newgem, :newgem_theme, :test_unit]
|
14
|
+
RubiGen::Scripts::Generate.new.run(ARGV)
|
@@ -0,0 +1,36 @@
|
|
1
|
+
begin
|
2
|
+
require File.dirname(__FILE__) + '/test_helper'
|
3
|
+
rescue LoadError
|
4
|
+
require 'test/unit'
|
5
|
+
end
|
6
|
+
require 'fileutils'
|
7
|
+
|
8
|
+
# Load generator libs in the form needed for generating a new rails project
|
9
|
+
gem 'rails'
|
10
|
+
require 'rails/version'
|
11
|
+
require 'rails_generator'
|
12
|
+
require 'rails_generator/scripts/generate'
|
13
|
+
|
14
|
+
# Configure the path of the temporary rails project in which our component
|
15
|
+
# generators will be run in tests.
|
16
|
+
TMP_ROOT = File.dirname(__FILE__) + "/tmp" unless defined?(TMP_ROOT)
|
17
|
+
PROJECT_NAME = "myproject" unless defined?(PROJECT_NAME)
|
18
|
+
app_root = File.join(TMP_ROOT, PROJECT_NAME)
|
19
|
+
if defined?(APP_ROOT)
|
20
|
+
APP_ROOT.replace(app_root)
|
21
|
+
else
|
22
|
+
APP_ROOT = app_root
|
23
|
+
end
|
24
|
+
if defined?(RAILS_ROOT)
|
25
|
+
RAILS_ROOT.replace(app_root)
|
26
|
+
else
|
27
|
+
RAILS_ROOT = app_root
|
28
|
+
end
|
29
|
+
|
30
|
+
begin
|
31
|
+
require 'rubigen'
|
32
|
+
rescue LoadError
|
33
|
+
require 'rubygems'
|
34
|
+
require 'rubigen'
|
35
|
+
end
|
36
|
+
require 'rubigen/helpers/generator_test_helper'
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), "test_generator_helper.rb")
|
2
|
+
|
3
|
+
class TestInvoicingLedgerGenerator < Test::Unit::TestCase
|
4
|
+
include RubiGen::GeneratorTestHelper
|
5
|
+
|
6
|
+
def setup
|
7
|
+
bare_setup
|
8
|
+
Rails::Generator::Base.use_application_sources!
|
9
|
+
Rails::Generator::Scripts::Generate.new.run([APP_ROOT], :generator => 'app')
|
10
|
+
end
|
11
|
+
|
12
|
+
def teardown
|
13
|
+
bare_teardown
|
14
|
+
end
|
15
|
+
|
16
|
+
# Some generator-related assertions:
|
17
|
+
# assert_generated_file(name, &block) # block passed the file contents
|
18
|
+
# assert_directory_exists(name)
|
19
|
+
# assert_generated_class(name, &block)
|
20
|
+
# assert_generated_module(name, &block)
|
21
|
+
# assert_generated_test_for(name, &block)
|
22
|
+
# The assert_generated_(class|module|test_for) &block is passed the body of the class/module within the file
|
23
|
+
# assert_has_method(body, *methods) # check that the body has a list of methods (methods with parentheses not supported yet)
|
24
|
+
#
|
25
|
+
# Other helper methods are:
|
26
|
+
# app_root_files - put this in teardown to show files generated by the test method (e.g. p app_root_files)
|
27
|
+
# bare_setup - place this in setup method to create the APP_ROOT folder for each test
|
28
|
+
# bare_teardown - place this in teardown method to destroy the TMP_ROOT or APP_ROOT folder after each test
|
29
|
+
|
30
|
+
def test_generator_without_options
|
31
|
+
name = "myapp"
|
32
|
+
run_generator('invoicing_ledger', [name], sources)
|
33
|
+
assert_directory_exists "some_folder"
|
34
|
+
assert_generated_file "some_file_after_erb.rb"
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
def sources
|
39
|
+
[RubiGen::PathSource.new(:test, File.join(File.dirname(__FILE__),"..", generator_path))
|
40
|
+
]
|
41
|
+
end
|
42
|
+
|
43
|
+
def generator_path
|
44
|
+
"rails_generators"
|
45
|
+
end
|
46
|
+
end
|
metadata
ADDED
@@ -0,0 +1,138 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: invoicing_generator
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.2.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Martin Kleppmann
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain:
|
11
|
+
- |
|
12
|
+
-----BEGIN CERTIFICATE-----
|
13
|
+
MIIDQjCCAiqgAwIBAgIBADANBgkqhkiG9w0BAQUFADBHMRIwEAYDVQQDDAlydWJ5
|
14
|
+
Zm9yZ2UxHDAaBgoJkiaJk/IsZAEZFgxlcHRjb21wdXRpbmcxEzARBgoJkiaJk/Is
|
15
|
+
ZAEZFgNjb20wHhcNMDkwNDE0MTkwMTIwWhcNMTAwNDE0MTkwMTIwWjBHMRIwEAYD
|
16
|
+
VQQDDAlydWJ5Zm9yZ2UxHDAaBgoJkiaJk/IsZAEZFgxlcHRjb21wdXRpbmcxEzAR
|
17
|
+
BgoJkiaJk/IsZAEZFgNjb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
|
18
|
+
AQDTglrdKabHepgLjgzM1Z52ABCZZ/xsBbn+4pE2jm7aGj2c0fZ+5Uhg0/MfyKsz
|
19
|
+
g3jEFjj0GeNFW7l1sHJYf8mTrdCyAuaymyB2LcLwPtte7/3daAWcs6wAJR8nm9xa
|
20
|
+
CzoeAFjCLcbhZazNBURMizj1z9eTArxhpfpNw4uSIExxNRoykeiFGZ4iq5Getj5x
|
21
|
+
B/JNTdxM7m2KLx+3YKp2LsV0NLL7hRp6zZ6KW1NTWt1mQ58SjDUiRhThfxPfVpCA
|
22
|
+
NI3O+ikDERJWo+nLMUNAZXiB6iMimgUNIUm462tbrumloEjQ/wJE6veGyCRS4jfq
|
23
|
+
Ldnuf6CKFRIxhb9QaN+cu0lLAgMBAAGjOTA3MAkGA1UdEwQCMAAwCwYDVR0PBAQD
|
24
|
+
AgSwMB0GA1UdDgQWBBRR5pYhCiPVxI3rYNDwFcCSqOUGOTANBgkqhkiG9w0BAQUF
|
25
|
+
AAOCAQEAnrM8+tf8f5y8icgcWErHTZZ6oInZxbH8DbAbJ9zIdD/u2o8agzykc/ub
|
26
|
+
Kza0RLJoINt0f1Bb6hzIja82EPeEEQKgR6BJAwPZlxZLGYBTyZeZRy3PJ8IjQ9Zk
|
27
|
+
Fl6OOjmxFfjgF0UGvFHPYUJaNtN8kPs2lyZYmmUrf7qF0n6nwShnVkYePUlvyW0i
|
28
|
+
kqloUNEB5PbHhEwmSTSYOseqm2l2rOAgN3aKVzNFWiMjzZn8OwYaETtD83yB9zlK
|
29
|
+
hqnkWqtTW1V1mEZRyNIx71QdrsaovPQhIKp0bMFUwntYXklVKqxYGHebwt//Gb++
|
30
|
+
jT4Qh3sYJbvqWGFAQcuOIf4spvpNiA==
|
31
|
+
-----END CERTIFICATE-----
|
32
|
+
|
33
|
+
date: 2009-04-20 00:00:00 +01:00
|
34
|
+
default_executable:
|
35
|
+
dependencies:
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: invoicing
|
38
|
+
type: :runtime
|
39
|
+
version_requirement:
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
requirements:
|
42
|
+
- - "="
|
43
|
+
- !ruby/object:Gem::Version
|
44
|
+
version: 0.2.0
|
45
|
+
version:
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: newgem
|
48
|
+
type: :development
|
49
|
+
version_requirement:
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 1.3.0
|
55
|
+
version:
|
56
|
+
- !ruby/object:Gem::Dependency
|
57
|
+
name: hoe
|
58
|
+
type: :development
|
59
|
+
version_requirement:
|
60
|
+
version_requirements: !ruby/object:Gem::Requirement
|
61
|
+
requirements:
|
62
|
+
- - ">="
|
63
|
+
- !ruby/object:Gem::Version
|
64
|
+
version: 1.8.0
|
65
|
+
version:
|
66
|
+
description: This is a framework for generating and displaying invoices (ideal for commercial Rails apps). It allows for flexible business logic; provides tools for tax handling, commission calculation etc. It aims to be both developer-friendly and accountant-friendly. The Ruby Invoicing Framework is based on {ActiveRecord}[http://api.rubyonrails.org/classes/ActiveRecord/Base.html]. Please see {the website}[http://ept.github.com/invoicing/] for an introduction to using Invoicing, and check the {API reference}[http://invoicing.rubyforge.org/doc/] for in-depth details.
|
67
|
+
email:
|
68
|
+
- rubyforge@eptcomputing.com
|
69
|
+
executables: []
|
70
|
+
|
71
|
+
extensions: []
|
72
|
+
|
73
|
+
extra_rdoc_files:
|
74
|
+
- History.txt
|
75
|
+
- Manifest.txt
|
76
|
+
- PostInstall.txt
|
77
|
+
- README.rdoc
|
78
|
+
files:
|
79
|
+
- History.txt
|
80
|
+
- LICENSE
|
81
|
+
- Manifest.txt
|
82
|
+
- PostInstall.txt
|
83
|
+
- README.rdoc
|
84
|
+
- Rakefile
|
85
|
+
- lib/invoicing_generator.rb
|
86
|
+
- lib/invoicing_generator/generator_extensions.rb
|
87
|
+
- lib/invoicing_generator/name_tools.rb
|
88
|
+
- lib/invoicing_generator/option_tools.rb
|
89
|
+
- rails_generators/invoicing_ledger/USAGE
|
90
|
+
- rails_generators/invoicing_ledger/invoicing_ledger_generator.rb
|
91
|
+
- rails_generators/invoicing_ledger/templates/controller.rb
|
92
|
+
- rails_generators/invoicing_ledger/templates/credit_note.rb
|
93
|
+
- rails_generators/invoicing_ledger/templates/initializer.rb
|
94
|
+
- rails_generators/invoicing_ledger/templates/invoice.rb
|
95
|
+
- rails_generators/invoicing_ledger/templates/ledger_item.rb
|
96
|
+
- rails_generators/invoicing_ledger/templates/ledger_view.html
|
97
|
+
- rails_generators/invoicing_ledger/templates/line_item.rb
|
98
|
+
- rails_generators/invoicing_ledger/templates/migration.rb
|
99
|
+
- rails_generators/invoicing_ledger/templates/payment.rb
|
100
|
+
- rails_generators/invoicing_ledger/templates/statement_view.html
|
101
|
+
- rails_generators/invoicing_ledger/templates/stylesheet.css
|
102
|
+
- script/console
|
103
|
+
- script/destroy
|
104
|
+
- script/generate
|
105
|
+
- test/test_generator_helper.rb
|
106
|
+
- test/test_helper.rb
|
107
|
+
- test/test_invoicing_ledger_generator.rb
|
108
|
+
has_rdoc: true
|
109
|
+
homepage: "{Ruby Invoicing Framework website}[http://ept.github.com/invoicing/]"
|
110
|
+
post_install_message: PostInstall.txt
|
111
|
+
rdoc_options:
|
112
|
+
- --main
|
113
|
+
- README.rdoc
|
114
|
+
require_paths:
|
115
|
+
- lib
|
116
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
117
|
+
requirements:
|
118
|
+
- - ">="
|
119
|
+
- !ruby/object:Gem::Version
|
120
|
+
version: "0"
|
121
|
+
version:
|
122
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
123
|
+
requirements:
|
124
|
+
- - ">="
|
125
|
+
- !ruby/object:Gem::Version
|
126
|
+
version: "0"
|
127
|
+
version:
|
128
|
+
requirements: []
|
129
|
+
|
130
|
+
rubyforge_project: invoicing
|
131
|
+
rubygems_version: 1.3.1
|
132
|
+
signing_key:
|
133
|
+
specification_version: 2
|
134
|
+
summary: This is a framework for generating and displaying invoices (ideal for commercial Rails apps)
|
135
|
+
test_files:
|
136
|
+
- test/test_generator_helper.rb
|
137
|
+
- test/test_helper.rb
|
138
|
+
- test/test_invoicing_ledger_generator.rb
|
metadata.gz.sig
ADDED
Binary file
|