effective_reports 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 (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)