jobshop 0.0.167 → 0.0.179

Sign up to get free protection for your applications and to get access to all the features.
Files changed (120) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -2
  3. data/Rakefile +2 -1
  4. data/app/assets/javascripts/application.js +1 -0
  5. data/app/controllers/jobshop/application_controller.rb +3 -1
  6. data/app/controllers/jobshop/dashboards_controller.rb +11 -0
  7. data/app/controllers/jobshop/departments_controller.rb +52 -0
  8. data/app/controllers/jobshop/employees_controller.rb +77 -0
  9. data/app/controllers/jobshop/organizations_controller.rb +29 -0
  10. data/app/controllers/jobshop/rfqs_controller.rb +49 -0
  11. data/app/controllers/jobshop/roles_controller.rb +52 -0
  12. data/app/helpers/jobshop/application_helper.rb +10 -0
  13. data/app/listeners/jobshop/assignment_listener.rb +13 -0
  14. data/app/mailers/jobshop/rfq_responder_mailer.rb +1 -1
  15. data/app/models/jobshop/auditing.rb +20 -0
  16. data/app/models/jobshop/collection.rb +2 -7
  17. data/app/models/jobshop/customer.rb +6 -17
  18. data/app/models/jobshop/customer/category.rb +2 -8
  19. data/app/models/jobshop/customer/contact.rb +3 -8
  20. data/app/models/jobshop/department.rb +10 -0
  21. data/app/models/jobshop/department/assignment.rb +15 -0
  22. data/app/models/jobshop/employee.rb +13 -6
  23. data/app/models/jobshop/employment.rb +8 -8
  24. data/app/models/jobshop/inspection.rb +1 -1
  25. data/app/models/jobshop/inspection/boolean_criterion.rb +13 -17
  26. data/app/models/jobshop/inspection/criterion.rb +9 -10
  27. data/app/models/jobshop/inspection/deviation_criterion.rb +10 -12
  28. data/app/models/jobshop/inspection/limit_criterion.rb +12 -10
  29. data/app/models/jobshop/inspection/report.rb +22 -34
  30. data/app/models/jobshop/mailman.rb +2 -4
  31. data/app/models/jobshop/order.rb +3 -27
  32. data/app/models/jobshop/order_line.rb +4 -10
  33. data/app/models/jobshop/organization.rb +8 -15
  34. data/app/models/jobshop/organization_state.rb +10 -0
  35. data/app/models/jobshop/place.rb +3 -8
  36. data/app/models/jobshop/product.rb +4 -7
  37. data/app/models/jobshop/rfq.rb +32 -14
  38. data/app/models/jobshop/rfq/line.rb +28 -0
  39. data/app/models/jobshop/rfq/line/assignment.rb +25 -0
  40. data/app/models/jobshop/rfq/line/quantity.rb +23 -0
  41. data/app/models/jobshop/role.rb +3 -9
  42. data/app/models/jobshop/role_assignment.rb +10 -4
  43. data/app/models/jobshop/routing_process.rb +4 -10
  44. data/app/models/jobshop/routing_step.rb +3 -5
  45. data/app/models/jobshop/thing.rb +3 -8
  46. data/app/models/jobshop/user.rb +24 -11
  47. data/app/views/jobshop/dashboards/show.html.haml +35 -0
  48. data/app/views/jobshop/departments/_form.html.haml +12 -0
  49. data/app/views/jobshop/departments/edit.html.haml +7 -0
  50. data/app/views/jobshop/departments/index.html.haml +17 -0
  51. data/app/views/jobshop/departments/new.html.haml +5 -0
  52. data/app/views/jobshop/departments/show.html.haml +15 -0
  53. data/app/views/jobshop/employees/_form.html.haml +46 -0
  54. data/app/views/jobshop/employees/edit.html.haml +1 -0
  55. data/app/views/jobshop/employees/index.html.haml +12 -0
  56. data/app/views/jobshop/employees/new.html.haml +1 -0
  57. data/app/views/jobshop/employees/show.html.haml +16 -0
  58. data/app/views/jobshop/organizations/_form.html.haml +10 -0
  59. data/app/views/jobshop/organizations/edit.html.haml +7 -0
  60. data/app/views/jobshop/organizations/index.html.haml +19 -0
  61. data/app/views/jobshop/organizations/new.html.haml +5 -0
  62. data/app/views/jobshop/organizations/show.html.haml +16 -0
  63. data/app/views/jobshop/rfqs/_form.html.haml +60 -0
  64. data/app/views/jobshop/rfqs/edit.html.haml +4 -0
  65. data/app/views/jobshop/rfqs/index.html.haml +2 -0
  66. data/app/views/jobshop/roles/_form.html.haml +11 -0
  67. data/app/views/jobshop/roles/edit.html.haml +7 -0
  68. data/app/views/jobshop/roles/index.html.haml +21 -0
  69. data/app/views/jobshop/roles/new.html.haml +5 -0
  70. data/app/views/jobshop/roles/show.html.haml +3 -0
  71. data/app/views/layouts/jobshop/application.html.haml +50 -2
  72. data/config/initializers/devise.rb +2 -2
  73. data/config/routes.rb +18 -0
  74. data/db/migrate/20170311194758_initialize_jobshop.rb +4 -39
  75. data/db/migrate/20171216021339_create_organizations.rb +3 -8
  76. data/db/migrate/20171216021350_create_users.rb +28 -0
  77. data/db/migrate/20171216021400_create_employees.rb +27 -9
  78. data/db/migrate/20171216021853_create_customers.rb +29 -14
  79. data/db/migrate/20171216022020_create_places.rb +3 -4
  80. data/db/migrate/20171216022135_create_products.rb +4 -4
  81. data/db/migrate/20171216022605_create_orders.rb +27 -13
  82. data/db/migrate/20171216023018_create_roles.rb +30 -27
  83. data/db/migrate/20171216035357_create_things.rb +26 -23
  84. data/db/migrate/20171219022118_create_routing_processes.rb +28 -33
  85. data/db/migrate/20180107203241_create_inspections.rb +48 -115
  86. data/db/migrate/20181117023949_create_rfqs.rb +74 -15
  87. data/db/migrate/20181118014603_create_mailmen.rb +5 -6
  88. data/db/migrate/20190309163306_create_departments.rb +38 -0
  89. data/db/migrate/support/temporal_tables.sql +210 -0
  90. data/db/seeds.rb +92 -64
  91. data/exe/jobshop +1 -0
  92. data/lib/generators/jobshop/app/app_generator.rb +13 -3
  93. data/lib/generators/jobshop/app/templates/Procfile.tt +1 -1
  94. data/lib/generators/jobshop/app/templates/config/databases/postgresql.yml.tt +24 -0
  95. data/lib/generators/jobshop/app/templates/config/puma.rb.tt +44 -0
  96. data/lib/generators/jobshop/canary/canary_generator.rb +21 -9
  97. data/lib/generators/jobshop/dev_cert/dev_cert_generator.rb +124 -0
  98. data/lib/jobshop.rb +1 -1
  99. data/lib/jobshop/cli.rb +3 -3
  100. data/lib/jobshop/cli/app_generator.rb +15 -10
  101. data/lib/jobshop/cli/application.rb +7 -7
  102. data/lib/jobshop/cli/base.rb +65 -0
  103. data/lib/jobshop/cli/canary.rb +120 -22
  104. data/lib/jobshop/configuration.rb +1 -1
  105. data/lib/jobshop/engine.rb +8 -4
  106. data/lib/jobshop/helpers/migration.rb +27 -66
  107. data/lib/jobshop/mailroom/base_handler.rb +1 -1
  108. data/lib/jobshop/mailroom/null_handler.rb +1 -1
  109. data/lib/jobshop/mailroom/postmaster.rb +1 -1
  110. data/lib/jobshop/mailroom/rfq_handler.rb +1 -1
  111. data/lib/jobshop/version.rb +2 -2
  112. data/lib/tasks/jobshop_tasks.rake +15 -1
  113. metadata +114 -61
  114. data/app/controllers/jobshop/organizations/lookups_controller.rb +0 -18
  115. data/app/models/jobshop/employment_version.rb +0 -10
  116. data/app/models/jobshop/inspection/result.rb +0 -46
  117. data/app/models/jobshop/inspection/tuple.rb +0 -17
  118. data/app/models/jobshop/rfq_line.rb +0 -12
  119. data/db/migrate/20171216021554_create_people.rb +0 -43
  120. data/lib/generators/jobshop/app/templates/config/database.yml.tt +0 -19
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Jobshop
4
+ class Organization::State
5
+ include Statesman::Machine
6
+
7
+ state :created, initial: true
8
+ state :deleted
9
+ end
10
+ end
@@ -1,15 +1,10 @@
1
- # frozen_string_literals: true
1
+ # frozen_string_literal: true
2
2
 
