chiliproject_invoice 0.1.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.
Files changed (45) hide show
  1. data/COPYRIGHT.txt +18 -0
  2. data/CREDITS.rdoc +4 -0
  3. data/GPL.txt +339 -0
  4. data/README.rdoc +57 -0
  5. data/Rakefile +34 -0
  6. data/VERSION +1 -0
  7. data/app/controllers/invoice_controller.rb +94 -0
  8. data/app/controllers/payments_controller.rb +35 -0
  9. data/app/helpers/invoices_helper.rb +52 -0
  10. data/app/models/autofill.rb +69 -0
  11. data/app/models/invoice.rb +70 -0
  12. data/app/models/payment.rb +10 -0
  13. data/app/views/invoice/_form.rhtml +34 -0
  14. data/app/views/invoice/_row.rhtml +17 -0
  15. data/app/views/invoice/_table.rhtml +11 -0
  16. data/app/views/invoice/autocreate.rhtml +48 -0
  17. data/app/views/invoice/autofill.js.rjs +28 -0
  18. data/app/views/invoice/edit.rhtml +17 -0
  19. data/app/views/invoice/index.rhtml +26 -0
  20. data/app/views/invoice/new.rhtml +15 -0
  21. data/app/views/invoice/show.rhtml +63 -0
  22. data/app/views/layouts/print.rhtml +12 -0
  23. data/app/views/payments/_payment.html.erb +4 -0
  24. data/app/views/payments/new.html.erb +38 -0
  25. data/app/views/settings/_invoice_settings.rhtml +37 -0
  26. data/assets/images/creditcards.png +0 -0
  27. data/assets/images/money.png +0 -0
  28. data/assets/images/money_add.png +0 -0
  29. data/assets/images/printer.png +0 -0
  30. data/assets/stylesheets/invoice.css +36 -0
  31. data/assets/stylesheets/invoice_print.css +5 -0
  32. data/config/locales/en.yml +31 -0
  33. data/config/locales/fr.yml +14 -0
  34. data/config/routes.rb +15 -0
  35. data/init.rb +50 -0
  36. data/lang/en.yml +30 -0
  37. data/lang/fr.yml +13 -0
  38. data/lib/invoice_compatibility.rb +16 -0
  39. data/rails/init.rb +1 -0
  40. data/test/functional/invoice_controller_test.rb +25 -0
  41. data/test/integration/access_test.rb +86 -0
  42. data/test/integration/routing_test.rb +31 -0
  43. data/test/test_helper.rb +24 -0
  44. data/test/unit/invoice_test.rb +82 -0
  45. metadata +114 -0
