effective_reports 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (33) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +110 -0
  4. data/Rakefile +18 -0
  5. data/app/assets/config/effective_reports_manifest.js +3 -0
  6. data/app/assets/javascripts/effective_reports/base.js +15 -0
  7. data/app/assets/javascripts/effective_reports.js +1 -0
  8. data/app/assets/stylesheets/effective_reports/base.scss +0 -0
  9. data/app/assets/stylesheets/effective_reports.scss +1 -0
  10. data/app/controllers/admin/reports_controller.rb +16 -0
  11. data/app/datatables/admin/effective_reports_datatable.rb +31 -0
  12. data/app/datatables/effective_report_datatable.rb +21 -0
  13. data/app/helpers/effective_reports_helper.rb +60 -0
  14. data/app/mailers/effective/reports_mailer.rb +38 -0
  15. data/app/models/concerns/acts_as_reportable.rb +72 -0
  16. data/app/models/effective/report.rb +94 -0
  17. data/app/models/effective/report_column.rb +89 -0
  18. data/app/models/effective/report_scope.rb +48 -0
  19. data/app/views/admin/reports/_form.html.haml +8 -0
  20. data/app/views/admin/reports/_form_report.html.haml +194 -0
  21. data/app/views/admin/reports/_layout.html.haml +2 -0
  22. data/app/views/admin/reports/_report.html.haml +17 -0
  23. data/config/effective_reports.rb +37 -0
  24. data/config/routes.rb +16 -0
  25. data/db/migrate/01_create_effective_reports.rb.erb +54 -0
  26. data/db/seeds.rb +1 -0
  27. data/lib/effective_reports/engine.rb +18 -0
  28. data/lib/effective_reports/version.rb +3 -0
  29. data/lib/effective_reports.rb +27 -0
  30. data/lib/generators/effective_reports/install_generator.rb +32 -0
  31. data/lib/generators/templates/effective_reports_mailer_preview.rb +4 -0
  32. data/lib/tasks/effective_reports_tasks.rake +8 -0
  33. metadata +270 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: ff9625e424780a9a874037cce46b15b10cf69eb0d515fb6e426d61076099990c