3
3
  module Jobshop
4
4
  class Place < ApplicationRecord
5
- self.primary_keys = %i[ organization_id place_id ]
5
+ belongs_to :organization, -> { readonly }, inverse_of: :places
6
6
 
7
- after_initialize { self.place_id ||= SecureRandom.hex if new_record? }
8
-
9
- belongs_to :organization, inverse_of: :places
10
-
11
- validates :name,
12
- presence: true,
7
+ validates :name, presence: true,
13
8
  uniqueness: { scope: :organization_id, case_sensitive: false }
14
9
  end
15
10
  end
@@ -1,15 +1,12 @@
1
- # frozen_string_literals: true
1
+ # frozen_string_literal: true
2
2
 
3
3
  module Jobshop
4
4
  class Product < ApplicationRecord
5
- self.primary_keys = %i[ organization_id product_id ]
5
+ belongs_to :organization, -> { readonly }, inverse_of: :products
6
6
 
7
- after_initialize { self.product_id ||= SecureRandom.uuid if new_record? }
7
+ has_many :orders, inverse_of: :product
8
8
 
9
- belongs_to :organization, inverse_of: :products
10
-
11
- has_one :routing_process, inverse_of: :product,
12
- foreign_key: %i[ organization_id product_id ]
9
+ has_one :routing_process, inverse_of: :product
13
10
 
