uomi 0.2.13
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +6 -0
- data/.rvmrc +1 -0
- data/.travis.yml +3 -0
- data/Gemfile +10 -0
- data/README.md +35 -0
- data/Rakefile +8 -0
- data/config.ru +7 -0
- data/invoicing.gemspec +27 -0
- data/lib/generators/active_record/invoicing_generator.rb +30 -0
- data/lib/generators/active_record/templates/migration.rb +84 -0
- data/lib/invoicing/buyer.rb +10 -0
- data/lib/invoicing/credit_note.rb +70 -0
- data/lib/invoicing/credit_note_credit_transaction.rb +6 -0
- data/lib/invoicing/credit_note_invoice.rb +6 -0
- data/lib/invoicing/credit_transaction.rb +9 -0
- data/lib/invoicing/debit_transaction.rb +9 -0
- data/lib/invoicing/exception.rb +4 -0
- data/lib/invoicing/invoice.rb +233 -0
- data/lib/invoicing/invoice_adjustment.rb +59 -0
- data/lib/invoicing/invoice_decorator.rb +6 -0
- data/lib/invoicing/invoiceable.rb +22 -0
- data/lib/invoicing/late_payment.rb +11 -0
- data/lib/invoicing/line_item.rb +17 -0
- data/lib/invoicing/line_item_type.rb +6 -0
- data/lib/invoicing/overdue_invoice.rb +16 -0
- data/lib/invoicing/payment_reference.rb +10 -0
- data/lib/invoicing/seller.rb +10 -0
- data/lib/invoicing/transaction.rb +13 -0
- data/lib/invoicing/version.rb +3 -0
- data/lib/invoicing.rb +46 -0
- data/spec/README +0 -0
- data/spec/internal/config/database.yml.sample +3 -0
- data/spec/internal/config/routes.rb +3 -0
- data/spec/internal/db/schema.rb +81 -0
- data/spec/internal/log/.gitignore +1 -0
- data/spec/internal/public/favicon.ico +0 -0
- data/spec/lib/invoicing/credit_note_spec.rb +140 -0
- data/spec/lib/invoicing/invoice_adjustment_spec.rb +136 -0
- data/spec/lib/invoicing/invoice_late_payment_spec.rb +10 -0
- data/spec/lib/invoicing/invoice_spec.rb +419 -0
- data/spec/lib/invoicing/line_item_spec.rb +14 -0
- data/spec/lib/invoicing/overdue_invoice_spec.rb +67 -0
- data/spec/spec_helper.rb +20 -0
- data/spec/support/helpers.rb +20 -0
- metadata +185 -0
data/.gitignore
ADDED
data/.rvmrc
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
rvm use 1.9.2@invoicing --create
|
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
|
2
|
+
[![Build Status](https://secure.travis-ci.org/tehtorq/invoicing.png)](http://travis-ci.org/tehtorq/invoicing)
|
3
|
+
|
4
|
+
Basic Usage
|
5
|
+
|
6
|
+
seller = Seller.find(3)
|
7
|
+
book = Book.find(1) # implements CostItem
|
8
|
+
decorations = {whatever_you_want: 'here'}
|
9
|
+
|
10
|
+
invoice = Invoicing::generate do
|
11
|
+
from seller
|
12
|
+
line_item book
|
13
|
+
due Time.now + 7.days
|
14
|
+
payment_reference "REF2345"
|
15
|
+
decorate_with decorations
|
16
|
+
end
|
17
|
+
|
18
|
+
Invoice Numbering:
|
19
|
+
|
20
|
+
A default invoice number will be set with the format INV[invoice id].
|
21
|
+
|
22
|
+
A custom invoice number can be specified as follows:
|
23
|
+
|
24
|
+
invoice = Invoicing::generate do
|
25
|
+
numbered "CUSTOMREF123"
|
26
|
+
end
|
27
|
+
|
28
|
+
You can specify a custom invoice number containing the invoice id as follows:
|
29
|
+
|
30
|
+
invoice = Invoicing::generate do
|
31
|
+
numbered "CUSTOMREF{id}"
|
32
|
+
end
|
33
|
+
|
34
|
+
|
35
|
+
|
data/Rakefile
ADDED
data/config.ru
ADDED
data/invoicing.gemspec
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "invoicing/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "uomi"
|
7
|
+
s.version = Invoicing::VERSION
|
8
|
+
s.authors = ["Douglas Anderson", "Jeffrey van Aswegen"]
|
9
|
+
s.email = ["i.am.douglas.anderson@gmail.com", "jeffmess@gmail.com"]
|
10
|
+
s.homepage = 'https://github.com/tehtorq/invoicing'
|
11
|
+
s.summary = %q{ An invoicing gem. }
|
12
|
+
s.description = %q{ Manage invoices. }
|
13
|
+
|
14
|
+
s.rubyforge_project = "uomi"
|
15
|
+
|
16
|
+
s.files = `git ls-files`.split("\n")
|
17
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
18
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
19
|
+
s.require_paths = ["lib"]
|
20
|
+
|
21
|
+
s.add_dependency "activesupport"
|
22
|
+
s.add_dependency "activerecord", "~> 3.0"
|
23
|
+
s.add_dependency "i18n"
|
24
|
+
s.add_dependency "workflow", '= 0.8.7'
|
25
|
+
|
26
|
+
s.add_development_dependency 'combustion', '~> 0.3.1'
|
27
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module Invoicing
|
2
|
+
module Generators
|
3
|
+
class InstallGenerator < Rails::Generators::Base
|
4
|
+
include Rails::Generators::Migration
|
5
|
+
|
6
|
+
source_root File.expand_path("../templates", __FILE__)
|
7
|
+
|
8
|
+
desc <<-CONTENT
|
9
|
+
Copies the invoicing migration file to the migrations
|
10
|
+
folder.
|
11
|
+
|
12
|
+
Please run rake db:migrate once the installer is
|
13
|
+
complete.
|
14
|
+
|
15
|
+
CONTENT
|
16
|
+
|
17
|
+
def self.next_migration_number(dirname) #:nodoc:
|
18
|
+
if ActiveRecord::Base.timestamped_migrations
|
19
|
+
Time.now.utc.strftime("%Y%m%d%H%M%S")
|
20
|
+
else
|
21
|
+
"%.3d" % (current_migration_number(dirname) + 1)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def create_migration_file
|
26
|
+
migration_template 'migration.rb', 'db/migrate/create_invoicing_tables.rb'
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
class CreateInvoicingTables < ActiveRecord::Migration
|
2
|
+
def self.change
|
3
|
+
|
4
|
+
create_table "invoicing_late_payments", :force => true do |t|
|
5
|
+
t.integer "invoice_id"
|
6
|
+
t.integer "amount"
|
7
|
+
t.datetime "penalty_date"
|
8
|
+
t.boolean "processed"
|
9
|
+
t.timestamps
|
10
|
+
end
|
11
|
+
|
12
|
+
create_table "invoicing_line_items", :force => true do |t|
|
13
|
+
t.integer "invoice_id"
|
14
|
+
t.string "description"
|
15
|
+
t.integer "amount"
|
16
|
+
t.integer "tax"
|
17
|
+
t.integer "invoiceable_id"
|
18
|
+
t.string "invoiceable_type"
|
19
|
+
t.integer "line_item_type_id"
|
20
|
+
t.timestamps
|
21
|
+
end
|
22
|
+
|
23
|
+
create_table "invoicing_transactions", :force => true do |t|
|
24
|
+
t.integer "invoice_id"
|
25
|
+
t.string "type"
|
26
|
+
t.integer "amount"
|
27
|
+
t.timestamps
|
28
|
+
end
|
29
|
+
|
30
|
+
create_table "invoicing_invoices", :force => true do |t|
|
31
|
+
t.integer "seller_id"
|
32
|
+
t.integer "buyer_id"
|
33
|
+
t.string "invoice_number"
|
34
|
+
t.datetime "due_date"
|
35
|
+
t.datetime "issued_at"
|
36
|
+
t.integer "total"
|
37
|
+
t.integer "tax"
|
38
|
+
t.integer "balance"
|
39
|
+
t.string "type"
|
40
|
+
t.string "workflow_state"
|
41
|
+
t.timestamps
|
42
|
+
end
|
43
|
+
|
44
|
+
create_table "invoicing_payment_references", :force => true do |t|
|
45
|
+
t.integer "invoice_id"
|
46
|
+
t.string "reference"
|
47
|
+
t.timestamps
|
48
|
+
end
|
49
|
+
|
50
|
+
create_table "invoicing_sellers", :force => true do |t|
|
51
|
+
t.integer "sellerable_id"
|
52
|
+
t.string "sellerable_type"
|
53
|
+
t.timestamps
|
54
|
+
end
|
55
|
+
|
56
|
+
create_table "invoicing_buyers", :force => true do |t|
|
57
|
+
t.integer "buyerable_id"
|
58
|
+
t.string "buyerable_type"
|
59
|
+
t.timestamps
|
60
|
+
end
|
61
|
+
|
62
|
+
create_table "invoicing_invoice_decorators", :force => true do |t|
|
63
|
+
t.integer "invoice_id"
|
64
|
+
t.text "data"
|
65
|
+
t.timestamps
|
66
|
+
end
|
67
|
+
|
68
|
+
create_table "invoicing_credit_note_invoices", :force => true do |t|
|
69
|
+
t.integer "invoice_id"
|
70
|
+
t.integer "credit_note_id"
|
71
|
+
end
|
72
|
+
|
73
|
+
create_table "invoicing_credit_note_credit_transactions", :force => true do |t|
|
74
|
+
t.integer "credit_note_id"
|
75
|
+
t.integer "transaction_id"
|
76
|
+
end
|
77
|
+
|
78
|
+
create_table "invoicing_line_item_types", :force => true do |t|
|
79
|
+
t.string "name"
|
80
|
+
end
|
81
|
+
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
module Invoicing
|
2
|
+
class Buyer < ActiveRecord::Base
|
3
|
+
has_many :invoices
|
4
|
+
belongs_to :buyerable, polymorphic: true
|
5
|
+
|
6
|
+
def self.for(buyerable)
|
7
|
+
Buyer.where(buyerable_type: buyerable.class.name, buyerable_id: buyerable.id).first || Buyer.create!(buyerable_type: buyerable.class.name, buyerable_id: buyerable.id)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
module Invoicing
|
2
|
+
class CreditNote < Invoice
|
3
|
+
alias_attribute :receipt_number, :invoice_number
|
4
|
+
|
5
|
+
has_one :credit_note_invoice, dependent: :destroy
|
6
|
+
has_one :invoice, through: :credit_note_invoice
|
7
|
+
has_many :credit_note_credit_transactions, dependent: :destroy
|
8
|
+
|
9
|
+
def issue(issued_at = Time.now)
|
10
|
+
self.issued_at = issued_at
|
11
|
+
create_initial_transaction!
|
12
|
+
record_transaction_against_invoice!
|
13
|
+
record_credit_notes!
|
14
|
+
end
|
15
|
+
|
16
|
+
def record_transaction_against_invoice!
|
17
|
+
raise RuntimeError, "You must allocate a credit note against an invoice" if invoice.blank?
|
18
|
+
raise RuntimeError, "You must allocate a credit note against an issued invoice" unless invoice.issued?
|
19
|
+
|
20
|
+
invoice.add_credit_transaction(amount: total)
|
21
|
+
invoice.save!
|
22
|
+
CreditNoteCreditTransaction.create!(transaction: invoice.transactions.last, credit_note_id: self.id)
|
23
|
+
end
|
24
|
+
|
25
|
+
def credit(options={})
|
26
|
+
add_line_item(
|
27
|
+
invoiceable: options[:line_item].invoiceable,
|
28
|
+
amount: options[:amount] || 0,
|
29
|
+
tax: options[:tax] || 0,
|
30
|
+
description: options[:description] || "Credit note against #{options[:line_item].description}" #?
|
31
|
+
)
|
32
|
+
end
|
33
|
+
|
34
|
+
def against_invoice(invoice)
|
35
|
+
raise RuntimeError, "You must allocate a credit note against an invoice" if invoice.blank?
|
36
|
+
raise RuntimeError, "You must allocate a credit note against an issued invoice" unless invoice.issued?
|
37
|
+
|
38
|
+
self.credit_note_invoice = CreditNoteInvoice.new(invoice_id: invoice.id)
|
39
|
+
self.buyer = invoice.buyer
|
40
|
+
self.seller = invoice.seller
|
41
|
+
end
|
42
|
+
|
43
|
+
def record_credit_notes!
|
44
|
+
line_items.each do |line_item|
|
45
|
+
invoiceable = line_item.invoiceable
|
46
|
+
next if invoiceable.blank?
|
47
|
+
invoiceable.handle_credit(line_item.amount) if invoiceable.respond_to?(:handle_credit)
|
48
|
+
invoiceable.save!
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def annul(params={})
|
53
|
+
record_amount_against_invoice(params[:amount], params[:against_invoice]) if params[:against_invoice]
|
54
|
+
end
|
55
|
+
|
56
|
+
def default_numbering_prefix
|
57
|
+
"CN"
|
58
|
+
end
|
59
|
+
|
60
|
+
def create_initial_transaction!
|
61
|
+
if total > 0
|
62
|
+
add_credit_transaction amount: total
|
63
|
+
else
|
64
|
+
add_debit_transaction amount: total
|
65
|
+
end
|
66
|
+
|
67
|
+
save!
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,233 @@
|
|
1
|
+
module Invoicing
|
2
|
+
class Invoice < ActiveRecord::Base
|
3
|
+
include ::Workflow
|
4
|
+
has_many :line_items, dependent: :destroy
|
5
|
+
has_many :transactions, dependent: :destroy
|
6
|
+
has_many :payment_references, dependent: :destroy
|
7
|
+
has_one :late_payment, dependent: :destroy
|
8
|
+
belongs_to :seller
|
9
|
+
belongs_to :buyer
|
10
|
+
has_one :invoice_decorator, dependent: :destroy
|
11
|
+
|
12
|
+
validates_uniqueness_of :invoice_number, scope: [:seller_id]
|
13
|
+
|
14
|
+
before_save :calculate_totals, :calculate_balance
|
15
|
+
after_create :set_invoice_number!
|
16
|
+
|
17
|
+
alias :decorator :invoice_decorator
|
18
|
+
|
19
|
+
workflow do
|
20
|
+
state :draft do
|
21
|
+
event :issue, transitions_to: :issued
|
22
|
+
event :void, transitions_to: :voided
|
23
|
+
end
|
24
|
+
|
25
|
+
state :issued do
|
26
|
+
event :settle, transitions_to: :settled
|
27
|
+
event :void, transitions_to: :voided
|
28
|
+
end
|
29
|
+
|
30
|
+
state :settled
|
31
|
+
state :voided
|
32
|
+
end
|
33
|
+
|
34
|
+
def issue(&block)
|
35
|
+
self.issued_at = Time.now
|
36
|
+
instance_eval(&block) if block_given?
|
37
|
+
create_initial_transaction!
|
38
|
+
mark_items_invoiced!
|
39
|
+
self
|
40
|
+
end
|
41
|
+
|
42
|
+
def void
|
43
|
+
raise CannotVoidDocumentException, "Cannot void a document that has a transaction recorded against it!" if transactions.many?
|
44
|
+
annul_remaining_amount! unless self.draft?
|
45
|
+
mark_items_uninvoiced!
|
46
|
+
self
|
47
|
+
end
|
48
|
+
|
49
|
+
def add_line_item(params)
|
50
|
+
self.line_items << LineItem.new(params)
|
51
|
+
end
|
52
|
+
|
53
|
+
def remove_line_item(item)
|
54
|
+
line_items.delete(item)
|
55
|
+
end
|
56
|
+
|
57
|
+
def add_debit_transaction(params)
|
58
|
+
self.transactions << DebitTransaction.new(params)
|
59
|
+
end
|
60
|
+
|
61
|
+
def add_credit_transaction(params)
|
62
|
+
self.transactions << CreditTransaction.new(params)
|
63
|
+
end
|
64
|
+
|
65
|
+
def calculate_totals
|
66
|
+
self.total = line_items.inject(0) {|res, item| res + item.amount.to_i}
|
67
|
+
self.tax = line_items.inject(0) {|res, item| res + item.tax.to_i}
|
68
|
+
end
|
69
|
+
|
70
|
+
def annul_remaining_amount!
|
71
|
+
add_credit_transaction amount: balance.abs
|
72
|
+
end
|
73
|
+
|
74
|
+
def create_initial_transaction!
|
75
|
+
if total < 0
|
76
|
+
add_credit_transaction amount: total
|
77
|
+
else
|
78
|
+
add_debit_transaction amount: total
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def credit_notes
|
83
|
+
CreditNoteInvoice.where(invoice_id: id).map(&:credit_note)
|
84
|
+
end
|
85
|
+
|
86
|
+
def default_numbering_prefix
|
87
|
+
"INV"
|
88
|
+
end
|
89
|
+
|
90
|
+
def set_invoice_number!
|
91
|
+
self.invoice_number ||= "#{default_numbering_prefix}#{id}"
|
92
|
+
self.invoice_number.gsub!("{id}", "#{id}")
|
93
|
+
save!
|
94
|
+
end
|
95
|
+
|
96
|
+
def debit_transactions
|
97
|
+
transactions.select{|t| t.is_a? DebitTransaction}
|
98
|
+
end
|
99
|
+
|
100
|
+
def credit_transactions
|
101
|
+
transactions.select{|t| t.is_a? CreditTransaction}
|
102
|
+
end
|
103
|
+
|
104
|
+
def calculate_balance
|
105
|
+
self.balance = (0 - debit_transactions.sum(&:amount)) + credit_transactions.sum(&:amount)
|
106
|
+
settle! if should_settle?
|
107
|
+
end
|
108
|
+
|
109
|
+
def should_settle?
|
110
|
+
issued? && balance_zero?
|
111
|
+
end
|
112
|
+
|
113
|
+
def net_total
|
114
|
+
total - tax
|
115
|
+
end
|
116
|
+
|
117
|
+
def balance_zero?
|
118
|
+
balance == 0
|
119
|
+
end
|
120
|
+
|
121
|
+
def owing?
|
122
|
+
balance < 0
|
123
|
+
end
|
124
|
+
|
125
|
+
def due_date_past?
|
126
|
+
due_date.to_date < Date.today
|
127
|
+
end
|
128
|
+
|
129
|
+
def overdue?
|
130
|
+
owing? and due_date_past?
|
131
|
+
end
|
132
|
+
|
133
|
+
def self.owing
|
134
|
+
where("balance < ?", 0)
|
135
|
+
end
|
136
|
+
|
137
|
+
def self.issued
|
138
|
+
where(workflow_state: "issued")
|
139
|
+
end
|
140
|
+
|
141
|
+
def self.draft
|
142
|
+
where(workflow_state: "draft")
|
143
|
+
end
|
144
|
+
|
145
|
+
def self.settled
|
146
|
+
where(workflow_state: "settled")
|
147
|
+
end
|
148
|
+
|
149
|
+
def self.voided
|
150
|
+
where(workflow_state: "voided")
|
151
|
+
end
|
152
|
+
|
153
|
+
def add_payment_reference(params)
|
154
|
+
self.payment_references << PaymentReference.new(params)
|
155
|
+
end
|
156
|
+
|
157
|
+
def remove_payment_reference(payment_reference)
|
158
|
+
payment_references.delete(payment_reference)
|
159
|
+
end
|
160
|
+
|
161
|
+
def self.for_payment_reference(reference)
|
162
|
+
PaymentReference.where(reference: reference).map(&:invoice)
|
163
|
+
end
|
164
|
+
|
165
|
+
def mark_items_invoiced!
|
166
|
+
line_items.map(&:invoiceable).compact.each do |item|
|
167
|
+
item.mark_invoiced(self) if item.respond_to?(:mark_invoiced)
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
def mark_items_uninvoiced!
|
172
|
+
line_items.map(&:invoiceable).compact.each do |item|
|
173
|
+
item.mark_uninvoiced(self) if item.respond_to?(:mark_uninvoiced)
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
def line_item(cost_item)
|
178
|
+
if cost_item.is_a? Hash
|
179
|
+
add_line_item(
|
180
|
+
amount: cost_item[:amount] || 0,
|
181
|
+
tax: cost_item[:tax] || 0,
|
182
|
+
description: cost_item[:description] || 'Line Item',
|
183
|
+
line_item_type_id: cost_item[:line_item_type_id]
|
184
|
+
)
|
185
|
+
else
|
186
|
+
add_line_item(
|
187
|
+
invoiceable: cost_item,
|
188
|
+
amount: cost_item.amount || 0,
|
189
|
+
tax: cost_item.tax || 0,
|
190
|
+
description: cost_item.description || 'Line Item',
|
191
|
+
line_item_type_id: cost_item.respond_to?(:line_item_type_id) ? cost_item.line_item_type_id : 0
|
192
|
+
)
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
def payment_reference(reference)
|
197
|
+
add_payment_reference(reference: reference)
|
198
|
+
end
|
199
|
+
|
200
|
+
def due(due_date)
|
201
|
+
self.due_date = due_date
|
202
|
+
end
|
203
|
+
|
204
|
+
def to(buyerable)
|
205
|
+
self.buyer = Buyer.for(buyerable)
|
206
|
+
end
|
207
|
+
|
208
|
+
def from(sellerable)
|
209
|
+
self.seller = Seller.for(sellerable)
|
210
|
+
end
|
211
|
+
|
212
|
+
def decorate_with(decorations)
|
213
|
+
if self.invoice_decorator
|
214
|
+
self.invoice_decorator.data = decorations
|
215
|
+
else
|
216
|
+
self.invoice_decorator = InvoiceDecorator.new(data: decorations)
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
220
|
+
def numbered(invoice_number)
|
221
|
+
self.invoice_number = invoice_number
|
222
|
+
end
|
223
|
+
|
224
|
+
def adjust(&block)
|
225
|
+
adjustment = InvoiceAdjustment.new(self)
|
226
|
+
adjustment.instance_eval(&block)
|
227
|
+
adjustment.persist!
|
228
|
+
adjustment.invoice
|
229
|
+
end
|
230
|
+
|
231
|
+
end
|
232
|
+
|
233
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
module Invoicing
|
2
|
+
|
3
|
+
class InvoiceAdjustment
|
4
|
+
|
5
|
+
attr_accessor :invoice
|
6
|
+
|
7
|
+
def initialize(invoice)
|
8
|
+
raise CannotAdjustIssuedDocument unless invoice.draft?
|
9
|
+
self.invoice = invoice
|
10
|
+
end
|
11
|
+
|
12
|
+
def due(due_date)
|
13
|
+
invoice.due(due_date)
|
14
|
+
end
|
15
|
+
|
16
|
+
def add_line_item(params)
|
17
|
+
invoice.line_item(params)
|
18
|
+
end
|
19
|
+
|
20
|
+
def edit_line_item(item, params)
|
21
|
+
raise CannotEditNonExistantLineItem if invoice.line_items.find(item.id).blank?
|
22
|
+
self.invoice.line_items.for(item).update_attributes(params)
|
23
|
+
end
|
24
|
+
|
25
|
+
def remove_line_item(item)
|
26
|
+
invoice.line_items.delete(item)
|
27
|
+
end
|
28
|
+
|
29
|
+
def add_payment_reference(payment_reference)
|
30
|
+
invoice.payment_reference(payment_reference)
|
31
|
+
end
|
32
|
+
|
33
|
+
def remove_payment_reference(payment_reference)
|
34
|
+
invoice.remove_payment_reference(payment_reference)
|
35
|
+
end
|
36
|
+
|
37
|
+
def to(buyerable)
|
38
|
+
invoice.to(buyerable)
|
39
|
+
end
|
40
|
+
|
41
|
+
def numbered(invoice_number)
|
42
|
+
invoice.numbered(invoice_number)
|
43
|
+
end
|
44
|
+
|
45
|
+
def decorate_with(decorations)
|
46
|
+
invoice.decorate_with(decorations)
|
47
|
+
end
|
48
|
+
|
49
|
+
def persist!
|
50
|
+
# this method makes me a little sad.
|
51
|
+
self.invoice.transaction do
|
52
|
+
self.invoice.invoice_decorator.save
|
53
|
+
self.invoice.line_items.map(&:reload)
|
54
|
+
self.invoice.save!
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# implement this module and override behaviour on items which will be invoiced
|
2
|
+
|
3
|
+
module Invoicing
|
4
|
+
module Invoiceable
|
5
|
+
|
6
|
+
attr_accessor :invoiced, :invoice_id, :amount, :tax, :line_item_type_id
|
7
|
+
|
8
|
+
def description
|
9
|
+
"Invoiceable Item"
|
10
|
+
end
|
11
|
+
|
12
|
+
def handle_credit(amount)
|
13
|
+
end
|
14
|
+
|
15
|
+
def mark_invoiced(invoice)
|
16
|
+
end
|
17
|
+
|
18
|
+
def mark_uninvoiced
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
end
|