invoicing_generator 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|