14
11
  has_many :routing_steps, through: :routing_process
15
12
 
@@ -1,28 +1,46 @@
1
- # frozen_string_literals: true
1
+ # frozen_string_literal: true
2
2
 
3
3
  module Jobshop
4
4
  class RFQ < ApplicationRecord
5
- self.primary_keys = %i[ organization_id rfq_id ]
5
+ belongs_to :organization, -> { readonly }, inverse_of: :rfqs
6
6
 
7
- after_initialize { self.rfq_id ||= SecureRandom.uuid if new_record? }
8
- after_create { reload }
9
-
10
- belongs_to :organization, inverse_of: :rfqs
11
-
12
- belongs_to :customer, inverse_of: :rfqs, optional: true,
13
- foreign_key: %i[ organization_id customer_id ]
7
+ belongs_to :customer, -> { readonly }, inverse_of: :rfqs, optional: true
14
8
 
15
9
  belongs_to :requested_by, class_name: "Jobshop::Customer::Contact",
16
- primary_key: %i[ organization_id customer_id contact_id ],
17
- foreign_key: %i[ organization_id customer_id requested_by_id ],
18
10
  optional: true
19
11
 
20
- has_many :rfq_lines, inverse_of: :rfq,
21
- foreign_key: %i[ organization_id rfq_id ]
12
+ has_many :lines, inverse_of: :rfq
13
+
14
+ scope :undelegated, -> {
15
+ left_outer_joins(lines: :assignments).merge(Jobshop::RFQ::Line.unassigned).distinct
16
+ }
17
+
18
+ scope :verified, -> { where.not(customer_id: nil, requested_by_id: :nil) }
19
+
20
+ scope :verifiable, -> { where.not(customer_id: nil)
21
+ .where(requested_by_id: :nil) }
22
+
23
+ scope :unverifiable, -> { where(customer_id: nil, requested_by_id: nil) }
24
+
25
+ accepts_nested_attributes_for :lines, reject_if: proc { |attributes|
26
+ attributes["description"].blank? && attributes["identifier"].blank?
27
+ }, allow_destroy: true
22
28
 
23
29
  def customer=(value)
