eboshi 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 (72) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +28 -0
  3. data/Rakefile +8 -0
  4. data/app/assets/config/eboshi_manifest.js +1 -0
  5. data/app/assets/stylesheets/eboshi/application.css +15 -0
  6. data/app/controllers/eboshi/adjustments_controller.rb +77 -0
  7. data/app/controllers/eboshi/application_controller.rb +62 -0
  8. data/app/controllers/eboshi/assignments_controller.rb +31 -0
  9. data/app/controllers/eboshi/budgets_controller.rb +43 -0
  10. data/app/controllers/eboshi/calendar_controller.rb +12 -0
  11. data/app/controllers/eboshi/clients_controller.rb +41 -0
  12. data/app/controllers/eboshi/invoices_controller.rb +69 -0
  13. data/app/controllers/eboshi/line_items_controller.rb +28 -0
  14. data/app/controllers/eboshi/payments_controller.rb +31 -0
  15. data/app/controllers/eboshi/user_sessions_controller.rb +25 -0
  16. data/app/controllers/eboshi/users_controller.rb +41 -0
  17. data/app/controllers/eboshi/works_controller.rb +83 -0
  18. data/app/helpers/eboshi/application_helper.rb +15 -0
  19. data/app/helpers/eboshi/calendar_helper.rb +80 -0
  20. data/app/helpers/eboshi/shallow_route_helper.rb +68 -0
  21. data/app/jobs/eboshi/application_job.rb +4 -0
  22. data/app/mailers/eboshi/application_mailer.rb +6 -0
  23. data/app/models/eboshi/adjustment.rb +37 -0
  24. data/app/models/eboshi/application_record.rb +5 -0
  25. data/app/models/eboshi/assignment.rb +20 -0
  26. data/app/models/eboshi/budget.rb +33 -0
  27. data/app/models/eboshi/client.rb +85 -0
  28. data/app/models/eboshi/invoice.rb +87 -0
  29. data/app/models/eboshi/line_item.rb +94 -0
  30. data/app/models/eboshi/payment.rb +20 -0
  31. data/app/models/eboshi/user.rb +128 -0
  32. data/app/models/eboshi/user_session.rb +6 -0
  33. data/app/models/eboshi/work.rb +66 -0
  34. data/app/views/eboshi/adjustments/_adjustment.html.slim +15 -0
  35. data/app/views/eboshi/adjustments/_form.html.haml +27 -0
  36. data/app/views/eboshi/adjustments/edit.html.haml +7 -0
  37. data/app/views/eboshi/adjustments/new.html.haml +8 -0
  38. data/app/views/eboshi/assignments/new.html.haml +18 -0
  39. data/app/views/eboshi/budgets/form.html.haml +22 -0
  40. data/app/views/eboshi/calendar/index.html.slim +32 -0
  41. data/app/views/eboshi/clients/_form.html.haml +34 -0
  42. data/app/views/eboshi/clients/edit.html.haml +4 -0
  43. data/app/views/eboshi/clients/index.html.haml +19 -0
  44. data/app/views/eboshi/clients/new.html.haml +4 -0
  45. data/app/views/eboshi/invoices/_form.html.slim +68 -0
  46. data/app/views/eboshi/invoices/_full.html.haml +76 -0
  47. data/app/views/eboshi/invoices/_invoice.html.haml +2 -0
  48. data/app/views/eboshi/invoices/_mini.html.haml +21 -0
  49. data/app/views/eboshi/invoices/edit.html.haml +14 -0
  50. data/app/views/eboshi/invoices/index.html.haml +46 -0
  51. data/app/views/eboshi/invoices/new.html.haml +11 -0
  52. data/app/views/eboshi/invoices/show.pdf.haml +118 -0
  53. data/app/views/eboshi/layouts/application.html.haml +120 -0
  54. data/app/views/eboshi/payments/_form.html.haml +10 -0
  55. data/app/views/eboshi/payments/new.html.haml +9 -0
  56. data/app/views/eboshi/user_sessions/new.html.haml +19 -0
  57. data/app/views/eboshi/users/_form.html.haml +65 -0
  58. data/app/views/eboshi/users/edit.html.haml +16 -0
  59. data/app/views/eboshi/users/index.html.haml +11 -0
  60. data/app/views/eboshi/users/new.html.haml +13 -0
  61. data/app/views/eboshi/users/show.html.haml +39 -0
  62. data/app/views/eboshi/works/_form.html.slim +25 -0
  63. data/app/views/eboshi/works/_work.html.slim +27 -0
  64. data/app/views/eboshi/works/edit.html.haml +9 -0
  65. data/app/views/eboshi/works/new.html.haml +8 -0
  66. data/app/views/layouts/eboshi/application.html.haml +120 -0
  67. data/config/routes.rb +35 -0
  68. data/lib/eboshi/engine.rb +15 -0
  69. data/lib/eboshi/version.rb +3 -0
  70. data/lib/eboshi.rb +6 -0
  71. data/lib/tasks/eboshi_tasks.rake +4 -0
  72. metadata +253 -0
