Merge pull request #9032 from seballot/reports-improvement

Reports improvement
This commit is contained in:
Rachel Arnould
2022-05-16 10:29:28 +02:00
committed by GitHub
240 changed files with 6054 additions and 7432 deletions

View File

@@ -1,6 +1,6 @@
# This configuration was generated by
# `rubocop --auto-gen-config --auto-gen-only-exclude --exclude-limit 1400`
# on 2022-02-25 01:04:47 UTC using RuboCop version 1.22.2.
# on 2022-03-29 16:07:39 UTC using RuboCop version 1.22.2.
# The point is for the user to remove these configuration records
# one by one as the offenses are removed from the code base.
# Note that changes in the inspected code, or installation of new
@@ -53,7 +53,7 @@ Layout/LeadingCommentSpace:
Exclude:
- 'spec/system/admin/enterprises_spec.rb'
# Offense count: 828
# Offense count: 856
# Cop supports --auto-correct.
# Configuration parameters: Max, AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, IgnoredPatterns.
# URISchemes: http, https
@@ -108,7 +108,6 @@ Layout/LineLength:
- 'app/services/order_syncer.rb'
- 'app/services/products_renderer.rb'
- 'app/services/variant_units/variant_and_line_item_naming.rb'
- 'engines/order_management/app/services/order_management/reports/bulk_coop/bulk_coop_report.rb'
- 'engines/order_management/app/services/order_management/subscriptions/validator.rb'
- 'engines/order_management/spec/services/order_management/order/updater_spec.rb'
- 'engines/web/app/helpers/web/cookies_policy_helper.rb'
@@ -117,15 +116,19 @@ Layout/LineLength:
- 'lib/open_food_network/enterprise_fee_applicator.rb'
- 'lib/open_food_network/enterprise_fee_calculator.rb'
- 'lib/open_food_network/enterprise_issue_validator.rb'
- 'lib/open_food_network/lettuce_share_report.rb'
- 'lib/open_food_network/order_cycle_form_applicator.rb'
- 'lib/open_food_network/order_cycle_management_report.rb'
- 'lib/open_food_network/order_cycle_permissions.rb'
- 'lib/open_food_network/payments_report.rb'
- 'lib/open_food_network/reports/line_items.rb'
- 'lib/open_food_network/sales_tax_report.rb'
- 'lib/open_food_network/scope_variants_for_search.rb'
- 'lib/open_food_network/xero_invoices_report.rb'
- 'lib/reporting/line_items.rb'
- 'lib/reporting/reports/bulk_coop/bulk_coop_report.rb'
- 'lib/reporting/reports/enterprise_fee_summary/report_data/enterprise_fee_type_total.rb'
- 'lib/reporting/reports/order_cycle_management/order_cycle_management_report.rb'
- 'lib/open_food_network/order_cycle_permissions.rb'
- 'lib/reporting/reports/orders_and_fulfillment/customer_totals_report.rb'
- 'lib/reporting/reports/orders_and_fulfillment/distributor_totals_by_supplier_report.rb'
- 'lib/reporting/reports/payments/payments_report.rb'
- 'lib/reporting/reports/products_and_inventory/lettuce_share_report.rb'
- 'lib/reporting/reports/sales_tax/sales_tax_report.rb'
- 'lib/reporting/reports/xero_invoices/base.rb'
- 'lib/spree/localized_number.rb'
- 'lib/tasks/data.rake'
- 'lib/tasks/enterprises.rake'
@@ -176,19 +179,20 @@ Layout/LineLength:
- 'spec/helpers/spree/admin/base_helper_spec.rb'
- 'spec/jobs/subscription_confirm_job_spec.rb'
- 'spec/jobs/subscription_placement_job_spec.rb'
- 'spec/lib/open_food_network/customers_report_spec.rb'
- 'spec/lib/open_food_network/enterprise_fee_calculator_spec.rb'
- 'spec/lib/open_food_network/group_buy_report_spec.rb'
- 'spec/lib/open_food_network/order_cycle_form_applicator_spec.rb'
- 'spec/lib/open_food_network/order_cycle_management_report_spec.rb'
- 'spec/lib/open_food_network/order_cycle_permissions_spec.rb'
- 'spec/lib/open_food_network/order_grouper_spec.rb'
- 'spec/lib/open_food_network/permissions_spec.rb'
- 'spec/lib/open_food_network/products_and_inventory_report_spec.rb'
- 'spec/lib/open_food_network/scope_variant_to_hub_spec.rb'
- 'spec/lib/open_food_network/tag_rule_applicator_spec.rb'
- 'spec/lib/open_food_network/users_and_enterprises_report_spec.rb'
- 'spec/lib/reports/customers_report_spec.rb'
- 'spec/lib/reports/order_cycle_management_report_spec.rb'
- 'spec/lib/reports/order_grouper_spec.rb'
- 'spec/lib/reports/orders_and_fulfillment/orders_and_fulfillment_report_spec.rb'
- 'spec/lib/reports/packing/packing_report_spec.rb'
- 'spec/lib/reports/products_and_inventory_report_spec.rb'
- 'spec/lib/reports/users_and_enterprises_report_spec.rb'
- 'spec/lib/reports/xero_invoices_report_spec.rb'
- 'spec/lib/stripe/authorize_response_patcher_spec.rb'
- 'spec/mailers/order_mailer_spec.rb'
- 'spec/mailers/producer_mailer_spec.rb'
@@ -309,7 +313,15 @@ Layout/MultilineMethodCallBraceLayout:
Exclude:
- 'lib/reporting/queries/joins.rb'
# Offense count: 17
# Offense count: 2
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle, IndentationWidth.
# SupportedStyles: aligned, indented, indented_relative_to_receiver
Layout/MultilineMethodCallIndentation:
Exclude:
- 'lib/reporting/reports/customers/customers_report.rb'
# Offense count: 20
# Cop supports --auto-correct.
# Configuration parameters: AllowInHeredoc.
Layout/TrailingWhitespace:
@@ -331,7 +343,7 @@ Lint/ConstantDefinitionInBlock:
- 'lib/tasks/users.rake'
- 'spec/controllers/spree/admin/base_controller_spec.rb'
- 'spec/helpers/serializer_helper_spec.rb'
- 'spec/lib/open_food_network/reports/line_items_spec.rb'
- 'spec/lib/reports/line_items_spec.rb'
- 'spec/models/spree/ability_spec.rb'
- 'spec/models/spree/gateway_spec.rb'
- 'spec/models/spree/preferences/configuration_spec.rb'
@@ -398,7 +410,7 @@ Lint/UselessMethodDefinition:
- 'app/controllers/spree/user_registrations_controller.rb'
- 'app/models/spree/gateway.rb'
# Offense count: 39
# Offense count: 38
# Configuration parameters: IgnoredMethods, CountRepeatedAttributes, Max.
Metrics/AbcSize:
Exclude:
@@ -419,22 +431,21 @@ Metrics/AbcSize:
- 'app/models/spree/order/checkout.rb'
- 'app/models/spree/preferences/preferable_class_methods.rb'
- 'app/models/spree/return_authorization.rb'
- 'engines/order_management/app/services/order_management/reports/bulk_coop/bulk_coop_report.rb'
- 'lib/discourse/single_sign_on.rb'
- 'lib/open_food_network/customers_report.rb'
- 'lib/open_food_network/group_buy_report.rb'
- 'lib/open_food_network/order_and_distributor_report.rb'
- 'lib/open_food_network/order_cycle_form_applicator.rb'
- 'lib/reporting/reports/bulk_coop/bulk_coop_report.rb'
- 'lib/reporting/reports/customers/customers_report.rb'
- 'lib/open_food_network/order_cycle_permissions.rb'
- 'lib/open_food_network/payments_report.rb'
- 'lib/open_food_network/sales_tax_report.rb'
- 'lib/reporting/reports/orders_and_distributors/orders_and_distributors_report.rb'
- 'lib/reporting/reports/packing/customer.rb'
- 'lib/reporting/reports/payments/payments_report.rb'
- 'lib/reporting/reports/sales_tax/sales_tax_report.rb'
- 'lib/spree/core/controller_helpers/order.rb'
- 'lib/spree/core/s3_support.rb'
- 'lib/tasks/enterprises.rake'
- 'spec/services/order_checkout_restart_spec.rb'
# Offense count: 45
# Offense count: 43
# Configuration parameters: CountComments, Max, CountAsOne, ExcludedMethods, IgnoredMethods.
# IgnoredMethods: refine
Metrics/BlockLength:
@@ -458,13 +469,12 @@ Metrics/BlockLength:
- 'spec/factories/subscription_factory.rb'
- 'spec/factories/user_factory.rb'
- 'spec/factories/variant_factory.rb'
- 'spec/lib/open_food_network/group_buy_report_spec.rb'
- 'spec/requests/api/orders_spec.rb'
- 'spec/spec_helper.rb'
- 'spec/swagger_helper.rb'
- 'spec/support/cancan_helper.rb'
- 'spec/support/matchers/select2_matchers.rb'
- 'spec/support/matchers/table_matchers.rb'
- 'spec/swagger_helper.rb'
- 'spec/system/admin/order_cycles/complex_updating_specific_time_spec.rb'
- 'spec/system/consumer/shopping/checkout_spec.rb'
@@ -474,7 +484,7 @@ Metrics/BlockNesting:
Exclude:
- 'app/models/spree/payment/processing.rb'
# Offense count: 49
# Offense count: 50
# Configuration parameters: CountComments, Max, CountAsOne.
Metrics/ClassLength:
Exclude:
@@ -518,18 +528,17 @@ Metrics/ClassLength:
- 'app/services/cart_service.rb'
- 'app/services/order_syncer.rb'
- 'engines/order_management/app/services/order_management/order/updater.rb'
- 'engines/order_management/app/services/order_management/reports/bulk_coop/bulk_coop_report.rb'
- 'engines/order_management/app/services/order_management/reports/enterprise_fee_summary/scope.rb'
- 'lib/open_food_network/enterprise_fee_calculator.rb'
- 'lib/open_food_network/order_cycle_form_applicator.rb'
- 'lib/open_food_network/order_cycle_management_report.rb'
- 'lib/open_food_network/order_cycle_permissions.rb'
- 'lib/open_food_network/payments_report.rb'
- 'lib/open_food_network/permissions.rb'
- 'lib/open_food_network/users_and_enterprises_report.rb'
- 'lib/open_food_network/xero_invoices_report.rb'
- 'lib/reporting/reports/bulk_coop/bulk_coop_report.rb'
- 'lib/reporting/reports/enterprise_fee_summary/scope.rb'
- 'lib/reporting/reports/order_cycle_management/order_cycle_management_report.rb'
- 'lib/open_food_network/order_cycle_permissions.rb'
- 'lib/reporting/reports/payments/payments_report.rb'
- 'lib/reporting/reports/xero_invoices/base.rb'
# Offense count: 40
# Offense count: 39
# Configuration parameters: IgnoredMethods, Max.
Metrics/CyclomaticComplexity:
Exclude:
@@ -555,20 +564,19 @@ Metrics/CyclomaticComplexity:
- 'app/models/spree/tax_rate.rb'
- 'app/models/spree/variant.rb'
- 'app/models/spree/zone.rb'
- 'engines/order_management/app/services/order_management/reports/bulk_coop/bulk_coop_report.rb'
- 'lib/discourse/single_sign_on.rb'
- 'lib/open_food_network/customers_report.rb'
- 'lib/open_food_network/enterprise_issue_validator.rb'
- 'lib/open_food_network/group_buy_report.rb'
- 'lib/open_food_network/orders_and_fulfillments_report/customer_totals_report.rb'
- 'lib/open_food_network/payments_report.rb'
- 'lib/open_food_network/xero_invoices_report.rb'
- 'lib/reporting/reports/bulk_coop/bulk_coop_report.rb'
- 'lib/reporting/reports/customers/customers_report.rb'
- 'lib/reporting/reports/orders_and_fulfillment/customer_totals_report.rb'
- 'lib/reporting/reports/payments/payments_report.rb'
- 'lib/reporting/reports/xero_invoices/base.rb'
- 'lib/spree/core/controller_helpers/order.rb'
- 'lib/spree/core/controller_helpers/respond_with.rb'
- 'lib/spree/localized_number.rb'
- 'spec/models/product_importer_spec.rb'
# Offense count: 31
# Offense count: 32
# Configuration parameters: CountComments, Max, CountAsOne, ExcludedMethods, IgnoredMethods.
Metrics/MethodLength:
Exclude:
@@ -578,23 +586,23 @@ Metrics/MethodLength:
- 'app/controllers/spree/orders_controller.rb'
- 'app/helpers/checkout_helper.rb'
- 'app/helpers/spree/admin/navigation_helper.rb'
- "app/json_schemas/json_api_schema.rb"
- 'app/json_schemas/json_api_schema.rb'
- 'app/models/spree/ability.rb'
- 'app/models/spree/gateway/pay_pal_express.rb'
- 'app/models/spree/order/checkout.rb'
- 'app/models/spree/payment/processing.rb'
- 'app/models/spree/preferences/preferable_class_methods.rb'
- 'engines/order_management/app/services/order_management/reports/bulk_coop/bulk_coop_report.rb'
- 'engines/order_management/app/services/order_management/reports/enterprise_fee_summary/scope.rb'
- 'lib/discourse/single_sign_on.rb'
- 'lib/open_food_network/order_cycle_form_applicator.rb'
- 'lib/open_food_network/order_cycle_management_report.rb'
- 'lib/reporting/reports/bulk_coop/bulk_coop_report.rb'
- 'lib/reporting/reports/enterprise_fee_summary/scope.rb'
- 'lib/reporting/reports/order_cycle_management/order_cycle_management_report.rb'
- 'lib/open_food_network/order_cycle_permissions.rb'
- 'lib/open_food_network/payments_report.rb'
- 'lib/open_food_network/xero_invoices_report.rb'
- 'lib/reporting/reports/payments/payments_report.rb'
- 'lib/reporting/reports/xero_invoices/base.rb'
- 'lib/tasks/sample_data/product_factory.rb'
# Offense count: 51
# Offense count: 54
# Configuration parameters: CountComments, Max, CountAsOne.
Metrics/ModuleLength:
Exclude:
@@ -625,17 +633,20 @@ Metrics/ModuleLength:
- 'spec/controllers/spree/admin/adjustments_controller_spec.rb'
- 'spec/controllers/spree/admin/payment_methods_controller_spec.rb'
- 'spec/lib/open_food_network/address_finder_spec.rb'
- 'spec/lib/open_food_network/customers_report_spec.rb'
- 'spec/lib/open_food_network/enterprise_fee_calculator_spec.rb'
- 'spec/lib/open_food_network/order_cycle_form_applicator_spec.rb'
- 'spec/lib/open_food_network/order_cycle_management_report_spec.rb'
- 'spec/lib/open_food_network/order_cycle_permissions_spec.rb'
- 'spec/lib/open_food_network/order_grouper_spec.rb'
- 'spec/lib/open_food_network/permissions_spec.rb'
- 'spec/lib/open_food_network/products_and_inventory_report_spec.rb'
- 'spec/lib/open_food_network/scope_variant_to_hub_spec.rb'
- 'spec/lib/open_food_network/tag_rule_applicator_spec.rb'
- 'spec/lib/open_food_network/users_and_enterprises_report_spec.rb'
- 'spec/lib/reports/customers_report_spec.rb'
- 'spec/lib/reports/enterprise_fee_summary/authorizer_spec.rb'
- 'spec/lib/reports/order_cycle_management_report_spec.rb'
- 'spec/lib/reports/order_grouper_spec.rb'
- 'spec/lib/reports/orders_and_fulfillment/customer_totals_report_spec.rb'
- 'spec/lib/reports/orders_and_fulfillment/orders_and_fulfillment_report_spec.rb'
- 'spec/lib/reports/products_and_inventory_report_spec.rb'
- 'spec/lib/reports/users_and_enterprises_report_spec.rb'
- 'spec/models/spree/adjustment_spec.rb'
- 'spec/models/spree/credit_card_spec.rb'
- 'spec/models/spree/line_item_spec.rb'
@@ -656,11 +667,11 @@ Metrics/ParameterLists:
Exclude:
- 'app/helpers/angular_form_builder.rb'
- 'app/models/product_import/entry_processor.rb'
- 'lib/open_food_network/xero_invoices_report.rb'
- 'lib/reporting/reports/xero_invoices/base.rb'
- 'spec/support/controller_requests_helper.rb'
- 'spec/system/admin/reports_spec.rb'
# Offense count: 8
# Offense count: 7
# Configuration parameters: IgnoredMethods, Max.
Metrics/PerceivedComplexity:
Exclude:
@@ -669,9 +680,8 @@ Metrics/PerceivedComplexity:
- 'app/models/enterprise_relationship.rb'
- 'app/models/spree/ability.rb'
- 'app/models/spree/order/checkout.rb'
- 'engines/order_management/app/services/order_management/reports/bulk_coop/bulk_coop_report.rb'
- 'lib/open_food_network/group_buy_report.rb'
- 'lib/open_food_network/payments_report.rb'
- 'lib/reporting/reports/bulk_coop/bulk_coop_report.rb'
- 'lib/reporting/reports/payments/payments_report.rb'
# Offense count: 9
Naming/AccessorMethodName:
@@ -716,7 +726,7 @@ Naming/VariableNumber:
- 'app/controllers/spree/orders_controller.rb'
- 'app/models/content_configuration.rb'
- 'app/models/preference_sections/main_links_section.rb'
- 'lib/open_food_network/orders_and_fulfillments_report/customer_totals_report.rb'
- 'lib/reporting/reports/orders_and_fulfillment/customer_totals_report.rb'
- 'lib/spree/core/controller_helpers/common.rb'
- 'spec/controllers/spree/admin/search_controller_spec.rb'
- 'spec/factories/stock_location_factory.rb'
@@ -895,7 +905,7 @@ Rails/LexicallyScopedActionFilter:
- 'app/controllers/spree/admin/zones_controller.rb'
- 'app/controllers/spree/users_controller.rb'
# Offense count: 18
# Offense count: 19
Rails/OutputSafety:
Exclude:
- 'app/controllers/spree/admin/reports_controller.rb'
@@ -1092,9 +1102,9 @@ Style/MissingRespondToMissing:
# Offense count: 1
Style/MixinUsage:
Exclude:
- 'lib/open_food_network/orders_and_fulfillments_report.rb'
- 'lib/reporting/reports/orders_and_fulfillment/orders_and_fulfillment_report.rb'
# Offense count: 2
# Offense count: 3
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle.
# SupportedStyles: literals, strict
@@ -1118,7 +1128,7 @@ Style/NestedModifier:
- 'spec/system/admin/payments_stripe_spec.rb'
- 'spec/system/admin/reports_spec.rb'
# Offense count: 25
# Offense count: 26
# Configuration parameters: AllowedMethods.
# AllowedMethods: respond_to_missing?
Style/OptionalBooleanParameter:
@@ -1132,16 +1142,7 @@ Style/OptionalBooleanParameter:
- 'app/models/spree/order_contents.rb'
- 'app/models/spree/preferences/file_configuration.rb'
- 'app/models/spree/shipment.rb'
- 'engines/order_management/app/services/order_management/reports/bulk_coop/bulk_coop_report.rb'
- 'engines/order_management/app/services/order_management/stock/estimator.rb'
- 'lib/open_food_network/customers_report.rb'
- 'lib/open_food_network/order_and_distributor_report.rb'
- 'lib/open_food_network/order_cycle_management_report.rb'
- 'lib/open_food_network/orders_and_fulfillments_report.rb'
- 'lib/open_food_network/payments_report.rb'
- 'lib/open_food_network/products_and_inventory_report_base.rb'
- 'lib/open_food_network/users_and_enterprises_report.rb'
- 'lib/open_food_network/xero_invoices_report.rb'
- 'lib/spree/core/controller_helpers/order.rb'
- 'lib/spree/core/delegate_belongs_to.rb'
- 'spec/support/request/web_helper.rb'
@@ -1161,11 +1162,10 @@ Style/RedundantReturn:
Exclude:
- 'app/controllers/spree/admin/shipping_methods_controller.rb'
# Offense count: 213
# Offense count: 205
Style/Send:
Exclude:
- 'app/controllers/split_checkout_controller.rb'
- 'engines/order_management/spec/services/order_management/reports/bulk_coop/bulk_coop_report_spec.rb'
- 'spec/controllers/admin/subscriptions_controller_spec.rb'
- 'spec/controllers/checkout_controller_spec.rb'
- 'spec/controllers/payment_gateways/paypal_controller_spec.rb'
@@ -1177,13 +1177,10 @@ Style/Send:
- 'spec/lib/open_food_network/address_finder_spec.rb'
- 'spec/lib/open_food_network/enterprise_fee_applicator_spec.rb'
- 'spec/lib/open_food_network/enterprise_fee_calculator_spec.rb'
- 'spec/lib/open_food_network/lettuce_share_report_spec.rb'
- 'spec/lib/open_food_network/order_cycle_form_applicator_spec.rb'
- 'spec/lib/open_food_network/permissions_spec.rb'
- 'spec/lib/open_food_network/products_and_inventory_report_spec.rb'
- 'spec/lib/open_food_network/sales_tax_report_spec.rb'
- 'spec/lib/open_food_network/tag_rule_applicator_spec.rb'
- 'spec/lib/open_food_network/xero_invoices_report_spec.rb'
- 'spec/lib/reports/xero_invoices_report_spec.rb'
- 'spec/lib/stripe/webhook_handler_spec.rb'
- 'spec/models/calculator/weight_spec.rb'
- 'spec/models/enterprise_spec.rb'
@@ -1208,7 +1205,7 @@ Style/SingleArgumentDig:
Exclude:
- 'app/services/checkout/form_data_adapter.rb'
# Offense count: 5
# Offense count: 4
# Cop supports --auto-correct.
Style/SlicingWithRange:
Exclude:
@@ -1216,9 +1213,8 @@ Style/SlicingWithRange:
- 'app/services/embedded_page_service.rb'
- 'engines/order_management/app/services/order_management/subscriptions/validator.rb'
- 'lib/discourse/single_sign_on.rb'
- 'spec/lib/open_food_network/order_grouper_spec.rb'
# Offense count: 31
# Offense count: 28
# Cop supports --auto-correct.
# Configuration parameters: Mode.
Style/StringConcatenation:
@@ -1235,11 +1231,8 @@ Style/StringConcatenation:
- 'app/serializers/api/enterprise_shopfront_list_serializer.rb'
- 'app/services/embedded_page_service.rb'
- 'app/services/products_renderer.rb'
- 'engines/order_management/app/services/order_management/reports/bulk_coop/bulk_coop_report.rb'
- 'lib/open_food_network/orders_and_fulfillments_report/customer_totals_report.rb'
- 'lib/spree/api/controller_setup.rb'
- 'lib/spree/core/environment_extension.rb'
- 'spec/lib/open_food_network/order_grouper_spec.rb'
- 'spec/models/spree/line_item_spec.rb'
- 'spec/models/spree/product_spec.rb'
- 'spec/models/spree/variant_spec.rb'