24
- customer_id = (Jobshop::Customer === value) ? value.customer_id : value
30
+ customer_id = (Jobshop::Customer === value) ? value.id : value
25
31
  write_attribute(:customer_id, customer_id)
26
32
  end
33
+
34
+ def verified?
35
+ customer.present? && requested_by.present?
36
+ end
37
+
38
+ def verifiable?
39
+ customer.present? && requested_by.nil?
40
+ end
41
+
42
+ def unverifiable?
43
+ customer.nil? && requested_by.nil?
44
+ end
27
45
  end
28
46
  end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Jobshop
4
+ class RFQ::Line < ApplicationRecord
5
+ belongs_to :organization, -> { readonly }
6
+
7
+ belongs_to :rfq, -> { readonly }, inverse_of: :lines
8
+
9
+ has_many :assignments, dependent: :destroy, inverse_of: :line
10
+
11
+ has_many :assignees, through: :assignments, source: :assigned_to
12
+
13
+ has_many :quantities, dependent: :destroy, inverse_of: :line
14
+
15
+ scope :unassigned, -> {
16
+ left_outer_joins(:assignments)
17
+ .where(jobshop_rfq_line_assignments: { line_id: nil })
18
+ }
19
+
20
+ accepts_nested_attributes_for :quantities, reject_if: proc { |attributes|
21
+ attributes["quantity"].blank?
22
+ }, allow_destroy: true
23
+
24
+ before_validation(on: :create) do
25
+ self.organization ||= rfq.organization
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Jobshop
4
+ class RFQ::Line::Assignment < ApplicationRecord
5
+ include Wisper::Publisher
6
+
7
+ belongs_to :organization, -> { readonly }
8
+
9
+ belongs_to :rfq, -> { readonly }
10
+
11
+ belongs_to :line, -> { readonly }, inverse_of: :assignments
12
+
13
+ belongs_to :assigned_to, class_name: "Jobshop::Employee",
14
+ inverse_of: :rfq_line_assignments
15
+
16
+ belongs_to :assigned_by, class_name: "Jobshop::Employee"
17
+
18
+ before_validation(on: :create) do
19
+ self.organization ||= line.organization
20
+ self.rfq ||= line.rfq
21
+
22
+ broadcast(:set_assigned_by, self)
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Jobshop
4
+ class RFQ::Line::Quantity < ApplicationRecord
5
+ belongs_to :organization, -> { readonly }
6
+
7
+ belongs_to :rfq, -> { readonly }
8
+
9
+ belongs_to :line, -> { readonly }, inverse_of: :quantities
10
+
11
+ validates :quantity, presence: true
12
+ validates :quantity, numericality: { greater_than: 0 }, on: :create
13
+
14
+ before_validation(on: :create) do
15
+ self.organization ||= line.organization
16
+ self.rfq ||= line.rfq
17
+ end
18
+
19
+ after_update do
20
+ destroy if quantity == 0
21
+ end
22
+ end
23
+ end
@@ -1,16 +1,10 @@
1
- # frozen_string_literals: true
1
+ # frozen_string_literal: true
2
2
 
3
3
  module Jobshop
4
4
  class Role < ApplicationRecord
5
- self.primary_keys = %i[ organization_id role_id ]
5
+ belongs_to :organization, inverse_of: :roles
6
6
 
7
- after_initialize { self.role_id ||= SecureRandom.uuid if new_record? }
8
-
9
- belongs_to :organization, inverse_of: :roles,
10
- primary_key: %i[ organization_id role_id ]
11
-
12
- has_many :role_assignments, inverse_of: :role,
13
- foreign_key: %i[ organization_id role_id ]
7
+ has_many :role_assignments, inverse_of: :role
14
8
 
15
9
  validates :name, presence: true
16
10
  end
@@ -1,9 +1,15 @@
1
- # frozen_string_literals: true
1
+ # frozen_string_literal: true
2
2
 
3
3
  module Jobshop
4
4
  class RoleAssignment < ApplicationRecord
5
- belongs_to :organization
6
- belongs_to :user, foreign_key: %i[ organization_id user_id ]
7
- belongs_to :role, foreign_key: %i[ organization_id role_id ]
5
+ belongs_to :organization, -> { readonly }
6
+
7
+ belongs_to :user
8
+
9
+ belongs_to :role
10
+
11
+ before_validation(on: :create) do
12
+ self.organization ||= user.organization || role.organization
13
+ end
8
14
  end
