eboshi 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 9357ca29543c5d56860ecc82f6adc51d0e35f3a9b75357f03af175209767a4bc
4
+ data.tar.gz: 9d327178f35dd138e215cf9a4bc3446231db82578809d33513b686ffc3b7e1d3
5
+ SHA512:
6
+ metadata.gz: f0b4b4d52a165e511aac881851a55dfb3dee206c49877d8914529932cd4a5c9ff3c605edcfdff7f3579b4f4e70ec75a43eb285b42435e7d90937f28243f20a1f
7
+ data.tar.gz: a21c9d6cf4d5812dcb1ce781912b19813b9df7d72f45cab8347a2633417deaa489b1f7a2e4d1bf24bea39c6248f8aca7eceb01b23670d7fd4b1fdde5c9f823db
data/README.md ADDED
@@ -0,0 +1,28 @@
1
+ # Eboshi
2
+ Short description and motivation.
3
+
4
+ ## Usage
5
+ How to use my plugin.
6
+
7
+ ## Installation
8
+ Add this line to your application's Gemfile:
9
+
10
+ ```ruby
11
+ gem "eboshi"
12
+ ```
13
+
14
+ And then execute:
15
+ ```bash
16
+ $ bundle
17
+ ```
18
+
19
+ Or install it yourself as:
20
+ ```bash
21
+ $ gem install eboshi
22
+ ```
23
+
24
+ ## Contributing
25
+ Contribution directions go here.
26
+
27
+ ## License
28
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,8 @@
1
+ require "bundler/setup"
2
+
3
+ APP_RAKEFILE = File.expand_path("test/dummy/Rakefile", __dir__)
4
+ load "rails/tasks/engine.rake"
5
+
6
+ load "rails/tasks/statistics.rake"
7
+
8
+ require "bundler/gem_tasks"
@@ -0,0 +1 @@
1
+ //= link_directory ../stylesheets/eboshi .css
@@ -0,0 +1,15 @@
1
+ /*
2
+ * This is a manifest file that'll be compiled into application.css, which will include all the files
3
+ * listed below.
4
+ *
5
+ * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
6
+ * or any plugin's vendor/assets/stylesheets directory can be referenced here using a relative path.
7
+ *
8
+ * You're free to add application-wide styles to this file and they'll appear at the bottom of the
9
+ * compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS
10
+ * files in this directory. Styles in this file should be added after the last require_* statement.
11
+ * It is generally better to create a new file per style scope.
12
+ *
13
+ *= require_tree .
14
+ *= require_self
15
+ */
@@ -0,0 +1,77 @@
1
+ module Eboshi
2
+ class AdjustmentsController < ApplicationController
3
+ before_action :get_client
4
+ before_action :authorized?
5
+
6
+ before_action :filter_date, only: [:create, :update]
7
+ before_action :filter_user, only: [:create, :update]
8
+
9
+ def new
10
+ @adjustment = @client.adjustments.build
11
+ end
12
+
13
+ def create
14
+ @adjustment = @client.adjustments.build params[:adjustment]
15
+ if @adjustment.save
16
+ flash[:notice] = "Adjustment successfully created."
17
+ redirect_to invoices_path(@client)
18
+ else
19
+ render :new
20
+ end
21
+ end
22
+
23
+ def edit
24
+ get_adjustment
25
+ end
26
+
27
+ def update
28
+ get_adjustment
29
+ if @adjustment.update params[:adjustment]
30
+ flash[:notice] = "Successfully updated Adjustment."
31
+ respond_to do |wants|
32
+ wants.html { redirect_to invoices_path(@client) }
33
+ wants.js { head :ok }
34
+ end
35
+ else
36
+ render :edit
37
+ end
38
+ end
39
+
40
+ def destroy
41
+ get_adjustment
42
+ @adjustment.destroy
43
+ respond_to do |wants|
44
+ wants.html { redirect_to invoices_path(@client) }
45
+ wants.js { render json: @adjustment.invoice_total }
46
+ end
47
+ end
48
+
49
+ private
50
+
51
+ def filter_date
52
+ a = params[:adjustment]
53
+ if a.delete(:no_date) == "1"
54
+ a[:start] = nil
55
+ a.delete_if { |key, _value| key =~ /^start\(.i\)$/ }
56
+ end
57
+ end
58
+
59
+ def filter_user
60
+ a = params[:adjustment]
61
+ a[:user_id] = current_user.id if a[:user_id].blank?
62
+ a[:user_id] = nil if a.delete(:no_user) == "1"
63
+ end
64
+
65
+ def get_adjustment
66
+ @adjustment ||= Adjustment.find params[:id]
67
+ end
68
+
69
+ def get_client
70
+ @client = params[:client_id] ? Client.find(params[:client_id]) : get_adjustment.client
71
+ end
72
+
73
+ def authorized?
74
+ current_user.authorized? @client
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,62 @@
1
+ module Eboshi
2
+ class ApplicationController < ActionController::Base
3
+ helper_method :current_user_session, :current_user
4
+
5
+ helper :all
6
+ include ShallowRouteHelper
7
+
8
+ before_action :activate_authlogic
9
+ before_action :require_user
10
+
11
+ private
12
+
13
+ def current_user_session
14
+ @current_user_session ||= UserSession.find
15
+ end
16
+
17
+ def current_user
18
+ @current_user ||= current_user_session && current_user_session.user
19
+ end
20
+
21
+ def require_admin
22
+ unless current_user.admin?
23
+ head :forbidden
24
+ return false
25
+ end
26
+ end
27
+
28
+ def require_user
29
+ unless current_user
30
+ store_location
31
+ flash[:notice] = "You must be logged in to access this page"
32
+ redirect_to login_path
33
+ return false
34
+ end
35
+ end
36
+
37
+ def require_no_user
38
+ if current_user
39
+ flash[:notice] = "You must be logged out to access this page"
40
+ redirect_to '/'
41
+ return false
42
+ end
43
+ end
44
+
45
+ def store_location
46
+ session[:return_to] = request.fullpath
47
+ end
48
+
49
+ def redirect_to_back_or_default(default)
50
+ redirect_to(session[:return_to] || default)
51
+ session[:return_to] = nil
52
+ end
53
+
54
+ def redirect_to_last_client_or_back_or_default
55
+ if current_user.last_client
56
+ redirect_to invoices_path(current_user.last_client)
57
+ else
58
+ redirect_to_back_or_default '/'
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,31 @@
1
+ module Eboshi
2
+ class AssignmentsController < ApplicationController
3
+ def new
4
+ @client = current_user.clients.find params[:client_id]
5
+ @assignment = @client.assignments.build user: current_user
6
+ end
7
+
8
+ def create
9
+ @client = current_user.clients.find params[:client_id]
10
+ user = User.find_by_id(params[:assignment][:user_id]) || User.find_by_email(params[:assignment][:email])
11
+ if user
12
+ @client.users << user
13
+ redirect_to invoices_path(@client), notice: "Successfully created!"
14
+ else
15
+ redirect_to({ action: :new }, alert: "A user with that email address does not exist!")
16
+ end
17
+ end
18
+
19
+ def destroy
20
+ @assignment = Assignment.find params[:id]
21
+ raise ActiveRecord::RecordNotFound unless current_user.clients.include? @assignment.client
22
+ @assignment.destroy
23
+ if @assignment.user == current_user
24
+ redirect_to "/"
25
+ else
26
+ redirect_back fallback_location: "/"
27
+ end
28
+ end
29
+ end
30
+ end
31
+
@@ -0,0 +1,43 @@
1
+ module Eboshi
2
+ class BudgetsController < ApplicationController
3
+ before_action :get_client, :authorized?
4
+
5
+ def new
6
+ @budget = @client.budgets.build
7
+ render :form
8
+ end
9
+
10
+ def create
11
+ @budget = @client.budgets.build(params[:budget])
12
+ @budget.save!
13
+ redirect_to [@client, :invoices], notice: "Budget created"
14
+ end
15
+
16
+ def edit
17
+ @budget = @client.budgets.find(params[:id])
18
+ render :form
19
+ end
20
+
21
+ def update
22
+ @budget = @client.budgets.find(params[:id])
23
+ @budget.update!(params[:budget])
24
+ redirect_to [@client, :invoices], notice: "Budget updated"
25
+ end
26
+
27
+ def destroy
28
+ @client.budgets.destroy(params[:id])
29
+ redirect_to [@client, :invoices], notice: "Budget destroy"
30
+ end
31
+
32
+ private
33
+
34
+ def get_client
35
+ @client = Client.find(params[:client_id])
36
+ end
37
+
38
+ def authorized?
39
+ current_user.authorized?(@client)
40
+ end
41
+ end
42
+ end
43
+
@@ -0,0 +1,12 @@
1
+ module Eboshi
2
+ class CalendarController < ApplicationController
3
+ def index
4
+ @date = begin
5
+ Date.parse("#{params[:year]}/#{params[:month]}/1")
6
+ rescue
7
+ Date.today
8
+ end
9
+ end
10
+ end
11
+ end
12
+
@@ -0,0 +1,41 @@
1
+ module Eboshi
2
+ class ClientsController < ApplicationController
3
+ def index
4
+ @clients = current_user.clients
5
+ end
6
+
7
+ def new
8
+ @client = Client.new
9
+ end
10
+
11
+ def edit
12
+ @client = current_user.clients.find(params[:id])
13
+ end
14
+
15
+ def create
16
+ @client = Client.new(params[:client])
17
+ if @client.save
18
+ @client.users << current_user
19
+ redirect_to clients_path, notice: "Client successfully created."
20
+ else
21
+ render :new
22
+ end
23
+ end
24
+
25
+ def update
26
+ @client = current_user.clients.find(params[:id])
27
+ if @client.update(params[:client])
28
+ redirect_to clients_path, notice: "Client successfully updated."
29
+ else
30
+ render :edit
31
+ end
32
+ end
33
+
34
+ def destroy
35
+ @client = current_user.clients.find params[:id]
36
+ @client.destroy
37
+ redirect_to clients_path
38
+ end
39
+ end
40
+ end
41
+
@@ -0,0 +1,69 @@
1
+ module Eboshi
2
+ class InvoicesController < ApplicationController
3
+ before_action :get_client, :authorized?
4
+
5
+ def index
6
+ @invoices = @client.invoices_with_unbilled
7
+ current_user.update_attribute(:last_client, @client)
8
+ respond_to do |wants|
9
+ wants.html
10
+ wants.js { render @client.invoices.paid }
11
+ end
12
+ end
13
+
14
+ def show
15
+ @invoice = @client.invoices.find(params[:id])
16
+ respond_to do |wants|
17
+ wants.html { render partial: params.fetch(:type, "full"), locals: { invoice: @invoice } }
18
+ wants.pdf do
19
+ filename = "#{current_user.business_name_or_name.parameterize}_invoice-\##{@invoice.id}.pdf"
20
+ render pdf: filename, show_as_html: params[:debug].present?
21
+ end
22
+ end
23
+ end
24
+
25
+ def new
26
+ @invoice = @client.build_invoice_from_unbilled(params[:line_item_ids])
27
+ end
28
+
29
+ def create
30
+ @invoice = @client.invoices.build
31
+ @invoice.attributes = params[:invoice]
32
+ if @invoice.save
33
+ redirect_to invoices_path(@client), notice: "Invoice successfully created."
34
+ else
35
+ render :new
36
+ end
37
+ end
38
+
39
+ def edit
40
+ @invoice = @client.invoices.find(params[:id])
41
+ end
42
+
43
+ def update
44
+ @invoice = @client.invoices.find(params[:id])
45
+ if @invoice.update(params[:invoice])
46
+ redirect_to invoices_path(@client), notice: "Invoice successfully updated."
47
+ else
48
+ render :edit
49
+ end
50
+ end
51
+
52
+ def destroy
53
+ @invoice = @client.invoices.find(params[:id])
54
+ @invoice.destroy
55
+ redirect_to invoices_path(@client)
56
+ end
57
+
58
+ private
59
+
60
+ def get_client
61
+ @client = Client.includes(:assignments).find(params[:client_id])
62
+ end
63
+
64
+ def authorized?
65
+ current_user.authorized?(@client)
66
+ end
67
+ end
68
+ end
69
+
@@ -0,0 +1,28 @@
1
+ module Eboshi
2
+ class LineItemsController < ApplicationController
3
+ before_action :get_client
4
+ before_action :authorized?
5
+
6
+ def update
7
+ if line_item.timestamp < params[:line_item][:timestamp].to_i
8
+ line_item.update(params[:line_item])
9
+ end
10
+ render json: {}, status: :ok
11
+ end
12
+
13
+ private
14
+
15
+ def line_item
16
+ @line_item ||= LineItem.find(params[:id])
17
+ end
18
+
19
+ def get_client
20
+ @client ||= (line_item.try(:client) || Client.find(params[:client_id]))
21
+ end
22
+
23
+ def authorized?
24
+ current_user.authorized? @client
25
+ end
26
+ end
27
+ end
28
+
@@ -0,0 +1,31 @@
1
+ module Eboshi
2
+ class PaymentsController < ApplicationController
3
+ before_action :get_invoice
4
+ before_action :authorized?
5
+
6
+ def new
7
+ @payment = @invoice.payments.build(params[:payment] || { total: @invoice.balance })
8
+ end
9
+
10
+ def create
11
+ @payment = @invoice.payments.build params[:payment]
12
+ if @payment.save
13
+ flash[:notice] = "Payment successfully created."
14
+ redirect_to invoices_path(@invoice.client)
15
+ else
16
+ render :new
17
+ end
18
+ end
19
+
20
+ protected
21
+
22
+ def get_invoice
23
+ @invoice = Invoice.find params[:invoice_id]
24
+ end
25
+
26
+ def authorized?
27
+ current_user.authorized? @invoice
28
+ end
29
+ end
30
+ end
31
+
@@ -0,0 +1,25 @@
1
+ module Eboshi
2
+ class UserSessionsController < ApplicationController
3
+ before_action :require_no_user, only: [:new, :create]
4
+ before_action :require_user, only: :destroy
5
+
6
+ def new
7
+ @user_session = UserSession.new
8
+ end
9
+
10
+ def create
11
+ @user_session = UserSession.new(params[:user_session].to_h)
12
+ if @user_session.save
13
+ flash[:notice] = "Login successful!"
14
+ redirect_to_last_client_or_back_or_default
15
+ else
16
+ render "new"
17
+ end
18
+ end
19
+
20
+ def destroy
21
+ current_user_session.destroy
22
+ redirect_to login_path, notice: "Logout successful!"
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,41 @@
1
+ module Eboshi
2
+ class UsersController < ApplicationController
3
+ def index
4
+ require_admin
5
+ @users = User.all
6
+ end
7
+
8
+ def show
9
+ @user = User.find params[:id]
10
+ end
11
+
12
+ def new
13
+ @user = User.new
14
+ end
15
+
16
+ def create
17
+ @user = User.new params[:user]
18
+ if @user.save
19
+ flash[:notice] = "Account registered!"
20
+ redirect_to @user
21
+ else
22
+ render :new
23
+ end
24
+ end
25
+
26
+ def edit
27
+ @user = User.find params[:id]
28
+ end
29
+
30
+ def update
31
+ @user = User.find params[:id]
32
+ if @user.update params[:user]
33
+ flash[:notice] = "Account updated!"
34
+ redirect_to_last_client_or_back_or_default
35
+ else
36
+ render :edit
37
+ end
38
+ end
39
+ end
40
+ end
41
+
@@ -0,0 +1,83 @@
1
+ module Eboshi
2
+ class WorksController < ApplicationController
3
+ before_action :get_client
4
+ before_action :authorized?
5
+
6
+ def new
7
+ @work = @client.works.build user: current_user
8
+ end
9
+
10
+ def create
11
+ @work = @client.works.build params[:work].merge(user: current_user)
12
+ if @work.save
13
+ flash[:notice] = "Successfully created Work."
14
+ redirect_to invoices_path(@client)
15
+ else
16
+ render :new
17
+ end
18
+ end
19
+
20
+ def edit
21
+ get_work
22
+ end
23
+
24
+ def update
25
+ get_work
26
+ if @work.update params[:work]
27
+ flash[:notice] = "Successfully updated Work."
28
+ respond_to do |wants|
29
+ wants.html { redirect_to invoices_path(@client) }
30
+ wants.js { head :ok }
31
+ end
32
+ else
33
+ render :edit
34
+ end
35
+ end
36
+
37
+ def destroy
38
+ get_work
39
+ @work.destroy
40
+ invoice = @work.invoice || @client.build_invoice_from_unbilled
41
+ redirect_to invoices_path(@client), status: :see_other
42
+ end
43
+
44
+ def clock_in
45
+ @work = @client.clock_in current_user
46
+ redirect_to invoices_path(@client), status: :see_other
47
+ end
48
+
49
+ def clock_out
50
+ get_work
51
+ @work.clock_out
52
+ redirect_to invoices_path(@client), status: :see_other
53
+ end
54
+
55
+ def merge
56
+ @work = Work.merge_from_ids params[:line_item_ids]
57
+ @invoice = @work.invoice || @client.build_invoice_from_unbilled
58
+ render @work
59
+ end
60
+
61
+ def convert
62
+ get_work
63
+ @work.to_adjustment!
64
+ flash[:notice] = "Time item converted to adjustment"
65
+ redirect_to invoices_path(@client)
66
+ end
67
+
68
+ private
69
+
70
+ def get_work
71
+ @work ||= Work.find params[:id]
72
+ end
73
+
74
+ def get_client
75
+ @client = params[:client_id] ? Client.find(params[:client_id]) : get_work.client
76
+ end
77
+
78
+ def authorized?
79
+ current_user.authorized? @client
80
+ end
81
+ end
82
+ end
83
+
@@ -0,0 +1,15 @@
1
+ module Eboshi
2
+ module ApplicationHelper
3
+ def number_to_per_hour(value)
4
+ return nil unless value
5
+ precision = (value.round == value ? 0 : 2)
6
+ number_to_currency(value, precision: precision) + "/hr"
7
+ end
8
+
9
+ def currency_or_empty(value)
10
+ return '--' unless value.nonzero?
11
+ number_to_currency value
12
+ end
13
+ end
14
+ end
15
+