View File

@@ -5,14 +5,23 @@ module Admin
include ReportsActions
helper ReportsHelper
before_action :authorize_report
before_action :authorize_report, only: [:show]
# Define model class for Can? permissions
def model_class
Admin::ReportsController
end
def index
@reports = reports.select do |report_type, _description|
can? report_type, :report
end
end
def show
render_report && return if ransack_params.blank?
@report = report_class.new(spree_current_user, params, request)
@report = report_class.new(spree_current_user, ransack_params, report_options)
if export_spreadsheet?
if report_format.present?
export_report
else
render_report
@@ -22,33 +31,23 @@ module Admin
private
def export_report
render report_format.to_sym => @report.public_send("to_#{report_format}"),
:filename => report_filename
send_data @report.render_as(report_format, controller: self), filename: report_filename
end
def render_report
assign_view_data
load_form_options
render report_type
render "show"
end
def assign_view_data
@report_type = report_type
@report_subtype = report_subtype || report_loader.default_report_subtype
@report_subtypes = report_class.report_subtypes.map do |subtype|
[t("packing.#{subtype}_report", scope: i18n_scope), subtype]
end
end
@report_subtypes = report_subtypes
@report_subtype = report_subtype
def load_form_options
return unless form_options_required?
# Initialize data
params[:display_summary_row] = true if request.get?
form_options = Reporting::FrontendData.new(spree_current_user)
@distributors = form_options.distributors.to_a
@suppliers = form_options.suppliers.to_a
@order_cycles = form_options.order_cycles.to_a
@data = Reporting::FrontendData.new(spree_current_user)
end
end
end

View File

@@ -10,7 +10,8 @@ module Api
before_action :validate_report, :authorize_report, :validate_query
def show
@report = report_class.new(current_api_user, ransack_params, report_options)
params[:report_format] = 'json'
@report = report_class.new(current_api_user, params)
render_report
end

View File

@@ -3,10 +3,14 @@
module ReportsActions
extend ActiveSupport::Concern
def reports
Reporting::Reports::List.all
end
private
def authorize_report
authorize! report_type&.to_sym, :report
authorize! report_type.to_sym, :report
end
def report_class
@@ -23,31 +27,26 @@ module ReportsActions
params[:report_type]
end
def report_subtypes
reports[report_type.to_sym] || []
end
def report_subtypes_codes
report_subtypes.map(&:second).map(&:to_s)
end
def report_subtype
params[:report_subtype]
params[:report_subtype] || report_subtypes_codes.first
end
def ransack_params
raw_params[:q]
end
def report_options
raw_params[:options]
end
def report_format
params[:report_format]
end
def export_spreadsheet?
['xlsx', 'ods', 'csv'].include?(report_format)
end
def form_options_required?
[:packing, :customers, :products_and_inventory, :order_cycle_management].
include? report_type.to_sym
end
def report_filename
"#{report_type || action_name}_#{file_timestamp}.#{report_format}"
end

View File

@@ -1,310 +0,0 @@
# frozen_string_literal: true
require 'csv'
require 'open_food_network/reports/list'
require 'open_food_network/order_and_distributor_report'
require 'open_food_network/products_and_inventory_report'
require 'open_food_network/lettuce_share_report'
require 'open_food_network/group_buy_report'
require 'open_food_network/order_grouper'
require 'open_food_network/customers_report'
require 'open_food_network/users_and_enterprises_report'
require 'open_food_network/order_cycle_management_report'
require 'open_food_network/sales_tax_report'
require 'open_food_network/xero_invoices_report'
require 'open_food_network/payments_report'
require 'open_food_network/orders_and_fulfillments_report'
module Spree
module Admin
class ReportsController < Spree::Admin::BaseController
include Spree::ReportsHelper
helper ::ReportsHelper
ORDER_MANAGEMENT_ENGINE_REPORTS = [
:bulk_coop,
:enterprise_fee_summary
].freeze
helper_method :render_content?
before_action :cache_search_state
# Fetches user's distributors, suppliers and order_cycles
before_action :load_basic_data, only: [:customers, :products_and_inventory, :order_cycle_management]
before_action :load_associated_data, only: [:orders_and_fulfillment]
respond_to :html
def report_types
OpenFoodNetwork::Reports::List.all
end
def index
@reports = authorized_reports
respond_with(@reports)
end
def customers
@report_types = report_types[:customers]
@report_type = params[:report_type]
@report = OpenFoodNetwork::CustomersReport.new spree_current_user, raw_params,
render_content?
render_report(@report.header, @report.table, params[:csv], "customers_#{timestamp}.csv")
end
def order_cycle_management
raw_params[:q] ||= {}
@report_types = report_types[:order_cycle_management]
@report_type = params[:report_type]
# -- Build Report with Order Grouper
@report = OpenFoodNetwork::OrderCycleManagementReport.new spree_current_user,
raw_params,
render_content?
@table = @report.table_items
render_report(@report.header, @table, params[:csv],
"order_cycle_management_#{timestamp}.csv")
end
def orders_and_distributors
@report = OpenFoodNetwork::OrderAndDistributorReport.new spree_current_user,
raw_params,
render_content?
@search = @report.search
csv_file_name = "orders_and_distributors_#{timestamp}.csv"
render_report(@report.header, @report.table, params[:csv], csv_file_name)
end
def sales_tax
@distributors = my_distributors
@report_type = params[:report_type]
@report = OpenFoodNetwork::SalesTaxReport.new spree_current_user, raw_params,
render_content?
render_report(@report.header, @report.table, params[:csv], "sales_tax.csv")
end
def payments
# -- Prepare Form Options
@distributors = my_distributors
@report_type = params[:report_type]
# -- Build Report with Order Grouper
@report = OpenFoodNetwork::PaymentsReport.new spree_current_user, raw_params,
render_content?
@table = order_grouper_table
csv_file_name = "payments_#{timestamp}.csv"
render_report(@report.header, @table, params[:csv], csv_file_name)
end
def orders_and_fulfillment
raw_params[:q] ||= orders_and_fulfillment_default_filters
@report_types = report_types[:orders_and_fulfillment]
@report_type = params[:report_type]
@include_blank = I18n.t(:all)
# -- Build Report with Order Grouper
@report = OpenFoodNetwork::OrdersAndFulfillmentsReport.new spree_current_user,
raw_params,
render_content?
@table = order_grouper_table
csv_file_name = "#{params[:report_type]}_#{timestamp}.csv"
render_report(@report.header, @table, params[:csv], csv_file_name)
end
def products_and_inventory
@report_types = report_types[:products_and_inventory]
@report = if params[:report_type] != 'lettuce_share'
OpenFoodNetwork::ProductsAndInventoryReport.new spree_current_user,
raw_params,
render_content?
else
OpenFoodNetwork::LettuceShareReport.new spree_current_user,
raw_params,
render_content?
end
render_report @report.header,
@report.table,
params[:csv],
"products_and_inventory_#{timestamp}.csv"
end
def users_and_enterprises
@report = OpenFoodNetwork::UsersAndEnterprisesReport.new raw_params, render_content?
render_report(@report.header, @report.table, params[:csv],
"users_and_enterprises_#{timestamp}.csv")
end
def xero_invoices
raw_params[:q] ||= {}
@distributors = my_distributors
@order_cycles = my_order_cycles
@report = OpenFoodNetwork::XeroInvoicesReport.new(spree_current_user,
raw_params,
render_content?)
render_report(@report.header, @report.table, params[:csv], "xero_invoices_#{timestamp}.csv")
end
private
def model_class
Spree::Admin::ReportsController
end
# Some actions are changing the `params` object. That is unfortunate Spree
# behavior and we are building on it. So we have to look at `params` early
# to check if we are searching or just displaying a report search form.
def cache_search_state
search_keys = [
# search parameter for ransack
:q,
# common in all reports, only set for CSV rendering
:csv,
# `button` is included in all forms. It's not important for searching,
# but the Users & Enterprises report doesn't have any other parameter
# for an empty search. So we use this one to display data.
:button,
# Some reports use filtering by enterprise or order cycle
:distributor_id,
:supplier_id,
:order_cycle_id,
# Xero Invoices can be filtered by date
:invoice_date,
:due_date
]
@searching = search_keys.any? { |key| raw_params.key? key }
end
# We don't want to render data unless search params are supplied.
# Compiling data can take a long time.
def render_content?
@searching
end
def render_report(header, table, create_csv, csv_file_name)
send_data csv_report(header, table), filename: csv_file_name if create_csv
@header = header
@table = table
# Rendering HTML is the default.
end
def load_associated_data
form_options = Reporting::FrontendData.new(spree_current_user)
@distributors = form_options.distributors
@suppliers = form_options.suppliers
@order_cycles = form_options.order_cycles
end
def csv_report(header, table)
CSV.generate do |csv|
csv << header
table.each { |row| csv << row }
end
end
def load_basic_data
@distributors = my_distributors
@suppliers = my_suppliers | suppliers_of_products_distributed_by(@distributors)
@order_cycles = my_order_cycles
end
# Load managed distributor enterprises of current user
def my_distributors
Enterprise.is_distributor.managed_by(spree_current_user)
end
# Load managed producer enterprises of current user
def my_suppliers
Enterprise.is_primary_producer.managed_by(spree_current_user)
end
def suppliers_of_products_distributed_by(distributors)
supplier_ids = Spree::Product.in_distributors(distributors.select('enterprises.id')).
select('spree_products.supplier_id')
Enterprise.where(id: supplier_ids)
end
# Load order cycles the current user has access to
def my_order_cycles
OrderCycle.
active_or_complete.
visible_by(spree_current_user).
order('orders_close_at DESC')
end
def order_grouper_table
order_grouper = OpenFoodNetwork::OrderGrouper.new @report.rules, @report.columns, @report
order_grouper.table(@report.table_items)
end
def authorized_reports
all_reports = [
:orders_and_distributors,
:bulk_coop,
:payments,
:orders_and_fulfillment,
:customers,
:products_and_inventory,
:users_and_enterprises,
:enterprise_fee_summary,
:order_cycle_management,
:sales_tax,
:xero_invoices,
:packing
]
reports = all_reports.select { |action| can? action, Spree::Admin::ReportsController }
reports.map { |report| [report, describe_report(report)] }.to_h
end
def describe_report(report)
name = I18n.t(:name, scope: [:admin, :reports, report])
description = begin
I18n.t!(:description, scope: [:admin, :reports, report])
rescue I18n::MissingTranslationData
render_to_string(
partial: "#{report}_description",
layout: false,
locals: { report_types: report_types[report] }
).html_safe
end
{ name: name, url: url_for_report(report), description: description }
end
def url_for_report(report)
if report_in_order_management_engine?(report)
main_app.public_send("new_order_management_reports_#{report}_url".to_sym)
else
spree.public_send("#{report}_admin_reports_url".to_sym)
end
rescue NoMethodError
main_app.admin_reports_url(report_type: report)
end
# List of reports that have been moved to the Order Management engine
def report_in_order_management_engine?(report)
ORDER_MANAGEMENT_ENGINE_REPORTS.include?(report)
end
def timestamp
Time.zone.now.strftime("%Y%m%d")
end
def orders_and_fulfillment_default_filters
now = Time.zone.now
{ completed_at_gt: (now - 1.month).beginning_of_day,
completed_at_lt: (now + 1.day).beginning_of_day }
end
end
end
end

View File

@@ -9,7 +9,24 @@ module ReportsHelper
end
end
def report_subtypes(report)
Reporting::ReportLoader.new(report).report_subtypes
def report_payment_method_options(orders)
orders.map do |order|
payment_method = order.payments.last&.payment_method
next unless payment_method
[payment_method.name, payment_method.id]
end.compact.uniq
end
def report_shipping_method_options(orders)
orders.map do |o|
sm = o.shipping_method
[sm&.name, sm&.id]
end.uniq
end
def currency_symbol
Spree::Money.currency_symbol
end
end

View File

@@ -77,7 +77,7 @@ module Spree
klass = EnterpriseGroup if klass == :group
klass = VariantOverride if klass == :Inventory
klass = ProductImport::ProductImporter if klass == :import
klass = Spree::Admin::ReportsController if klass == :report
klass = ::Admin::ReportsController if klass == :report
klass
end

View File