9
15
  end
@@ -1,17 +1,11 @@
1
- # frozen_string_literals: true
1
+ # frozen_string_literal: true
2
2
 
3
3
  module Jobshop
4
4
  class RoutingProcess < ApplicationRecord
5
- self.primary_keys = %i[ organization_id routing_process_id ]
5
+ belongs_to :organization, -> { readonly }, inverse_of: :routing_processes
6
6
 
7
- after_initialize { self.routing_process_id ||= SecureRandom.uuid if new_record? }
7
+ belongs_to :product, inverse_of: :routing_process
8
8
 
9
- belongs_to :organization, inverse_of: :routing_processes
10
-
11
- belongs_to :product, inverse_of: :routing_process,
12
- foreign_key: %i[ organization_id product_id ]
13
-
14
- has_many :routing_steps, inverse_of: :routing_process,
15
- foreign_key: %i[ organization_id routing_process_id ]
9
+ has_many :routing_steps, inverse_of: :routing_process
16
10
  end
17
11
  end
@@ -1,12 +1,10 @@
1
- # frozen_string_literals: true
1
+ # frozen_string_literal: true
2
2
 
3
3
  module Jobshop
4
4
  class RoutingStep < ApplicationRecord
5
- self.primary_keys = %i[ organization_id routing_step_id ]
5
+ belongs_to :organization, -> { readonly }
6
6
 
7
- belongs_to :organization
8
- belongs_to :routing_process, inverse_of: :routing_steps,
9
- foreign_key: %i[ organization_id routing_process_id ]
7
+ belongs_to :routing_process, inverse_of: :routing_steps
10
8
 
11
9
  validates :name, presence: true
12
10
  end
@@ -1,15 +1,10 @@
1
- # frozen_string_literals: true
1
+ # frozen_string_literal: true
2
2
 
3
3
  module Jobshop
4
4
  class Thing < ApplicationRecord
5
- self.primary_keys = %i[ organization_id thing_id ]
5
+ belongs_to :organization, -> { readonly }, inverse_of: :things
6
6
 
7
- after_initialize { self.thing_id ||= SecureRandom.uuid if new_record? }
8
-
9
- belongs_to :organization, inverse_of: :things
10
-
11
- belongs_to :collection, inverse_of: :things,
12
- foreign_key: %i[ organization_id collection_id ]
7
+ belongs_to :collection, -> { readonly }, inverse_of: :things
13
8
 
14
9
  validates :name, presence: true,
15
10
  uniqueness: { scope: :organization_id, case_sensitive: false }
@@ -1,22 +1,23 @@
1
- # frozen_string_literals: true
1
+ # frozen_string_literal: true
2
2
 
3
3
  module Jobshop
4
4
  class User < ApplicationRecord
5
- self.primary_keys = %i[ organization_id user_id ]
5
+ belongs_to :organization, -> { readonly }, inverse_of: :users
6
6
 
7
- after_initialize { self.user_id ||= SecureRandom.uuid if new_record? }
7
+ has_one :employee, inverse_of: :user
8
8
 
9
- belongs_to :organization, inverse_of: :users
9
+ has_many :rfq_line_assignments, foreign_key: :assigned_to_id,
10
+ class_name: "Jobshop::RFQ::Line::Assignment"
10
11
 
11
- has_many :role_assignments, inverse_of: :user,
12
- foreign_key: %i[ organization_id user_id ]
12
+ has_many :role_assignments, inverse_of: :user
13
13
 
14
14
  has_many :roles, through: :role_assignments
15
15
 
16
16
  validates :email,
17
17
  presence: { if: :email_required? },
18
18
  format: { if: :email_required?, with: /\A[^@\s]+@[^@\s]+\z/ },
19
- uniqueness: { if: :email_changed?, scope: :organization_id, case_sensitive: false }
19
+ uniqueness: { if: :email_changed?, scope: :organization_id,
20
+ case_sensitive: false }
20
21
 
21
22
  validates :encrypted_password, presence: true
