uomi 0.2.13
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/.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
|
+
[](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
|