@@ -1,33 +0,0 @@
# frozen_string_literal: true
require 'spree/money'
module Spree
module ReportsHelper
def report_payment_method_options(orders)
orders.map do |order|
payment_method = order.payments.last&.payment_method
next unless payment_method
[payment_method.name, payment_method.id]
end.compact.uniq
end
def report_shipping_method_options(orders)
orders.map do |o|
sm = o.shipping_method
[sm&.name, sm&.id]
end.uniq
end
def xero_report_types
[[I18n.t(:summary), 'summary'],
[I18n.t(:detailed), 'detailed']]
end
def currency_symbol
Spree::Money.currency_symbol
end
end
end

View File

@@ -236,12 +236,10 @@ module Spree
:validate_data, :reset_absent_products], ProductImport::ProductImporter
# Reports page
can [:admin, :index, :customers, :orders_and_distributors, :group_buys, :payments,
:orders_and_fulfillment, :products_and_inventory, :order_cycle_management, :packing],
Spree::Admin::ReportsController
can [:admin, :show, :packing], :report
add_bulk_coop_abilities
add_enterprise_fee_summary_abilities
can [:admin, :index, :show], ::Admin::ReportsController
can [:admin, :show, :customers, :orders_and_distributors, :group_buys, :payments,
:orders_and_fulfillment, :products_and_inventory, :order_cycle_management,
:packing, :enterprise_fee_summary, :bulk_coop], :report
end
def add_order_cycle_management_abilities(user)
@@ -317,11 +315,10 @@ module Spree
end
# Reports page
can [:admin, :index, :customers, :group_buys, :sales_tax, :payments,
can [:admin, :index, :show], ::Admin::ReportsController
can [:admin, :customers, :group_buys, :sales_tax, :payments,
:orders_and_distributors, :orders_and_fulfillment, :products_and_inventory,
:order_cycle_management, :xero_invoices], Spree::Admin::ReportsController
add_bulk_coop_abilities
add_enterprise_fee_summary_abilities
:order_cycle_management, :xero_invoices, :enterprise_fee_summary, :bulk_coop], :report
can [:create], Customer
can [:admin, :index, :update,
@@ -346,19 +343,5 @@ module Spree
user.enterprises.include?(enterprise_relationship.child)
end
end
def add_bulk_coop_abilities
# Reveal the report link in spree/admin/reports#index
can [:bulk_coop], Spree::Admin::ReportsController
# Allow direct access to the report resource
can [:admin, :new, :create], :bulk_coop
end
def add_enterprise_fee_summary_abilities
# Reveal the report link in spree/admin/reports#index
can [:enterprise_fee_summary], Spree::Admin::ReportsController
# Allow direct access to the report resource
can [:admin, :new, :create], :enterprise_fee_summary
end
end
end

View File

@@ -105,6 +105,10 @@ module Spree
render_address([city, zipcode, state&.name])
end
def address_and_city
[address1, address2, city].select(&:present?).join(' ')
end
private
def require_zipcode?

View File

@@ -38,8 +38,13 @@
# post.valid? # => false
# post.errors[:published_at] # => ["must be valid"]
class DateTimeStringValidator < ActiveModel::EachValidator
NOT_STRING_ERROR = I18n.t("validators.date_time_string_validator.not_string_error")
INVALID_FORMAT_ERROR = I18n.t("validators.date_time_string_validator.invalid_format_error")
def self.not_string_error
I18n.t("validators.date_time_string_validator.not_string_error")
end
def self.invalid_format_error
I18n.t("validators.date_time_string_validator.invalid_format_error")
end
def validate_each(record, attribute, value)
return if value.nil? || value == ""
@@ -53,13 +58,13 @@ class DateTimeStringValidator < ActiveModel::EachValidator
def validate_attribute_is_string(record, attribute, value)
return if value.is_a?(String)
record.errors.add(attribute, NOT_STRING_ERROR)
record.errors.add(attribute, DateTimeStringValidator.not_string_error)
end
def validate_attribute_is_datetime_string(record, attribute, value)
return unless value.is_a?(String)
datetime = Time.zone.parse(value)
record.errors.add(attribute, INVALID_FORMAT_ERROR) if datetime.blank?
record.errors.add(attribute, DateTimeStringValidator.invalid_format_error) if datetime.blank?
end
end

View File

@@ -35,8 +35,13 @@
# post.valid? # => false
# post.errors[:related_post_ids] # => ["must contain only valid integers"]
class IntegerArrayValidator < ActiveModel::EachValidator
NOT_ARRAY_ERROR = I18n.t("validators.integer_array_validator.not_array_error")
INVALID_ELEMENT_ERROR = I18n.t("validators.integer_array_validator.invalid_element_error")
def self.not_array_error
I18n.t("validators.integer_array_validator.not_array_error")
end
def self.invalid_element_error
I18n.t("validators.integer_array_validator.invalid_element_error")
end
def validate_each(record, attribute, value)
return if value.nil?
@@ -50,7 +55,7 @@ class IntegerArrayValidator < ActiveModel::EachValidator
def validate_attribute_is_array(record, attribute, value)
return if value.is_a?(Array)
record.errors.add(attribute, NOT_ARRAY_ERROR)
record.errors.add(attribute, IntegerArrayValidator.not_array_error)
end
def validate_attribute_elements_are_integer(record, attribute, array)
@@ -60,6 +65,6 @@ class IntegerArrayValidator < ActiveModel::EachValidator
Integer(element)
end
rescue ArgumentError
record.errors.add(attribute, INVALID_ELEMENT_ERROR)
record.errors.add(attribute, IntegerArrayValidator.invalid_element_error)
end
end

View File

@@ -1,9 +1,10 @@
-# Field used for ransack search. This date range is mostly used for Spree::Order
-# so default field is 'completed_at'
- field ||= 'completed_at'
.row.date-range-filter
= label_tag nil, t(:date_range)
%br
= label_tag nil, t(:start), :class => 'inline'
= text_field_tag "q[order_completed_at_gt]", params.dig(:q, :order_completed_at_gt), :class => 'datetimepicker datepicker-from'
%span.range-divider
%i.icon-arrow-right
= text_field_tag "q[order_completed_at_lt]", params.dig(:q, :order_completed_at_lt), :class => 'datetimepicker datepicker-to'
= label_tag nil, t(:end), :class => 'inline'
.alpha.two.columns= label_tag nil, t(:date_range)
.omega.fourteen.columns
= f.text_field "#{field}_gt", :class => 'datetimepicker datepicker-from', :placeholder => t(:start)
%span.range-divider
%i.icon-arrow-right
= f.text_field "#{field}_lt", :class => 'datetimepicker datepicker-to', :placeholder => t(:stop)

View File

@@ -1,8 +1,36 @@
.row.rendering-options
= label_tag :report_format, t(".generate_report")
%br
= select_tag :report_format, options_for_select({t('.on_screen') => '', t('.csv_spreadsheet') => 'csv', t('.excel_spreadsheet') => 'xlsx', t('.openoffice_spreadsheet') => 'ods'})
- if @report_subtypes.present? && @report_subtypes.count > 1
.row
.alpha.two.columns= label_tag nil, t(:report_type)
.omega.fourteen.columns
= select_tag(:report_subtype, options_for_select(@report_subtypes, @report_subtype))
- if @report.header_option? || @report.summary_row_option?
.row
.alpha.two.columns= label_tag nil, t(".display")
.omega.fourteen.columns
- if @report.header_option?
%span.inline-checkbox{ style: "margin-right: 1rem;" }
= check_box_tag :display_header_row, true, params[:display_header_row]
= label_tag :display_header_row, t(".header_row")
- if @report.summary_row_option?
%span.inline-checkbox
= check_box_tag :display_summary_row, true, params[:display_summary_row]
= label_tag :display_summary_row, t(".summary_row")
- if @report.available_headers.present?
.row
.alpha.two.columns= label_tag nil, t(:report_hide_columns)
.omega.fourteen.columns
= select_tag(:fields_to_hide, options_for_select(@report.available_headers, params[:fields_to_hide]),
class: "select2 fullwidth", multiple: true)
.row.rendering-options
.alpha.two.columns
= label_tag :report_format, t(".generate_report")
.omega.fourteen.columns
= select_tag :report_format, grouped_options_for_select({ |
t('.formatted_data') => { t('.on_screen') => '', "PDF" => 'pdf', t('.spreadsheet') => 'xlsx' }, |
t('.raw_data') => { "CSV" => 'csv', "JSON" => 'json'}, |
})
-#.inline-checkbox
-# = check_box_tag "options[exclude_summaries]", true, params[:options].andand[:exclude_summaries]
-# = label_tag t(".hide_summary_rows")

View File

@@ -0,0 +1,22 @@
-# Locals :
-# - data
-# - report
- data.each do |group_or_row|
- if group_or_row[:is_group].present?
/ Header Row
- if group_or_row[:header].present? && report.display_header_row?
%tr
%td.header-row{ colspan: report.table_headers.count, class: group_or_row[:header_class] }
= group_or_row[:header].html_safe
/ Rows
= render partial: 'admin/reports/row_group', locals: { report: report, data: group_or_row[:data] }
/ Summary Row
- if group_or_row[:summary_row].present? && report.display_summary_row?
%tr.summary-row{ class: group_or_row[:summary_row_class] }
- group_or_row[:summary_row].to_h.each do |key, value|
%td= value
- else
%tr
- group_or_row.row.to_h.each do |key, value|
%td= value

View File

@@ -1,20 +1,15 @@
- if params[:q].present?
%table.report__table{id: id}
- report ||= @report
.report__table-container
%table.report__table
%thead
%tr
- @report.table_headers.each do |heading|
- report.table_headers.each do |heading|
%th
= t("admin.reports.table.headings.#{heading}")
= heading
%tbody
- @report.table_rows.each do |row|
- if row
%tr
- row.each do |cell|
%td
= cell
- if @report.table_rows.empty?
- if report.grouped_data.present?
= render partial: 'admin/reports/row_group', locals: { report: report, data: report.grouped_data }
- else
%tr
%td{colspan: @report.table_headers.count}= t(:none)
- else
%p.report__message
= t(".select_and_search", option: msg_option.upcase)
%td{colspan: report.table_headers.count}= t(:none)

View File

@@ -0,0 +1,6 @@
= render 'admin/reports/date_range_form', f: f
.row
.alpha.two.columns= label_tag nil, t(:report_hubs)
.omega.fourteen.columns
= f.collection_select(:distributor_id_in, @data.distributors, :id, :name, {}, {class: "select2 fullwidth", multiple: true})

View File

@@ -0,0 +1,18 @@
.row
.alpha.two.columns= label_tag nil, t(:report_customers_distributor)
.omega.fourteen.columns
= select_tag(:distributor_id,
options_from_collection_for_select(@data.distributors, :id, :name, params[:distributor_id]),
{:include_blank => true, :class => "select2 fullwidth light"})
.row
.alpha.two.columns= label_tag nil, t(:report_customers_supplier)
.omega.fourteen.columns
= select_tag(:supplier_id,
options_from_collection_for_select(@data.suppliers, :id, :name, params[:supplier_id]),
{:include_blank => true, :class => "select2 fullwidth light"})
.row
.alpha.two.columns= label_tag nil, t(:report_customers_cycle)
.omega.fourteen.columns
= select_tag(:order_cycle_id,
options_for_select(report_order_cycle_options(@data.order_cycles), params[:order_cycle_id]),
{:include_blank => true, :class => "select2 fullwidth light"})

View File

@@ -0,0 +1,31 @@
= render 'admin/reports/date_range_form', f: f
.row
.alpha.two.columns= label_tag nil, t(:report_hubs)
.omega.fourteen.columns
= collection_select(:q, :distributor_ids, @report.permissions.allowed_distributors, :id, :name, {selected: params.dig(:q, :distributor_ids)}, {class: "select2 fullwidth", multiple: true})
.row
.alpha.two.columns= label_tag nil, t(:report_producers)
.omega.fourteen.columns
= collection_select(:q, :producer_ids, @report.permissions.allowed_producers, :id, :name, {selected: params.dig(:q, :producer_ids)}, {class: "select2 fullwidth", multiple: true})
.row
.alpha.two.columns= label_tag nil, t(:order_cycles)
.omega.fourteen.columns
= collection_select(:q, :order_cycle_ids, @report.permissions.allowed_order_cycles, :id, :name, {selected: params.dig(:q, :order_cycle_ids)}, {class: "select2 fullwidth", multiple: true})
.row
.alpha.two.columns= label_tag nil, t(:report_enterprise_fee)
.omega.fourteen.columns
= collection_select(:q, :enterprise_fee_ids, @report.permissions.allowed_enterprise_fees, :id, :name, {selected: params.dig(:q, :enterprise_fee_ids)}, {class: "select2 fullwidth", multiple: true})
.row
.alpha.two.columns= label_tag nil, t('spree.shipping_methods')
.omega.fourteen.columns
= collection_select(:q, :shipping_method_ids, @report.permissions.allowed_shipping_methods, :id, :name, {selected: params.dig(:q, :shipping_method_ids)}, {class: "select2 fullwidth", multiple: true})
.row
.alpha.two.columns= label_tag nil, t(:report_payment)
.omega.fourteen.columns
= collection_select(:q, :payment_method_ids, @report.permissions.allowed_payment_methods, :id, :name, {selected: params.dig(:q, :payment_method_ids)}, {class: "select2 fullwidth", multiple: true})

View File

@@ -0,0 +1,18 @@
= render 'admin/reports/date_range_form', f: f
.row
.alpha.two.columns= label_tag nil, t(:report_hubs)
.omega.fourteen.columns= f.collection_select(:distributor_id_in, @data.distributors, :id, :name, {}, {class: "select2 fullwidth", multiple: true})
.row
.alpha.two.columns= label_tag nil, t(:report_customers_cycle)
.omega.fourteen.columns
= f.select(:order_cycle_id_in, report_order_cycle_options(@data.order_cycles), {selected: params.dig(:q, :order_cycle_id_in)}, {class: "select2 fullwidth", multiple: true})
.row
.alpha.two.columns= label_tag nil, t(:report_payment)
.omega.fourteen.columns= select_tag(:payment_method_in, options_for_select(report_payment_method_options(@report.query_result), params[:payment_method_in]), {class: "select2 fullwidth", multiple: true})
.row
.alpha.two.columns= label_tag nil, t(:shipping_methods)
.omega.fourteen.columns= select_tag(:shipping_method_in, options_for_select(report_shipping_method_options(@report.query_result), params[:shipping_method_in]), {class: "select2 fullwidth", multiple: true})

View File

@@ -0,0 +1 @@
= render 'admin/reports/date_range_form', f: f

View File

@@ -0,0 +1,14 @@
= render 'admin/reports/date_range_form', f: f
.row
.alpha.two.columns= label_tag nil, t(:report_hubs)
.omega.fourteen.columns= f.collection_select(:distributor_id_in, @data.orders_distributors, :id, :name, {}, {class: "select2 fullwidth", multiple: true})
.row
.alpha.two.columns= label_tag nil, t(:report_producers)
.omega.fourteen.columns= select_tag(:supplier_id_in, options_from_collection_for_select(@data.orders_suppliers, :id, :name, params[:supplier_id_in]), {class: "select2 fullwidth", multiple: true})
.row
.alpha.two.columns= label_tag nil, t(:report_customers_cycle)
.omega.fourteen.columns
= f.select(:order_cycle_id_in, report_order_cycle_options(@data.order_cycles), {selected: params.dig(:q, :order_cycle_id_in)}, {class: "select2 fullwidth", multiple: true})

View File

@@ -0,0 +1,16 @@
= render partial: 'admin/reports/date_range_form', locals: { f: f, field: 'order_completed_at' }
.row
.alpha.two.columns= label_tag nil, t(:report_hubs)
.omega.fourteen.columns
= collection_select("q", "order_distributor_id_in", @data.orders_distributors, :id, :name, {selected: params.dig(:q, :order_distributor_id_in)}, {class: "select2 fullwidth", multiple: true})
.row
.alpha.two.columns= label_tag nil, t(:report_producers)
.omega.fourteen.columns
= select_tag("q[supplier_id_in]", options_from_collection_for_select(@data.orders_suppliers, :id, :name, params.dig(:q, :supplier_id_in)), {class: "select2 fullwidth", multiple: true})
.row
.alpha.two.columns= label_tag nil, t(:report_customers_cycle)
.omega.fourteen.columns
= select_tag("q[order_cycle_id_in]", options_for_select(report_order_cycle_options(@data.order_cycles), params.dig(:q, :order_cycle_id_in)), {class: "select2 fullwidth", multiple: true})

View File

@@ -0,0 +1,6 @@
= render 'admin/reports/date_range_form', f: f
.row
.alpha.two.columns= label_tag nil, t(:report_distributor)
.omega.fourteen.columns
= f.collection_select(:distributor_id_eq, @data.distributors, :id, :name, {:include_blank => t(:report_all)}, {:class => "select2 fullwidth light"})

View File

@@ -0,0 +1,20 @@
.row
.alpha.two.columns= label_tag nil, t(:report_distributor)
.omega.fourteen.columns
= select_tag(:distributor_id,
options_from_collection_for_select(@data.distributors, :id, :name, params[:distributor_id]),
{:include_blank => true, :class => "select2 fullwidth light"})
.row
.alpha.two.columns= label_tag nil, t(:report_customers_supplier)
.omega.fourteen.columns
= select_tag(:supplier_id,
options_from_collection_for_select(@data.suppliers, :id, :name, params[:supplier_id]),
{:include_blank => true, :class => "select2 fullwidth light"})
.row
.alpha.two.columns= label_tag nil, t(:report_order_cycle)
.omega.fourteen.columns
= select_tag(:order_cycle_id,
options_for_select(report_order_cycle_options(@data.order_cycles), params[:order_cycle_id]),
{:include_blank => true, :class => "select2 fullwidth light"})

View File

@@ -0,0 +1,6 @@
= render 'admin/reports/date_range_form', f: f
.row
.alpha.two.columns= label_tag nil, t(:report_distributor)
.omega.fourteen.columns
= f.collection_select(:distributor_id_eq, @data.distributors, :id, :name, {:include_blank => t(:all)}, {:class => "select2 fullwidth light"})

View File

@@ -0,0 +1,7 @@
.row
.alpha.two.columns= label_tag nil, t(:report_enterprises)
.omega.fourteen.columns= select_tag(:enterprise_id_in, options_from_collection_for_select(Enterprise.all, :id, :name, params[:enterprise_id_in]), {class: "select2 fullwidth", multiple: true})
.row
.alpha.two.columns= label_tag nil, t(:report_users)
.omega.fourteen.columns= select_tag(:user_id_in, options_from_collection_for_select(Spree::User.all, :id, :email, params[:user_id_in]), {class: "select2 fullwidth", multiple: true})

View File

@@ -0,0 +1,25 @@
= render 'admin/reports/date_range_form', f: f
.row
.two.columns.alpha= label_tag nil, t(:report_hubs)
.fourteen.columns.omega= f.collection_select(:distributor_id_eq, @data.distributors, :id, :name, {:include_blank => 'All'}, {:class => "select2 fullwidth light"})
.row
.two.columns.alpha= label_tag nil, t(:report_order_cycle)
.fourteen.columns.omega= f.select(:order_cycle_id_eq,
options_for_select(report_order_cycle_options(@data.order_cycles), params.dig(:q, :order_cycle_id_eq)),
{:include_blank => true}, {:class => "select2 fullwidth light"})
%fieldset.no-border-bottom.print-hidden{ style: "padding-bottom: 0" }
%legend{ align: 'center'}= t(:report_xero_configuration)
.row
.two.columns.alpha= label_tag :initial_invoice_number, t(:initial_invoice_number)
.fourteen.columns.omega= number_field_tag :initial_invoice_number, params[:initial_invoice_number]
.row
.two.columns.alpha= label_tag :invoice_date, t(:invoice_date)
.fourteen.columns.omega= text_field_tag :invoice_date, params[:invoice_date], class: 'datetimepicker'
.row
.two.columns.alpha= label_tag :due_date, t(:due_date)
.fourteen.columns.omega= text_field_tag :due_date, params[:due_date], class: 'datetimepicker'
.row
.two.columns.alpha= label_tag :account_code, t(:account_code)
.fourteen.columns.omega= text_field_tag :account_code, params[:account_code]

View File

@@ -0,0 +1,25 @@
- content_for :page_title do
= t(:listing_reports)
.columns.twelve
%table.index
%thead
%tr
%th= t(:name)
%th= t(:description)
%tbody
- @reports.each do |report_type, report_subtypes|
%tr
%td
- name = I18n.t(:name, scope: [:admin, :reports, report_type])
- url = main_app.admin_report_url(report_type: report_type)
= link_to name, url
%td
- begin
= I18n.t!(:description, scope: [:admin, :reports, report_type])
- rescue I18n::MissingTranslationData
%ul{style: "margin-left: 12pt"}
- report_subtypes.each do |report_subtype|
%li
- url = main_app.admin_report_url(report_type: report_type, report_subtype: report_subtype[1])
= link_to report_subtype[0], url

View File

@@ -1,31 +0,0 @@
= form_tag main_app.admin_reports_path, report_type: 'packing' do
= render partial: 'date_range_form'
.row
.alpha.two.columns= label_tag nil, t(:report_hubs)
.omega.fourteen.columns
= collection_select("q", "order_distributor_id_in", @distributors, :id, :name, {selected: params.dig(:q, :order_distributor_id_in)}, {class: "select2 fullwidth", multiple: true})
.row
.alpha.two.columns= label_tag nil, t(:report_producers)
.omega.fourteen.columns
= select_tag("q[supplier_id_in]", options_from_collection_for_select(@suppliers, :id, :name, params.dig(:q, :supplier_id_in)), {class: "select2 fullwidth", multiple: true})
.row
.alpha.two.columns= label_tag nil, t(:report_customers_cycle)
.omega.fourteen.columns
= select_tag("q[order_cycle_id_in]", options_for_select(report_order_cycle_options(@order_cycles), params.dig(:q, :order_cycle_id_in)), {class: "select2 fullwidth", multiple: true})
.row
.alpha.two.columns= label_tag nil, t(:report_type)
.omega.fourteen.columns
= select_tag(:report_subtype, options_for_select(@report_subtypes, @report_subtype))
= render partial: "rendering_options"
.row
= button t(:search)
= render partial: "spree/admin/reports/customer_names_message"
= render "table", id: "listing_orders", msg_option: t(:search)

View File

@@ -0,0 +1,22 @@
= form_for @report.search, :url => url_for(only_path: false) do |f|
%fieldset.no-border-bottom.print-hidden
%legend{ align: 'center'}= t(:report_filters)
= render partial: "admin/reports/filters/#{@report_type}", locals: { f: f }
%fieldset.print-hidden
%legend{ align: 'center'}= t(:report_render_options)
= render partial: "rendering_options"
.actions.filter-actions
= button t(:go), "report__submit-btn"
.report__header.print-hidden
- if @report.message.present?
%p.report__message= @report.message
- if request.post?
%button.btn-print.icon-print{ onclick: "window.print()"}= t(:report_print)
/ We don't want to render data unless search params are supplied.
/ Compiling data can take a long time.
- if request.post?
= render "table"

View File

@@ -0,0 +1,56 @@
!!!
%html
%head
%meta{charset: 'utf-8'}
-# Using wicked_pdf_stylesheet_pack_tag with a new pdf pack was not working when using
-# WickedPdf.new.pdf_from_string cause the css file reference was not absolute
-# So I ended up putting inline css here, so it's included for sure in the PDF
:css
body {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
font-family: system-ui,-apple-system,"Helvetica Neue",Arial,sans-serif;
color: #212529;
}
table {
width: 100%;
border-collapse: collapse;
}
th {
text-align: left;
}
th, td {
padding: 7px 5px;
vertical-align: middle;
text-overflow: ellipsis;
padding-top: 12px;
}
tr {
border-bottom: 1px solid #e2e2e2;
}
thead {
background-color: #f6f6f6;
border-bottom: 1px solid grey;
}
.h1, .h2, .h3 {
font-weight: bold;
padding-top: 15px;
}
.h1 {
font-size: 1.6rem;
padding-top: 20px;
}
.h2 {
font-size: 1.3rem;
}
.h3 {
font-size: 1.15rem;
}
.text-bold {
font-weight: bold;
}
%body
= yield

View File

@@ -1,2 +0,0 @@
%p.customer-names-tip
= t(".customer_names_tip")

View File

@@ -1,4 +0,0 @@
%ul{style: "margin-left: 12pt"}
- report_types.each do |report_type|
%li
= link_to report_type[0], "#{customers_admin_reports_url}?report_type=#{report_type[1]}"

View File

@@ -1,9 +0,0 @@
.row.date-range-filter
= label_tag nil, t(:date_range)
%br
= label_tag nil, t(:start), :class => 'inline'
= f.text_field :completed_at_gt, :class => 'datetimepicker datepicker-from'
%span.range-divider
%i.icon-arrow-right
= f.text_field :completed_at_lt, :class => 'datetimepicker datepicker-to'
= label_tag nil, t(:end), :class => 'inline'

View File

@@ -1,2 +0,0 @@
- order_number = value
= link_to order_number, edit_admin_order_url(order_number), class: 'edit-order'

View File

@@ -1,4 +0,0 @@
%ul{style: "margin-left: 12pt"}
- report_types.each do |report_type|
%li
= link_to report_type[0], "#{order_cycle_management_admin_reports_url}?report_type=#{report_type[1]}"

View File

@@ -1,5 +0,0 @@
%ul{style: "margin-left: 12pt"}
- report_types.each do |report_type|
%li
= link_to report_type[0], "#{orders_and_fulfillment_admin_reports_url}?report_type=#{report_type[1]}"

View File

@@ -1,5 +0,0 @@
%ul{style: "margin-left: 12pt"}
- report_subtypes("packing").each do |report_subtype|
%li
= link_to t("admin.reports.packing.#{report_subtype}_report"),
main_app.admin_reports_url(report_type: 'packing', report_subtype: report_subtype)

View File

@@ -1,4 +0,0 @@
%ul{style: "margin-left: 12pt"}
- report_types.each do |report_type|
%li
= link_to report_type[0], "#{products_and_inventory_admin_reports_url}?report_type=#{report_type[1]}"

View File

@@ -1,4 +0,0 @@
%ul{style: "margin-left: 12pt"}
- report_types.each do |report_type|
%li
= link_to report_type[0], "#{sales_tax_admin_reports_url}?report_type=#{report_type[1]}"

View File

@@ -1,23 +0,0 @@
- column_partials ||= {}
- if render_content?
%table.report__table{id: id}
%thead
%tr
- @header.each do |heading|
%th= heading
%tbody
- @table.each do |row|
%tr
- row.each_with_index do |cell_value, column_index|
%td
- partial = column_partials[column_index]
- if partial
= render partial, value: cell_value
- else
= cell_value
- if @table.empty?
%tr
%td{colspan: @header.count}= t(:none)
- else
%p.report__message
= t(".select_and_search", option: msg_option.upcase)

View File

@@ -1,32 +0,0 @@
= form_tag spree.customers_admin_reports_url do |f|
%br
.row
.four.columns.alpha
= label_tag nil, t(:report_customers_distributor)
= select_tag(:distributor_id,
options_from_collection_for_select(@distributors, :id, :name, params[:distributor_id]),
{:include_blank => true, :class => "select2 fullwidth"})
.four.columns
= label_tag nil, t(:report_customers_supplier)
= select_tag(:supplier_id,
options_from_collection_for_select(@suppliers, :id, :name, params[:supplier_id]),
{:include_blank => true, :class => "select2 fullwidth"})
.six.columns
= label_tag nil, t(:report_customers_cycle)
= select_tag(:order_cycle_id,
options_for_select(report_order_cycle_options(@order_cycles), params[:order_cycle_id]),
{:include_blank => true, :class => "select2 fullwidth"})
= label_tag nil, t(:report_customers_type)
= select_tag(:report_type, options_for_select(@report_types, @report_type))
%br
%br
= check_box_tag :csv
= label_tag :csv, t(:report_customers_csv)
%br
= button t(:go)
= render "table", id: "listing_customers", msg_option: t(:go)

View File

@@ -1,14 +0,0 @@
- content_for :page_title do
= t(:listing_reports)
.columns.twelve
%table.index
%thead
%tr
%th= t(:name)
%th= t(:description)
%tbody
- @reports.each do |key, value|
%tr
%td= link_to value[:name], value[:url]
%td= value[:description]

View File

@@ -1,32 +0,0 @@
= form_for @report.search, :url => spree.order_cycle_management_admin_reports_path do |f|
= render 'date_range_form', f: f
.row
.alpha.two.columns= label_tag nil, t(:report_hubs)
.omega.fourteen.columns= f.collection_select(:distributor_id_in, @distributors, :id, :name, {}, {class: "select2 fullwidth", multiple: true})
.row
.alpha.two.columns= label_tag nil, t(:report_customers_cycle)
.omega.fourteen.columns
= f.select(:order_cycle_id_in, report_order_cycle_options(@order_cycles), {selected: params.dig(:q, :order_cycle_id_in)}, {class: "select2 fullwidth", multiple: true})
.row
.alpha.two.columns= label_tag nil, t(:report_payment)
.omega.fourteen.columns= select_tag(:payment_method_in, options_for_select(report_payment_method_options(@report.orders), params[:payment_method_in]), {class: "select2 fullwidth", multiple: true})
.row
.alpha.two.columns= label_tag nil, "#{t(:shipping_methods)}: "
.omega.fourteen.columns= select_tag(:shipping_method_in, options_for_select(report_shipping_method_options(@report.orders), params[:shipping_method_in]), {class: "select2 fullwidth", multiple: true})
.row
.alpha.two.columns= label_tag nil, "#{t(:report_type)}: "
.omega.fourteen.columns= select_tag(:report_type, options_for_select(@report_types, @report_type))
.row
= check_box_tag :csv
= label_tag :csv, t(:report_customers_csv)
.row
= button t(:search)
= render "table", id: "listing_ocm_orders", msg_option: t(:search)

View File

@@ -1,11 +0,0 @@
= form_for @search, :url => spree.orders_and_distributors_admin_reports_path do |f|
= render 'date_range_form', f: f
= check_box_tag :csv
= label_tag :csv, t(:report_customers_csv)
%br
= button t(:search)
= render partial: "customer_names_message"
= render "table", id: "listing_orders", msg_option: t(:search)

View File

@@ -1,30 +0,0 @@
= form_for @report.search, :url => spree.orders_and_fulfillment_admin_reports_path do |f|
= render 'date_range_form', f: f
.row
.alpha.two.columns= label_tag nil, t(:report_hubs)
.omega.fourteen.columns= f.collection_select(:distributor_id_in, @distributors, :id, :name, {}, {class: "select2 fullwidth", multiple: true})
.row
.alpha.two.columns= label_tag nil, t(:report_producers)
.omega.fourteen.columns= select_tag(:supplier_id_in, options_from_collection_for_select(@suppliers, :id, :name, params[:supplier_id_in]), {class: "select2 fullwidth", multiple: true})
.row
.alpha.two.columns= label_tag nil, t(:report_customers_cycle)
.omega.fourteen.columns
= f.select(:order_cycle_id_in, report_order_cycle_options(@order_cycles), {selected: params.dig(:q, :order_cycle_id_in)}, {class: "select2 fullwidth", multiple: true})
.row
.alpha.two.columns= label_tag nil, t(:report_type)
.omega.fourteen.columns= select_tag(:report_type, options_for_select(@report_types, @report_type))
.row
= check_box_tag :csv
= label_tag :csv, t(:report_customers_csv)
.row
= button t(:search)
= render partial: "customer_names_message"
= render "table", id: "listing_orders", msg_option: t(:search)

View File

@@ -1,19 +0,0 @@
= form_for @report.search, :url => spree.payments_admin_reports_path do |f|
= render 'date_range_form', f: f
.row
.four.columns.alpha
= label_tag nil, t(:report_distributor)
= f.collection_select(:distributor_id_eq, @distributors, :id, :name, {:include_blank => t(:report_all)}, {:class => "select2 fullwidth"})
= label_tag nil, t(:report_customers_type)
%br
= select_tag(:report_type, options_for_select([[t(:report_payment_by),:payments_by_payment_type],[t(:report_itemised_payment),:itemised_payment_totals],[t(:report_payment_totals),:payment_totals]], @report_type))
%br
%br
= check_box_tag :csv
= label_tag :csv, t(:report_customers_csv)
%br
%br
= button t(:search)
= render "table", id: "listing_orders", msg_option: t(:search)

View File

@@ -1,34 +0,0 @@
= form_tag spree.products_and_inventory_admin_reports_url do |f|
%br
.row
.four.columns.alpha
= label_tag nil, t(:report_distributor)
= select_tag(:distributor_id,
options_from_collection_for_select(@distributors, :id, :name, params[:distributor_id]),
{:include_blank => true, :class => "select2 fullwidth"})
.four.columns
= label_tag nil, t(:report_customers_supplier)
= select_tag(:supplier_id,
options_from_collection_for_select(@suppliers, :id, :name, params[:supplier_id]),
{:include_blank => true, :class => "select2 fullwidth"})
.six.columns
= label_tag nil, t(:report_order_cycle)
= select_tag(:order_cycle_id,
options_for_select(report_order_cycle_options(@order_cycles), params[:order_cycle_id]),
{:include_blank => true, :class => "select2 fullwidth"})
= label_tag nil, t(:report_type)
%br
= select_tag(:report_type, options_for_select(@report_types, params[:report_type]))
%br
%br
= check_box_tag :csv
= label_tag :csv, t(:report_customers_csv)
%br
= button t(:go)
= render "table", id: "listing_products", msg_option: t(:go)

View File

@@ -1,19 +0,0 @@
= form_for @report.search, :url => spree.sales_tax_admin_reports_path do |f|
= render 'date_range_form', f: f
.row
.four.columns.alpha
= label_tag nil, t(:report_distributor)
= f.collection_select(:distributor_id_eq, @distributors, :id, :name, {:include_blank => t(:all)}, {:class => "select2 fullwidth"})
= label_tag nil, t(:report_customers_type)
%br
= select_tag(:report_type, options_for_select([[t(:report_tax_types),:tax_types],[t(:report_tax_rates),:tax_rates]], @report_type))
%br
%br
= check_box_tag :csv
= label_tag :csv, t(:report_customers_csv)
%br
%br
= button t(:search)
= render "table", id: "listing_orders", column_partials: {0 => "link_order"}, msg_option: t(:search)

View File

@@ -1,21 +0,0 @@
= form_tag spree.users_and_enterprises_admin_reports_url do |f|
.row
.alpha.two.columns= label_tag nil, t(:report_enterprises)
.omega.fourteen.columns= select_tag(:enterprise_id_in, options_from_collection_for_select(Enterprise.all, :id, :name, params[:enterprise_id_in]&.split(",")), {class: "select2 fullwidth", multiple: true})
.row
.alpha.two.columns= label_tag nil, t(:report_users)
.omega.fourteen.columns= select_tag(:user_id_in, options_from_collection_for_select(Spree::User.all, :id, :email, params[:user_id_in]&.split(",")), {class: "select2 fullwidth", multiple: true})
-# Might need this later if we add different kinds of reports
-# .row
-# .alpha.two.columns= label_tag nil, "Report Type: "
-# .omega.fourteen.columns= select_tag(:report_type, options_for_select(@report_types, params[:report_type]))
.row
= check_box_tag :csv
= label_tag :csv, t(:report_customers_csv)
.row
= button t(:search)
= render "table", id: "users_and_enterprises", msg_option: t(:search)

View File

@@ -1,35 +0,0 @@
= form_for @report.search, url: spree.xero_invoices_admin_reports_path do |f|
= render 'date_range_form', f: f
.row
.four.columns.alpha= label_tag :report_type, t(:report_type)
.four.columns.omega= select_tag :report_type, options_for_select(xero_report_types, params[:report_type]), {include_blank: false, class: "select2 fullwidth"}
.row
.four.columns.alpha= label_tag nil, t(:report_hubs)
.four.columns.omega= f.collection_select(:distributor_id_eq, @distributors, :id, :name, {:include_blank => 'All'}, {:class => "select2 fullwidth"})
.row
.four.columns.alpha= label_tag nil, t(:report_order_cycle)
.four.columns.omega= f.select(:order_cycle_id_eq,
options_for_select(report_order_cycle_options(@order_cycles), params.dig(:q, :order_cycle_id_eq)),
{:include_blank => true}, {:class => "select2 fullwidth"})
.row
.four.columns.alpha= label_tag :initial_invoice_number, t(:initial_invoice_number)
.twelve.columns.omega= text_field_tag :initial_invoice_number, params[:initial_invoice_number]
.row
.four.columns.alpha= label_tag :invoice_date, t(:invoice_date)
.twelve.columns.omega= text_field_tag :invoice_date, params[:invoice_date], class: 'datetimepicker'
.row
.four.columns.alpha= label_tag :due_date, t(:due_date)
.twelve.columns.omega= text_field_tag :due_date, params[:due_date], class: 'datetimepicker'
.row
.four.columns.alpha= label_tag :account_code, t(:account_code)
.twelve.columns.omega= text_field_tag :account_code, params[:account_code]
.row
.four.columns.alpha= label_tag :csv, t(:report_customers_csv)
.twelve.columns.omega= check_box_tag :csv
.row
.four.columns.alpha= button t(:search)
= render "table", id: "listing_invoices", msg_option: t(:search)

View File

@@ -11,7 +11,7 @@
%link{:href => "https://fonts.googleapis.com/css?family=Open+Sans:400italic,600italic,400,600&subset=latin,cyrillic,greek,vietnamese", :rel => "stylesheet", :type => "text/css"}
= stylesheet_pack_tag 'admin-styles'
= stylesheet_pack_tag 'admin-styles', media: "screen, print"
= render "layouts/bugsnag_js"
= javascript_include_tag 'admin/all'

View File

@@ -2,7 +2,7 @@
= tab :products, :properties, :inventory, :product_import, :images, :variants, :product_properties, :group_buy_options, :seo, url: admin_products_path, icon: 'icon-th-large'
= tab :order_cycles, url: main_app.admin_order_cycles_path, icon: 'icon-refresh'
= tab :orders, :subscriptions, :customer_details, :adjustments, :payments, :return_authorizations, url: admin_orders_path('q[s]' => 'completed_at desc'), icon: 'icon-shopping-cart'
= tab :reports, icon: 'icon-file'
= tab :reports, url: main_app.admin_reports_path, icon: 'icon-file'
= tab :general_settings, :mail_methods, :tax_categories, :tax_rates, :tax_settings, :zones, :countries, :states, :payment_methods, :taxonomies, :shipping_methods, :shipping_categories, :enterprise_fees, :contents, :invoice_settings, :matomo_settings, :stripe_connect_settings, label: 'configuration', icon: 'icon-wrench', url: edit_admin_general_settings_path
= tab :enterprises, :enterprise_relationships, url: main_app.admin_enterprises_path
= tab :customers, url: main_app.admin_customers_path

View File

@@ -3,6 +3,7 @@
.date-range-filter {
.range-divider {
padding: 0;
margin-left: 5px;
}
input.datepicker {
width: 96px !important;

View File

@@ -6,6 +6,10 @@
text-align: right;
}
.text-bold{
font-weight: bold;
}
.underline {
text-decoration: underline;
}

View File

@@ -1,29 +1,62 @@
.report__table {
margin-top: 2em;
table.report__table {
margin-top: 1em;
@media print {
margin: 0;
td, th {
border-left: none;
border-right: none;
}
}
thead th {
text-align: left;
padding: 10px 6px;
}
.header-row {
&.h1, &.h2, &.h3 {
font-weight: bold;
margin-top: 8px;
}
&.h1 {
font-size: 2em;
}
&.h2 {
font-size: 1.5em;
}
&.h3 {
font-size: 1.25em;
}
&.h4 {
font-size: 1;
}
&.with-background {
background-color: #eef5fc;
}
}
}
.report__message {
.report__header {
display: flex;
margin-top: 2em;
border: 1px solid $pale-blue;
border-radius: .5em;
padding: .5em;
text-align: center;
justify-content: flex-end;
.btn-print {
margin-left: 1em;
}
.report__message {
border: 1px solid $pale-blue;
border-radius: .5em;
padding: .5em;
text-align: center;
flex-grow: 1;
}
}
.customer-names-tip {
margin-top: 1em;
}
.rendering-options {
select {
display: block;
float: left;
}
.inline-checkbox {
line-height: 2.5em;
margin-left: 1em;
display: block;
float: left;
}
.report__submit-btn {
margin: 0 !important;
width: 120px;
}

View File

@@ -242,3 +242,17 @@ select {
@extend input, [type="text"];
background-color: white;
}
.inline-checkbox {
display: inline-flex;
align-items: center;
margin-top: 3px;
input, label {
cursor: pointer;
}
label {
margin: 0;
padding-left: .4rem;
}
}

View File

@@ -68,6 +68,11 @@
.hidden {
display: none;
}
@media print {
.print-hidden {
display: none !important;
}
}
// Header
//---------------------------------------------------
@@ -103,3 +108,9 @@
border-top: 1px solid $color-border;
padding: 10px 0;
}
@media print {
header, nav {
display:none;
}
}

View File

@@ -160,16 +160,6 @@ en:
withdrawal_count_limit_exceeded: "The customer has exceeded the balance or credit limit available on their card."
activemodel:
attributes:
order_management/reports/enterprise_fee_summary/parameters:
start_at: "Start"
end_at: "End"
distributor_ids: "Hubs"
producer_ids: "Producers"
order_cycle_ids: "Order Cycles"
enterprise_fee_ids: "Fees Names"
shipping_method_ids: "Shipping Methods"
payment_method_ids: "Payment Methods"
errors:
messages:
inclusion: "is not included in the list"
@@ -1265,6 +1255,8 @@ en:
unitsize: UNITSIZE
total: TOTAL
total_items: TOTAL ITEMS
total_by_customer: Total By Customer
total_by_supplier: Total By Supplier
supplier_totals: Order Cycle Supplier Totals
supplier_totals_by_distributor: Order Cycle Supplier Totals by Distributor
totals_by_supplier: Order Cycle Distributor Totals by Supplier
@@ -1280,6 +1272,7 @@ en:
tax_rates: Tax Rates
pack_by_customer: Pack By Customer
pack_by_supplier: Pack By Supplier
pack_by_product: Pack By Product
orders_and_distributors:
name: Orders And Distributors
description: Orders with distributor details
@@ -1295,7 +1288,6 @@ en:
name: Customers
products_and_inventory:
name: Products & Inventory
description:
users_and_enterprises:
name: Users & Enterprises
description: Enterprise Ownership & Status
@@ -1329,17 +1321,18 @@ en:
quantity: "Quantity"
is_temperature_controlled: "TempControlled?"
temp_controlled: "TempControlled?"
price: "Price"
rendering_options:
generate_report: "Generate report:"
generate_report: "Generate report"
on_screen: "On screen"
csv_spreadsheet: "CSV Spreadsheet"
excel_spreadsheet: "Excel Spreadsheet"
openoffice_spreadsheet: "OpenOffice Spreadsheet"
hide_summary_rows: "Hide summary Rows"
spreadsheet: "Spreadsheet (Excel, OpenOffice..)"
display: Display
summary_row: Summary Row
header_row: Header Row
raw_data: Raw Data
formatted_data: Formatted Data
packing:
name: "Packing Reports"
customer_report: "Pack By Customer"
supplier_report: "Pack By Supplier"
subscriptions:
index:
title: "Subscriptions"
@@ -2622,20 +2615,25 @@ See the %{link} to find out more about %{sitename}'s features and to start using
report_customers_cycle: "Order Cycle"
report_customers_type: "Report Type"
report_customers_csv: "Download as csv"
report_producers: "Producers: "
report_type: "Report Type: "
report_hubs: "Hubs: "
report_payment: "Payment Methods: "
report_distributor: "Distributor: "
report_producers: "Producers"
report_type: "Report Type"
report_hubs: "Hubs"
report_payment: "Payment Methods"
report_distributor: "Distributor"
report_payment_by: 'Payments By Type'
report_itemised_payment: 'Itemised Payment Totals'
report_payment_totals: 'Payment Totals'
report_all: 'all'
report_order_cycle: "Order Cycle: "
report_enterprises: "Enterprises: "
report_users: "Users: "
report_order_cycle: "Order Cycle"
report_hide_columns: Columns to Hide
report_enterprises: "Enterprises"
report_enterprise_fee: "Fees Names"
report_users: "Users"
report_tax_rates: Tax rates
report_tax_types: Tax types
report_filters: Report Filters
report_print: Print Report
report_render_options: Rendering Options
report_header_order_cycle: Order Cycle
report_header_user: User
report_header_email: Email
@@ -2771,10 +2769,11 @@ See the %{link} to find out more about %{sitename}'s features and to start using
report_header_transaction_fee: Transaction Fee (no tax)
report_header_total_untaxable_admin: Total untaxable admin adjustments (no tax)
report_header_total_taxable_admin: Total taxable admin adjustments (tax inclusive)
initial_invoice_number: "Initial invoice number:"
invoice_date: "Invoice date:"
due_date: "Due date:"
account_code: "Account code:"
report_xero_configuration: Xero Configuration
initial_invoice_number: "Initial invoice number"
invoice_date: "Invoice date"
due_date: "Due date"
account_code: "Account code"
equals: "Equals"
contains: "contains"
discount: "Discount"
@@ -3310,9 +3309,6 @@ See the %{link} to find out more about %{sitename}'s features and to start using
bulk_coop_customer_payments: "Bulk Co-op Customer Payments"
bulk_coop_packing_sheets: "Bulk Co-op Packing Sheets"
bulk_coop_supplier_report: "Bulk Co-op Supplier Report"
date_range: "Date range"
generate_report: "Generate Report"
report_format_csv: "Report format CSV"
enterprise_fee_summaries:
filters:
date_range: "Date Range"
@@ -3954,11 +3950,6 @@ See the %{link} to find out more about %{sitename}'s features and to start using
reports:
table:
select_and_search: "Select filters and click on %{option} to access your data."
bulk_coop:
bulk_coop_supplier_report: 'Bulk Co-op - Totals by Supplier'
bulk_coop_allocation: 'Bulk Co-op - Allocation'
bulk_coop_packing_sheets: 'Bulk Co-op - Packing Sheets'
bulk_coop_customer_payments: 'Bulk Co-op - Customer Payments'
customer_names_message:
customer_names_tip: "If customer names are hidden for orders you have supplied, you can contact the distributor and ask if they can update their shop preferences to allow their suppliers to view customer names."
users:

View File

@@ -115,6 +115,7 @@ Openfoodnetwork::Application.routes.draw do
put :resume, on: :member, format: :json
end
match '/reports/:report_type(/:report_subtype)', to: 'reports#show', via: [:get, :post], as: :reports
get '/reports', to: 'reports#index', as: :reports
match '/reports/:report_type(/:report_subtype)', to: 'reports#show', via: [:get, :post], as: :report
end
end

View File

@@ -31,18 +31,6 @@ Spree::Core::Engine.routes.draw do
resource :account, :controller => 'users'
match '/admin/reports/orders_and_distributors' => 'admin/reports#orders_and_distributors', :as => "orders_and_distributors_admin_reports", :via => [:get, :post]
match '/admin/reports/order_cycle_management' => 'admin/reports#order_cycle_management', :as => "order_cycle_management_admin_reports", :via => [:get, :post]
match '/admin/reports/group_buys' => 'admin/reports#group_buys', :as => "group_buys_admin_reports", :via => [:get, :post]
match '/admin/reports/bulk_coop' => 'admin/reports#bulk_coop', :as => "bulk_coop_admin_reports", :via => [:get, :post]
match '/admin/reports/payments' => 'admin/reports#payments', :as => "payments_admin_reports", :via => [:get, :post]
match '/admin/reports/orders_and_fulfillment' => 'admin/reports#orders_and_fulfillment', :as => "orders_and_fulfillment_admin_reports", :via => [:get, :post]
match '/admin/reports/users_and_enterprises' => 'admin/reports#users_and_enterprises', :as => "users_and_enterprises_admin_reports", :via => [:get, :post]
match '/admin/reports/sales_tax' => 'admin/reports#sales_tax', :as => "sales_tax_admin_reports", :via => [:get, :post]
match '/admin/reports/products_and_inventory' => 'admin/reports#products_and_inventory', :as => "products_and_inventory_admin_reports", :via => [:get, :post]
match '/admin/reports/customers' => 'admin/reports#customers', :as => "customers_admin_reports", :via => [:get, :post]
match '/admin/reports/xero_invoices' => 'admin/reports#xero_invoices', :as => "xero_invoices_admin_reports", :via => [:get, :post]
match '/admin/orders/bulk_management' => 'admin/orders#bulk_management', :as => "admin_bulk_order_management", via: :get
match '/admin/payment_methods/show_provider_preferences' => 'admin/payment_methods#show_provider_preferences', :via => :get
put 'credit_cards/new_from_token', to: 'credit_cards#new_from_token'
@@ -126,8 +114,6 @@ Spree::Core::Engine.routes.draw do
end
end
resources :reports, only: :index
resources :users do
member do
put :generate_api_key

View File

@@ -1,78 +0,0 @@
# frozen_string_literal: true
module OrderManagement
module Reports
class BulkCoopController < Spree::Admin::BaseController
before_action :load_report_parameters
before_action :load_permissions
def new; end
def create
return respond_to_invalid_parameters unless @report_parameters.valid?
@report_parameters.authorize!(@permissions)
@report = report_klass::ReportService.new(@permissions, legacy_format_report_params,
spree_current_user)
renderer.render(self)
rescue ::Reports::Authorizer::ParameterNotAllowedError => e
flash[:error] = e.message
render_report_form
end
private
def respond_to_invalid_parameters
flash[:error] = I18n.t("invalid_filter_parameters", scope: i18n_scope)
render_report_form
end
def i18n_scope
"order_management.reports.enterprise_fee_summary"
end
def render_report_form
render action: :new
end
def report_klass
OrderManagement::Reports::BulkCoop
end
def legacy_format_report_params
{
q: {
completed_at_gt: params[:report][:start_at],
completed_at_lt: params[:report][:end_at],
distributor_id_in: params[:report][:distributor_ids],
},
report_type: params[:report][:report_type]
}
end
def load_report_parameters
@report_parameters = report_klass::Parameters.new(params[:report] || {})
end
def load_permissions
@permissions = report_klass::Permissions.new(spree_current_user)
end
def report_renderer_klass
case params[:report_format]
when "csv"
report_klass::Renderers::CsvRenderer
when nil, "", "html"
report_klass::Renderers::HtmlRenderer
else
raise Reports::UnsupportedReportFormatException
end
end
def renderer
@renderer ||= report_renderer_klass.new(@report)
end
end
end
end

View File

@@ -1,66 +0,0 @@
# frozen_string_literal: true
module OrderManagement
module Reports
class EnterpriseFeeSummariesController < Spree::Admin::BaseController
before_action :load_report_parameters
before_action :load_permissions
def new; end
def create
return respond_to_invalid_parameters unless @report_parameters.valid?
@report_parameters.authorize!(@permissions)
@report = report_klass::ReportService.new(@permissions, @report_parameters)
renderer.render(self)
rescue ::Reports::Authorizer::ParameterNotAllowedError => e
flash[:error] = e.message
render_report_form
end
private
def respond_to_invalid_parameters
flash[:error] = I18n.t("invalid_filter_parameters", scope: i18n_scope)
render_report_form
end
def i18n_scope
"order_management.reports.enterprise_fee_summary"
end
def render_report_form
render action: :new
end
def report_klass
OrderManagement::Reports::EnterpriseFeeSummary
end
def load_report_parameters
@report_parameters = report_klass::Parameters.new(params[:report] || {})
end
def load_permissions
@permissions = report_klass::Permissions.new(spree_current_user)
end
def report_renderer_klass
case params[:report_format]
when "csv"
report_klass::Renderers::CsvRenderer
when nil, "", "html"
report_klass::Renderers::HtmlRenderer
else
raise Reports::UnsupportedReportFormatException
end
end
def renderer
@renderer ||= report_renderer_klass.new(@report)
end
end
end
end

View File

@@ -1,13 +0,0 @@
# frozen_string_literal: true
module OrderManagement
module Reports
module BulkCoop
class Authorizer < ::Reports::Authorizer
def authorize!
require_ids_allowed(parameters.distributor_ids, permissions.allowed_distributors)
end
end
end
end
end

View File

@@ -1,67 +0,0 @@
# frozen_string_literal: true
module OrderManagement
module Reports
module BulkCoop
class BulkCoopAllocationReport
def header
[
I18n.t(:report_header_customer),
I18n.t(:report_header_product),
I18n.t(:report_header_bulk_unit_size),
I18n.t(:report_header_variant),
I18n.t(:report_header_variant_value),
I18n.t(:report_header_variant_unit),
I18n.t(:report_header_weight),
I18n.t(:report_header_sum_total),
I18n.t(:report_header_total_available),
I18n.t(:report_header_unallocated),
I18n.t(:report_header_max_quantity_excess),
]
end
def rules
[
{
group_by: proc { |line_item| line_item.product },
sort_by: proc { |product| product.name },
summary_columns: [
:total_label,
:variant_product_name,
:variant_product_group_buy_unit_size_f,
:empty_cell,
:empty_cell,
:empty_cell,
:empty_cell,
:total_amount,
:total_available,
:remainder,
:max_quantity_excess
]
},
{
group_by: proc { |line_item| line_item.order },
sort_by: proc { |order| order.to_s }
}
]
end
def columns
[
:order_billing_address_name,
:product_name,
:product_group_buy_unit_size,
:full_name,
:option_value_value,
:option_value_unit,
:weight_from_unit_value,
:total_amount,
:empty_cell,
:empty_cell,
:empty_cell
]
end
end
end
end
end

View File

@@ -1,323 +0,0 @@
# frozen_string_literal: true
require "open_food_network/reports/line_items"
module OrderManagement
module Reports
module BulkCoop
class BulkCoopReport
REPORT_TYPES = [
:bulk_coop_supplier_report,
:bulk_coop_allocation,
:bulk_coop_packing_sheets,
:bulk_coop_customer_payments
].freeze
attr_reader :params
def initialize(user, params = {}, render_table = false)
@params = params
@user = user
@render_table = render_table
@supplier_report = BulkCoopSupplierReport.new
@allocation_report = BulkCoopAllocationReport.new
@filter_canceled = false
end
def header
case params[:report_type]
when "bulk_coop_supplier_report"
@supplier_report.header
when "bulk_coop_allocation"
@allocation_report.header
when "bulk_coop_packing_sheets"
[I18n.t(:report_header_customer),
I18n.t(:report_header_product),
I18n.t(:report_header_variant),
I18n.t(:report_header_sum_total)]
when "bulk_coop_customer_payments"
[I18n.t(:report_header_customer),
I18n.t(:report_header_date_of_order),
I18n.t(:report_header_total_cost),
I18n.t(:report_header_amount_owing),
I18n.t(:report_header_amount_paid)]
else
[I18n.t(:report_header_supplier),
I18n.t(:report_header_product),
I18n.t(:report_header_product),
I18n.t(:report_header_bulk_unit_size),
I18n.t(:report_header_variant),
I18n.t(:report_header_weight),
I18n.t(:report_header_sum_total),
I18n.t(:report_header_sum_max_total),
I18n.t(:report_header_units_required),
I18n.t(:report_header_remainder)]
end
end
def search
report_line_items.orders
end
def table_items
return [] unless @render_table
report_line_items.list(line_item_includes)
end
def rules
case params[:report_type]
when "bulk_coop_supplier_report"
@supplier_report.rules
when "bulk_coop_allocation"
@allocation_report.rules
when "bulk_coop_packing_sheets"
[{ group_by: proc { |li| li.product },
sort_by: proc { |product| product.name } },
{ group_by: proc { |li| li.full_name },
sort_by: proc { |full_name| full_name } },
{ group_by: proc { |li| li.order },
sort_by: proc { |order| order.to_s } }]
when "bulk_coop_customer_payments"
[{ group_by: proc { |li| li.order },
sort_by: proc { |order| order.completed_at } }]
else
[{ group_by: proc { |li| li.product.supplier },
sort_by: proc { |supplier| supplier.name } },
{ group_by: proc { |li| li.product },
sort_by: proc { |product| product.name },
summary_columns: [proc { |lis| lis.first.product.supplier.name },
proc { |lis| lis.first.product.name },
proc { |lis| lis.first.product.group_buy_unit_size || 0.0 },
proc { |_lis| "" },
proc { |_lis| "" },
proc { |lis|
lis.sum { |li|
li.quantity * (li.weight_from_unit_value || 0)
}
},
proc { |lis|
lis.sum { |li|
(li.max_quantity || 0) * (li.weight_from_unit_value || 0)
}
},
proc { |lis|
( if (lis.first.product.group_buy_unit_size || 0).zero?
0
else
( lis.sum { |li|
[li.max_quantity || 0,
li.quantity || 0].max * (li.weight_from_unit_value || 0)
} / lis.first.product.group_buy_unit_size )
end ).floor
},
proc { |lis|
lis.sum { |li|
[li.max_quantity || 0,
li.quantity || 0].max * (li.weight_from_unit_value || 0)
} - ( ( if (lis.first.product.group_buy_unit_size || 0).zero?
0
else
( lis.sum { |li|
[li.max_quantity || 0,
li.quantity || 0].max * (li.weight_from_unit_value || 0)
} / lis.first.product.group_buy_unit_size )
end ).floor * (lis.first.product.group_buy_unit_size || 0) )
}] },
{ group_by: proc { |li| li.full_name },
sort_by: proc { |full_name| full_name } }]
end
end
def columns
case params[:report_type]
when "bulk_coop_supplier_report"
@supplier_report.columns
when "bulk_coop_allocation"
@allocation_report.columns
when "bulk_coop_packing_sheets"
[
:order_billing_address_name,
:product_name,
:full_name,
:total_quantity
]
when "bulk_coop_customer_payments"
[
:order_billing_address_name,
:order_completed_at,
:customer_payments_total_cost,
:customer_payments_amount_owed,
:customer_payments_amount_paid
]
else
[
:product_supplier_name,
:product_name,
:product_group_buy_unit_size,
:full_name,
:weight_from_unit_value,
:total_quantity,
:total_max_quantity,
:empty_cell,
:empty_cell
]
end
end
private
attr_reader :filter_canceled
def line_item_includes
[
{
order: [:bill_address],
variant: [{ option_values: :option_type }, { product: :supplier }]
},
:option_values
]
end
def order_permissions
@order_permissions ||= ::Permissions::Order.new(@user, filter_canceled)
end
def report_line_items
@report_line_items ||= OpenFoodNetwork::Reports::LineItems.new(
order_permissions,
@params,
CompleteVisibleOrders.new(order_permissions).query
)
end
def customer_payments_total_cost(line_items)
unique_orders(line_items).sum(&:total)
end
def customer_payments_amount_owed(line_items)
unique_orders(line_items).sum(&:new_outstanding_balance)
end
def customer_payments_amount_paid(line_items)
unique_orders(line_items).sum(&:payment_total)
end
def unique_orders(line_items)
line_items.map(&:order).uniq
end
def empty_cell(_line_items)
""
end
def full_name(line_items)
line_items.first.full_name
end
def group_buy_unit_size(line_items)
(line_items.first.variant.product.group_buy_unit_size || 0.0) /
(line_items.first.product.variant_unit_scale || 1)
end
def max_quantity_excess(line_items)
max_quantity_amount(line_items) - total_amount(line_items)
end
def max_quantity_amount(line_items)
line_items.sum do |line_item|
max_quantity = [line_item.max_quantity || 0, line_item.quantity || 0].max
max_quantity * scaled_unit_value(line_item.variant)
end
end
def option_value_value(line_items)
VariantUnits::OptionValueNamer.new(line_items.first).value
end
def option_value_unit(line_items)
VariantUnits::OptionValueNamer.new(line_items.first).unit
end
def order_billing_address_name(line_items)
billing_address = line_items.first.order.bill_address
billing_address.firstname + " " + billing_address.lastname
end
def order_completed_at(line_items)
line_items.first.order.completed_at.to_s
end
def product_group_buy_unit_size(line_items)
line_items.first.product.group_buy_unit_size || 0.0
end
def product_name(line_items)
line_items.first.product.name
end
def product_supplier_name(line_items)
line_items.first.product.supplier.name
end
def remainder(line_items)
remainder = total_available(line_items) - total_amount(line_items)
remainder >= 0 ? remainder : ''
end
def scaled_final_weight_volume(line_item)
(line_item.final_weight_volume || 0) / (line_item.product.variant_unit_scale || 1)
end
def scaled_unit_value(variant)
(variant.unit_value || 0) / (variant.product.variant_unit_scale || 1)
end
def total_amount(line_items)
line_items.sum { |li| scaled_final_weight_volume(li) }
end
def total_available(line_items)
units_required(line_items) * group_buy_unit_size(line_items)
end
def total_max_quantity(line_items)
line_items.sum { |line_item| line_item.max_quantity || 0 }
end
def total_quantity(line_items)
line_items.sum(&:quantity)
end
def total_label(_line_items)
I18n.t('admin.reports.total')
end
def units_required(line_items)
if group_buy_unit_size(line_items).zero?
0
else
( total_amount(line_items) / group_buy_unit_size(line_items) ).ceil
end
end
def variant_product_group_buy_unit_size_f(line_items)
group_buy_unit_size(line_items)
end
def variant_product_name(line_items)
line_items.first.variant.product.name
end
def variant_product_supplier_name(line_items)
line_items.first.variant.product.supplier.name
end
def weight_from_unit_value(line_items)
line_items.first.weight_from_unit_value || 0
end
end
end
end
end

View File

@@ -1,65 +0,0 @@
# frozen_string_literal: true
module OrderManagement
module Reports
module BulkCoop
class BulkCoopSupplierReport
def header
[
I18n.t(:report_header_supplier),
I18n.t(:report_header_product),
I18n.t(:report_header_bulk_unit_size),
I18n.t(:report_header_variant),
I18n.t(:report_header_variant_value),
I18n.t(:report_header_variant_unit),
I18n.t(:report_header_weight),
I18n.t(:report_header_sum_total),
I18n.t(:report_header_units_required),
I18n.t(:report_header_unallocated),
I18n.t(:report_header_max_quantity_excess),
]
end
def rules
[
{ group_by: proc { |line_item| line_item.product.supplier },
sort_by: proc { |supplier| supplier.name } },
{ group_by: proc { |line_item| line_item.product },
sort_by: proc { |product| product.name },
summary_columns: [
:variant_product_supplier_name,
:variant_product_name,
:variant_product_group_buy_unit_size_f,
:empty_cell,
:empty_cell,
:empty_cell,
:empty_cell,
:total_amount,
:units_required,
:remainder,
:max_quantity_excess
] },
{ group_by: proc { |line_item| line_item.full_name },
sort_by: proc { |full_name| full_name } }
]
end
def columns
[
:variant_product_supplier_name,
:variant_product_name,
:variant_product_group_buy_unit_size_f,
:full_name,
:option_value_value,
:option_value_unit,
:weight_from_unit_value,
:total_amount,
:empty_cell,
:empty_cell,
:empty_cell
]
end
end
end
end
end

View File

@@ -1,45 +0,0 @@
# frozen_string_literal: true
module OrderManagement
module Reports
module BulkCoop
class Parameters < ::Reports::Parameters::Base
extend ActiveModel::Naming
extend ActiveModel::Translation
include ActiveModel::Validations
attr_accessor :start_at, :end_at, :distributor_ids, :report_type
before_validation :cleanup_arrays
validates :start_at, :end_at, date_time_string: true
validates :distributor_ids, integer_array: true
validates_inclusion_of :report_type, in: BulkCoopReport::REPORT_TYPES.map(&:to_s)
validate :require_valid_datetime_range
def initialize(attributes = {})
self.distributor_ids = []
super(attributes)
end
def authorize!(permissions)
authorizer = Authorizer.new(self, permissions)
authorizer.authorize!
end
protected
# Remove the blank strings that Rails multiple selects add by default to
# make sure that blank lists are still submitted to the server as arrays
# instead of nil.
#
# https://api.rubyonrails.org/classes/ActionView/Helpers/FormOptionsHelper.html#method-i-select
def cleanup_arrays
distributor_ids.reject!(&:blank?)
end
end
end
end
end

View File

@@ -1,13 +0,0 @@
# frozen_string_literal: true
module OrderManagement
module Reports
module BulkCoop
class Permissions < ::Reports::Permissions
def allowed_distributors
@allowed_distributors ||= Enterprise.is_distributor.managed_by(user)
end
end
end
end
end

View File

@@ -1,32 +0,0 @@
# frozen_string_literal: true
module OrderManagement
module Reports
module BulkCoop
module Renderers
class CsvRenderer < ::Reports::Renderers::Base
def render(context)
context.send_data(generate, filename: filename)
end
def generate
CSV.generate do |csv|
csv << report_data.header
report_data.list.each do |data|
csv << data
end
end
end
private
def filename
timestamp = Time.zone.now.strftime("%Y%m%d")
"#{report_data.parameters[:report_type]}_#{timestamp}.csv"
end
end
end
end
end
end

View File

@@ -1,22 +0,0 @@
# frozen_string_literal: true
module OrderManagement
module Reports
module BulkCoop
module Renderers
class HtmlRenderer < ::Reports::Renderers::Base
def render(context)
context.instance_variable_set :@renderer, self
context.render(action: :create, renderer: self)
end
delegate :header, to: :report_data
def data_rows
report_data.list
end
end
end
end
end
end

View File

@@ -1,29 +0,0 @@
# frozen_string_literal: true
require 'open_food_network/order_grouper'
module OrderManagement
module Reports
module BulkCoop
class ReportService
attr_accessor :permissions, :parameters, :user
def initialize(permissions, parameters, user)
@permissions = permissions
@parameters = parameters
@user = user
@report = BulkCoopReport.new(user, parameters, true)
end
def header
@report.header
end
def list
order_grouper = OpenFoodNetwork::OrderGrouper.new @report.rules, @report.columns, @report
order_grouper.table(@report.table_items)
end
end
end
end
end

View File

@@ -1,66 +0,0 @@
# frozen_string_literal: true
module OrderManagement
module Reports
module EnterpriseFeeSummary
module Renderers
class CsvRenderer < ::Reports::Renderers::Base
def render(context)
context.send_data(generate, filename: filename)
end
def generate
CSV.generate do |csv|
render_header(csv)
report_data.list.each do |data|
render_data_row(csv, data)
end
end
end
private
def filename
timestamp = Time.zone.now.strftime("%Y%m%d")
"enterprise_fee_summary_#{timestamp}.csv"
end
def render_header(csv)
csv << [
header_label(:fee_type),
header_label(:enterprise_name),
header_label(:fee_name),
header_label(:customer_name),
header_label(:fee_placement),
header_label(:fee_calculated_on_transfer_through_name),
header_label(:tax_category_name),
header_label(:total_amount)
]
end
def render_data_row(csv, data)
csv << [
data.fee_type,
data.enterprise_name,
data.fee_name,
data.customer_name,
data.fee_placement,
data.fee_calculated_on_transfer_through_name,
data.tax_category_name,
data.total_amount
]
end
def header_label(attribute)
I18n.t("header.#{attribute}", scope: i18n_scope)
end
def i18n_scope
"order_management.reports.enterprise_fee_summary.formats.csv"
end
end
end
end
end
end

View File

@@ -1,53 +0,0 @@
# frozen_string_literal: true
module OrderManagement
module Reports
module EnterpriseFeeSummary
module Renderers
class HtmlRenderer < ::Reports::Renderers::Base
def render(context)
context.instance_variable_set :@renderer, self
context.render(action: :create, renderer: self)
end
def header
data_row_attributes.map do |attribute|
header_label(attribute)
end
end
def data_rows
report_data.list.map do |data|
data_row_attributes.map do |attribute|
data.public_send(attribute)
end
end
end
private
def data_row_attributes
[
:fee_type,
:enterprise_name,
:fee_name,
:customer_name,
:fee_placement,
:fee_calculated_on_transfer_through_name,
:tax_category_name,
:total_amount
]
end
def header_label(attribute)
I18n.t("header.#{attribute}", scope: i18n_scope)
end
def i18n_scope
"order_management.reports.enterprise_fee_summary.formats.csv"
end
end
end
end
end
end

View File

@@ -1,49 +0,0 @@
# frozen_string_literal: true
module OrderManagement
module Reports
module EnterpriseFeeSummary
class ReportService
attr_accessor :permissions, :parameters
def initialize(permissions, parameters)
@permissions = permissions
@parameters = parameters
end
def enterprise_fees_by_customer
Scope.new.apply_filters(permission_filters).apply_filters(parameters).result
end
def list
enterprise_fee_type_total_list.sort
end
private
def permission_filters
Parameters.new(order_cycle_ids: permissions.allowed_order_cycles.map(&:id))
end
def enterprise_fee_type_total_list
enterprise_fees_by_customer.map do |total_data|
summarizer = Summarizer.new(total_data)
ReportData::EnterpriseFeeTypeTotal.new.tap do |total|
enterprise_fee_type_summarizer_to_total_attributes.each do |attribute|
total.public_send("#{attribute}=", summarizer.public_send(attribute))
end
end
end
end
def enterprise_fee_type_summarizer_to_total_attributes
[
:fee_type, :enterprise_name, :fee_name, :customer_name, :fee_placement,
:fee_calculated_on_transfer_through_name, :tax_category_name, :total_amount
]
end
end
end
end
end

View File

@@ -1,5 +0,0 @@
# frozen_string_literal: true
module Reports
class UnsupportedReportFormatException < StandardError; end
end

View File

@@ -1,29 +0,0 @@
# frozen_string_literal: true
module Reports
class Authorizer
class ParameterNotAllowedError < StandardError; end
attr_accessor :parameters, :permissions
def initialize(parameters, permissions)
@parameters = parameters
@permissions = permissions
end
def self.parameter_not_allowed_error_message
i18n_scope = "order_management.reports.enterprise_fee_summary"
I18n.t("parameter_not_allowed_error", scope: i18n_scope)
end
private
def require_ids_allowed(array, allowed_objects)
error_klass = ::Reports::Authorizer::ParameterNotAllowedError
error_message = self.class.parameter_not_allowed_error_message
ids_allowed = (array - allowed_objects.map(&:id).map(&:to_s)).blank?
raise error_klass, error_message unless ids_allowed
end
end
end

View File

@@ -1,35 +0,0 @@
# frozen_string_literal: true
module Reports
module Parameters
class Base
extend ActiveModel::Naming
extend ActiveModel::Translation
include ActiveModel::Validations
include ActiveModel::Validations::Callbacks
def initialize(attributes = {})
attributes.each do |key, value|
public_send("#{key}=", value)
end
end
def self.date_end_before_start_error_message
i18n_scope = "order_management.reports.enterprise_fee_summary"
I18n.t("date_end_before_start_error", scope: i18n_scope)
end
# The parameters are never persisted.
def to_key; end
protected
def require_valid_datetime_range
return if start_at.blank? || end_at.blank?
error_message = self.class.date_end_before_start_error_message
errors.add(:end_at, error_message) unless start_at < end_at
end
end
end
end

View File

@@ -1,11 +0,0 @@
# frozen_string_literal: true
module Reports
class Permissions
attr_accessor :user
def initialize(user)
@user = user
end
end
end

View File

@@ -1,13 +0,0 @@
# frozen_string_literal: true
module Reports
module Renderers
class Base
attr_reader :report_data
def initialize(report_data)
@report_data = report_data
end
end
end
end

View File

@@ -1,13 +0,0 @@
# frozen_string_literal: true
module Reports
module ReportData
class Base
def initialize(attributes = {})
attributes.each do |key, value|
public_send("#{key}=", value)
end
end
end
end
end

View File

@@ -1,19 +0,0 @@
- if @report.present?
%table#enterprise_fee_summary_report.report__table
%thead
%tr
- @renderer.header.each do |heading|
%th= heading
%tbody
- @renderer.data_rows.each do |row|
%tr
- row.each do |cell_value|
%td= cell_value
- if @renderer.data_rows.empty?
%tr
%td{colspan: @renderer.header.length}= t('.none')
- else
%p.report__message
= t(".select_and_search")

View File

@@ -1,34 +0,0 @@
= form_for @report_parameters, as: :report, url: main_app.order_management_reports_bulk_coop_path, method: :post do |f|
.row.date-range-filter
.sixteen.columns.alpha
= label_tag nil, t(".date_range")
%br
= f.label :start_at, class: "inline"
= f.text_field :start_at, class: "datetimepicker datepicker-from"
%span.range-divider
%i.icon-arrow-right
= f.text_field :end_at, class: "datetimepicker datepicker-to"
= f.label :end_at, class: "inline"
.row
.sixteen.columns.alpha
= f.label :distributor_ids
= f.collection_select(:distributor_ids, @permissions.allowed_distributors, :id, :name, {}, {class: "select2 fullwidth", multiple: true})
.row
.sixteen.columns.alpha
= f.label :report_type
= f.collection_select(:report_type, OrderManagement::Reports::BulkCoop::BulkCoopReport::REPORT_TYPES.map { |report_type| [t(".#{report_type}"), report_type] }, :last, :first, {}, {class: "select2 fullwidth", multiple: false})
.row
.sixteen.columns.alpha
= check_box_tag :report_format, "csv", false, id: "report_format_csv"
= label_tag :report_format_csv, t(".report_format_csv")
= button t(".generate_report")
= render partial: "spree/admin/reports/customer_names_message"

View File

@@ -1,20 +0,0 @@
- if @report.present?
%table#bulk_coop_report.report__table
%thead
%tr
- @renderer.header.each do |heading|
%th= heading
%tbody
- @renderer.data_rows.each do |row|
%tr
- row.each do |cell_value|
%td= cell_value
- if @renderer.data_rows.empty?
%tr
%td{colspan: @renderer.header.length}= t('.none')
- else
%p.report__message
= t(".select_and_search")

View File

@@ -1,2 +0,0 @@
= render "filters"
= render "order_management/reports/report"

View File

@@ -1,52 +0,0 @@
= form_for @report_parameters, as: :report, url: main_app.order_management_reports_enterprise_fee_summary_path, method: :post do |f|
.row.date-range-filter
.sixteen.columns.alpha
= label_tag nil, t(".date_range")
%br
= f.label :start_at, class: "inline"
= f.text_field :start_at, class: "datetimepicker datepicker-from"
%span.range-divider
%i.icon-arrow-right
= f.text_field :end_at, class: "datetimepicker datepicker-to"
= f.label :end_at, class: "inline"
.row
.sixteen.columns.alpha
= f.label :distributor_ids
= f.collection_select(:distributor_ids, @permissions.allowed_distributors, :id, :name, {}, {class: "select2 fullwidth", multiple: true})
.row
.sixteen.columns.alpha
= f.label :producer_ids
= f.collection_select(:producer_ids, @permissions.allowed_producers, :id, :name, {}, {class: "select2 fullwidth", multiple: true})
.row
.sixteen.columns.alpha
= f.label :order_cycle_ids
= f.collection_select(:order_cycle_ids, @permissions.allowed_order_cycles, :id, :name, {}, {class: "select2 fullwidth", multiple: true})
.row
.eight.columns.alpha
= f.label :enterprise_fee_ids
= f.collection_select(:enterprise_fee_ids, @permissions.allowed_enterprise_fees, :id, :name, {}, {class: "select2 fullwidth", multiple: true})
.eight.columns.omega
= f.label :shipping_method_ids
= f.collection_select(:shipping_method_ids, @permissions.allowed_shipping_methods, :id, :name, {}, {class: "select2 fullwidth", multiple: true})
.row
.eight.columns.alpha &nbsp;
.eight.columns.omega
= f.label :payment_method_ids
= f.collection_select(:payment_method_ids, @permissions.allowed_payment_methods, :id, :name, {}, {class: "select2 fullwidth", multiple: true})
.row
.sixteen.columns.alpha
= check_box_tag :report_format, "csv", false, id: "report_format_csv"
= label_tag :report_format_csv, t(".report_format_csv")
= button t(".generate_report")
= render partial: "spree/admin/reports/customer_names_message"

View File

@@ -1,2 +0,0 @@
= render "filters"
= render "order_management/reports/report"

View File

@@ -1,103 +0,0 @@
# frozen_string_literal: true
require "spec_helper"
describe OrderManagement::Reports::BulkCoopController, type: :controller do
let(:report_klass) { OrderManagement::Reports::BulkCoop }
let!(:distributor) { create(:distributor_enterprise) }
let(:current_user) { distributor.owner }
before do
allow(controller).to receive(:spree_current_user) { current_user }
end
describe "#new" do
it "renders the report form" do
get :new
expect(response.status).to eq 200
expect(response).to render_template(new_template_path)
end
end
describe "#create" do
context "when the parameters are valid" do
it "sends the generated report in the correct format" do
post :create, params: {
report: {
start_at: "2018-10-09 07:30:00",
report_type: "bulk_coop_supplier_report"
}, report_format: "csv"
}
expect(response.status).to eq 200
expect(response.body).not_to be_blank
expect(response.header["Content-Type"]).to eq("text/csv")
end
end
context "when the parameters are invalid" do
it "renders the report form with an error" do
post :create, params: {
report: {
start_at: "invalid_date",
report_type: "bulk_coop_supplier_report"
}, report_format: "csv"
}
expect(flash[:error]).to eq(I18n.t("invalid_filter_parameters", scope: i18n_scope))
expect(response).to render_template(new_template_path)
end
end
context "when some parameters are now allowed" do
let!(:distributor) { create(:distributor_enterprise) }
let!(:other_distributor) { create(:distributor_enterprise) }
let(:current_user) { distributor.owner }
it "renders the report form with an error" do
post :create, params: {
report: {
distributor_ids: [other_distributor.id],
report_type: "bulk_coop_supplier_report"
}, report_format: "csv"
}
expect(flash[:error]).to eq(report_klass::Authorizer.parameter_not_allowed_error_message)
expect(response).to render_template(new_template_path)
end
end
describe "filtering results based on permissions" do
let!(:distributor) { create(:distributor_enterprise) }
let!(:other_distributor) { create(:distributor_enterprise) }
let(:current_user) { distributor.owner }
it "applies permissions to report" do
post :create, params: { report: {}, report_format: "csv" }
expect(assigns(:permissions).allowed_distributors.to_a).to eq([distributor])
end
end
end
private
def default_report_params
{
report_type: "bulk_coop_supplier_report"
}
end
def i18n_scope
"order_management.reports.enterprise_fee_summary"
end
def new_template_path
"order_management/reports/bulk_coop/new"
end
end

View File

@@ -1,89 +0,0 @@
# frozen_string_literal: true
require "spec_helper"
describe OrderManagement::Reports::EnterpriseFeeSummariesController, type: :controller do
let(:report_klass) { OrderManagement::Reports::EnterpriseFeeSummary }
let!(:distributor) { create(:distributor_enterprise) }
let(:current_user) { distributor.owner }
before do
allow(controller).to receive(:spree_current_user) { current_user }
end
describe "#new" do
it "renders the report form" do
get :new
expect(response.status).to eq 200
expect(response).to render_template(new_template_path)
end
end
describe "#create" do
context "when the parameters are valid" do
it "sends the generated report in the correct format" do
post :create, params: {
report: { start_at: "2018-10-09 07:30:00" }, report_format: "csv"
}
expect(response.status).to eq 200
expect(response.body).not_to be_blank
expect(response.header["Content-Type"]).to eq("text/csv")
end
end
context "when the parameters are invalid" do
it "renders the report form with an error" do
post :create, params: {
report: { start_at: "invalid date" }, report_format: "csv"
}
expect(flash[:error]).to eq(I18n.t("invalid_filter_parameters", scope: i18n_scope))
expect(response).to render_template(new_template_path)
end
end
context "when some parameters are now allowed" do
let!(:distributor) { create(:distributor_enterprise) }
let!(:other_distributor) { create(:distributor_enterprise) }
let(:current_user) { distributor.owner }
it "renders the report form with an error" do
post :create, params: {
report: { distributor_ids: [other_distributor.id] }, report_format: "csv"
}
expect(flash[:error]).to eq(report_klass::Authorizer.parameter_not_allowed_error_message)
expect(response).to render_template(new_template_path)
end
end
describe "filtering results based on permissions" do
let!(:distributor) { create(:distributor_enterprise) }
let!(:other_distributor) { create(:distributor_enterprise) }
let!(:order_cycle) { create(:simple_order_cycle, coordinator: distributor) }
let!(:other_order_cycle) { create(:simple_order_cycle, coordinator: other_distributor) }
let(:current_user) { distributor.owner }
it "applies permissions to report" do
post :create, params: { report: {}, report_format: "csv" }
expect(assigns(:permissions).allowed_order_cycles.to_a).to eq([order_cycle])
end
end
end
def i18n_scope
"order_management.reports.enterprise_fee_summary"
end
def new_template_path
"order_management/reports/enterprise_fee_summaries/new"
end
end

View File

@@ -1,75 +0,0 @@
# frozen_string_literal: true
require "spec_helper"
feature "bulk coop" do
include AuthenticationHelper
include WebHelper
scenario "generating Bulk Co-op Supplier Report" do
login_as_admin_and_visit new_order_management_reports_bulk_coop_path
select "Bulk Co-op Supplier Report", from: "report_report_type"
click_button 'Generate Report'
expect(page).to have_table_row [
"Supplier",
"Product",
"Bulk Unit Size",
"Variant",
"Variant Value",
"Variant Unit",
"Weight",
"Sum Total",
"Units Required",
"Unallocated",
"Max Quantity Excess"
]
end
scenario "generating Bulk Co-op Allocation report" do
login_as_admin_and_visit new_order_management_reports_bulk_coop_path
select "Bulk Co-op Allocation", from: "report_report_type"
click_button 'Generate Report'
expect(page).to have_table_row [
"Customer",
"Product",
"Bulk Unit Size",
"Variant",
"Variant Value",
"Variant Unit",
"Weight",
"Sum Total",
"Total available",
"Unallocated",
"Max Quantity Excess"
]
end
scenario "generating Bulk Co-op Packing Sheets report" do
login_as_admin_and_visit new_order_management_reports_bulk_coop_path
select "Bulk Co-op Packing Sheets", from: "report_report_type"
click_button 'Generate Report'
expect(page).to have_table_row [
"Customer",
"Product",
"Variant",
"Sum Total"
]
end
scenario "generating Bulk Co-op Customer Payments report" do
login_as_admin_and_visit new_order_management_reports_bulk_coop_path
select "Bulk Co-op Customer Payments", from: "report_report_type"
click_button 'Generate Report'
expect(page).to have_table_row [
"Customer",
"Date of Order",
"Total Cost",
"Amount Owing",
"Amount Paid"
]
end
end

View File

@@ -1,186 +0,0 @@
# frozen_string_literal: true
require 'spec_helper'
describe OrderManagement::Reports::BulkCoop::BulkCoopReport do
subject { OrderManagement::Reports::BulkCoop::BulkCoopReport.new user, params, true }
let(:user) { create(:admin_user) }
describe '#table_items' do
let(:params) { {} }
let(:d1) { create(:distributor_enterprise) }
let(:oc1) { create(:simple_order_cycle) }
let(:o1) { create(:order, completed_at: 1.day.ago, order_cycle: oc1, distributor: d1) }
let(:li1) { build(:line_item_with_shipment) }
before { o1.line_items << li1 }
context "as a site admin" do
context 'when searching' do
let(:params) { { q: { completed_at_gt: '', completed_at_lt: '', distributor_id_in: [] } } }
it "fetches completed orders" do
o2 = create(:order, state: 'cart')
o2.line_items << build(:line_item)
expect(subject.table_items).to eq([li1])
end
it 'shows canceled orders' do
o2 = create(:order, state: 'canceled', completed_at: 1.day.ago, order_cycle: oc1,
distributor: d1)
line_item = build(:line_item_with_shipment)
o2.line_items << line_item
expect(subject.table_items).to include(line_item)
end
end
context 'when not searching' do
let(:params) { {} }
it "fetches completed orders" do
o2 = create(:order, state: 'cart')
o2.line_items << build(:line_item)
expect(subject.table_items).to eq([li1])
end
it 'shows canceled orders' do
o2 = create(:order, state: 'canceled', completed_at: 1.day.ago, order_cycle: oc1,
distributor: d1)
line_item = build(:line_item_with_shipment)
o2.line_items << line_item
expect(subject.table_items).to include(line_item)
end
end
end
context "filtering by date" do
it do
user = create(:admin_user)
o2 = create(:order, completed_at: 3.days.ago, order_cycle: oc1, distributor: d1)
li2 = build(:line_item_with_shipment)
o2.line_items << li2
report = OrderManagement::Reports::BulkCoop::BulkCoopReport.new user, {}, true
expect(report.table_items).to match_array [li1, li2]
report = OrderManagement::Reports::BulkCoop::BulkCoopReport.new(
user, { q: { completed_at_gt: 2.days.ago } }, true
)
expect(report.table_items).to eq([li1])
report = OrderManagement::Reports::BulkCoop::BulkCoopReport.new(
user, { q: { completed_at_lt: 2.days.ago } }, true
)
expect(report.table_items).to eq([li2])
end
end
context "filtering by distributor" do
it do
user = create(:admin_user)
d2 = create(:distributor_enterprise)
o2 = create(:order, distributor: d2, order_cycle: oc1,
completed_at: Time.zone.now)
li2 = build(:line_item_with_shipment)
o2.line_items << li2
report = OrderManagement::Reports::BulkCoop::BulkCoopReport.new user, {}, true
expect(report.table_items).to match_array [li1, li2]
report = OrderManagement::Reports::BulkCoop::BulkCoopReport.new(
user, { q: { distributor_id_in: [d1.id] } }, true
)
expect(report.table_items).to eq([li1])
report = OrderManagement::Reports::BulkCoop::BulkCoopReport.new(
user, { q: { distributor_id_in: [d2.id] } }, true
)
expect(report.table_items).to eq([li2])
end
end
context "as a manager of a supplier" do
let!(:user) { create(:user) }
subject { OrderManagement::Reports::BulkCoop::BulkCoopReport.new user, {}, true }
let(:s1) { create(:supplier_enterprise) }
before do
s1.enterprise_roles.create!(user: user)
end
context "that has granted P-OC to the distributor" do
let(:o2) do
create(:order, distributor: d1, completed_at: 1.day.ago, bill_address: create(:address),
ship_address: create(:address))
end
let(:li2) do
build(:line_item_with_shipment, product: create(:simple_product, supplier: s1))
end
before do
o2.line_items << li2
create(:enterprise_relationship, parent: s1, child: d1,
permissions_list: [:add_to_order_cycle])
end
it "shows line items supplied by my producers, with names hidden" do
expect(subject.table_items).to eq([li2])
expect(subject.table_items.first.order.bill_address.firstname).to eq("HIDDEN")
end
end
context "that has not granted P-OC to the distributor" do
let(:o2) do
create(:order, distributor: d1, completed_at: 1.day.ago, bill_address: create(:address),
ship_address: create(:address))
end
let(:li2) do
build(:line_item_with_shipment, product: create(:simple_product, supplier: s1))
end
before do
o2.line_items << li2
end
it "does not show line items supplied by my producers" do
expect(subject.table_items).to eq([])
end
end
end
end
describe '#columns' do
context 'when report type is bulk_coop_customer_payments' do
let(:params) { { report_type: 'bulk_coop_customer_payments' } }
it 'returns' do
expect(subject.columns).to eq(
[
:order_billing_address_name,
:order_completed_at,
:customer_payments_total_cost,
:customer_payments_amount_owed,
:customer_payments_amount_paid,
]
)
end
end
end
# Yes, I know testing a private method is bad practice but report's design, tighly coupling
# OpenFoodNetwork::OrderGrouper and OrderManagement::Reports::BulkCoop::BulkCoopReport, makes it
# very hard to make things testeable without ending up in a wormwhole. This is a trade-off.
describe '#customer_payments_amount_owed' do
let(:params) { {} }
let(:user) { build(:user) }
let!(:line_item) { create(:line_item) }
let(:order) { line_item.order }
it 'calls #new_outstanding_balance' do
expect_any_instance_of(Spree::Order).to receive(:new_outstanding_balance)
subject.send(:customer_payments_amount_owed, [line_item])
end
end
end

View File

@@ -1,174 +0,0 @@
# frozen_string_literal: true
require "spec_helper"
describe OrderManagement::Reports::EnterpriseFeeSummary::Authorizer do
let(:report_klass) { OrderManagement::Reports::EnterpriseFeeSummary }
let(:user) { create(:user) }
let(:parameters) { report_klass::Parameters.new(params) }
let(:permissions) { report_klass::Permissions.new(user) }
let(:authorizer) { described_class.new(parameters, permissions) }
context "for distributors" do
before do
allow(permissions).to receive(:allowed_distributors) do
stub_model_collection(Enterprise, :id, ["1", "2", "3"])
end
end
context "when distributors are allowed" do
let(:params) { { distributor_ids: ["1", "3"] } }
it "does not raise error" do
expect { authorizer.authorize! }.not_to raise_error
end
end
context "when a distributor is not allowed" do
let(:params) { { distributor_ids: ["1", "4"] } }
it "raises ParameterNotAllowedError" do
expect { authorizer.authorize! }
.to raise_error(Reports::Authorizer::ParameterNotAllowedError)
end
end
end
context "for producers" do
before do
allow(permissions).to receive(:allowed_producers) do
stub_model_collection(Enterprise, :id, ["1", "2", "3"])
end
end
context "when producers are allowed" do
let(:params) { { producer_ids: ["1", "3"] } }
it "does not raise error" do
expect { authorizer.authorize! }.not_to raise_error
end
end
context "when a producer is not allowed" do
let(:params) { { producer_ids: ["1", "4"] } }
it "raises ParameterNotAllowedError" do
expect { authorizer.authorize! }
.to raise_error(Reports::Authorizer::ParameterNotAllowedError)
end
end
end
context "for order cycles" do
before do
allow(permissions).to receive(:allowed_order_cycles) do
stub_model_collection(OrderCycle, :id, ["1", "2", "3"])
end
end
context "when order cycles are allowed" do
let(:params) { { order_cycle_ids: ["1", "3"] } }
it "does not raise error" do
expect { authorizer.authorize! }.not_to raise_error
end
end
context "when an order cycle is not allowed" do
let(:params) { { order_cycle_ids: ["1", "4"] } }
it "raises ParameterNotAllowedError" do
expect { authorizer.authorize! }
.to raise_error(Reports::Authorizer::ParameterNotAllowedError)
end
end
end
context "for enterprise fees" do
before do
allow(permissions).to receive(:allowed_enterprise_fees) do
stub_model_collection(EnterpriseFee, :id, ["1", "2", "3"])
end
end
context "when enterprise fees are allowed" do
let(:params) { { enterprise_fee_ids: ["1", "3"] } }
it "does not raise error" do
expect { authorizer.authorize! }.not_to raise_error
end
end
context "when an enterprise fee is not allowed" do
let(:params) { { enterprise_fee_ids: ["1", "4"] } }
it "raises ParameterNotAllowedError" do
expect { authorizer.authorize! }
.to raise_error(Reports::Authorizer::ParameterNotAllowedError)
end
end
end
context "for shipping methods" do
before do
allow(permissions).to receive(:allowed_shipping_methods) do
stub_model_collection(Spree::ShippingMethod, :id, ["1", "2", "3"])
end
end
context "when shipping methods are allowed" do
let(:params) { { shipping_method_ids: ["1", "3"] } }
it "does not raise error" do
expect { authorizer.authorize! }.not_to raise_error
end
end
context "when a shipping method is not allowed" do
let(:params) { { shipping_method_ids: ["1", "4"] } }
it "raises ParameterNotAllowedError" do
expect { authorizer.authorize! }
.to raise_error(Reports::Authorizer::ParameterNotAllowedError)
end
end
end
context "for payment methods" do
before do
allow(permissions).to receive(:allowed_payment_methods) do
stub_model_collection(Spree::PaymentMethod, :id, ["1", "2", "3"])
end
end
context "when payment methods are allowed" do
let(:params) { { payment_method_ids: ["1", "3"] } }
it "does not raise error" do
expect { authorizer.authorize! }.not_to raise_error
end
end
context "when a payment method is not allowed" do
let(:params) { { payment_method_ids: ["1", "4"] } }
it "raises ParameterNotAllowedError" do
expect { authorizer.authorize! }
.to raise_error(Reports::Authorizer::ParameterNotAllowedError)
end
end
end
def stub_model_collection(model, attribute_name, attribute_list)
attribute_list.map do |attribute_value|
stub_model(model, attribute_name => attribute_value)
end
end
def stub_model(model, params)
model.new.tap do |instance|
instance.stub(params)
end
end
end

View File

@@ -1,90 +0,0 @@
# frozen_string_literal: true
require "spec_helper"
require "date_time_string_validator"
describe OrderManagement::Reports::EnterpriseFeeSummary::Parameters do
describe "validation" do
let(:parameters) { described_class.new }
it "allows all parameters to be blank" do
expect(parameters).to be_valid
end
context "for type of parameters" do
it { is_expected.to validate_date_time_format_of(:start_at) }
it { is_expected.to validate_date_time_format_of(:end_at) }
it { is_expected.to validate_integer_array(:distributor_ids) }
it { is_expected.to validate_integer_array(:producer_ids) }
it { is_expected.to validate_integer_array(:order_cycle_ids) }
it { is_expected.to validate_integer_array(:enterprise_fee_ids) }
it { is_expected.to validate_integer_array(:shipping_method_ids) }
it { is_expected.to validate_integer_array(:payment_method_ids) }
it "allows integer arrays to include blank string and cleans it up" do
subject.distributor_ids = ["", "1"]
subject.producer_ids = ["", "1"]
subject.order_cycle_ids = ["", "1"]
subject.enterprise_fee_ids = ["", "1"]
subject.shipping_method_ids = ["", "1"]
subject.payment_method_ids = ["", "1"]
expect(subject).to be_valid
expect(subject.distributor_ids).to eq(["1"])
expect(subject.producer_ids).to eq(["1"])
expect(subject.order_cycle_ids).to eq(["1"])
expect(subject.enterprise_fee_ids).to eq(["1"])
expect(subject.shipping_method_ids).to eq(["1"])
expect(subject.payment_method_ids).to eq(["1"])
end
describe "requiring start_at to be before end_at" do
let(:now) { Time.zone.now.utc }
it "adds error when start_at is after end_at" do
allow(subject).to receive(:start_at) { now.to_s }
allow(subject).to receive(:end_at) { (now - 1.hour).to_s }
expect(subject).not_to be_valid
error_message = described_class.date_end_before_start_error_message
expect(subject.errors[:end_at]).to eq([error_message])
end
it "does not add error when start_at is before end_at" do
allow(subject).to receive(:start_at) { now.to_s }
allow(subject).to receive(:end_at) { (now + 1.hour).to_s }
expect(subject).to be_valid
end
end
end
end
describe "smoke authorization" do
let!(:order_cycle) { create(:order_cycle) }
let!(:user) { create(:user) }
let(:permissions) do
report_klass::Permissions.new(nil).tap do |instance|
instance.stub(allowed_order_cycles: [order_cycle])
end
end
it "does not raise error when the parameters are allowed" do
parameters = described_class.new(order_cycle_ids: [order_cycle.id.to_s])
expect { parameters.authorize!(permissions) }.not_to raise_error
end
it "raises error when the parameters are not allowed" do
parameters = described_class.new(order_cycle_ids: [(order_cycle.id + 1).to_s])
expect { parameters.authorize!(permissions) }
.to raise_error(Reports::Authorizer::ParameterNotAllowedError)
end
end
def report_klass
OrderManagement::Reports::EnterpriseFeeSummary
end
end

View File

@@ -1,95 +0,0 @@
# frozen_string_literal: true
require "spec_helper"
describe OrderManagement::Reports::EnterpriseFeeSummary::Renderers::CsvRenderer do
let(:report_klass) { OrderManagement::Reports::EnterpriseFeeSummary }
let!(:permissions) { report_klass::Permissions.new(current_user) }
let!(:parameters) { report_klass::Parameters.new }
let!(:service) { report_klass::ReportService.new(permissions, parameters) }
let!(:renderer) { described_class.new(service) }
# Context which will be passed to the renderer. The response object is not automatically prepared,
# so this has to be assigned explicitly.
let!(:response) { ActionDispatch::TestResponse.new }
let!(:request) { double(Rack::Request) }
let!(:controller) do
ActionController::Base.new.tap do |controller_mock|
controller_mock.instance_variable_set(:@_response, response)
controller_mock.instance_variable_set(:@_request, request)
end
end
let!(:enterprise_fee_type_totals) do
[
report_klass::ReportData::EnterpriseFeeTypeTotal.new(
fee_type: "Fee Type A",
enterprise_name: "Enterprise A",
fee_name: "Fee A",
customer_name: "Custoemr A",
fee_placement: "Fee Placement A",
fee_calculated_on_transfer_through_name: "Transfer Enterprise A",
tax_category_name: "Tax Category A",
total_amount: "1.00"
),
report_klass::ReportData::EnterpriseFeeTypeTotal.new(
fee_type: "Fee Type B",
enterprise_name: "Enterprise B",
fee_name: "Fee C",
customer_name: "Custoemr D",
fee_placement: "Fee Placement E",
fee_calculated_on_transfer_through_name: "Transfer Enterprise F",
tax_category_name: "Tax Category G",
total_amount: "2.00"
)
]
end
let(:current_user) { nil }
before do
allow(service).to receive(:list) { enterprise_fee_type_totals }
allow(request).to receive_messages(variant: double(Spree::Variant),
should_apply_vary_header?: true)
end
it "generates CSV header" do
renderer.render(controller)
result = response.body
csv = CSV.parse(result)
header_row = csv[0]
# Test all header cells have values
expect(header_row.length).to eq(8)
expect(header_row.all?(&:present?)).to be_truthy
end
it "generates CSV data rows" do
renderer.render(controller)
result = response.body
csv = CSV.parse(result, headers: true)
expect(csv.length).to eq(2)
# Test random cells
expect(csv[0][i18n_translate("header.fee_type")]).to eq("Fee Type A")
expect(csv[0][i18n_translate("header.total_amount")]).to eq("1.00")
expect(csv[1][i18n_translate("header.total_amount")]).to eq("2.00")
end
it "generates filename correctly" do
Timecop.freeze(Time.zone.local(2018, 10, 9, 7, 30, 0)) do
filename = renderer.__send__(:filename)
expect(filename).to eq("enterprise_fee_summary_20181009.csv")
end
end
def i18n_translate(key)
I18n.t(key, scope: i18n_scope)
end
def i18n_scope
"order_management.reports.enterprise_fee_summary.formats.csv"
end
end

View File

@@ -1,72 +0,0 @@
# frozen_string_literal: true
require "spec_helper"
describe OrderManagement::Reports::EnterpriseFeeSummary::Renderers::HtmlRenderer do
let(:report_klass) { OrderManagement::Reports::EnterpriseFeeSummary }
let!(:permissions) { report_klass::Permissions.new(current_user) }
let!(:parameters) { report_klass::Parameters.new }
let!(:controller) { OrderManagement::Reports::EnterpriseFeeSummariesController.new }
let!(:service) { report_klass::ReportService.new(permissions, parameters) }
let!(:renderer) { described_class.new(service) }
let!(:enterprise_fee_type_totals) do
[
report_klass::ReportData::EnterpriseFeeTypeTotal.new(
fee_type: "Fee Type A",
enterprise_name: "Enterprise A",
fee_name: "Fee A",
customer_name: "Custoemr A",
fee_placement: "Fee Placement A",
fee_calculated_on_transfer_through_name: "Transfer Enterprise A",
tax_category_name: "Tax Category A",
total_amount: "1.00"
),
report_klass::ReportData::EnterpriseFeeTypeTotal.new(
fee_type: "Fee Type B",
enterprise_name: "Enterprise B",
fee_name: "Fee C",
customer_name: "Custoemr D",
fee_placement: "Fee Placement E",
fee_calculated_on_transfer_through_name: "Transfer Enterprise F",
tax_category_name: "Tax Category G",
total_amount: "2.00"
)
]
end
let(:current_user) { nil }
before do
allow(service).to receive(:list) { enterprise_fee_type_totals }
end
it "generates header values" do
header_row = renderer.header
# Test all header cells have values
expect(header_row.length).to eq(8)
expect(header_row.all?(&:present?)).to be_truthy
end
it "generates data rows" do
header_row = renderer.header
result = renderer.data_rows
expect(result.length).to eq(2)
# Test random cells
expect(result[0][header_row.index(i18n_translate("header.fee_type"))]).to eq("Fee Type A")
expect(result[0][header_row.index(i18n_translate("header.total_amount"))]).to eq("1.00")
expect(result[1][header_row.index(i18n_translate("header.total_amount"))]).to eq("2.00")
end
def i18n_translate(key)
I18n.t(key, scope: i18n_scope)
end
def i18n_scope
"order_management.reports.enterprise_fee_summary.formats.csv"
end
end

View File

@@ -1,99 +0,0 @@
# frozen_string_literal: true
module OpenFoodNetwork
class CustomersReport
attr_reader :params
def initialize(user, params = {}, compile_table = false)
@params = params
@user = user
@compile_table = compile_table
end
def header
if is_mailing_list?
[I18n.t(:report_header_email),
I18n.t(:report_header_first_name),
I18n.t(:report_header_last_name),
I18n.t(:report_header_suburb)]
else
[I18n.t(:report_header_first_name),
I18n.t(:report_header_last_name),
I18n.t(:report_header_billing_address),
I18n.t(:report_header_email),
I18n.t(:report_header_phone),
I18n.t(:report_header_hub),
I18n.t(:report_header_hub_address),
I18n.t(:report_header_shipping_method)]
end
end
def table
return [] unless @compile_table
orders.map do |order|
if is_mailing_list?
[order.email,
order.billing_address.firstname,
order.billing_address.lastname,
order.billing_address.city]
else
ba = order.billing_address
da = order.distributor&.address
[ba.firstname,
ba.lastname,
[ba.address1, ba.address2, ba.city].join(" "),
order.email,
ba.phone,
order.distributor&.name,
[da&.address1, da&.address2, da&.city].join(" "),
order.shipping_method&.name]
end
end
end
def orders
filter Spree::Order.managed_by(@user).distributed_by_user(@user).complete.not_state(:canceled)
end
def filter(orders)
filter_to_supplier filter_to_distributor filter_to_order_cycle orders
end
def filter_to_supplier(orders)
if params[:supplier_id].to_i > 0
orders.select do |order|
order.line_items.includes(:product)
.where("spree_products.supplier_id = ?", params[:supplier_id].to_i)
.references(:product)
.count
.positive?
end
else
orders
end
end
def filter_to_distributor(orders)
if params[:distributor_id].to_i > 0
orders.where(distributor_id: params[:distributor_id])
else
orders
end
end
def filter_to_order_cycle(orders)
if params[:order_cycle_id].to_i > 0
orders.where(order_cycle_id: params[:order_cycle_id])
else
orders
end
end
private
def is_mailing_list?
params[:report_type] == "mailing_list"
end
end
end

View File

@@ -1,71 +0,0 @@
# frozen_string_literal: true
module OpenFoodNetwork
GroupBuyVariantRow = Struct.new(:variant, :sum_quantities, :sum_max_quantities) do
def to_row
[variant.product.supplier.name, variant.product.name, I18n.t('admin.reports.unitsize'),
variant.options_text, variant.weight, sum_quantities, sum_max_quantities]
end
end
GroupBuyProductRow = Struct.new(:product, :sum_quantities, :sum_max_quantities) do
def to_row
[product.supplier.name, product.name, I18n.t('admin.reports.unitsize'),
I18n.t('admin.reports.total'), "", sum_quantities, sum_max_quantities]
end
end
class GroupBuyReport
def initialize(orders)
@orders = orders
end
def header
[
I18n.t(:report_header_supplier),
I18n.t(:report_header_product),
I18n.t(:report_header_unit_size),
I18n.t(:report_header_variant),
I18n.t(:report_header_weight),
I18n.t(:report_header_total_ordered),
I18n.t(:report_header_total_max),
]
end
def variants_and_quantities
variants_and_quantities = []
line_items = @orders.map(&:line_items).flatten
supplier_groups = line_items.group_by { |li| li.variant.product.supplier }
supplier_groups.each do |_supplier, line_items_by_supplier|
product_groups = line_items_by_supplier.group_by { |li| li.variant.product }
product_groups.each do |product, line_items_by_product|
# Cycle thorugh variant of a product
variant_groups = line_items_by_product.group_by(&:variant)
variant_groups.each do |variant, line_items_by_variant|
sum_quantities = line_items_by_variant.to_a.sum(&:quantity)
sum_max_quantities = line_items_by_variant.sum { |li| li.max_quantity || 0 }
variants_and_quantities << GroupBuyVariantRow.new(variant, sum_quantities,
sum_max_quantities)
end
# Sum quantities for each product (Total line)
sum_quantities = line_items_by_product.sum { |li| (li.variant.weight || 0) * li.quantity }
sum_max_quantities = line_items_by_product.sum { |li|
(li.variant.weight || 0) * (li.max_quantity || 0)
}
variants_and_quantities << GroupBuyProductRow.new(product, sum_quantities,
sum_max_quantities)
end
end
variants_and_quantities
end
def table
table = []
variants_and_quantities.each do |vr|
table << vr.to_row
end
table
end
end
end

Some files were not shown because too many files have changed in this diff Show More