22
23
 
@@ -24,14 +25,26 @@ module Jobshop
24
25
  presence: { if: :password_required? },
25
26
  confirmation: { if: :password_required? }
26
27
 
27
- devise :database_authenticatable
28
+ before_validation(on: :create) do
29
+ self.organization ||= employee&.organization
30
+ end
31
+
32
+ devise :database_authenticatable, :recoverable
33
+
34
+ def self.serialize_from_session(key, salt)
35
+ record = where(id: key).eager_load(:organization).first
36
+ record if record && record.authenticatable_salt == salt
37
+ end
38
+
39
+ def name
40
+ employee&.name || id
41
+ end
28
42
 
29
- private
30
- def password_required?
43
+ private def password_required?
31
44
  !persisted? || !password.nil? || !password_confirmation.nil?
32
45
  end
33
46
 
34
- def email_required?
47
+ private def email_required?
35
48
  true
36
49
  end
37
50
  end
@@ -0,0 +1,35 @@
1
+ #open-rfqs
2
+ New RFQs:
3
+ %h3 #{@rfqs.count} High Priority RFQs
4
+ %dl
5
+ - @rfqs.each do |rfq|
6
+ %dt
7
+ = link_to edit_rfq_path(rfq) do
8
+ #{rfq.subject}
9
+ %dd
10
+ - if rfq.verified?
11
+ #{rfq.requested_by&.full_name} @ #{rfq.customer&.name}
12
+ - else
13
+ Unknown Contact (#{rfq.from}) @ #{rfq.customer&.name}
14
+
15
+ %h3 #{@low_priority_rfqs.count} Low Priority RFQs
16
+ %dl
17
+ - @low_priority_rfqs.each do |rfq|
18
+ %dt
19
+ = link_to edit_rfq_path(rfq) do
20
+ #{rfq.subject}
21
+ %dd
22
+ #{rfq.from}
23
+
24
+ #assigned-quotes
25
+ %h3 Assigned RFQs
26
+ %dl
27
+ - @assigned_rfq_lines.each do |line|
28
+ %dt
29
+ = link_to edit_rfq_path(line.rfq) do
30
+ #{line.rfq.subject}
31
+ %dd
32
+ - if line.rfq.verified?
33
+ #{line.rfq.requested_by&.full_name} @ #{line.rfq.customer&.name}
34
+ - else
35
+ Unknown Contact (#{line.rfq.from}) @ #{line.rfq.customer&.name}
@@ -0,0 +1,12 @@
1
+ = form_for @department do |f|
2
+ - if @department.errors.any?
3
+ #error_explanation
4
+ %h2= "#{pluralize(@department.errors.count, "error")} prohibited this department from being saved:"
5
+ %ul
6
+ - @department.errors.full_messages.each do |message|
7
+ %li= message
8
+
9
+ = f.text_field :name, placeholder: "Department Name"
10
+
11
+ .actions
12
+ = f.submit
@@ -0,0 +1,7 @@
1
+ %h1 Editing department
2
+
3
+ = render 'form'
4
+
5
+ = link_to 'Show', @department
6
+ \|
7
+ = link_to 'Back', departments_path
@@ -0,0 +1,17 @@
1
+ %h1 Listing departments
2
+
3
+ %table
4
+ %thead
5
+ %tr
6
+ %th Name
7
+ %th
8
+ %th
9
+
10
+ %tbody
11
+ - @departments.each do |department|
12
+ %tr
13
+ %td= link_to department.name, department
14
+
15
+ %br
16
+
17
+ = link_to 'New Department', new_department_path
@@ -0,0 +1,5 @@
1
+ %h1 New department
2
+
3
+ = render 'form'
4
+
5
+ = link_to 'Back', departments_path
@@ -0,0 +1,15 @@
1
+ %h1= @department.name
2
+
3
+ %h2 Employees
4
+ %table
5
+ %thead
6
+ %tr
7
+ %td Name
8
+ %tbody
9
+ - @department.employees.each do |employee|
10
+ %tr
11
+ %td= employee.name
12
+
13
+ = link_to 'Edit', edit_department_path(@department)
14
+ \|
15
+ = link_to 'Back', departments_path