4
+ data.tar.gz: a0b984c522429e79878d24064dbd24b3835c7777aa741654ab30a1c1ded90058
5
+ SHA512:
6
+ metadata.gz: 63940ee370287f6b3b517ce8b7f3a113b1b817615d6443f21fa045165b152b9da19bb925a25abf58f7be63a9172d0770b36d9c0d8e9c3a2f7980d5c614d9ab19
7
+ data.tar.gz: 7e0494cadaa8e2e98bcf79b0dce96cfd21e518448fdccb8386d335ffac7fe350fe076468a8ede1f61cc1ed85bbafa96d2291d6520aa073e70d191a2da00e2a3a
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright 2023 Code and Effect Inc.
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,110 @@
1
+ # Effective Reports
2
+
3
+ A dynamic ActiveRecord report builder.
4
+
5
+ ## Getting Started
6
+
7
+ This requires Rails 6+ and Twitter Bootstrap 4 and just works with Devise.
8
+
9
+ Please first install the [effective_datatables](https://github.com/code-and-effect/effective_datatables) gem.
10
+
11
+ Please download and install the [Twitter Bootstrap4](http://getbootstrap.com)
12
+
13
+ Add to your Gemfile:
14
+
15
+ ```ruby
16
+ gem 'effective_reports'
17
+ ```
18
+
19
+ Run the bundle command to install it:
20
+
21
+ ```console
22
+ bundle install
23
+ ```
24
+
25
+ Then run the generator:
26
+
27
+ ```ruby
28
+ rails generate effective_reports:install
29
+ ```
30
+
31
+ The generator will install an initializer which describes all configuration options and creates a database migration.
32
+
33
+ If you want to tweak the table names, manually adjust both the configuration file and the migration now.
34
+
35
+ Then migrate the database:
36
+
37
+ ```ruby
38
+ rake db:migrate
39
+ ```
40
+
41
+ Please add the following to your User model:
42
+
43
+ ```
44
+ acts_as_reportable
45
+
46
+ # { active: nil, inactive: nil, with_first_name: :string, not_in_good_standing: :boolean }
47
+ def reportable_scopes
48
+ {}
49
+ end
50
+ ```
51
+
52
+ and
53
+
54
+ ```
55
+ Add a link to the admin menu:
56
+
57
+ ```haml
58
+ - if can? :admin, :effective_reports
59
+ - if can? :index, Effective::Reports
60
+ = nav_link_to 'Reports', effective_reports.admin_reports_path
61
+ ```
62
+
63
+ ## Configuration
64
+
65
+ ## Authorization
66
+
67
+ All authorization checks are handled via the effective_resources gem found in the `config/initializers/effective_resources.rb` file.
68
+
69
+ ## Effective Roles
70
+
71
+ This gem works with effective roles for the representative roles.
72
+
73
+ Configure your `config/initializers/effective_roles.rb` something like this:
74
+
75
+ ```
76
+ ```
77
+
78
+ ## Permissions
79
+
80
+ The permissions you actually want to define are as follows (using CanCan):
81
+
82
+ ```ruby
83
+ if user.persisted?
84
+ end
85
+
86
+ if user.admin?
87
+ can :admin, :effective_reports
88
+ end
89
+ ```
90
+
91
+ ## License
92
+
93
+ MIT License. Copyright [Code and Effect Inc.](http://www.codeandeffect.com/)
94
+
95
+ ## Testing
96
+
97
+ Run tests by:
98
+
99
+ ```ruby
100
+ rails test
101
+ ```
102
+
103
+ ## Contributing
104
+
105
+ 1. Fork it
106
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
107
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
108
+ 4. Push to the branch (`git push origin my-new-feature`)
109
+ 5. Bonus points for test coverage
110
+ 6. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,18 @@
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"
9
+
10
+ require "rake/testtask"
11
+
12
+ Rake::TestTask.new(:test) do |t|
13
+ t.libs << 'test'
14
+ t.pattern = 'test/**/*_test.rb'
15
+ t.verbose = false
16
+ end
17
+
18
+ task default: :test
@@ -0,0 +1,3 @@
1
+ //= link_directory ../javascripts .js
2
+ //= link_directory ../stylesheets .css
3
+ //= link_tree ../images
@@ -0,0 +1,15 @@
1
+ // Show and hide the filter values when click filter by column
2
+ // This additional JS is required because the f.show_if doesn't work quite right
3
+ $(document).on('change', "[name^='effective_report[report_columns_attributes]'][name$='[filter]']", function(event) {
4
+ let $filter = $(event.currentTarget);
5
+ let $values = $filter.closest('.row').find('.effective-report-filter');
6
+ let $inputs = $values.find('input,textarea,select,button');
7
+
8
+ if($filter.is(':checked')) {
9
+ $values.show();
10
+ $inputs.removeAttr('disabled');
11
+ } else {
12
+ $values.hide();
13
+ $inputs.prop('disabled', true);
14
+ }
15
+ });
@@ -0,0 +1 @@
1
+ //= require_tree ./effective_reports
@@ -0,0 +1 @@
1
+ @import 'effective_reports/base';
@@ -0,0 +1,16 @@
1
+ module Admin
2
+ class ReportsController < ApplicationController
3
+ before_action(:authenticate_user!) if defined?(Devise)
4
+ before_action { EffectiveResources.authorize!(self, :admin, :effective_reports) }
5
+
6
+ include Effective::CrudController
7
+
8
+ private
9
+
10
+ def permitted_params
11
+ model = (params.key?(:effective_report) ? :effective_report: :report)
12
+ params.require(model).permit!
13
+ end
14
+
15
+ end
16
+ end
@@ -0,0 +1,31 @@
1
+ module Admin
2
+ class EffectiveReportsDatatable < Effective::Datatable
3
+
4
+ datatable do
5
+ order :id
6
+ col :id, visible: false
7
+
8
+ col :created_at, visible: false
9
+ col :created_by, visible: false
10
+
11
+ col :title
12
+ col :description
13
+
14
+ col :reportable_class_name, label: 'Resource', search: EffectiveReports.reportable_classes.map(&:to_s), visible: false
15
+
16
+ col :report_columns, label: 'Columns', visible: false
17
+ col :report_scopes, label: 'Scopes', visible: false
18
+
19
+ col(:current_rows_count) do |report|
20
+ report.collection().count
21
+ end
22
+
23
+ actions_col
24
+ end
25
+
26
+ collection do
27
+ Effective::Report.deep.all
28
+ end
29
+
30
+ end
31
+ end
@@ -0,0 +1,21 @@
1
+ # This renders a datatable based off a report
2
+
3
+ class EffectiveReportDatatable < Effective::Datatable
4
+ datatable do
5
+ skip_save_state! # Forgets the previous show/hide columns settings
6
+
7
+ report.report_columns.each do |column|
8
+ col(column.name, as: column.as.to_sym)
9
+ end
10
+
11
+ end
12
+
13
+ collection do
14
+ report.collection()
15
+ end
16
+
17
+ def report
18
+ Effective::Report.find(attributes[:report_id])
19
+ end
20
+
21
+ end
@@ -0,0 +1,60 @@
1
+ module EffectiveReportsHelper
2
+
3
+ def reportable_boolean_collection
4
+ [['Yes', true], ['No', false]]
5
+ end
6
+
7
+ def reportable_attributes_collection(attributes)
8
+ macros = [:belongs_to, :belongs_to_polymorphic, :has_many, :has_one]
9
+
10
+ {
11
+ 'Attributes' => attributes.select { |_, type| macros.exclude?(type) }.map { |att, _| [att, att] }.sort,
12
+ 'Associations' => attributes.select { |_, type| macros.include?(type) }.map { |att, _| [att, att] }.sort,
13
+ }
14
+ end
15
+
16
+ def reportable_scopes_collection(scopes)
17
+ {
18
+ 'Basic' => scopes.select { |_, type| type.blank? }.map { |scope, _| [scope, scope] }.sort,
19
+ 'Advanced' => scopes.select { |_, type| type.present? }.map { |scope, _| [scope, scope] }.sort
20
+ }
21
+ end
22
+
23
+ def reportable_operations_collection(type)
24
+ case type
25
+ when :boolean
26
+ [
27
+ ['Equals', :eq],
28
+ ['Does Not Equal', :not_eq]
29
+ ]
30
+ when :string
31
+ [
32
+ ['Equals =', :eq],
33
+ ['Does Not Equal !=', :not_eq],
34
+ ['Includes', :matches],
35
+ ['Does Not Include', :does_not_match],
36
+ ['Starts with', :starts_with],
37
+ ['Ends with', :ends_with]
38
+ ]
39
+ when :integer, :price, :date, :decimal
40
+ [
41
+ ['Equals =', :eq],
42
+ ['Does Not Equal !=', :not_eq],
43
+ ['Greater than >', :gt],
44
+ ['Greater than or equal to >=', :gteq],
45
+ ['Less than <', :lt],
46
+ ['Less than or equal to <', :lteq],
47
+ ]
48
+ when :belongs_to, :belongs_to_polymorphic, :has_many, :has_one
49
+ [
50
+ ['ID(s) Equals =', :eq],
51
+ ['Matches', :matches],
52
+ ['Does Not Match', :does_not_match],
53
+ ['SQL', :sql],
54
+ ]
55
+ else
56
+ raise("unexpected reportable operations collection type: #{type || 'nil'}")
57
+ end
58
+ end
59
+
60
+ end
@@ -0,0 +1,38 @@
1
+ module Effective
2
+ class ReportsMailer < EffectiveReports.parent_mailer_class
3
+
4
+ include EffectiveMailer
5
+ include EffectiveEmailTemplatesMailer if EffectiveReports.use_effective_email_templates
6
+
7
+ # def reports_submitted(resource, opts = {})
8
+ # @assigns = assigns_for(resource)
9
+ # @applicant = resource
10
+
11
+ # subject = subject_for(__method__, "Reports Submitted - #{resource}", resource, opts)
12
+ # headers = headers_for(resource, opts)
13
+
14
+ # mail(to: resource.user.email, subject: subject, **headers)
15
+ # end
16
+
17
+ protected
18
+
19
+ def assigns_for(resource)
20
+ if resource.kind_of?(Effective::Reports)
21
+ return reports_assigns(resource)
22
+ end
23
+
24
+ raise('unexpected resource')
25
+ end
26
+
27
+ def reports_assigns(resource)
28
+ raise('expected an reports') unless resource.class.respond_to?(:effective_reports_resource?)
29
+
30
+ values = {
31
+ date: reports.created_at.strftime('%F')
32
+ }.compact
33
+
34
+ { reports: values }
35
+ end
36
+
37
+ end
38
+ end
@@ -0,0 +1,72 @@
1
+ # ActsAsReportable
2
+ #
3
+ # Mark your model with 'acts_as_reportable' to be included in the reports
4
+
5
+ module ActsAsReportable
6
+ extend ActiveSupport::Concern
7
+
8
+ PRICE_NAME_ATTRIBUTES = ['price', 'subtotal', 'tax', 'total', 'current_revenue', 'current_revenue_subtotal', 'current_revenue_tax', 'deferred_revenue', 'deferred_revenue_subtotal', 'deferred_revenue_tax', 'amount_owing', 'surcharge']
9
+
10
+ module Base
11
+ def acts_as_reportable(options = nil)
12
+ include ::ActsAsReportable
13
+ end
14
+ end
15
+
16
+ module ClassMethods
17
+ def acts_as_reportable?; true; end
18
+ end
19
+
20
+ # Instance Methods
21
+
22
+ # { id: :integer, price: :price, created_at: :date }
23
+ def reportable_attributes
24
+ all_reportable_attributes || {}
25
+ end
26
+
27
+ # { active: nil, inactive: nil, with_first_name: :string, not_in_good_standing: :boolean }
28
+ def reportable_scopes
29
+ {}
30
+ end
31
+
32
+ private
33
+
34
+ def all_reportable_attributes
35
+ columns = (self.class.columns_hash rescue {})
36
+ names = (self.attributes rescue {})
37
+ reflections = (self.class.reflect_on_all_associations rescue [])
38
+
39
+ atts = names.inject({}) do |h, (name, _)|
40
+ type = columns[name].type
41
+
42
+ type = case type
43
+ when :datetime then :date
44
+ when :integer then ((PRICE_NAME_ATTRIBUTES.include?(name) || name.include?('price')) ? :price : :integer)
45
+ when :text then :string
46
+ else type
47
+ end
48
+
49
+ h[name.to_sym] = type; h
50
+ end
51
+
52
+ associated = reflections.inject({}) do |h, reflection|
53
+ case reflection
54
+ when ActiveRecord::Reflection::BelongsToReflection
55
+ if reflection.options[:polymorphic]
56
+ h[reflection.name.to_sym] = :belongs_to_polymorphic
57
+ else
58
+ h[reflection.name.to_sym] = :belongs_to
59
+ end
60
+ when ActiveRecord::Reflection::HasManyReflection
61
+ h[reflection.name.to_sym] = :has_many
62
+ when ActiveRecord::Reflection::HasOneReflection
63
+ h[reflection.name.to_sym] = :has_one
64
+ when ActiveRecord::Reflection::ThroughReflection
65
+ h[reflection.name.to_sym] = :has_many
66
+ end; h
67
+ end
68
+
69
+ atts.merge(associated)
70
+ end
71
+
72
+ end
@@ -0,0 +1,94 @@
1
+ module Effective
2
+ class Report < ActiveRecord::Base
3
+ self.table_name = EffectiveReports.reports_table_name.to_s
4
+
5
+ belongs_to :created_by, polymorphic: true
6
+
7
+ has_many :report_columns, -> { ReportColumn.sorted }, inverse_of: :report, dependent: :delete_all
8
+ accepts_nested_attributes_for :report_columns, allow_destroy: true, reject_if: proc { |atts| atts['name'].blank? }
9
+
10
+ has_many :report_scopes, -> { ReportScope.sorted }, inverse_of: :report, dependent: :delete_all
11
+ accepts_nested_attributes_for :report_scopes, allow_destroy: true, reject_if: proc { |atts| atts['name'].blank? }
12
+
13
+ log_changes if respond_to?(:log_changes)
14
+
15
+ DATATYPES = [:boolean, :date, :decimal, :integer, :price, :string, :belongs_to, :belongs_to_polymorphic, :has_many, :has_one]
16
+
17
+ # Arel::Predications.instance_methods
18
+ OPERATIONS = [:eq, :not_eq, :matches, :does_not_match, :starts_with, :ends_with, :gt, :gteq, :lt, :lteq, :sql]
19
+
20
+ effective_resource do
21
+ title :string
22
+ reportable_class_name :string
23
+
24
+ timestamps
25
+ end
26
+
27
+ scope :deep, -> { includes(:report_columns, :report_scopes) }
28
+ scope :sorted, -> { order(:title) }
29
+
30
+ validates :title, presence: true, uniqueness: true
31
+ validates :reportable_class_name, presence: true
32
+
33
+ def to_s
34
+ title.presence || 'report'
35
+ end
36
+
37
+ def reportable
38
+ reportable_class_name.constantize if reportable_class_name.present?
39
+ end
40
+
41
+ def filtered_report_columns
42
+ report_columns.select(&:filter?)
43
+ end
44
+
45
+ # Used to build the Reports form
46
+ # { id: :integer, archived: :boolean }
47
+ def reportable_attributes
48
+ attributes = Hash((reportable.new.reportable_attributes if reportable))
49
+
50
+ attributes.each do |attribute, type|
51
+ raise("#{reportable}.reportable_attributes #{attribute} => #{type || 'nil'} is invalid. Key must be a symbol") unless attribute.kind_of?(Symbol)
52
+ raise("#{reportable}.reportable_attributes :#{attribute} => #{type || 'nil'} is invalid. Value must be one of #{DATATYPES.map { |s| ":#{s}"}.join(', ')}") unless DATATYPES.include?(type)
53
+ end
54
+
55
+ attributes
56
+ end
57
+
58
+ # { active: nil, inactive: nil, with_first_name: :string, not_in_good_standing: :boolean }
59
+ def reportable_scopes
60
+ scopes = Hash((reportable.new.reportable_scopes if reportable))
61
+
62
+ scopes.each do |scope, type|
63
+ raise("#{reportable}.reportable_scopes #{scope} => #{type || 'nil'} is invalid. Key must be a symbol") unless scope.kind_of?(Symbol)
64
+ raise("#{reportable}.reportable_scopes :#{scope} => #{type || 'nil'} is invalid. Value must be one of #{DATATYPES.map { |s| ":#{s}"}.join(', ')}") if type.present? && !DATATYPES.include?(type)
65
+ raise("#{reportable} must respond to reportable scope :#{name}") unless reportable.respond_to?(scope)
66
+ end
67
+
68
+ scopes
69
+ end
70
+
71
+ # The klass to base the collection from
72
+ def collection
73
+ collection = reportable.all
74
+
75
+ # Apply Scopes
76
+ report_scopes.each do |scope|
77
+ collection = (scope.value.nil? ? collection.send(scope.name) : collection.send(scope.name, scope.value))
78
+ end
79
+
80
+ # Apply Includes
81
+ report_columns.select(&:as_associated?).each do |column|
82
+ collection = collection.includes(column.name)
83
+ end
84
+
85
+ # Apply Filters
86
+ report_columns.select(&:filter?).each do |column|
87
+ collection = Resource.new(collection).search(column.name, column.value, operation: column.operation)
88
+ end
89
+
90
+ collection
91
+ end
92
+
93
+ end
94
+ end
@@ -0,0 +1,89 @@
1
+ module Effective
2
+ class ReportColumn < ActiveRecord::Base
3
+ self.table_name = EffectiveReports.report_columns_table_name.to_s
4
+
5
+ belongs_to :report
6
+
7
+ log_changes(to: :report) if respond_to?(:log_changes)
8
+
9
+ effective_resource do
10
+ name :string
11
+ as :string
12
+ position :integer
13
+
14
+ filter :boolean
15
+ operation :string
16
+
17
+ value_associated :text
18
+ value_boolean :boolean
19
+ value_date :date
20
+ value_decimal :decimal
21
+ value_integer :integer
22
+ value_price :integer
23
+ value_string :string
24
+
25
+ timestamps
26
+ end
27
+
28
+ scope :deep, -> { includes(:report) }
29
+ scope :sorted, -> { order(:position) }
30
+
31
+ before_validation(if: -> { report.present? }) do
32
+ self.position ||= (report.report_columns.map(&:position).compact.max || -1) + 1
33
+ end
34
+
35
+ before_validation(if: -> { filter? == false }) do
36
+ assign_attributes(operation: nil, value_associated: nil, value_boolean: nil, value_date: nil, value_decimal: nil, value_integer: nil, value_price: nil, value_string: nil)
37
+ end
38
+
39
+ validates :name, presence: true
40
+ validates :as, presence: true, inclusion: { in: Report::DATATYPES.map(&:to_s) }
41
+ validates :position, presence: true
42
+ validates :operation, presence: true, if: -> { filter? }
43
+
44
+ validate(if: -> { filter? }) do
45
+ if value.blank? && (value != false)
46
+ self.errors.add(:value_date, "can't be blank")
47
+ self.errors.add(:value_decimal, "can't be blank")
48
+ self.errors.add(:value_integer, "can't be blank")
49
+ self.errors.add(:value_price, "can't be blank")
50
+ self.errors.add(:value_string, "can't be blank")
51
+ self.errors.add(:value_associated, "can't be blank")
52
+ self.errors.add(:value_boolean, "can't be blank")
53
+ end
54
+ end
55
+
56
+ def to_s
57
+ [name, operation_label, value].compact.join(' ').presence || 'report column'
58
+ end
59
+
60
+ def as_associated?
61
+ [:belongs_to, :belongs_to_polymorphic, :has_many, :has_one].include?(as.to_sym)
62
+ end
63
+
64
+ def value
65
+ value_date || value_decimal || value_integer || value_price || value_string.presence || value_associated.presence || value_boolean
66
+ end
67
+
68
+ def operation_label
69
+ return unless operation.present?
70
+
71
+ case operation.to_sym
72
+ when :eq then '='
73
+ when :not_eq then '!='
74
+ when :matches then '~='
75
+ when :does_not_match then '!~='
76
+ when :starts_with then 'starts with'
77
+ when :ends_with then 'ends with'
78
+ when :gt then '>'
79
+ when :gteq then '>='
80
+ when :lt then '<'
81
+ when :lteq then '<='
82
+ when :sql then 'sql'
83
+ else
84
+ raise("unexpected operation: #{operation}")
85
+ end
86
+ end
87
+
88
+ end
89
+ end
@@ -0,0 +1,48 @@
1
+ module Effective
2
+ class ReportScope < ActiveRecord::Base
3
+ self.table_name = EffectiveReports.report_scopes_table_name.to_s
4
+
5
+ belongs_to :report
6
+
7
+ log_changes(to: :report) if respond_to?(:log_changes)
8
+
9
+ effective_resource do
10
+ name :string
11
+ advanced :boolean # The scope is a 0 arity symbol when false, or a 1 arity hash when true
12
+
13
+ value_boolean :boolean
14
+ value_date :date
15
+ value_decimal :decimal
16
+ value_integer :integer
17
+ value_price :integer
18
+ value_string :string
19
+
20
+ timestamps
21
+ end
22
+
23
+ scope :deep, -> { includes(:report) }
24
+ scope :sorted, -> { order(:name) }
25
+
26
+ validates :name, presence: true
27
+
28
+ validate(if: -> { advanced? }) do
29
+ if value.blank? && (value != false)
30
+ self.errors.add(:value_date, "can't be blank")
31
+ self.errors.add(:value_decimal, "can't be blank")
32
+ self.errors.add(:value_integer, "can't be blank")
33
+ self.errors.add(:value_price, "can't be blank")
34
+ self.errors.add(:value_string, "can't be blank")
35
+ self.errors.add(:value_boolean, "can't be blank")
36
+ end
37
+ end
38
+
39
+ def to_s
40
+ name.presence || 'report scope'
41
+ end
42
+
43
+ def value
44
+ value_date || value_decimal || value_integer || value_price || value_string.presence || value_boolean
45
+ end
46
+
47
+ end
48
+ end
@@ -0,0 +1,8 @@
1
+ = tabs do
2
+ = tab 'Report' do
3
+ = render 'admin/reports/form_report', report: report
4
+
5
+ - if report.persisted?
6
+ - if report.respond_to?(:log_changes_datatable)
7
+ = tab 'Logs' do
8
+ = render_inline_datatable(report.log_changes_datatable)