@@ -0,0 +1,80 @@
1
+ require 'date'
2
+
3
+ module Eboshi
4
+ module CalendarHelper
5
+ def calendar(options = {}, &block)
6
+ defaults = {
7
+ year: Date.today.year,
8
+ month: Date.today.month,
9
+ previous_month_text: nil,
10
+ next_month_text: nil
11
+ }
12
+ options.reverse_merge! defaults
13
+
14
+ first = Date.civil(options[:year], options[:month], 1)
15
+ last = first.end_of_month
16
+
17
+ build first, last, options, &block
18
+ end
19
+
20
+ private
21
+
22
+ def build(first, last, _options, &block)
23
+ content_tag :tbody do
24
+ content_tag :tr do
25
+ concat last_month_days(first) # start out the week with last month's days
26
+ concat this_month_days(first, last, &block)
27
+ concat next_month_days(last) # finish out the week with next month's days
28
+ "".html_safe
29
+ end
30
+ end
31
+ end
32
+
33
+ def last_month_days(first)
34
+ first.beginning_of_week(:sunday).upto(first - 1.day).map do |d|
35
+ content_tag :td, class: "otherMonth" do
36
+ concat d.day
37
+ end
38
+ end.join("\n").html_safe
39
+ end
40
+
41
+ def this_month_days(first, last, &block)
42
+ first.upto(last).map do |date|
43
+ out = content_tag :td, id: "day_#{date.day}", class: "#{date.today? && "today"}" do
44
+ date.mday.to_s.html_safe + capture(date, &block).to_s.html_safe
45
+ end
46
+ unless date == last || !date.saturday?
47
+ out << raw("</tr><tr>")
48
+ end
49
+ out.html_safe
50
+ end.join("\n").html_safe
51
+ end
52
+
53
+ def next_month_days(last)
54
+ unless last.saturday?
55
+ (last + 1).upto(last.end_of_week(:sunday)) do |d|
56
+ content_tag :td, class: "otherMonth" do
57
+ concat d.day
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
63
+
64
+ module CalendarHelper
65
+ def link_to_previous_month(date, options = {})
66
+ link_to_month(:prev, date, options)
67
+ end
68
+
69
+ def link_to_next_month(date, options = {})
70
+ link_to_month(:next, date, options)
71
+ end
72
+
73
+ private
74
+
75
+ def link_to_month(direction, date, options = {})
76
+ date += (direction == :next ? 1 : -1).month
77
+ link_to direction, "/calendar/#{date.year}/#{date.month}", options
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,68 @@
1
+ module Eboshi
2
+ module ShallowRouteHelper
3
+ def invoices_path(client, options = {})
4
+ client_invoices_path(client, options)
5
+ end
6
+
7
+ def invoice_path(invoice, options = {})
8
+ client_invoice_path(invoice.client, invoice, options)
9
+ end
10
+
11
+ def new_invoice_path(client, options = {})
12
+ new_client_invoice_path(client, options)
13
+ end
14
+
15
+ def edit_invoice_path(invoice, options = {})
16
+ edit_client_invoice_path(invoice.client, invoice, options)
17
+ end
18
+
19
+ def payments_path(invoice, options = {})
20
+ client_invoice_payments_path(invoice.client, invoice, options)
21
+ end
22
+
23
+ def new_payment_path(invoice, options = {})
24
+ new_client_invoice_payment_path(invoice.client, invoice, options)
25
+ end
26
+
27
+ def new_adjustment_path(adjustment, options = {})
28
+ new_client_adjustment_path(adjustment.client, adjustment, options)
29
+ end
30
+
31
+ def edit_adjustment_path(adjustment, options = {})
32
+ edit_client_adjustment_path(adjustment.client, adjustment, options)
33
+ end
34
+
35
+ def work_path(work, options = {})
36
+ client_work_path(work.client, work, options)
37
+ end
38
+
39
+ def edit_work_path(work, options = {})
40
+ edit_client_work_path(work.client, work, options)
41
+ end
42
+
43
+ def merge_works_path(client, options = {})
44
+ merge_client_works_path(client, options)
45
+ end
46
+
47
+ def convert_work_path(work, options = {})
48
+ convert_client_work_path(work.client, work, options)
49
+ end
50
+
51
+ def line_item_path(work, options = {})
52
+ client_line_item_path(work.client, work, options)
53
+ end
54
+
55
+ def assignments_path(client, options = {})
56
+ client_assignments_path(client, options)
57
+ end
58
+
59
+ def assignment_path(assignment, options = {})
60
+ client_assignment_path(assignment.client, assignment, options)
61
+ end
62
+
63
+ def new_assignment_path(client, options = {})
64
+ new_client_assignment_path(client, options)
65
+ end
66
+ end
67
+ end
68
+
@@ -0,0 +1,4 @@
1
+ module Eboshi
2
+ class ApplicationJob < ActiveJob::Base
3
+ end
4
+ end
@@ -0,0 +1,6 @@
1
+ module Eboshi
2
+ class ApplicationMailer < ActionMailer::Base
3
+ default from: "from@example.com"
4
+ layout "mailer"
5
+ end
6
+ end
@@ -0,0 +1,37 @@
1
+ # == Schema Information
2
+ # Schema version: 20081222015211
3
+ #
4
+ # Table name: line_items
5
+ #
6
+ # id :integer(4) not null, primary key
7
+ # client_id :integer(4)
8
+ # user_id :integer(4)
9
+ # start :datetime
10
+ # finish :datetime
11
+ # rate :decimal(10, 2)
12
+ # notes :text
13
+ # created_at :datetime
14
+ # updated_at :datetime
15
+ # invoice_id :integer(4)
16
+ # type :string(255)
17
+ #
18
+
19
+ module Eboshi
20
+ class Adjustment < LineItem
21
+ def total
22
+ rate
23
+ end
24
+
25
+ def total=(value)
26
+ self.rate = value
27
+ end
28
+
29
+ def no_user
30
+ user.nil? && !new_record?
31
+ end
32
+
33
+ def no_date
34
+ start.nil? && !new_record?
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,5 @@
1
+ module Eboshi
2
+ class ApplicationRecord < ActiveRecord::Base
3
+ self.abstract_class = true
4
+ end
5
+ end
@@ -0,0 +1,20 @@
1
+ # == Schema Information
2
+ # Schema version: 20090412030221
3
+ #
4
+ # Table name: assignments
5
+ #
6
+ # id :integer(4) not null, primary key
7
+ # user_id :integer(4)
8
+ # client_id :integer(4)
9
+ # created_at :datetime
10
+ # updated_at :datetime
11
+ #
12
+
13
+ module Eboshi
14
+ class Assignment < ApplicationRecord
15
+ self.table_name = "assignments"
16
+
17
+ belongs_to :user, required: false
18
+ belongs_to :client, required: false
19
+ end
20
+ end
@@ -0,0 +1,33 @@
1
+ module Eboshi
2
+ class Budget < ApplicationRecord
3
+ self.table_name = "budgets"
4
+
5
+ belongs_to :client, touch: true, required: false
6
+ has_many :invoices
7
+
8
+ validates_presence_of :total
9
+
10
+ attr_accessor :unbilled_invoice
11
+
12
+ def used
13
+ invoices.to_a.sum(&:total) + unbilled_invoice.try(:total).to_f
14
+ end
15
+
16
+ def remaining
17
+ total - used
18
+ end
19
+
20
+ def name_and_total
21
+ [name, total.to_s].join(" - $")
22
+ end
23
+
24
+ def under?
25
+ used < total
26
+ end
27
+
28
+ def over?
29
+ used > total
30
+ end
31
+ end
32
+ end
33
+
@@ -0,0 +1,85 @@
1
+ # == Schema Information
2
+ # Schema version: 20081222015211
3
+ #
4
+ # Table name: clients
5
+ #
6
+ # id :integer(4) not null, primary key
7
+ # name :string(255)
8
+ # address :string(255)
9
+ # city :string(255)
10
+ # state :string(255)
11
+ # zip :string(255)
12
+ # country :string(255)
13
+ # email :string(255)
14
+ # contact :string(255)
15
+ # phone :string(255)
16
+ # created_at :datetime
17
+ # updated_at :datetime
18
+ #
19
+
20
+ module Eboshi
21
+ class Client < ApplicationRecord
22
+ self.table_name = "clients"
23
+
24
+ default_scope -> { order(:name) }
25
+
26
+ has_many :line_items, dependent: :destroy
27
+ has_many :works
28
+ has_many :adjustments
29
+ has_many :invoices, -> { includes(:line_items, :payments).order(date: :desc) }, dependent: :destroy
30
+ has_many :payments, through: :invoices
31
+ has_many :budgets
32
+ has_many :assignments, dependent: :destroy
33
+ has_many :users, through: :assignments
34
+
35
+ validates_presence_of :name
36
+
37
+ def build_invoice_from_unbilled(line_items_ids = nil)
38
+ lis = line_items_ids ? LineItem.find(line_items_ids) : line_items.unbilled
39
+ budget = budgets.order(:created_at).last
40
+ invoice = invoices.build line_items: lis, budget: budget
41
+ budget.unbilled_invoice = invoice if budget
42
+ invoice
43
+ end
44
+
45
+ def invoices_with_unbilled
46
+ new_invoices = invoices.to_a
47
+ new_invoices.unshift build_invoice_from_unbilled
48
+ new_invoices
49
+ end
50
+
51
+ def balance
52
+ credits - debits
53
+ end
54
+
55
+ def unbilled_balance
56
+ return 0.0 if line_items.empty?
57
+ line_items.to_a.select(&:unbilled?).sum(&:total)
58
+ end
59
+
60
+ def overdue_balance
61
+ balance - unbilled_balance
62
+ end
63
+
64
+ def credits
65
+ line_items.to_a.sum(&:total)
66
+ end
67
+
68
+ def debits
69
+ payments.to_a.sum(&:total)
70
+ end
71
+
72
+ def clock_in(user)
73
+ Work.new.tap do |line_item|
74
+ now = Time.now
75
+ line_item.attributes = { start: now, finish: now, user: user, rate: user.default_rate_for(self) }
76
+ line_items << line_item
77
+ end
78
+ end
79
+
80
+ def default_project_name
81
+ invoices.last.try(:project_name)
82
+ end
83
+ end
84
+ end
85
+
@@ -0,0 +1,87 @@
1
+ # == Schema Information
2
+ # Schema version: 20081222015211
3
+ #
4
+ # Table name: invoices
5
+ #
6
+ # id :integer(4) not null, primary key
7
+ # client_id :integer(4)
8
+ # date :datetime
9
+ # project_name :string(255)
10
+ #
11
+
12
+ module Eboshi
13
+ class Invoice < ApplicationRecord
14
+ self.table_name = "invoices"
15
+
16
+ belongs_to :client, touch: true, required: false
17
+ has_many :line_items, dependent: :nullify
18
+ has_many :payments, -> { order(created_at: :desc) }, dependent: :destroy
19
+ belongs_to :budget, touch: true, required: false
20
+
21
+ validates_presence_of :client, :date
22
+
23
+ default_value_for(:date) { Time.zone.today.midnight }
24
+
25
+ def self.unpaid
26
+ order(date: :desc).reject(&:paid?)
27
+ end
28
+
29
+ def self.paid
30
+ order(date: :desc).select(&:paid?)
31
+ end
32
+
33
+ def works
34
+ line_items.where(type: "Eboshi::Work")
35
+ end
36
+
37
+ def adjustments
38
+ line_items.where(type: "Eboshi::Adjustment")
39
+ end
40
+
41
+ def total
42
+ line_items.to_a.sum(&:total)
43
+ end
44
+
45
+ def total_for_user(user)
46
+ line_items.to_a.select { |li| li.user == user }.sum(&:total)
47
+ end
48
+
49
+ def total=(value)
50
+ difference = value.to_f - total
51
+ return total if difference.abs < 0.01
52
+ line_items << Adjustment.new(total: difference, client_id: client_id)
53
+ end
54
+
55
+ def balance
56
+ total - payments.to_a.sum(&:total)
57
+ end
58
+
59
+ def status
60
+ return 'unbilled' if new_record?
61
+ paid? ? 'paid' : 'unpaid'
62
+ end
63
+
64
+ def paid?
65
+ !line_items.empty? && !payments.empty? && balance == 0
66
+ end
67
+
68
+ def total_hours
69
+ line_items.to_a.sum(&:hours)
70
+ end
71
+
72
+ def total_hours_for_user(user)
73
+ line_items.to_a.select { |li| li.user == user }.sum(&:hours)
74
+ end
75
+
76
+ def consistant_rate
77
+ return true if works.empty?
78
+ rates = works.collect(&:rate).uniq
79
+ rates.length == 1 ? rates.first : false
80
+ end
81
+
82
+ def users
83
+ line_items.collect(&:user).uniq.sort_by { |user| user ? user.name : "" }
84
+ end
85
+ end
86
+ end
87
+
@@ -0,0 +1,94 @@
1
+ # == Schema Information
2
+ # Schema version: 20081222015211
3
+ #
4
+ # Table name: line_items
5
+ #
6
+ # id :integer(4) not null, primary key
7
+ # client_id :integer(4)
8
+ # user_id :integer(4)
9
+ # start :datetime
10
+ # finish :datetime
11
+ # rate :decimal(10, 2)
12
+ # notes :text
13
+ # created_at :datetime
14
+ # updated_at :datetime
15
+ # invoice_id :integer(4)
16
+ # type :string(255)
17
+ #
18
+
19
+ module Eboshi
20
+ class LineItem < ApplicationRecord
21
+ self.table_name = "line_items"
22
+
23
+ include Comparable
24
+
25
+ belongs_to :client, touch: true, required: false
26
+ belongs_to :user, required: false
27
+ belongs_to :invoice, touch: true, required: false
28
+
29
+ validates_presence_of :client, :rate
30
+
31
+ def self.unbilled
32
+ where(invoice_id: nil).order(start: :desc)
33
+ end
34
+
35
+ def self.on_date(date)
36
+ start = date.beginning_of_day
37
+ finish = date.end_of_day
38
+ where(start: start..finish)
39
+ end
40
+
41
+ def self.on_week(date)
42
+ start = date.beginning_of_week(:sunday).beginning_of_day
43
+ finish = date.end_of_week(:sunday).end_of_day
44
+ where(start: start..finish)
45
+ end
46
+
47
+ def self.on_month(date)
48
+ start = date.beginning_of_month.beginning_of_day
49
+ finish = date.end_of_month.end_of_day
50
+ where(start: start..finish)
51
+ end
52
+
53
+ def self.on_year(date)
54
+ start = date.beginning_of_year.beginning_of_day
55
+ finish = date.end_of_year.end_of_day
56
+ where(start: start..finish)
57
+ end
58
+
59
+ def total
60
+ 0
61
+ end
62
+
63
+ def hours
64
+ return 0 unless finish && start
65
+ seconds = BigDecimal((finish - start).to_s)
66
+ seconds / 60 / 60
67
+ end
68
+
69
+ def ==(target)
70
+ target == id
71
+ end
72
+
73
+ def unbilled?
74
+ invoice_id.nil?
75
+ end
76
+
77
+ def checked?
78
+ unbilled?
79
+ end
80
+
81
+ def user_name=(name)
82
+ self.user = User.find_by_name!(name) unless name.nil?
83
+ end
84
+
85
+ def invoice_total
86
+ invoice.try(:total) || client.unbilled_balance
87
+ end
88
+
89
+ def <=>(target)
90
+ (target.start || Time.zone.parse("0000-01-01")) <=> (start || Time.zone.parse("0000-01-01"))
91
+ end
92
+ end
93
+ end
94
+
@@ -0,0 +1,20 @@
1
+ # == Schema Information
2
+ # Schema version: 20081222015211
3
+ #
4
+ # Table name: payments
5
+ #
6
+ # id :integer(4) not null, primary key
7
+ # invoice_id :integer(4)
8
+ # total :decimal(10, 2)
9
+ # created_at :datetime
10
+ # updated_at :datetime
11
+ #
12
+
13
+ module Eboshi
14
+ class Payment < ApplicationRecord
15
+ self.table_name = "payments"
16
+
17
+ belongs_to :invoice, touch: true, required: false
18
+ end
19
+ end
20
+
@@ -0,0 +1,128 @@
1
+ # == Schema Information
2
+ # Schema version: 20090412030221
3
+ #
4
+ # Table name: users
5
+ #
6
+ # id :integer(4) not null, primary key
7
+ # name :string(255)
8
+ # email :string(255)
9
+ # crypted_password :string(255)
10
+ # password_salt :string(255)
11
+ # created_at :datetime
12
+ # updated_at :datetime
13
+ # rate :decimal(10, 2)
14
+ # color :string(255)
15
+ # persistence_token :string(255)
16
+ # login_count :integer(4)
17
+ # last_request_at :datetime
18
+ # last_login_at :datetime
19
+ # current_login_at :datetime
20
+ # last_login_ip :string(255)
21
+ # current_login_ip :string(255)
22
+ # last_client_id :integer(4)
23
+ # admin :boolean(1)
24
+ # logo_file_name :string(255)
25
+ # logo_content_type :string(255)
26
+ # logo_file_size :integer(4)
27
+ # logo_updated_at :datetime
28
+ # signature_file_name :string(255)
29
+ # signature_content_type :string(255)
30
+ # signature_file_size :integer(4)
31
+ # signature_updated_at :datetime
32
+ #
33
+
34
+ module Eboshi
35
+ class User < ApplicationRecord
36
+ self.table_name = "eboshi_users"
37
+
38
+ acts_as_authentic do |config|
39
+ config.crypto_provider = Authlogic::CryptoProviders::SCrypt
40
+ config.transition_from_crypto_providers = [Authlogic::CryptoProviders::Sha512]
41
+ end
42
+
43
+ validates_uniqueness_of :email, case_sensitive: false
44
+ validates_email_format_of :email
45
+ validates_presence_of :password, :on => :create
46
+ validates_confirmation_of :password, if: proc { |user| user.password.present? }
47
+ validates_length_of :password, minimum: 4, allow_blank: true, message: "must be at least five characters long"
48
+ validates_length_of :password_confirmation, minimum: 4, allow_blank: true, message: "must be at least five characters long"
49
+
50
+ belongs_to :last_client, class_name: "Client", required: false
51
+ has_many :assignments, dependent: :destroy
52
+ has_many :clients, through: :assignments
53
+ has_many :works
54
+
55
+ has_attached_file :logo,
56
+ styles: { pdf: "200x200>" },
57
+ path: ":rails_root/public/:attachment/:id/:style.:extension",
58
+ url: "/:attachment/:id/:style.:extension"
59
+ has_attached_file :signature,
60
+ styles: { pdf: "450x100>" },
61
+ path: ":rails_root/public/:attachment/:id/:style.:extension",
62
+ url: "/:attachment/:id/:style.:extension"
63
+ validates_attachment_content_type :logo, content_type: ["image/jpg", "image/jpeg", "image/png", "image/gif"]
64
+ validates_attachment_content_type :signature, content_type: ["image/jpg", "image/jpeg", "image/png", "image/gif"]
65
+
66
+ def default_rate_for(client)
67
+ last_work = client.works.complete.where(user: self).order(start: :desc).first
68
+ last_work.try(:rate) || rate
69
+ end
70
+
71
+ def related_users
72
+ clients.collect(&:users).flatten.uniq - [self]
73
+ end
74
+
75
+ def authorized?(object)
76
+ return if object.is_a?(Client) && clients.include?(object)
77
+ return if object.is_a?(Invoice) && clients.include?(object.client)
78
+ return if object.is_a?(Assignment) && object.client.users.include?(self)
79
+ raise ActiveRecord::RecordNotFound
80
+ end
81
+
82
+ def city_state_zip
83
+ return nil if city.blank? || state.blank? || zip.blank?
84
+ "#{city}, #{state} #{zip}"
85
+ end
86
+
87
+ def business_name_or_name
88
+ business_name.blank? ? name : business_name
89
+ end
90
+
91
+ def business_email_or_email
92
+ business_email.blank? ? email : business_email
93
+ end
94
+
95
+ def hours_by_date(date)
96
+ works.on_date(date).to_a.sum(&:hours)
97
+ end
98
+
99
+ def total_by_date(date)
100
+ works.on_date(date).to_a.sum(&:total)
101
+ end
102
+
103
+ def hours_by_week(date)
104
+ works.on_week(date).to_a.sum(&:hours)
105
+ end
106
+
107
+ def total_by_week(date)
108
+ works.on_week(date).to_a.sum(&:total)
109
+ end
110
+
111
+ def hours_by_month(date)
112
+ works.on_month(date).to_a.sum(&:hours)
113
+ end
114
+
115
+ def total_by_month(date)
116
+ works.on_month(date).to_a.sum(&:total)
117
+ end
118
+
119
+ def hours_by_year(date)
120
+ works.on_year(date).to_a.sum(&:hours)
121
+ end
122
+
123
+ def total_by_year(date)
124
+ works.on_year(date).to_a.sum(&:total)
125
+ end
126
+ end
127
+ end
128
+
@@ -0,0 +1,6 @@
1
+ module Eboshi
2
+ class UserSession < Authlogic::Session::Base
3
+ authenticate_with Eboshi::User
4
+ end
5
+ end
6
+