@@ -0,0 +1,28 @@
1
+ page['invoice_amount'].value = @autofill.total
2
+ page['invoice_project_id'].value = @autofill.project.id if @autofill.project.present?
3
+
4
+ # Invoice description
5
+ description = ''
6
+ description << "h3. " + h(@autofill.project.try(:name)) + "\n\n"
7
+ description << h(@autofill.project.try(:description)) + "\n\n"
8
+ description << "h3. Work Completed\n\n"
9
+
10
+ @autofill.issues.each do |issue|
11
+ description << "* #{issue.subject} (#{issue.status} ##{issue.id})\n"
12
+ end if @autofill.issues.present?
13
+
14
+ # Total time
15
+ description << "\n"
16
+ description << "Total billable time: " + pluralize(@autofill.total_time, "hour")
17
+
18
+ description << " (" + h(@autofill.date_from) + " to " + h(@autofill.date_to) + ")"
19
+
20
+ page['invoice_description'].value = description
21
+
22
+ # Customer
23
+ if @autofill.customer.blank?
24
+ page.alert l(:label_no_customer_on_project)
25
+ else
26
+ page['invoice_customer_id'].value = @autofill.customer.id
27
+ page.replace_html 'customer_name', h(@autofill.customer.name)
28
+ end
@@ -0,0 +1,17 @@
1
+ <% content_for :header_tags do %>
2
+ <%= header_tags %>
3
+ <% end %>
4
+
5
+ <%= invoice_menu %>
6
+
7
+ <h1><%= l(:label_edit_invoice) %></h1>
8
+
9
+ <% labelled_tabular_form_for(:invoice, @invoice, :url => { :action => 'update', :id => @project}) do |form| %>
10
+
11
+ <%= form.hidden_field 'id' -%>
12
+
13
+ <%= render(:partial => 'form', :object => form) %>
14
+
15
+ <p><%= form.submit l(:button_update) %></p>
16
+
17
+ <% end %>
@@ -0,0 +1,26 @@
1
+ <% content_for :header_tags do %>
2
+ <%= header_tags %>
3
+ <% end %>
4
+
5
+ <%= invoice_menu %>
6
+
7
+ <% tabs = invoice_list_tabs({ :open => @open_invoices, :late => @late_invoices, :closed => @closed_invoices}) %>
8
+ <% selected_tab = tabs.first[:name] %>
9
+
10
+ <div class="tabs">
11
+ <ul>
12
+ <% tabs.each do |tab| -%>
13
+ <li><%= link_to l(tab[:label]), { :tab => tab[:name] },
14
+ :id => "tab-#{tab[:name]}",
15
+ :class => (tab[:name] != selected_tab ? nil : 'selected'),
16
+ :onclick => "showTab('#{tab[:name]}'); this.blur(); return false;" %></li>
17
+ <% end -%>
18
+ </ul>
19
+ </div>
20
+
21
+ <% tabs.each do |tab| -%>
22
+ <%= content_tag('div', render(:partial => 'table', :locals => { :invoices => tab[:items]}),
23
+ :id => "tab-content-#{tab[:name]}",
24
+ :style => (tab[:name] != selected_tab ? 'display:none' : nil),
25
+ :class => 'tab-content') %>
26
+ <% end -%>
@@ -0,0 +1,15 @@
1
+ <% content_for :header_tags do %>
2
+ <%= header_tags %>
3
+ <% end %>
4
+
5
+ <%= invoice_menu %>
6
+
7
+ <h1><%= l(:label_new_invoice) %></h1>
8
+
9
+ <% labelled_tabular_form_for(:invoice, @invoice, :url => { :action => 'create', :id => @project}) do |form| %>
10
+
11
+ <%= render(:partial => 'form', :object => form) %>
12
+
13
+ <p><%= form.submit l(:button_create) %></p>
14
+
15
+ <% end %>
@@ -0,0 +1,63 @@
1
+ <% content_for :header_tags do %>
2
+ <%= header_tags %>
3
+ <% end %>
4
+
5
+ <%= invoice_menu(@invoice) do |menu|
6
+ menu << link_to(l(:label_printable_view), invoice_path(@invoice, :print => true), :class => 'icon icon-invoice-print')
7
+ menu << link_to(l(:label_edit_invoice), edit_invoice_path(@invoice), :class => 'icon icon-edit')
8
+ end %>
9
+
10
+ <%= invoice_status(@invoice) %>
11
+
12
+ <div id="invoice">
13
+
14
+ <div style="text-align:center">
15
+ <img src="/images/logo.jpg" style="margin-bottom:20px;"/>
16
+ </div>
17
+
18
+ <div class="clear"></div>
19
+
20
+ <div id="company">
21
+ <h3><%= h @settings['invoice_company_name'] %></h3>
22
+ <%= simple_format h(@settings['invoice_company_address']) -%>
23
+ <p><%= link_to h(@settings['invoice_company_website']), @settings['invoice_company_website'] -%></p>
24
+ </div>
25
+
26
+ <div id="customer">
27
+ <h3><%= l(:label_bill_to) %></h3>
28
+ <p><%= h @invoice.customer.company -%></p>
29
+ <p><%= h @invoice.customer.name -%></p>
30
+ <%= simple_format h(@invoice.customer.address)-%>
31
+ <p><%= h @invoice.customer.phone -%></p>
32
+ <p><%= link_to h(@invoice.customer.website), @invoice.customer.website -%></p>
33
+ </div>
34
+
35
+ <div class="clear"></div>
36
+
37
+ <div id="invoice-meta">
38
+ <p id="invoice-number"><%= l(:field_invoice) %> #<%= h @invoice.invoice_number -%></p>
39
+ <p class="date"><%= l(:field_invoiced_on) %>: <%= h @invoice.invoiced_on.strftime('%B %d, %Y') if @invoice.invoiced_on.present? %></p>
40
+ <p class="date"><%= l(:field_due_date) %>: <%= @invoice.due_date.strftime('%B %d, %Y') %></p>
41
+ </div>
42
+
43
+ <div id="invoice-description">
44
+ <h1><%= l(:label_work_description) %></h1>
45
+ <%= @invoice.description_html %>
46
+ </div>
47
+
48
+
49
+ <div id="amount">
50
+ <p><%= l(:label_total_due) %> <%= h number_to_currency(@invoice.amount, :unit => @settings['invoice_currency_symbol']) %></p>
51
+ </div>
52
+
53
+ <div id="foot-note">
54
+ <%= simple_format h(@settings['invoice_foot_note']) %>
55
+ </div>
56
+ </div>
57
+
58
+ <% unless @payments.empty? %>
59
+ <div class="payments nonprinting">
60
+ <h2><%= l(:label_payments) %></h2>
61
+ <%= render :partial => 'payments/payment', :collection => @payments %>
62
+ </div>
63
+ <% end %>
@@ -0,0 +1,12 @@
1
+ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
2
+ <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
3
+ <head>
4
+ <title></title>
5
+ <meta http-equiv="content-type" content="text/html; charset=utf-8" />
6
+ <%= stylesheet_link_tag 'application', :media => 'all' %>
7
+ <%= yield :header_tags %>
8
+ </head>
9
+ <body>
10
+ <%= yield %>
11
+ </body>
12
+ </html>
@@ -0,0 +1,4 @@
1
+ <div class="payment">
2
+ <p><%= h number_to_currency(payment.amount, :unit => @settings['invoice_currency_symbol']) %> paid on <%= h payment.applied_on %></p>
3
+ <p class="note"><%= h payment.note %></p>
4
+ </div>
@@ -0,0 +1,38 @@
1
+ <% content_for :header_tags do %>
2
+ <%= header_tags %>
3
+ <% end %>
4
+ <%= invoice_menu %>
5
+
6
+ <h1>New Payment</h1>
7
+
8
+ <% labelled_tabular_form_for(:payment, @payment, :url => payments_path) do |form| %>
9
+ <fieldset>
10
+ <legend>Payment</legend>
11
+ <%= error_messages_for 'payment' %>
12
+
13
+ <p>
14
+ <%= form.select :invoice_id, Invoice.find(:all).collect { |i| ["##{i.invoice_number}", i.id]}, {},
15
+ { :onchange => "new Ajax.Request('#{url_for :action => 'load_assigned_issues'}', { parameters: { invoice_id: $F(this) } } )" } %>
16
+ <%= observe_field 'payment_invoice_id', :url => { :controller => 'invoice', :action => 'outstanding' },
17
+ :with => "invoice_id",
18
+ :update => 'outstanding' %>
19
+ </p>
20
+
21
+ <p>
22
+ <%= form.text_field :applied_on, :size => 10 %><%= calendar_for('payment_applied_on') %>
23
+ </p>
24
+
25
+ <p>
26
+ <%= form.text_field :amount %> <small>Amount outstanding is $<span id="outstanding"><%= @payment.invoice.outstanding unless @payment.invoice.nil? %></span></small>
27
+ </p>
28
+
29
+ <p>
30
+ <%= form.text_area :note, :cols => 60, :rows => 2 %>
31
+ </p>
32
+
33
+ </fieldset>
34
+
35
+
36
+ <p><%= form.submit "Create" %></p>
37
+
38
+ <% end %>
@@ -0,0 +1,37 @@
1
+ <fieldset>
2
+ <legend>Company settings</legend>
3
+ <p>
4
+ <label>Name</label><%= text_field_tag 'settings[invoice_company_name]', @settings['invoice_company_name'], :size => 50 %>
5
+ </p>
6
+
7
+ <p>
8
+ <label>Address</label><%= text_area_tag 'settings[invoice_company_address]', @settings['invoice_company_address'], :rows => 5, :cols => 50 %>
9
+ </p>
10
+
11
+ <p>
12
+ <label>Email</label><%= text_field_tag 'settings[invoice_company_email]', @settings['invoice_company_email'], :size => 50 %>
13
+ </p>
14
+
15
+ <p>
16
+ <label>Website</label><%= text_field_tag 'settings[invoice_company_website]', @settings['invoice_company_website'], :size => 50 %>
17
+ </p>
18
+ </fieldset>
19
+
20
+ <fieldset>
21
+ <legend>Invoice settings</legend>
22
+ <p>
23
+ <label>Footnote</label><%= text_area_tag 'settings[invoice_foot_note]', @settings['invoice_foot_note'], :rows => 5, :cols => 50 %>
24
+ </p>
25
+
26
+ <p>
27
+ <label>Currency Symbol</label><%= text_field_tag 'settings[invoice_currency_symbol]', @settings['invoice_currency_symbol'], :size => 5 %>
28
+ </p>
29
+
30
+ <p>
31
+ <label>Payment Terms (days)</label><%= text_field_tag 'settings[invoice_payment_terms]', @settings['invoice_payment_terms'], :size => 5 %>
32
+ </p>
33
+
34
+ <p>
35
+ <label>Default Hourly Rate</label><%= text_field_tag 'settings[invoice_default_rate]', @settings['invoice_default_rate'], :size => 5 %>
36
+ </p>
37
+ </fieldset>
Binary file
Binary file
Binary file
Binary file
@@ -0,0 +1,36 @@
1
+ table.invoices { text-align: center; width: 80%; }
2
+ div#invoice { }
3
+ div#invoice-meta { float:right; border: 1px solid #CCCCCC; width: auto; padding: 8px 15px; margin: 30px 0 25px 0; background: #FFFFFF}
4
+ div#invoice-meta p { margin: 0 15px 0 0;}
5
+ div#company { float:right; margin: 0 25px; }
6
+ div#customer { }
7
+ div#invoice-description {margin: 30px 0; padding: 10px; background: #FFFFFF; border: 1px solid #CCCCCC;}
8
+ #invoice-description h1 { margin: 0 0 15px 0; }
9
+ div#amount { float:right; font-size:large; color: #334E6C;}
10
+ div#payment-terms { }
11
+ div#foot-note { margin: 20px 0; padding: 10px; width: 70%;}
12
+ div.clear { clear:both; }
13
+
14
+ #invoice-number { font-weight: bold; font-size: large; color: #334E6C; }
15
+
16
+ .invoice-message { float:right; font-weight: bold; font-size: larger; padding: 0px 15px; border: #000000 dashed 1px;}
17
+ .fully-paid { color: #FFFFFF; background: #00CC00; }
18
+ .pending { color: #000000; background: #EEEE00;}
19
+ .late { color: #FFFFFF; background: #FF0000; }
20
+
21
+
22
+ div#amount p { font-weight: bold; }
23
+ div#customer p, div#company p { margin:0; padding:0; }
24
+ div#customer h3, div#company h3 { font-size:large; }
25
+
26
+ div#invoice h3 { color: #334E6C }
27
+
28
+ div#invoice .date { font-style: italic; }
29
+
30
+ div.payment p.note { padding-left: 30px; font-style:italic; }
31
+
32
+ div#invoice-menu { padding:5px; }
33
+ .icon-invoice-list { background-image: url(../images/money.png); }
34
+ .icon-invoice-new { background-image: url(../images/money_add.png); }
35
+ .icon-payment-new { background-image: url(../images/creditcards.png); }
36
+ .icon-invoice-print { background-image: url(../images/printer.png); }
@@ -0,0 +1,5 @@
1
+ /* Hide all the non printing elements */
2
+ .nonprinting { display:none; }
3
+
4
+ /* Larger font please */
5
+ body { font-size:medium }
@@ -0,0 +1,31 @@
1
+ en:
2
+ field_amount: Invoice Amount
3
+ field_applied_on: Payment applied on
4
+ field_customer: Customer
5
+ field_date_from: From
6
+ field_date_issued: "Date Issued"
7
+ field_date_to: To
8
+ field_due_date: "Due Date:"
9
+ field_invoice: Invoice
10
+ field_invoice_number: Invoice number
11
+ field_invoiced_on: Invoice Date
12
+ field_note: Payment note
13
+ field_rate: Hourly Rate
14
+ label_auto_fill: Auto Fill
15
+ label_bill_to: "Bill To:"
16
+ label_closed_invoices: Closed Invoices
17
+ label_edit_invoice: Edit Invoice
18
+ label_invoice: Invoice
19
+ label_invoice_list: Invoice List
20
+ label_last_number: "last number was"
21
+ label_late_invoices: Late Invoices
22
+ label_new_autofilled_invoice: New Autofilled Invoice
23
+ label_new_invoice: New Invoice
24
+ label_new_payment: New Payment
25
+ label_no_customer_on_project: "That project does not have a customer, please set one before continuing"
26
+ label_open_invoices: Open Invoices
27
+ label_paid_invoice: Invoice Paid
28
+ label_payments: Payments
29
+ label_printable_view: Printable view
30
+ label_total_due: "Total Due:"
31
+ label_work_description: Work Description
@@ -0,0 +1,14 @@
1
+ fr:
2
+ field_invoice_number: Numéro de facture
3
+ field_customer: Client
4
+ field_invoiced_on: Date de facturation
5
+ field_amount: Montant facturé
6
+ field_date_from: De
7
+ field_date_to: Vers
8
+ field_rate: Tarif heure
9
+ field_invoice: Facture
10
+ field_applied_on: Paiement appliqué sur
11
+ field_note: Note du paiement
12
+ label_open_invoices: Factures ouvertes
13
+ label_late_invoices: Factures en retard
14
+ label_closed_invoices: Factures fermées
data/config/routes.rb ADDED
@@ -0,0 +1,15 @@
1
+ ActionController::Routing::Routes.draw do |map|
2
+ map.resources(:invoice,
3
+ :collection => {
4
+ :autocreate => :get,
5
+ :autofill => :get
6
+ },
7
+ :member => {
8
+ :outstanding => :get
9
+ },
10
+ :shallow => true) do |invoice|
11
+ invoice.resources :payments, :only => [:new, :create]
12
+ end
13
+
14
+ map.resources :payments, :only => [:new, :create]
15
+ end
data/init.rb ADDED
@@ -0,0 +1,50 @@
1
+ require 'redmine'
2
+
3
+ require 'dispatcher'
4
+ Dispatcher.to_prepare :chiliproject_invoice do
5
+ # Needed for the compatibility check
6
+ begin
7
+ require_dependency 'time_entry_activity'
8
+ rescue LoadError
9
+ # TimeEntryActivity is not available
10
+ end
11
+ end
12
+
13
+
14
+ Redmine::Plugin.register :chiliproject_invoice do
15
+ name 'Invoice'
16
+ author 'Eric Davis'
17
+ description 'Plugin to create and manage invoices'
18
+ version '0.1.0'
19
+ url 'https://projects.littlestreamsoftware.com/projects/chiliproject_invoice'
20
+ author_url 'http://www.littlestreamsoftware.com'
21
+
22
+ settings:default => {
23
+ 'invoice_company_name' => 'Your Company Name',
24
+ 'invoice_company_address' => '100 Address',
25
+ 'invoice_company_email' => 'email@example.com',
26
+ 'invoice_company_website' => 'http://www.example.com',
27
+ 'invoice_foot_note' => 'Thank you for your business',
28
+ 'invoice_currency_symbol' => '$',
29
+ 'invoice_payment_terms' => '30',
30
+ 'invoice_default_rate' => '50'
31
+ }, :partial => 'settings/invoice_settings'
32
+
33
+ permission :show_invoices, { :invoice => [:index, :show]}
34
+ permission :edit_invoices, { :invoice => [:new, :edit, :autocreate, :create, :update, :autofill]}
35
+ permission :delete_invoices, { :invoice => [:destroy]}
36
+
37
+ permission :pay_invoices, { :payments => [:new, :create], :invoice => [:outstanding]}
38
+
39
+ menu :top_menu, "Invoices", {:controller => 'invoice', :action => 'index'}, :if => Proc.new {
40
+ User.current.allowed_to?(:show_invoices, nil, :global => true)
41
+ }
42
+ end
43
+
44
+ begin
45
+ require_dependency 'customer' unless Object.const_defined?('Customer')
46
+ rescue LoadError
47
+ # customer_plugin is not installed
48
+ raise Exception.new("ERROR: The Customer plugin is not installed. Please install the Customer plugin")
49
+ end
50
+
data/lang/en.yml ADDED
@@ -0,0 +1,30 @@
1
+ field_amount: Invoice Amount
2
+ field_applied_on: Payment applied on
3
+ field_customer: Customer
4
+ field_date_from: From
5
+ field_date_issued: "Date Issued"
6
+ field_date_to: To
7
+ field_due_date: "Due Date:"
8
+ field_invoice: Invoice
9
+ field_invoice_number: Invoice number
10
+ field_invoiced_on: Invoice Date
11
+ field_note: Payment note
12
+ field_rate: Hourly Rate
13
+ label_auto_fill: Auto Fill
14
+ label_bill_to: "Bill To:"
15
+ label_closed_invoices: Closed Invoices
16
+ label_edit_invoice: Edit Invoice
17
+ label_invoice: Invoice
18
+ label_invoice_list: Invoice List
19
+ label_last_number: "last number was"
20
+ label_late_invoices: Late Invoices
21
+ label_new_autofilled_invoice: New Autofilled Invoice
22
+ label_new_invoice: New Invoice
23
+ label_new_payment: New Payment
24
+ label_no_customer_on_project: "That project does not have a customer, please set one before continuing"
25
+ label_open_invoices: Open Invoices
26
+ label_paid_invoice: Invoice Paid
27
+ label_payments: Payments
28
+ label_printable_view: Printable view
29
+ label_total_due: "Total Due:"
30
+ label_work_description: Work Description