mirror of
https://github.com/openfoodfoundation/openfoodnetwork
synced 2026-03-04 02:31:33 +00:00
Reports Refactor 2: Move all code to lib/reporting
This commit is contained in:
committed by
Jean-Baptiste Bellet
parent
392166b57a
commit
5f78fdce8b
@@ -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'
|
||||
- 'lib/open_food_network/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,20 @@ 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/users_and_enterprises/users_and_enterprises_report.rb'
|
||||
- 'lib/reporting/reports/xero_invoices/xero_invoices_report.rb'
|
||||
- 'lib/spree/localized_number.rb'
|
||||
- 'lib/tasks/data.rake'
|
||||
- 'lib/tasks/enterprises.rake'
|
||||
@@ -176,19 +180,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_default_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 +314,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 +344,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 +411,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 +432,21 @@ Metrics/AbcSize:
|
||||
- 'app/models/spree/order/checkout.rb'
|
||||
- 'app/models/spree/preferences/preferable_class_methods.rb'
|
||||
- 'app/models/spree/return_authorization.rb'
|
||||
- 'lib/open_food_network/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/orders_and_distributors_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 +470,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 +485,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,19 +529,18 @@ Metrics/ClassLength:
|
||||
- 'app/services/cart_service.rb'
|
||||
- 'app/services/order_syncer.rb'
|
||||
- 'engines/order_management/app/services/order_management/order/updater.rb'
|
||||
- 'lib/open_food_network/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/open_food_network/bulk_coop_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/users_and_enterprises/users_and_enterprises_report.rb'
|
||||
- 'lib/reporting/reports/xero_invoices/xero_invoices_report.rb'
|
||||
|
||||
# Offense count: 40
|
||||
# Offense count: 39
|
||||
# Configuration parameters: IgnoredMethods, Max.
|
||||
Metrics/CyclomaticComplexity:
|
||||
Exclude:
|
||||
@@ -556,20 +566,19 @@ Metrics/CyclomaticComplexity:
|
||||
- 'app/models/spree/tax_rate.rb'
|
||||
- 'app/models/spree/variant.rb'
|
||||
- 'app/models/spree/zone.rb'
|
||||
- 'lib/open_food_network/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_fulfillment_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/xero_invoices_report.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:
|
||||
@@ -579,23 +588,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'
|
||||
- 'lib/open_food_network/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/xero_invoices_report.rb'
|
||||
- 'lib/tasks/sample_data/product_factory.rb'
|
||||
|
||||
# Offense count: 51
|
||||
# Offense count: 54
|
||||
# Configuration parameters: CountComments, Max, CountAsOne.
|
||||
Metrics/ModuleLength:
|
||||
Exclude:
|
||||
@@ -626,17 +635,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_default_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'
|
||||
@@ -657,11 +669,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/xero_invoices_report.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:
|
||||
@@ -670,9 +682,8 @@ Metrics/PerceivedComplexity:
|
||||
- 'app/models/enterprise_relationship.rb'
|
||||
- 'app/models/spree/ability.rb'
|
||||
- 'app/models/spree/order/checkout.rb'
|
||||
- 'lib/open_food_network/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:
|
||||
@@ -717,7 +728,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_fulfillment_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'
|
||||
@@ -896,7 +907,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'
|
||||
@@ -1093,9 +1104,9 @@ Style/MissingRespondToMissing:
|
||||
# Offense count: 1
|
||||
Style/MixinUsage:
|
||||
Exclude:
|
||||
- 'lib/open_food_network/orders_and_fulfillment_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
|
||||
@@ -1119,7 +1130,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:
|
||||
@@ -1133,18 +1144,17 @@ Style/OptionalBooleanParameter:
|
||||
- 'app/models/spree/order_contents.rb'
|
||||
- 'app/models/spree/preferences/file_configuration.rb'
|
||||
- 'app/models/spree/shipment.rb'
|
||||
- 'lib/open_food_network/bulk_coop_report.rb'
|
||||
- 'engines/order_management/app/services/order_management/stock/estimator.rb'
|
||||
- 'engines/order_management/app/services/order_management/reports/enterprise_fee_summary/enterprise_fee_summary_report.rb'
|
||||
- 'lib/open_food_network/customers_report.rb'
|
||||
- 'lib/open_food_network/orders_and_distributors_report.rb'
|
||||
- 'lib/open_food_network/order_cycle_management_report.rb'
|
||||
- 'lib/open_food_network/orders_and_fulfillment_report.rb'
|
||||
- 'lib/open_food_network/payments_report.rb'
|
||||
- 'lib/open_food_network/products_and_inventory_report.rb'
|
||||
- 'lib/open_food_network/users_and_enterprises_report.rb'
|
||||
- 'lib/open_food_network/xero_invoices_report.rb'
|
||||
- 'lib/open_food_network/bulk_coop_report.rb'
|
||||
- 'lib/reporting/reports/bulk_coop/bulk_coop_report.rb'
|
||||
- 'lib/reporting/reports/customers/customers_report.rb'
|
||||
- 'lib/reporting/reports/enterprise_fee_summary/enterprise_fee_summary_report.rb'
|
||||
- 'lib/reporting/reports/order_cycle_management/order_cycle_management_report.rb'
|
||||
- 'lib/reporting/reports/orders_and_distributors/orders_and_distributors_report.rb'
|
||||
- 'lib/reporting/reports/orders_and_fulfillment/orders_and_fulfillment_report.rb'
|
||||
- 'lib/reporting/reports/payments/payments_report.rb'
|
||||
- 'lib/reporting/reports/products_and_inventory/products_and_inventory_report.rb'
|
||||
- 'lib/reporting/reports/users_and_enterprises/users_and_enterprises_report.rb'
|
||||
- 'lib/reporting/reports/xero_invoices/xero_invoices_report.rb'
|
||||
- 'lib/spree/core/controller_helpers/order.rb'
|
||||
- 'lib/spree/core/delegate_belongs_to.rb'
|
||||
- 'spec/support/request/web_helper.rb'
|
||||
@@ -1164,11 +1174,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'
|
||||
@@ -1180,13 +1189,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'
|
||||
@@ -1211,7 +1217,7 @@ Style/SingleArgumentDig:
|
||||
Exclude:
|
||||
- 'app/services/checkout/form_data_adapter.rb'
|
||||
|
||||
# Offense count: 5
|
||||
# Offense count: 4
|
||||
# Cop supports --auto-correct.
|
||||
Style/SlicingWithRange:
|
||||
Exclude:
|
||||
@@ -1219,9 +1225,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:
|
||||
@@ -1238,11 +1243,8 @@ Style/StringConcatenation:
|
||||
- 'app/serializers/api/enterprise_shopfront_list_serializer.rb'
|
||||
- 'app/services/embedded_page_service.rb'
|
||||
- 'app/services/products_renderer.rb'
|
||||
- 'lib/open_food_network/bulk_coop_report.rb'
|
||||
- 'lib/open_food_network/orders_and_fulfillment_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'
|
||||
|
||||
@@ -1,19 +1,17 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'open_food_network/reports/list'
|
||||
require 'open_food_network/orders_and_distributors_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_fulfillment_report'
|
||||
require 'open_food_network/bulk_coop_report'
|
||||
# require 'open_food_network/orders_and_distributors_report'
|
||||
# require 'open_food_network/products_and_inventory_report'
|
||||
# require 'open_food_network/lettuce_share_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_fulfillment_report'
|
||||
# require 'open_food_network/bulk_coop_report'
|
||||
|
||||
module Spree
|
||||
module Admin
|
||||
@@ -30,7 +28,7 @@ module Spree
|
||||
respond_to :html
|
||||
|
||||
def report_types
|
||||
OpenFoodNetwork::Reports::List.all
|
||||
Reporting::Reports::List.all
|
||||
end
|
||||
|
||||
def index
|
||||
@@ -119,11 +117,7 @@ module Spree
|
||||
def render_report
|
||||
@report_subtypes = report_types[action_name.to_sym]
|
||||
@report_subtype = params[:report_subtype]
|
||||
klass = if action_name == 'enterprise_fee_summary'
|
||||
OrderManagement::Reports::EnterpriseFeeSummary::EnterpriseFeeSummaryReport
|
||||
else
|
||||
"OpenFoodNetwork::#{action_name.camelize}Report".constantize
|
||||
end
|
||||
klass = "Reporting::Reports::#{action_name.camelize}::#{action_name.camelize}Report".constantize
|
||||
@report = klass.new spree_current_user, raw_params, render_content?
|
||||
if report_format.present?
|
||||
data = Reporting::ReportRenderer.new(@report).public_send("to_#{report_format}")
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -1,63 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module OpenFoodNetwork
|
||||
class BulkCoopAllocationReport
|
||||
def table_headers
|
||||
[
|
||||
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
|
||||
@@ -1,320 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "open_food_network/reports/line_items"
|
||||
require 'open_food_network/order_grouper'
|
||||
require 'open_food_network/bulk_coop_allocation_report'
|
||||
require 'open_food_network/bulk_coop_supplier_report'
|
||||
|
||||
module OpenFoodNetwork
|
||||
class BulkCoopReport
|
||||
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 table_headers
|
||||
case params[:report_subtype]
|
||||
when "bulk_coop_supplier_report"
|
||||
@supplier_report.table_headers
|
||||
when "bulk_coop_allocation"
|
||||
@allocation_report.table_headers
|
||||
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 table_rows
|
||||
order_grouper = OpenFoodNetwork::OrderGrouper.new rules, columns, self
|
||||
order_grouper.table(table_items)
|
||||
end
|
||||
|
||||
def rules
|
||||
case params[:report_subtype]
|
||||
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_subtype]
|
||||
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)
|
||||
unit_size = line_items.first.variant.product.group_buy_unit_size || 0.0
|
||||
unit_size / (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
|
||||
@@ -1,61 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module OpenFoodNetwork
|
||||
class BulkCoopSupplierReport
|
||||
def table_headers
|
||||
[
|
||||
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
|
||||
@@ -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 table_headers
|
||||
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_rows
|
||||
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_subtype] == "mailing_list"
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,89 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'variant_units/option_value_namer'
|
||||
|
||||
module OpenFoodNetwork
|
||||
class LettuceShareReport
|
||||
attr_reader :context
|
||||
|
||||
delegate :variants, :render_table, to: :context
|
||||
|
||||
def initialize(context)
|
||||
@context = context
|
||||
end
|
||||
|
||||
def table_headers
|
||||
# NOTE: These are NOT to be translated, they need to be in this exact format to work with LettucShare
|
||||
[
|
||||
"PRODUCT",
|
||||
"Description",
|
||||
"Qty",
|
||||
"Pack Size",
|
||||
"Unit",
|
||||
"Unit Price",
|
||||
"Total",
|
||||
"GST incl.",
|
||||
"Grower and growing method",
|
||||
"Taxon"
|
||||
]
|
||||
end
|
||||
|
||||
def table_rows
|
||||
return [] unless render_table
|
||||
|
||||
variants.select(&:in_stock?)
|
||||
.map do |variant|
|
||||
[
|
||||
variant.product.name,
|
||||
variant.full_name,
|
||||
'',
|
||||
VariantUnits::OptionValueNamer.new(variant).value,
|
||||
VariantUnits::OptionValueNamer.new(variant).unit,
|
||||
variant.price,
|
||||
'',
|
||||
gst(variant),
|
||||
grower_and_method(variant),
|
||||
variant.product.primary_taxon.name
|
||||
]
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def gst(variant)
|
||||
tax_category = variant.product.tax_category
|
||||
if tax_category && tax_category.tax_rates.present?
|
||||
tax_rate = tax_category.tax_rates.first
|
||||
line_item = mock_line_item(variant)
|
||||
tax_rate.calculator.compute line_item
|
||||
else
|
||||
0
|
||||
end
|
||||
end
|
||||
|
||||
def mock_line_item(variant)
|
||||
line_item = Spree::LineItem.new quantity: 1
|
||||
line_item.define_singleton_method(:product) { variant.product }
|
||||
line_item.define_singleton_method(:price) { variant.price }
|
||||
line_item
|
||||
end
|
||||
|
||||
def grower_and_method(variant)
|
||||
cert = certification(variant)
|
||||
|
||||
result = producer_name(variant)
|
||||
result += " (#{cert})" if cert.present?
|
||||
result
|
||||
end
|
||||
|
||||
def producer_name(variant)
|
||||
variant.product.supplier.name
|
||||
end
|
||||
|
||||
def certification(variant)
|
||||
variant.product.properties_including_inherited.map do |p|
|
||||
"#{p[:name]} - #{p[:value]}"
|
||||
end.join(', ')
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,165 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module OpenFoodNetwork
|
||||
class OrderCycleManagementReport
|
||||
DEFAULT_DATE_INTERVAL = { from: -1.month, to: 1.day }.freeze
|
||||
|
||||
attr_reader :params
|
||||
|
||||
def initialize(user, params = {}, render_table = false)
|
||||
@params = sanitize_params(params)
|
||||
@user = user
|
||||
@render_table = render_table
|
||||
end
|
||||
|
||||
def table_headers
|
||||
if is_payment_methods?
|
||||
[
|
||||
I18n.t(:report_header_first_name),
|
||||
I18n.t(:report_header_last_name),
|
||||
I18n.t(:report_header_hub),
|
||||
I18n.t(:report_header_hub_code),
|
||||
I18n.t(:report_header_email),
|
||||
I18n.t(:report_header_phone),
|
||||
I18n.t(:report_header_shipping_method),
|
||||
I18n.t(:report_header_payment_method),
|
||||
I18n.t(:report_header_amount),
|
||||
I18n.t(:report_header_balance),
|
||||
]
|
||||
else
|
||||
[
|
||||
I18n.t(:report_header_first_name),
|
||||
I18n.t(:report_header_last_name),
|
||||
I18n.t(:report_header_hub),
|
||||
I18n.t(:report_header_hub_code),
|
||||
I18n.t(:report_header_delivery_address),
|
||||
I18n.t(:report_header_delivery_postcode),
|
||||
I18n.t(:report_header_phone),
|
||||
I18n.t(:report_header_shipping_method),
|
||||
I18n.t(:report_header_payment_method),
|
||||
I18n.t(:report_header_amount),
|
||||
I18n.t(:report_header_balance),
|
||||
I18n.t(:report_header_temp_controlled_items),
|
||||
I18n.t(:report_header_special_instructions),
|
||||
]
|
||||
end
|
||||
end
|
||||
|
||||
def search
|
||||
Spree::Order.
|
||||
finalized.
|
||||
not_state(:canceled).
|
||||
distributed_by_user(@user).
|
||||
managed_by(@user).
|
||||
ransack(params[:q])
|
||||
end
|
||||
|
||||
def orders
|
||||
search_result = search.result.order(:completed_at)
|
||||
orders_with_balance = OutstandingBalance.new(search_result).
|
||||
query.
|
||||
select('spree_orders.*')
|
||||
|
||||
filter(orders_with_balance)
|
||||
end
|
||||
|
||||
def table_rows
|
||||
return [] unless @render_table
|
||||
|
||||
if is_payment_methods?
|
||||
orders.map { |o| payment_method_row o }
|
||||
else
|
||||
orders.map { |o| delivery_row o }
|
||||
end
|
||||
end
|
||||
|
||||
def filter(search_result)
|
||||
filter_to_payment_method filter_to_shipping_method filter_to_order_cycle search_result
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# This method relies on `balance_value` as a computed DB column. See `CompleteOrdersWithBalance`
|
||||
# for reference.
|
||||
def balance(order)
|
||||
order.balance_value
|
||||
end
|
||||
|
||||
def payment_method_row(order)
|
||||
ba = order.billing_address
|
||||
[ba&.firstname,
|
||||
ba&.lastname,
|
||||
order.distributor&.name,
|
||||
customer_code(order.email),
|
||||
order.email,
|
||||
ba&.phone,
|
||||
order.shipping_method&.name,
|
||||
order.payments.last&.payment_method&.name,
|
||||
order.total,
|
||||
balance(order)]
|
||||
end
|
||||
|
||||
def delivery_row(order)
|
||||
sa = order.shipping_address
|
||||
[sa.firstname,
|
||||
sa.lastname,
|
||||
order.distributor&.name,
|
||||
customer_code(order.email),
|
||||
"#{sa.address1} #{sa.address2} #{sa.city}",
|
||||
sa.zipcode,
|
||||
sa.phone,
|
||||
order.shipping_method&.name,
|
||||
order.payments.first&.payment_method&.name,
|
||||
order.total,
|
||||
balance(order),
|
||||
has_temperature_controlled_items?(order),
|
||||
order.special_instructions]
|
||||
end
|
||||
|
||||
def filter_to_payment_method(orders)
|
||||
if params[:payment_method_in].present?
|
||||
orders.joins(payments: :payment_method).where(spree_payments: { payment_method_id: params[:payment_method_in] })
|
||||
else
|
||||
orders
|
||||
end
|
||||
end
|
||||
|
||||
def filter_to_shipping_method(orders)
|
||||
if params[:shipping_method_in].present?
|
||||
orders.joins(shipments: :shipping_rates).where(spree_shipping_rates: { selected: true, shipping_method_id: params[:shipping_method_in] })
|
||||
else
|
||||
orders
|
||||
end
|
||||
end
|
||||
|
||||
def filter_to_order_cycle(orders)
|
||||
if params[:order_cycle_id].present?
|
||||
orders.where(order_cycle_id: params[:order_cycle_id])
|
||||
else
|
||||
orders
|
||||
end
|
||||
end
|
||||
|
||||
def has_temperature_controlled_items?(order)
|
||||
order.line_items.any? { |line_item|
|
||||
line_item.product.shipping_category&.temperature_controlled
|
||||
}
|
||||
end
|
||||
|
||||
def is_payment_methods?
|
||||
params[:report_subtype] == "payment_methods"
|
||||
end
|
||||
|
||||
def customer_code(email)
|
||||
customer = Customer.where(email: email).first
|
||||
customer.nil? ? "" : customer.code
|
||||
end
|
||||
|
||||
def sanitize_params(params)
|
||||
params[:q] ||= {}
|
||||
params[:q][:completed_at_gt] ||= Time.zone.today + DEFAULT_DATE_INTERVAL[:from]
|
||||
params[:q][:completed_at_lt] ||= Time.zone.today + DEFAULT_DATE_INTERVAL[:to]
|
||||
params
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -25,7 +25,7 @@ module OpenFoodNetwork
|
||||
if @coordinator.sells == "any"
|
||||
# If the coordinator sells any, relationships come into play
|
||||
related_enterprises_granting(:add_to_order_cycle,
|
||||
to: [@coordinator.id]).each do |enterprise_id|
|
||||
to: [@coordinator.id]).each do |enterprise_id|
|
||||
coordinator_permitted_ids << enterprise_id
|
||||
end
|
||||
|
||||
@@ -92,8 +92,8 @@ module OpenFoodNetwork
|
||||
variant_ids = Spree::Variant.joins(:exchanges).
|
||||
where(
|
||||
"exchanges.receiver_id IN (?)
|
||||
AND exchanges.order_cycle_id = (?)
|
||||
AND exchanges.incoming = 'f'",
|
||||
AND exchanges.order_cycle_id = (?)
|
||||
AND exchanges.incoming = 'f'",
|
||||
managed_participating_hubs.select("enterprises.id"),
|
||||
@order_cycle
|
||||
).pluck(:id).uniq
|
||||
@@ -125,7 +125,7 @@ module OpenFoodNetwork
|
||||
# Find the variants that a user can POTENTIALLY see within incoming exchanges
|
||||
def visible_variants_for_incoming_exchanges_from(producer)
|
||||
if @order_cycle &&
|
||||
(user_manages_coordinator_or(producer) || user_is_permitted_add_to_oc_by(producer))
|
||||
(user_manages_coordinator_or(producer) || user_is_permitted_add_to_oc_by(producer))
|
||||
all_variants_supplied_by(producer)
|
||||
else
|
||||
no_variants
|
||||
@@ -188,8 +188,8 @@ module OpenFoodNetwork
|
||||
# so things don't break. TODO: Remove this when all P-OC are sorted out
|
||||
active_variants = Spree::Variant.joins(:exchanges, :product).
|
||||
where("exchanges.receiver_id = (?)
|
||||
AND spree_products.supplier_id IN (?)
|
||||
AND incoming = 'f'",
|
||||
AND spree_products.supplier_id IN (?)
|
||||
AND incoming = 'f'",
|
||||
hub.id,
|
||||
managed_producer_ids)
|
||||
|
||||
@@ -212,8 +212,8 @@ module OpenFoodNetwork
|
||||
# Variants produced by MY PRODUCERS that are in this OC,
|
||||
# where my producer has granted P-OC to the hub
|
||||
granting_producer_ids = related_enterprises_granting(:add_to_order_cycle,
|
||||
to: [hub.id],
|
||||
scope: granted_producers)
|
||||
to: [hub.id],
|
||||
scope: granted_producers)
|
||||
permitted_variants = variants_from_suppliers(granting_producer_ids)
|
||||
|
||||
Spree::Variant.where(id: permitted_variants)
|
||||
@@ -299,8 +299,8 @@ module OpenFoodNetwork
|
||||
# any incoming exchanges supplying variants in my outgoing exchanges
|
||||
variant_ids = Spree::Variant.joins(:exchanges).
|
||||
where("exchanges.receiver_id IN (?)
|
||||
AND exchanges.order_cycle_id = (?)
|
||||
AND exchanges.incoming = 'f'",
|
||||
AND exchanges.order_cycle_id = (?)
|
||||
AND exchanges.incoming = 'f'",
|
||||
hubs.select("enterprises.id"),
|
||||
@order_cycle).pluck(:id).uniq
|
||||
|
||||
|
||||
@@ -1,112 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module OpenFoodNetwork
|
||||
class OrdersAndDistributorsReport
|
||||
def initialize(user, params = {}, render_table = false)
|
||||
@params = params
|
||||
@user = user
|
||||
@render_table = render_table
|
||||
|
||||
@permissions = ::Permissions::Order.new(user, @params[:q])
|
||||
end
|
||||
|
||||
def table_headers
|
||||
[
|
||||
I18n.t(:report_header_order_date),
|
||||
I18n.t(:report_header_order_id),
|
||||
I18n.t(:report_header_customer_name),
|
||||
I18n.t(:report_header_customer_email),
|
||||
I18n.t(:report_header_customer_phone),
|
||||
I18n.t(:report_header_customer_city),
|
||||
I18n.t(:report_header_sku),
|
||||
I18n.t(:report_header_item_name),
|
||||
I18n.t(:report_header_variant),
|
||||
I18n.t(:report_header_quantity),
|
||||
I18n.t(:report_header_max_quantity),
|
||||
I18n.t(:report_header_cost),
|
||||
I18n.t(:report_header_shipping_cost),
|
||||
I18n.t(:report_header_payment_method),
|
||||
I18n.t(:report_header_distributor),
|
||||
I18n.t(:report_header_distributor_address),
|
||||
I18n.t(:report_header_distributor_city),
|
||||
I18n.t(:report_header_distributor_postcode),
|
||||
I18n.t(:report_header_shipping_method),
|
||||
I18n.t(:report_header_shipping_instructions)
|
||||
]
|
||||
end
|
||||
|
||||
def search
|
||||
@permissions.visible_orders.select("DISTINCT spree_orders.*").
|
||||
complete.not_state(:canceled).
|
||||
ransack(@params[:q])
|
||||
end
|
||||
|
||||
def table_rows
|
||||
return [] unless @render_table
|
||||
|
||||
orders = search.result
|
||||
|
||||
orders.select{ |order| orders_with_hidden_details(orders).include? order }.each do |order|
|
||||
OrderDataMasker.new(order).call
|
||||
end
|
||||
|
||||
line_item_details orders
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def orders_with_hidden_details(orders)
|
||||
# If empty array is passed in, the where clause will return all line_items, which is bad
|
||||
if @permissions.editable_orders.empty?
|
||||
orders
|
||||
else
|
||||
orders.
|
||||
where('spree_orders.id NOT IN (?)',
|
||||
@permissions.editable_orders.select(&:id))
|
||||
end
|
||||
end
|
||||
|
||||
def line_item_details(orders)
|
||||
order_and_distributor_details = []
|
||||
|
||||
orders.each do |order|
|
||||
order.line_items.each do |line_item|
|
||||
order_and_distributor_details << row_for(line_item, order)
|
||||
end
|
||||
end
|
||||
|
||||
order_and_distributor_details
|
||||
end
|
||||
|
||||
# Returns a row with the data to display for the specified line_item and
|
||||
# its order
|
||||
#
|
||||
# @param line_item [Spree::LineItem]
|
||||
# @param order [Spree::Order]
|
||||
# @return [Array]
|
||||
def row_for(line_item, order)
|
||||
[
|
||||
order.completed_at.strftime("%F %T"),
|
||||
order.id,
|
||||
order.bill_address.full_name,
|
||||
order.email,
|
||||
order.bill_address.phone,
|
||||
order.bill_address.city,
|
||||
line_item.product.sku,
|
||||
line_item.product.name,
|
||||
line_item.options_text,
|
||||
line_item.quantity,
|
||||
line_item.max_quantity,
|
||||
line_item.price * line_item.quantity,
|
||||
line_item.distribution_fee,
|
||||
order.payments.first&.payment_method&.name,
|
||||
order.distributor&.name,
|
||||
order.distributor.address.address1,
|
||||
order.distributor.address.city,
|
||||
order.distributor.address.zipcode,
|
||||
order.shipping_method&.name,
|
||||
order.special_instructions
|
||||
]
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,120 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "open_food_network/reports/line_items"
|
||||
require "open_food_network/orders_and_fulfillment_report/supplier_totals_report"
|
||||
require "open_food_network/orders_and_fulfillment_report/supplier_totals_by_distributor_report"
|
||||
require "open_food_network/orders_and_fulfillment_report/distributor_totals_by_supplier_report"
|
||||
require "open_food_network/orders_and_fulfillment_report/customer_totals_report"
|
||||
require 'open_food_network/orders_and_fulfillment_report/default_report'
|
||||
require 'open_food_network/order_grouper'
|
||||
|
||||
include Spree::ReportsHelper
|
||||
|
||||
module OpenFoodNetwork
|
||||
class OrdersAndFulfillmentReport
|
||||
attr_reader :options, :report_type
|
||||
|
||||
delegate :table_headers, :rules, :columns, to: :report
|
||||
|
||||
def initialize(user, options = {}, render_table = false)
|
||||
@user = user
|
||||
@options = options
|
||||
@report_type = options[:report_subtype]
|
||||
@render_table = render_table
|
||||
@variant_scopers_by_distributor_id = {}
|
||||
end
|
||||
|
||||
def search
|
||||
report_line_items.orders
|
||||
end
|
||||
|
||||
def table_items
|
||||
return [] unless @render_table
|
||||
|
||||
report_line_items.list(report.line_item_includes)
|
||||
end
|
||||
|
||||
def table_rows
|
||||
order_grouper = OpenFoodNetwork::OrderGrouper.new report.rules, report.columns, report
|
||||
order_grouper.table(table_items)
|
||||
end
|
||||
|
||||
def line_item_name
|
||||
proc { |line_item| line_item.variant.full_name }
|
||||
end
|
||||
|
||||
def line_items_name
|
||||
proc { |line_items| line_items.first.variant.full_name }
|
||||
end
|
||||
|
||||
def supplier_name
|
||||
proc { |line_items| line_items.first.variant.product.supplier.name }
|
||||
end
|
||||
|
||||
def product_name
|
||||
proc { |line_items| line_items.first.variant.product.name }
|
||||
end
|
||||
|
||||
def total_units(line_items)
|
||||
return " " if not_all_have_unit?(line_items)
|
||||
|
||||
total_units = line_items.sum do |li|
|
||||
product = li.variant.product
|
||||
li.quantity * li.unit_value / scale_factor(product)
|
||||
end
|
||||
|
||||
total_units.round(3)
|
||||
end
|
||||
|
||||
def variant_scoper_for(distributor_id)
|
||||
@variant_scopers_by_distributor_id[distributor_id] ||=
|
||||
OpenFoodNetwork::ScopeVariantToHub.new(
|
||||
distributor_id,
|
||||
report_variant_overrides[distributor_id] || {},
|
||||
)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def report
|
||||
@report ||= report_klass.new(self)
|
||||
end
|
||||
|
||||
def report_klass
|
||||
case report_type
|
||||
when SupplierTotalsReport::REPORT_TYPE then SupplierTotalsReport
|
||||
when SupplierTotalsByDistributorReport::REPORT_TYPE then SupplierTotalsByDistributorReport
|
||||
when DistributorTotalsBySupplierReport::REPORT_TYPE then DistributorTotalsBySupplierReport
|
||||
when CustomerTotalsReport::REPORT_TYPE then CustomerTotalsReport
|
||||
else
|
||||
DefaultReport
|
||||
end
|
||||
end
|
||||
|
||||
def not_all_have_unit?(line_items)
|
||||
line_items.map { |li| li.unit_value.nil? }.any?
|
||||
end
|
||||
|
||||
def scale_factor(product)
|
||||
product.variant_unit == 'weight' ? 1000 : 1
|
||||
end
|
||||
|
||||
def order_permissions
|
||||
return @order_permissions unless @order_permissions.nil?
|
||||
|
||||
@order_permissions = ::Permissions::Order.new(@user, options[:q])
|
||||
end
|
||||
|
||||
def report_line_items
|
||||
@report_line_items ||= Reports::LineItems.new(order_permissions, options)
|
||||
end
|
||||
|
||||
def report_variant_overrides
|
||||
@report_variant_overrides ||=
|
||||
VariantOverridesIndexed.new(
|
||||
order_permissions.visible_line_items.select('DISTINCT variant_id'),
|
||||
report_line_items.orders.result.select('DISTINCT distributor_id'),
|
||||
).indexed
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,221 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# rubocop:disable Metrics/ClassLength
|
||||
module OpenFoodNetwork
|
||||
class OrdersAndFulfillmentReport
|
||||
class CustomerTotalsReport
|
||||
REPORT_TYPE = "order_cycle_customer_totals"
|
||||
|
||||
attr_reader :context
|
||||
|
||||
delegate :line_item_name, to: :context
|
||||
delegate :variant_scoper_for, to: :context
|
||||
|
||||
def initialize(context)
|
||||
@context = context
|
||||
@scopers_by_distributor_id = {}
|
||||
end
|
||||
|
||||
# rubocop:disable Metrics/AbcSize
|
||||
def table_headers
|
||||
[I18n.t(:report_header_hub), I18n.t(:report_header_customer), I18n.t(:report_header_email),
|
||||
I18n.t(:report_header_phone), I18n.t(:report_header_producer),
|
||||
I18n.t(:report_header_product), I18n.t(:report_header_variant),
|
||||
I18n.t(:report_header_quantity),
|
||||
I18n.t(:report_header_item_price, currency: currency_symbol),
|
||||
I18n.t(:report_header_item_fees_price, currency: currency_symbol),
|
||||
I18n.t(:report_header_admin_handling_fees, currency: currency_symbol),
|
||||
I18n.t(:report_header_ship_price, currency: currency_symbol),
|
||||
I18n.t(:report_header_pay_fee_price, currency: currency_symbol),
|
||||
I18n.t(:report_header_total_price, currency: currency_symbol),
|
||||
I18n.t(:report_header_paid), I18n.t(:report_header_shipping),
|
||||
I18n.t(:report_header_delivery), I18n.t(:report_header_ship_street),
|
||||
I18n.t(:report_header_ship_street_2), I18n.t(:report_header_ship_city),
|
||||
I18n.t(:report_header_ship_postcode), I18n.t(:report_header_ship_state),
|
||||
I18n.t(:report_header_comments), I18n.t(:report_header_sku),
|
||||
I18n.t(:report_header_order_cycle), I18n.t(:report_header_payment_method),
|
||||
I18n.t(:report_header_customer_code), I18n.t(:report_header_tags),
|
||||
I18n.t(:report_header_billing_street), I18n.t(:report_header_billing_street_2),
|
||||
I18n.t(:report_header_billing_city), I18n.t(:report_header_billing_postcode),
|
||||
I18n.t(:report_header_billing_state),
|
||||
I18n.t(:report_header_order_number),
|
||||
I18n.t(:report_header_date)]
|
||||
end
|
||||
|
||||
# rubocop:enable Metrics/AbcSize
|
||||
# rubocop:disable Metrics/AbcSize
|
||||
# rubocop:disable Metrics/MethodLength
|
||||
def rules
|
||||
[
|
||||
{
|
||||
group_by: proc { |line_item| line_item.order.distributor },
|
||||
sort_by: proc { |distributor| distributor.name }
|
||||
},
|
||||
{
|
||||
group_by: proc { |line_item| line_item.order },
|
||||
sort_by: proc { |order| order.bill_address.full_name_reverse },
|
||||
summary_columns: [
|
||||
proc { |line_items| line_items.first.order.distributor.name },
|
||||
proc { |line_items| line_items.first.order.bill_address.full_name },
|
||||
proc { |_line_items| "" },
|
||||
proc { |_line_items| "" },
|
||||
proc { |_line_items| "" },
|
||||
proc { |_line_items| I18n.t('admin.reports.total') },
|
||||
proc { |_line_items| "" },
|
||||
|
||||
proc { |_line_items| "" },
|
||||
proc { |line_items| line_items.sum(&:amount) },
|
||||
proc { |line_items| line_items.sum(&:amount_with_adjustments) },
|
||||
proc { |line_items| line_items.first.order.admin_and_handling_total },
|
||||
proc { |line_items| line_items.first.order.ship_total },
|
||||
proc { |line_items| line_items.first.order.payment_fee },
|
||||
proc { |line_items| line_items.first.order.total },
|
||||
proc { |line_items| line_items.first.order.paid? ? I18n.t(:yes) : I18n.t(:no) },
|
||||
|
||||
proc { |_line_items| "" },
|
||||
proc { |_line_items| "" },
|
||||
|
||||
proc { |_line_items| "" },
|
||||
proc { |_line_items| "" },
|
||||
proc { |_line_items| "" },
|
||||
proc { |_line_items| "" },
|
||||
proc { |_line_items| "" },
|
||||
|
||||
proc { |line_items| line_items.first.order.special_instructions },
|
||||
proc { |_line_items| "" },
|
||||
|
||||
proc { |line_items| line_items.first.order.order_cycle&.name },
|
||||
proc { |line_items|
|
||||
line_items.first.order.payments.first&.payment_method&.name
|
||||
},
|
||||
proc { |_line_items| "" },
|
||||
proc { |_line_items| "" },
|
||||
proc { |_line_items| "" },
|
||||
proc { |_line_items| "" },
|
||||
proc { |_line_items| "" },
|
||||
proc { |_line_items| "" },
|
||||
proc { |_line_items| "" },
|
||||
proc { |line_items| line_items.first.order.number },
|
||||
proc { |line_items| line_items.first.order.completed_at.strftime("%F %T") },
|
||||
]
|
||||
},
|
||||
{
|
||||
group_by: proc { |line_item| line_item.variant.product },
|
||||
sort_by: proc { |product| product.name }
|
||||
},
|
||||
{
|
||||
group_by: proc { |line_item| line_item.variant },
|
||||
sort_by: proc { |variant| variant.full_name }
|
||||
},
|
||||
{
|
||||
group_by: line_item_name,
|
||||
sort_by: proc { |full_name| full_name }
|
||||
}
|
||||
]
|
||||
end
|
||||
# rubocop:enable Metrics/AbcSize
|
||||
# rubocop:enable Metrics/MethodLength
|
||||
|
||||
# rubocop:disable Metrics/AbcSize
|
||||
# rubocop:disable Metrics/MethodLength
|
||||
# rubocop:disable Metrics/PerceivedComplexity
|
||||
def columns
|
||||
rsa = proc { |line_items| shipping_method(line_items)&.delivery? }
|
||||
[
|
||||
proc { |line_items| line_items.first.order.distributor.name },
|
||||
proc { |line_items|
|
||||
bill_address = line_items.first.order.bill_address
|
||||
bill_address.firstname + " " + bill_address.lastname
|
||||
},
|
||||
proc { |line_items| line_items.first.order.email },
|
||||
proc { |line_items| line_items.first.order.bill_address.phone },
|
||||
proc { |line_items| line_items.first.variant.product.supplier.name },
|
||||
proc { |line_items| line_items.first.variant.product.name },
|
||||
proc { |line_items| line_items.first.variant.full_name },
|
||||
|
||||
proc { |line_items| line_items.to_a.sum(&:quantity) },
|
||||
proc { |line_items| line_items.sum(&:amount) },
|
||||
proc { |line_items| line_items.sum(&:amount_with_adjustments) },
|
||||
proc { |_line_items| "" },
|
||||
proc { |_line_items| "" },
|
||||
proc { |_line_items| "" },
|
||||
proc { |_line_items| "" },
|
||||
proc { |line_items|
|
||||
line_items.all? { |li| li.order.paid? } ? I18n.t(:yes) : I18n.t(:no)
|
||||
},
|
||||
|
||||
proc { |line_items| shipping_method(line_items)&.name },
|
||||
proc { |line_items| rsa.call(line_items) ? I18n.t(:yes) : I18n.t(:no) },
|
||||
|
||||
proc { |line_items|
|
||||
line_items.first.order.ship_address&.address1 if rsa.call(line_items)
|
||||
},
|
||||
proc { |line_items|
|
||||
line_items.first.order.ship_address&.address2 if rsa.call(line_items)
|
||||
},
|
||||
proc { |line_items|
|
||||
line_items.first.order.ship_address&.city if rsa.call(line_items)
|
||||
},
|
||||
proc { |line_items|
|
||||
line_items.first.order.ship_address&.zipcode if rsa.call(line_items)
|
||||
},
|
||||
proc { |line_items|
|
||||
line_items.first.order.ship_address&.state if rsa.call(line_items)
|
||||
},
|
||||
|
||||
proc { |_line_items| "" },
|
||||
proc do |line_items|
|
||||
line_item = line_items.first
|
||||
variant_scoper_for(line_item.order.distributor_id).scope(line_item.variant)
|
||||
line_item.variant.sku
|
||||
end,
|
||||
|
||||
proc { |line_items| line_items.first.order.order_cycle&.name },
|
||||
proc { |line_items|
|
||||
payment = line_items.first.order.payments.first
|
||||
payment&.payment_method&.name
|
||||
},
|
||||
proc { |line_items|
|
||||
distributor = line_items.first.order.distributor
|
||||
user = line_items.first.order.user
|
||||
user&.customer_of(distributor)&.code
|
||||
},
|
||||
proc { |line_items|
|
||||
distributor = line_items.first.order.distributor
|
||||
user = line_items.first.order.user
|
||||
user&.customer_of(distributor)&.tags&.join(', ')
|
||||
},
|
||||
|
||||
proc { |line_items| line_items.first.order.bill_address&.address1 },
|
||||
proc { |line_items| line_items.first.order.bill_address&.address2 },
|
||||
proc { |line_items| line_items.first.order.bill_address&.city },
|
||||
proc { |line_items| line_items.first.order.bill_address&.zipcode },
|
||||
proc { |line_items| line_items.first.order.bill_address&.state },
|
||||
proc { |line_items| line_items.first.order.number },
|
||||
proc { |line_items| line_items.first.order.completed_at.strftime("%F %T") },
|
||||
]
|
||||
end
|
||||
# rubocop:enable Metrics/AbcSize
|
||||
# rubocop:enable Metrics/MethodLength
|
||||
# rubocop:enable Metrics/PerceivedComplexity
|
||||
|
||||
def line_item_includes
|
||||
[{ variant: [{ option_values: :option_type }, { product: :supplier }],
|
||||
order: [:bill_address, :ship_address, :order_cycle, :adjustments, :payments,
|
||||
:user, :distributor, :shipments] }]
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def shipping_method(line_items)
|
||||
shipping_rates = line_items.first.order.shipments.first&.shipping_rates
|
||||
|
||||
return unless shipping_rates
|
||||
|
||||
shipping_rate = shipping_rates.find(&:selected) || shipping_rates.first
|
||||
shipping_rate.try(:shipping_method)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
# rubocop:enable Metrics/ClassLength
|
||||
@@ -1,64 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module OpenFoodNetwork
|
||||
class OrdersAndFulfillmentReport
|
||||
class DefaultReport
|
||||
delegate :line_item_name, :supplier_name, :product_name, :line_items_name, to: :context
|
||||
|
||||
def initialize(context)
|
||||
@context = context
|
||||
end
|
||||
|
||||
def table_headers
|
||||
[
|
||||
I18n.t(:report_header_producer),
|
||||
I18n.t(:report_header_product),
|
||||
I18n.t(:report_header_variant),
|
||||
I18n.t(:report_header_quantity),
|
||||
I18n.t(:report_header_curr_cost_per_unit),
|
||||
I18n.t(:report_header_total_cost),
|
||||
I18n.t(:report_header_status),
|
||||
I18n.t(:report_header_incoming_transport)
|
||||
]
|
||||
end
|
||||
|
||||
def rules
|
||||
[
|
||||
{
|
||||
group_by: proc { |line_item| line_item.variant.product.supplier },
|
||||
sort_by: proc { |supplier| supplier.name }
|
||||
},
|
||||
{
|
||||
group_by: proc { |line_item| line_item.variant.product },
|
||||
sort_by: proc { |product| product.name }
|
||||
},
|
||||
{
|
||||
group_by: line_item_name,
|
||||
sort_by: proc { |full_name| full_name }
|
||||
}
|
||||
]
|
||||
end
|
||||
|
||||
def columns
|
||||
[
|
||||
supplier_name,
|
||||
product_name,
|
||||
line_items_name,
|
||||
proc { |line_items| line_items.to_a.sum(&:quantity) },
|
||||
proc { |line_items| line_items.first.price },
|
||||
proc { |line_items| line_items.sum { |li| li.quantity * li.price } },
|
||||
proc { |_line_items| "" },
|
||||
proc { |_line_items| I18n.t(:report_header_incoming_transport) }
|
||||
]
|
||||
end
|
||||
|
||||
def line_item_includes
|
||||
[]
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
attr_reader :context
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,78 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module OpenFoodNetwork
|
||||
class OrdersAndFulfillmentReport
|
||||
class DistributorTotalsBySupplierReport
|
||||
REPORT_TYPE = "order_cycle_distributor_totals_by_supplier"
|
||||
|
||||
attr_reader :context
|
||||
|
||||
def initialize(context)
|
||||
@context = context
|
||||
end
|
||||
|
||||
def table_headers
|
||||
[I18n.t(:report_header_hub), I18n.t(:report_header_producer),
|
||||
I18n.t(:report_header_product), I18n.t(:report_header_variant),
|
||||
I18n.t(:report_header_quantity), I18n.t(:report_header_curr_cost_per_unit),
|
||||
I18n.t(:report_header_total_cost), I18n.t(:report_header_total_shipping_cost),
|
||||
I18n.t(:report_header_shipping_method)]
|
||||
end
|
||||
|
||||
# rubocop:disable Metrics/AbcSize
|
||||
# rubocop:disable Metrics/MethodLength
|
||||
def rules
|
||||
[
|
||||
{
|
||||
group_by: proc { |line_item| line_item.order.distributor },
|
||||
sort_by: proc { |distributor| distributor.name },
|
||||
summary_columns: [
|
||||
proc { |_line_items| "" },
|
||||
proc { |_line_items| I18n.t('admin.reports.total') },
|
||||
proc { |_line_items| "" },
|
||||
proc { |_line_items| "" },
|
||||
proc { |_line_items| "" },
|
||||
proc { |_line_items| "" },
|
||||
proc { |line_items| line_items.sum(&:amount) },
|
||||
proc { |line_items| line_items.map(&:order).uniq.sum(&:ship_total) },
|
||||
proc { |_line_items| "" }
|
||||
]
|
||||
},
|
||||
{
|
||||
group_by: proc { |line_item| line_item.variant.product.supplier },
|
||||
sort_by: proc { |supplier| supplier.name }
|
||||
},
|
||||
{
|
||||
group_by: proc { |line_item| line_item.variant.product },
|
||||
sort_by: proc { |product| product.name }
|
||||
},
|
||||
{
|
||||
group_by: proc { |line_item| line_item.variant.full_name },
|
||||
sort_by: proc { |full_name| full_name }
|
||||
}
|
||||
]
|
||||
end
|
||||
# rubocop:enable Metrics/AbcSize
|
||||
# rubocop:enable Metrics/MethodLength
|
||||
|
||||
# rubocop:disable Metrics/AbcSize
|
||||
def columns
|
||||
[proc { |line_items| line_items.first.order.distributor.name },
|
||||
proc { |line_items| line_items.first.variant.product.supplier.name },
|
||||
proc { |line_items| line_items.first.variant.product.name },
|
||||
proc { |line_items| line_items.first.variant.full_name },
|
||||
proc { |line_items| line_items.to_a.sum(&:quantity) },
|
||||
proc { |line_items| line_items.first.price },
|
||||
proc { |line_items| line_items.sum(&:amount) },
|
||||
proc { |_line_items| "" },
|
||||
proc { |_line_items| I18n.t(:report_header_shipping_method) }]
|
||||
end
|
||||
# rubocop:enable Metrics/AbcSize
|
||||
|
||||
def line_item_includes
|
||||
[{ order: [:distributor, :adjustments, { shipments: { shipping_rates: :shipping_method } }],
|
||||
variant: [{ option_values: :option_type }, { product: :supplier }] }]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,77 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module OpenFoodNetwork
|
||||
class OrdersAndFulfillmentReport
|
||||
class SupplierTotalsByDistributorReport
|
||||
REPORT_TYPE = "order_cycle_supplier_totals_by_distributor"
|
||||
|
||||
attr_reader :context
|
||||
|
||||
delegate :supplier_name, to: :context
|
||||
|
||||
def initialize(context)
|
||||
@context = context
|
||||
end
|
||||
|
||||
def table_headers
|
||||
[I18n.t(:report_header_producer), I18n.t(:report_header_product),
|
||||
I18n.t(:report_header_variant), I18n.t(:report_header_to_hub),
|
||||
I18n.t(:report_header_quantity), I18n.t(:report_header_curr_cost_per_unit),
|
||||
I18n.t(:report_header_total_cost), I18n.t(:report_header_shipping_method)]
|
||||
end
|
||||
|
||||
# rubocop:disable Metrics/AbcSize
|
||||
# rubocop:disable Metrics/MethodLength
|
||||
def rules
|
||||
[
|
||||
{
|
||||
group_by: proc { |line_item| line_item.variant.product.supplier },
|
||||
sort_by: proc { |supplier| supplier.name }
|
||||
},
|
||||
{
|
||||
group_by: proc { |line_item| line_item.variant.product },
|
||||
sort_by: proc { |product| product.name }
|
||||
},
|
||||
{
|
||||
group_by: proc { |line_item| line_item.variant.full_name },
|
||||
sort_by: proc { |full_name| full_name },
|
||||
summary_columns: [
|
||||
proc { |_line_items| "" },
|
||||
proc { |_line_items| "" },
|
||||
proc { |_line_items| "" },
|
||||
proc { |_line_items| I18n.t('admin.reports.total') },
|
||||
proc { |_line_items| "" },
|
||||
proc { |_line_items| "" },
|
||||
proc { |line_items| line_items.sum(&:amount) },
|
||||
proc { |_line_items| "" }
|
||||
]
|
||||
},
|
||||
{
|
||||
group_by: proc { |line_item| line_item.order.distributor },
|
||||
sort_by: proc { |distributor| distributor.name }
|
||||
}
|
||||
]
|
||||
end
|
||||
# rubocop:enable Metrics/AbcSize
|
||||
# rubocop:enable Metrics/MethodLength
|
||||
|
||||
def columns
|
||||
[
|
||||
supplier_name,
|
||||
proc { |line_items| line_items.first.variant.product.name },
|
||||
proc { |line_items| line_items.first.variant.full_name },
|
||||
proc { |line_items| line_items.first.order.distributor.name },
|
||||
proc { |line_items| line_items.to_a.sum(&:quantity) },
|
||||
proc { |line_items| line_items.first.price },
|
||||
proc { |line_items| line_items.sum(&:amount) },
|
||||
proc { |_line_items| I18n.t(:report_header_shipping_method) }
|
||||
]
|
||||
end
|
||||
|
||||
def line_item_includes
|
||||
[{ order: :distributor,
|
||||
variant: [{ option_values: :option_type }, { product: :supplier }] }]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,60 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module OpenFoodNetwork
|
||||
class OrdersAndFulfillmentReport
|
||||
class SupplierTotalsReport
|
||||
REPORT_TYPE = "order_cycle_supplier_totals"
|
||||
|
||||
attr_reader :context
|
||||
|
||||
delegate :supplier_name, :product_name, :line_items_name, :total_units, to: :context
|
||||
|
||||
def initialize(context)
|
||||
@context = context
|
||||
end
|
||||
|
||||
def table_headers
|
||||
[I18n.t(:report_header_producer), I18n.t(:report_header_product),
|
||||
I18n.t(:report_header_variant), I18n.t(:report_header_quantity),
|
||||
I18n.t(:report_header_total_units), I18n.t(:report_header_curr_cost_per_unit),
|
||||
I18n.t(:report_header_total_cost), I18n.t(:report_header_status),
|
||||
I18n.t(:report_header_incoming_transport)]
|
||||
end
|
||||
|
||||
def rules
|
||||
[
|
||||
{
|
||||
group_by: proc { |line_item| line_item.variant.product.supplier },
|
||||
sort_by: proc { |supplier| supplier.name }
|
||||
},
|
||||
{
|
||||
group_by: proc { |line_item| line_item.variant.product },
|
||||
sort_by: proc { |product| product.name }
|
||||
},
|
||||
{
|
||||
group_by: proc { |line_item| line_item.variant.full_name },
|
||||
sort_by: proc { |full_name| full_name }
|
||||
}
|
||||
]
|
||||
end
|
||||
|
||||
def columns
|
||||
[
|
||||
supplier_name,
|
||||
product_name,
|
||||
line_items_name,
|
||||
proc { |line_items| line_items.to_a.sum(&:quantity) },
|
||||
proc { |line_items| total_units(line_items) },
|
||||
proc { |line_items| line_items.first.price },
|
||||
proc { |line_items| line_items.sum(&:amount) },
|
||||
proc { |_line_items| "" },
|
||||
proc { |_line_items| I18n.t(:report_header_incoming_transport) }
|
||||
]
|
||||
end
|
||||
|
||||
def line_item_includes
|
||||
[{ variant: [{ option_values: :option_type }, { product: :supplier }] }]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,144 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'open_food_network/order_grouper'
|
||||
|
||||
module OpenFoodNetwork
|
||||
class PaymentsReport
|
||||
attr_reader :params
|
||||
|
||||
def initialize(user, params = {}, render_table = false)
|
||||
@params = params
|
||||
@user = user
|
||||
@render_table = render_table
|
||||
end
|
||||
|
||||
def table_headers
|
||||
case params[:report_subtype]
|
||||
when "payments_by_payment_type"
|
||||
I18n.t(:report_header_payment_type)
|
||||
[I18n.t(:report_header_payment_state), I18n.t(:report_header_distributor), I18n.t(:report_header_payment_type),
|
||||
I18n.t(:report_header_total_price, currency: currency_symbol)]
|
||||
when "itemised_payment_totals"
|
||||
[I18n.t(:report_header_payment_state), I18n.t(:report_header_distributor),
|
||||
I18n.t(:report_header_product_total_price, currency: currency_symbol),
|
||||
I18n.t(:report_header_shipping_total_price, currency: currency_symbol),
|
||||
I18n.t(:report_header_outstanding_balance_price, currency: currency_symbol),
|
||||
I18n.t(:report_header_total_price, currency: currency_symbol)]
|
||||
when "payment_totals"
|
||||
[I18n.t(:report_header_payment_state), I18n.t(:report_header_distributor),
|
||||
I18n.t(:report_header_product_total_price, currency: currency_symbol),
|
||||
I18n.t(:report_header_shipping_total_price, currency: currency_symbol),
|
||||
I18n.t(:report_header_total_price, currency: currency_symbol),
|
||||
I18n.t(:report_header_eft_price, currency: currency_symbol),
|
||||
I18n.t(:report_header_paypal_price, currency: currency_symbol),
|
||||
I18n.t(:report_header_outstanding_balance_price, currency: currency_symbol)]
|
||||
else
|
||||
[I18n.t(:report_header_payment_state), I18n.t(:report_header_distributor), I18n.t(:report_header_payment_type),
|
||||
I18n.t(:report_header_total_price, currency: currency_symbol)]
|
||||
end
|
||||
end
|
||||
|
||||
def search
|
||||
Spree::Order.complete.not_state(:canceled).managed_by(@user).ransack(params[:q])
|
||||
end
|
||||
|
||||
def table_items
|
||||
return [] unless @render_table
|
||||
|
||||
orders = search.result
|
||||
payments = orders.includes(:payments).map do |order|
|
||||
order.payments.select(&:completed?)
|
||||
end.flatten
|
||||
|
||||
case params[:report_subtype]
|
||||
when "payments_by_payment_type"
|
||||
payments
|
||||
when "itemised_payment_totals"
|
||||
orders
|
||||
when "payment_totals"
|
||||
orders
|
||||
else
|
||||
payments
|
||||
end
|
||||
end
|
||||
|
||||
def table_rows
|
||||
order_grouper = OpenFoodNetwork::OrderGrouper.new rules, columns, self
|
||||
order_grouper.table(table_items)
|
||||
end
|
||||
|
||||
def rules
|
||||
case params[:report_subtype]
|
||||
when "payments_by_payment_type"
|
||||
[{ group_by: proc { |payment| payment.order.payment_state },
|
||||
sort_by: proc { |payment_state| payment_state } },
|
||||
{ group_by: proc { |payment| payment.order.distributor },
|
||||
sort_by: proc { |distributor| distributor.name } },
|
||||
{ group_by: proc { |payment| Spree::PaymentMethod.unscoped { payment.payment_method } },
|
||||
sort_by: proc { |method| method.name } }]
|
||||
when "itemised_payment_totals"
|
||||
[{ group_by: proc { |order| order.payment_state },
|
||||
sort_by: proc { |payment_state| payment_state } },
|
||||
{ group_by: proc { |order| order.distributor },
|
||||
sort_by: proc { |distributor| distributor.name } }]
|
||||
when "payment_totals"
|
||||
[{ group_by: proc { |order| order.payment_state },
|
||||
sort_by: proc { |payment_state| payment_state } },
|
||||
{ group_by: proc { |order| order.distributor },
|
||||
sort_by: proc { |distributor| distributor.name } }]
|
||||
else
|
||||
[{ group_by: proc { |payment| payment.order.payment_state },
|
||||
sort_by: proc { |payment_state| payment_state } },
|
||||
{ group_by: proc { |payment| payment.order.distributor },
|
||||
sort_by: proc { |distributor| distributor.name } },
|
||||
{ group_by: proc { |payment| payment.payment_method },
|
||||
sort_by: proc { |method| method.name } }]
|
||||
end
|
||||
end
|
||||
|
||||
def columns
|
||||
case params[:report_subtype]
|
||||
when "payments_by_payment_type"
|
||||
[proc { |payments| payments.first.order.payment_state },
|
||||
proc { |payments| payments.first.order.distributor.name },
|
||||
proc { |payments| payments.first.payment_method.name },
|
||||
proc { |payments| payments.sum(&:amount) }]
|
||||
when "itemised_payment_totals"
|
||||
[proc { |orders| orders.first.payment_state },
|
||||
proc { |orders| orders.first.distributor.name },
|
||||
proc { |orders| orders.to_a.sum(&:item_total) },
|
||||
proc { |orders| orders.sum(&:ship_total) },
|
||||
proc { |orders| orders.sum{ |order| order.outstanding_balance.to_f } },
|
||||
proc { |orders| orders.map(&:total).sum }]
|
||||
when "payment_totals"
|
||||
[proc { |orders| orders.first.payment_state },
|
||||
proc { |orders| orders.first.distributor.name },
|
||||
proc { |orders| orders.to_a.sum(&:item_total) },
|
||||
proc { |orders| orders.sum(&:ship_total) },
|
||||
proc { |orders| orders.map(&:total).sum },
|
||||
proc { |orders|
|
||||
orders.sum { |o|
|
||||
o.payments.select { |payment|
|
||||
payment.completed? &&
|
||||
(payment.payment_method.name.to_s.include? "EFT")
|
||||
}.sum(&:amount)
|
||||
}
|
||||
},
|
||||
proc { |orders|
|
||||
orders.sum { |o|
|
||||
o.payments.select { |payment|
|
||||
payment.completed? &&
|
||||
(payment.payment_method.name.to_s.include? "PayPal")
|
||||
}.sum(&:amount)
|
||||
}
|
||||
},
|
||||
proc { |orders| orders.sum{ |order| order.outstanding_balance.to_f } }]
|
||||
else
|
||||
[proc { |payments| payments.first.order.payment_state },
|
||||
proc { |payments| payments.first.order.distributor.name },
|
||||
proc { |payments| payments.first.payment_method.name },
|
||||
proc { |payments| payments.sum(&:amount) }]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,53 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module OpenFoodNetwork
|
||||
class ProductsAndInventoryDefaultReport
|
||||
attr_reader :context
|
||||
|
||||
delegate :variants, :render_table, to: :context
|
||||
|
||||
def initialize(context)
|
||||
@context = context
|
||||
end
|
||||
|
||||
def table_headers
|
||||
[
|
||||
I18n.t(:report_header_supplier),
|
||||
I18n.t(:report_header_producer_suburb),
|
||||
I18n.t(:report_header_product),
|
||||
I18n.t(:report_header_product_properties),
|
||||
I18n.t(:report_header_taxons),
|
||||
I18n.t(:report_header_variant_value),
|
||||
I18n.t(:report_header_price),
|
||||
I18n.t(:report_header_group_buy_unit_quantity),
|
||||
I18n.t(:report_header_amount),
|
||||
I18n.t(:report_header_sku)
|
||||
]
|
||||
end
|
||||
|
||||
def table_rows
|
||||
return [] unless render_table
|
||||
|
||||
variants.map do |variant|
|
||||
[
|
||||
variant.product.supplier.name,
|
||||
variant.product.supplier.address.city,
|
||||
variant.product.name,
|
||||
variant.product.properties.map(&:name).join(", "),
|
||||
variant.product.taxons.map(&:name).join(", "),
|
||||
variant.full_name,
|
||||
variant.price,
|
||||
variant.product.group_buy_unit_size,
|
||||
"",
|
||||
sku_for(variant)
|
||||
]
|
||||
end
|
||||
end
|
||||
|
||||
def sku_for(variant)
|
||||
return variant.sku if variant.sku.present?
|
||||
|
||||
variant.product.sku
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,100 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'open_food_network/scope_variant_to_hub'
|
||||
require 'open_food_network/products_and_inventory_default_report'
|
||||
require 'open_food_network/lettuce_share_report'
|
||||
|
||||
module OpenFoodNetwork
|
||||
class ProductsAndInventoryReport
|
||||
attr_reader :params, :render_table
|
||||
|
||||
delegate :table_rows, :table_headers, :rules, :columns, :sku_for, to: :report
|
||||
|
||||
def initialize(user, params = {}, render_table = false)
|
||||
@user = user
|
||||
@params = params
|
||||
@render_table = render_table
|
||||
end
|
||||
|
||||
def report
|
||||
@report ||= report_klass.new(self)
|
||||
end
|
||||
|
||||
def report_type
|
||||
params[:report_subtype]
|
||||
end
|
||||
|
||||
def report_klass
|
||||
if report_type == 'lettuce_share'
|
||||
OpenFoodNetwork::LettuceShareReport
|
||||
else
|
||||
OpenFoodNetwork::ProductsAndInventoryDefaultReport
|
||||
end
|
||||
end
|
||||
|
||||
def permissions
|
||||
@permissions ||= OpenFoodNetwork::Permissions.new(@user)
|
||||
end
|
||||
|
||||
def visible_products
|
||||
@visible_products ||= permissions.visible_products
|
||||
end
|
||||
|
||||
def variants
|
||||
filter(child_variants)
|
||||
end
|
||||
|
||||
def child_variants
|
||||
Spree::Variant.
|
||||
where(is_master: false).
|
||||
includes(option_values: :option_type).
|
||||
joins(:product).
|
||||
merge(visible_products).
|
||||
order('spree_products.name')
|
||||
end
|
||||
|
||||
def filter(variants)
|
||||
filter_on_hand filter_to_distributor filter_to_order_cycle filter_to_supplier variants
|
||||
end
|
||||
|
||||
# Using the `in_stock?` method allows overrides by distributors.
|
||||
def filter_on_hand(variants)
|
||||
if report_type == 'inventory'
|
||||
variants.select(&:in_stock?)
|
||||
else
|
||||
variants
|
||||
end
|
||||
end
|
||||
|
||||
def filter_to_supplier(variants)
|
||||
if params[:supplier_id].to_i > 0
|
||||
variants.where("spree_products.supplier_id = ?", params[:supplier_id])
|
||||
else
|
||||
variants
|
||||
end
|
||||
end
|
||||
|
||||
def filter_to_distributor(variants)
|
||||
if params[:distributor_id].to_i > 0
|
||||
distributor = Enterprise.find params[:distributor_id]
|
||||
scoper = OpenFoodNetwork::ScopeVariantToHub.new(distributor)
|
||||
variants.in_distributor(distributor).each { |v| scoper.scope(v) }
|
||||
else
|
||||
variants
|
||||
end
|
||||
end
|
||||
|
||||
def filter_to_order_cycle(variants)
|
||||
if params[:order_cycle_id].to_i > 0
|
||||
order_cycle = OrderCycle.find params[:order_cycle_id]
|
||||
variant_ids = Exchange.in_order_cycle(order_cycle).
|
||||
joins("INNER JOIN exchange_variants ON exchanges.id = exchange_variants.exchange_id").
|
||||
select("DISTINCT exchange_variants.variant_id")
|
||||
|
||||
variants.where("spree_variants.id IN (#{variant_ids.to_sql})")
|
||||
else
|
||||
variants
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,59 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module OpenFoodNetwork
|
||||
module Reports
|
||||
# shared code to search and list line items
|
||||
class LineItems
|
||||
def initialize(order_permissions, params, orders_relation = nil)
|
||||
@order_permissions = order_permissions
|
||||
@params = params
|
||||
complete_not_canceled_visible_orders = CompleteVisibleOrders.new(order_permissions).query.not_state(:canceled)
|
||||
@orders_relation = orders_relation || complete_not_canceled_visible_orders
|
||||
end
|
||||
|
||||
def orders
|
||||
@orders ||= search_orders
|
||||
end
|
||||
|
||||
def list(line_item_includes = nil)
|
||||
line_items = order_permissions.visible_line_items.in_orders(orders.result)
|
||||
|
||||
if @params[:supplier_id_in].present?
|
||||
line_items = line_items.supplied_by_any(@params[:supplier_id_in])
|
||||
end
|
||||
|
||||
if line_item_includes.present?
|
||||
line_items = line_items.includes(*line_item_includes).references(:line_items)
|
||||
end
|
||||
|
||||
without_editable_line_items = line_items - editable_line_items(line_items)
|
||||
|
||||
without_editable_line_items.each do |line_item|
|
||||
OrderDataMasker.new(line_item.order).call
|
||||
end
|
||||
|
||||
line_items
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
attr_reader :orders_relation, :order_permissions
|
||||
|
||||
def search_orders
|
||||
orders_relation.ransack(@params[:q])
|
||||
end
|
||||
|
||||
# From the line_items given, returns the ones that are editable by the user
|
||||
def editable_line_items(line_items)
|
||||
editable_line_items_ids = order_permissions.editable_line_items.select(:id)
|
||||
|
||||
# Although merge could take a relation, here we convert line_items to array
|
||||
# because, if we pass a relation, merge will overwrite the conditions on the same field
|
||||
# In this case: the IN clause on spree_line_items.order_id from line_items
|
||||
# overwrites the IN clause on spree_line_items.order_id on editable_line_items_ids
|
||||
# We convert to array the relation with less elements: line_items
|
||||
editable_line_items_ids.merge(line_items.to_a)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,108 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module OpenFoodNetwork
|
||||
class SalesTaxReport
|
||||
include Spree::ReportsHelper
|
||||
attr_accessor :user, :params
|
||||
|
||||
def initialize(user, params, render_table)
|
||||
@user = user
|
||||
@params = params
|
||||
@render_table = render_table
|
||||
end
|
||||
|
||||
def table_headers
|
||||
case params[:report_subtype]
|
||||
when "tax_rates"
|
||||
[I18n.t(:report_header_order_number),
|
||||
I18n.t(:report_header_total_excl_vat, currency_symbol: currency_symbol)] +
|
||||
relevant_rates.map { |rate| "%.1f%% (%s)" % [rate.amount.to_f * 100, currency_symbol] } +
|
||||
[I18n.t(:report_header_total_tax, currency_symbol: currency_symbol),
|
||||
I18n.t(:report_header_total_incl_vat, currency_symbol: currency_symbol)]
|
||||
else
|
||||
[I18n.t(:report_header_order_number),
|
||||
I18n.t(:report_header_date),
|
||||
I18n.t(:report_header_items),
|
||||
I18n.t(:report_header_items_total, currency_symbol: currency_symbol),
|
||||
I18n.t(:report_header_taxable_items_total, currency_symbol: currency_symbol),
|
||||
I18n.t(:report_header_sales_tax, currency_symbol: currency_symbol),
|
||||
I18n.t(:report_header_delivery_charge, currency_symbol: currency_symbol),
|
||||
I18n.t(:report_header_tax_on_delivery, currency_symbol: currency_symbol),
|
||||
I18n.t(:report_header_tax_on_fees, currency_symbol: currency_symbol),
|
||||
I18n.t(:report_header_total_tax, currency_symbol: currency_symbol),
|
||||
I18n.t(:report_header_customer),
|
||||
I18n.t(:report_header_distributor)]
|
||||
end
|
||||
end
|
||||
|
||||
def search
|
||||
permissions = ::Permissions::Order.new(user)
|
||||
permissions.editable_orders.complete.not_state(:canceled).ransack(params[:q])
|
||||
end
|
||||
|
||||
def orders
|
||||
search.result
|
||||
end
|
||||
|
||||
def table_rows
|
||||
return [] unless @render_table
|
||||
|
||||
case params[:report_subtype]
|
||||
when "tax_rates"
|
||||
orders.map do |order|
|
||||
[order.number, order.total - order.total_tax] +
|
||||
relevant_rates.map { |rate|
|
||||
OrderTaxAdjustmentsFetcher.new(order).totals.fetch(rate, 0)
|
||||
} + [order.total_tax, order.total]
|
||||
end
|
||||
else
|
||||
orders.map do |order|
|
||||
totals = totals_of order.line_items
|
||||
shipping_cost = shipping_cost_for order
|
||||
|
||||
[order.number, order.completed_at.strftime("%F %T"), totals[:items], totals[:items_total],
|
||||
totals[:taxable_total], totals[:sales_tax], shipping_cost, order.shipping_tax, order.enterprise_fee_tax, order.total_tax,
|
||||
order.bill_address.full_name, order.distributor&.name]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def relevant_rates
|
||||
return @relevant_rates unless @relevant_rates.nil?
|
||||
|
||||
@relevant_rates = Spree::TaxRate.distinct
|
||||
end
|
||||
|
||||
def totals_of(line_items)
|
||||
totals = { items: 0, items_total: 0.0, taxable_total: 0.0, sales_tax: 0.0 }
|
||||
|
||||
line_items.each do |line_item|
|
||||
totals[:items] += line_item.quantity
|
||||
totals[:items_total] += line_item.amount
|
||||
|
||||
sales_tax = tax_included_in line_item
|
||||
|
||||
if sales_tax > 0
|
||||
totals[:taxable_total] += line_item.amount
|
||||
totals[:sales_tax] += sales_tax
|
||||
end
|
||||
end
|
||||
|
||||
totals.each_pair do |k, _v|
|
||||
totals[k] = totals[k].round(2)
|
||||
end
|
||||
|
||||
totals
|
||||
end
|
||||
|
||||
def shipping_cost_for(order)
|
||||
order.shipments.first&.cost || 0.0
|
||||
end
|
||||
|
||||
def tax_included_in(line_item)
|
||||
line_item.adjustments.tax.inclusive.sum(:amount)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,138 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module OpenFoodNetwork
|
||||
class UsersAndEnterprisesReport
|
||||
attr_reader :params
|
||||
|
||||
def initialize(user, params = {}, compile_table = false)
|
||||
@user = user
|
||||
@params = params
|
||||
@compile_table = compile_table
|
||||
|
||||
# Convert arrays of ids to comma delimited strings
|
||||
if @params[:enterprise_id_in].is_a? Array
|
||||
@params[:enterprise_id_in] = @params[:enterprise_id_in].join(',')
|
||||
end
|
||||
@params[:user_id_in] = @params[:user_id_in].join(',') if @params[:user_id_in].is_a? Array
|
||||
end
|
||||
|
||||
def table_headers
|
||||
[
|
||||
I18n.t(:report_header_user),
|
||||
I18n.t(:report_header_relationship),
|
||||
I18n.t(:report_header_enterprise),
|
||||
I18n.t(:report_header_is_producer),
|
||||
I18n.t(:report_header_sells),
|
||||
I18n.t(:report_header_visible),
|
||||
I18n.t(:report_header_confirmation_date),
|
||||
]
|
||||
end
|
||||
|
||||
def table_rows
|
||||
return [] unless @compile_table
|
||||
|
||||
users_and_enterprises.map do |uae|
|
||||
[
|
||||
uae["user_email"],
|
||||
uae["relationship_type"],
|
||||
uae["name"],
|
||||
to_bool(uae["is_primary_producer"]),
|
||||
uae["sells"],
|
||||
uae["visible"],
|
||||
to_local_datetime(uae["created_at"])
|
||||
]
|
||||
end
|
||||
end
|
||||
|
||||
def owners_and_enterprises
|
||||
query = Enterprise.joins("LEFT JOIN spree_users AS owner ON enterprises.owner_id = owner.id")
|
||||
.where("enterprises.id IS NOT NULL")
|
||||
|
||||
query = filter_by_int_list_if_present(query, "enterprises.id", params[:enterprise_id_in])
|
||||
query = filter_by_int_list_if_present(query, "owner.id", params[:user_id_in])
|
||||
|
||||
query_helper(query, :owner, :owns)
|
||||
end
|
||||
|
||||
def managers_and_enterprises
|
||||
query = Enterprise
|
||||
.joins("LEFT JOIN enterprise_roles ON enterprises.id = enterprise_roles.enterprise_id")
|
||||
.joins("LEFT JOIN spree_users AS managers ON enterprise_roles.user_id = managers.id")
|
||||
.where("enterprise_id IS NOT NULL")
|
||||
.where("user_id IS NOT NULL")
|
||||
|
||||
query = filter_by_int_list_if_present(query, "enterprise_id", params[:enterprise_id_in])
|
||||
query = filter_by_int_list_if_present(query, "user_id", params[:user_id_in])
|
||||
|
||||
query_helper(query, :managers, :manages)
|
||||
end
|
||||
|
||||
def query_helper(query, email_user, relationship_type)
|
||||
query.order("enterprises.created_at DESC")
|
||||
.select(["enterprises.name",
|
||||
"enterprises.sells",
|
||||
"enterprises.visible",
|
||||
"enterprises.is_primary_producer",
|
||||
"enterprises.created_at",
|
||||
"#{email_user}.email AS user_email"])
|
||||
.to_a
|
||||
.map { |x|
|
||||
{
|
||||
name: x.name,
|
||||
sells: x.sells,
|
||||
visible: (x.visible ? 't' : 'f'),
|
||||
is_primary_producer: (x.is_primary_producer ? 't' : 'f'),
|
||||
created_at: x.created_at.utc.iso8601,
|
||||
relationship_type: relationship_type,
|
||||
user_email: x.user_email
|
||||
}.stringify_keys
|
||||
}
|
||||
end
|
||||
|
||||
def users_and_enterprises
|
||||
sort( owners_and_enterprises.concat(managers_and_enterprises) )
|
||||
end
|
||||
|
||||
def filter_by_int_list_if_present(query, filtered_field_name, int_list)
|
||||
if int_list.present?
|
||||
query = query.where("#{filtered_field_name} IN (?)", split_int_list(int_list))
|
||||
end
|
||||
query
|
||||
end
|
||||
|
||||
def split_int_list(int_list)
|
||||
int_list.split(',').map(&:to_i)
|
||||
end
|
||||
|
||||
def sort(results)
|
||||
results.sort do |a, b|
|
||||
if a["created_at"].nil? || b["created_at"].nil?
|
||||
[(a["created_at"].nil? ? 0 : 1), a["name"], b["relationship_type"], a["user_email"]] <=>
|
||||
[(b["created_at"].nil? ? 0 : 1), b["name"], a["relationship_type"], b["user_email"]]
|
||||
else
|
||||
[
|
||||
DateTime.parse(b["created_at"]).in_time_zone,
|
||||
a["name"],
|
||||
b["relationship_type"],
|
||||
a["user_email"]
|
||||
] <=> [
|
||||
DateTime.parse(a["created_at"]).in_time_zone,
|
||||
b["name"],
|
||||
a["relationship_type"],
|
||||
b["user_email"]
|
||||
]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def to_bool(value)
|
||||
ActiveRecord::Type::Boolean.new.cast(value)
|
||||
end
|
||||
|
||||
def to_local_datetime(date)
|
||||
return "" if date.nil?
|
||||
|
||||
date.to_datetime.in_time_zone.strftime "%Y-%m-%d %H:%M"
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,235 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module OpenFoodNetwork
|
||||
class XeroInvoicesReport
|
||||
def initialize(user, opts = {}, compile_table = false)
|
||||
@user = user
|
||||
|
||||
@opts = opts.
|
||||
symbolize_keys.
|
||||
reject { |_k, v| v.blank? }.
|
||||
reverse_merge( report_subtype: 'summary',
|
||||
invoice_date: Time.zone.today,
|
||||
due_date: Time.zone.today + 1.month,
|
||||
account_code: 'food sales' )
|
||||
@compile_table = compile_table
|
||||
end
|
||||
|
||||
def table_headers
|
||||
# NOTE: These are NOT to be translated, they need to be in this exact format to work with Xero
|
||||
%w(*ContactName EmailAddress POAddressLine1 POAddressLine2 POAddressLine3 POAddressLine4
|
||||
POCity PORegion POPostalCode POCountry *InvoiceNumber Reference *InvoiceDate *DueDate InventoryItemCode *Description *Quantity *UnitAmount Discount *AccountCode *TaxType TrackingName1 TrackingOption1 TrackingName2 TrackingOption2 Currency BrandingTheme Paid?)
|
||||
end
|
||||
|
||||
def search
|
||||
permissions = ::Permissions::Order.new(@user)
|
||||
permissions.editable_orders.complete.not_state(:canceled).ransack(@opts[:q])
|
||||
end
|
||||
|
||||
def orders
|
||||
search.result.reorder('id DESC')
|
||||
end
|
||||
|
||||
def table_rows
|
||||
return [] unless @compile_table
|
||||
|
||||
rows = []
|
||||
|
||||
orders.each_with_index do |order, i|
|
||||
invoice_number = invoice_number_for(order, i)
|
||||
rows += detail_rows_for_order(order, invoice_number, @opts) if detail?
|
||||
rows += summary_rows_for_order(order, invoice_number, @opts)
|
||||
end
|
||||
|
||||
rows.compact
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def report_options
|
||||
@opts.merge(line_item_includes: line_item_includes)
|
||||
end
|
||||
|
||||
def line_item_includes
|
||||
[:bill_address, :adjustments,
|
||||
{ line_items: { variant: [{ option_values: :option_type }, { product: :supplier }] } }]
|
||||
end
|
||||
|
||||
def detail_rows_for_order(order, invoice_number, opts)
|
||||
rows = []
|
||||
|
||||
rows += line_item_detail_rows(order, invoice_number, opts)
|
||||
rows += adjustment_detail_rows(order, invoice_number, opts)
|
||||
|
||||
rows
|
||||
end
|
||||
|
||||
def line_item_detail_rows(order, invoice_number, opts)
|
||||
order.line_items.map do |line_item|
|
||||
line_item_detail_row(line_item, invoice_number, opts)
|
||||
end
|
||||
end
|
||||
|
||||
def line_item_detail_row(line_item, invoice_number, opts)
|
||||
row(line_item.order,
|
||||
line_item.variant.sku,
|
||||
line_item.product_and_full_name,
|
||||
line_item.quantity.to_s,
|
||||
line_item.price.to_s,
|
||||
invoice_number,
|
||||
tax_type(line_item),
|
||||
opts)
|
||||
end
|
||||
|
||||
def adjustment_detail_rows(order, invoice_number, opts)
|
||||
admin_adjustments(order).map do |adjustment|
|
||||
adjustment_detail_row(adjustment, invoice_number, opts)
|
||||
end
|
||||
end
|
||||
|
||||
def adjustment_detail_row(adjustment, invoice_number, opts)
|
||||
row(adjustment_order(adjustment),
|
||||
'',
|
||||
adjustment.label,
|
||||
1,
|
||||
adjustment.amount,
|
||||
invoice_number,
|
||||
tax_type(adjustment),
|
||||
opts)
|
||||
end
|
||||
|
||||
def summary_rows_for_order(order, invoice_number, opts)
|
||||
rows = []
|
||||
|
||||
rows += produce_summary_rows(order, invoice_number, opts) unless detail?
|
||||
rows += fee_summary_rows(order, invoice_number, opts)
|
||||
rows += shipping_summary_rows(order, invoice_number, opts)
|
||||
rows += payment_summary_rows(order, invoice_number, opts)
|
||||
rows += admin_adjustment_summary_rows(order, invoice_number, opts) unless detail?
|
||||
|
||||
rows
|
||||
end
|
||||
|
||||
def produce_summary_rows(order, invoice_number, opts)
|
||||
[summary_row(order, I18n.t(:report_header_total_untaxable_produce), total_untaxable_products(order), invoice_number, I18n.t(:report_header_gst_free_income), opts),
|
||||
summary_row(order, I18n.t(:report_header_total_taxable_produce),
|
||||
total_taxable_products(order), invoice_number, I18n.t(:report_header_gst_on_income), opts)]
|
||||
end
|
||||
|
||||
def fee_summary_rows(order, invoice_number, opts)
|
||||
[summary_row(order, I18n.t(:report_header_total_untaxable_fees), total_untaxable_fees(order), invoice_number, I18n.t(:report_header_gst_free_income), opts),
|
||||
summary_row(order, I18n.t(:report_header_total_taxable_fees), total_taxable_fees(order),
|
||||
invoice_number, I18n.t(:report_header_gst_on_income), opts)]
|
||||
end
|
||||
|
||||
def shipping_summary_rows(order, invoice_number, opts)
|
||||
[summary_row(order, I18n.t(:report_header_delivery_shipping_cost), total_shipping(order),
|
||||
invoice_number, tax_on_shipping_s(order), opts)]
|
||||
end
|
||||
|
||||
def payment_summary_rows(order, invoice_number, opts)
|
||||
[summary_row(order, I18n.t(:report_header_transaction_fee), total_transaction(order),
|
||||
invoice_number, I18n.t(:report_header_gst_free_income), opts)]
|
||||
end
|
||||
|
||||
def admin_adjustment_summary_rows(order, invoice_number, opts)
|
||||
[summary_row(order, I18n.t(:report_header_total_untaxable_admin), total_untaxable_admin_adjustments(order), invoice_number, I18n.t(:report_header_gst_free_income), opts),
|
||||
summary_row(order, I18n.t(:report_header_total_taxable_admin),
|
||||
total_taxable_admin_adjustments(order), invoice_number, I18n.t(:report_header_gst_on_income), opts)]
|
||||
end
|
||||
|
||||
def summary_row(order, description, amount, invoice_number, tax_type, opts = {})
|
||||
row order, '', description, '1', amount, invoice_number, tax_type, opts
|
||||
end
|
||||
|
||||
def row(order, sku, description, quantity, amount, invoice_number, tax_type, opts = {})
|
||||
return nil if amount == 0
|
||||
|
||||
[order.bill_address&.full_name,
|
||||
order.email,
|
||||
order.bill_address&.address1,
|
||||
order.bill_address&.address2,
|
||||
'',
|
||||
'',
|
||||
order.bill_address&.city,
|
||||
order.bill_address&.state,
|
||||
order.bill_address&.zipcode,
|
||||
order.bill_address&.country&.name,
|
||||
invoice_number,
|
||||
order.number,
|
||||
opts[:invoice_date],
|
||||
opts[:due_date],
|
||||
sku,
|
||||
description,
|
||||
quantity,
|
||||
amount,
|
||||
'',
|
||||
opts[:account_code],
|
||||
tax_type,
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
Spree::Config.currency,
|
||||
'',
|
||||
order.paid? ? I18n.t(:y) : I18n.t(:n)]
|
||||
end
|
||||
|
||||
def admin_adjustments(order)
|
||||
order.adjustments.admin
|
||||
end
|
||||
|
||||
def adjustment_order(adjustment)
|
||||
adjustment.adjustable.is_a?(Spree::Order) ? adjustment.adjustable : nil
|
||||
end
|
||||
|
||||
def invoice_number_for(order, idx)
|
||||
@opts[:initial_invoice_number] ? @opts[:initial_invoice_number].to_i + idx : order.number
|
||||
end
|
||||
|
||||
def total_untaxable_products(order)
|
||||
order.line_items.without_tax.to_a.sum(&:amount)
|
||||
end
|
||||
|
||||
def total_taxable_products(order)
|
||||
order.line_items.with_tax.to_a.sum(&:amount)
|
||||
end
|
||||
|
||||
def total_untaxable_fees(order)
|
||||
order.all_adjustments.enterprise_fee.where(tax_category: nil).sum(:amount)
|
||||
end
|
||||
|
||||
def total_taxable_fees(order)
|
||||
order.all_adjustments.enterprise_fee.where.not(tax_category: nil).sum(:amount)
|
||||
end
|
||||
|
||||
def total_shipping(order)
|
||||
order.all_adjustments.shipping.sum(:amount)
|
||||
end
|
||||
|
||||
def total_transaction(order)
|
||||
order.all_adjustments.payment_fee.sum(:amount)
|
||||
end
|
||||
|
||||
def tax_on_shipping_s(order)
|
||||
tax_on_shipping = order.shipments.sum("additional_tax_total + included_tax_total").positive?
|
||||
tax_on_shipping ? I18n.t(:report_header_gst_on_income) : I18n.t(:report_header_gst_free_income)
|
||||
end
|
||||
|
||||
def total_untaxable_admin_adjustments(order)
|
||||
order.adjustments.admin.where(tax_category: nil).sum(:amount)
|
||||
end
|
||||
|
||||
def total_taxable_admin_adjustments(order)
|
||||
order.adjustments.admin.where.not(tax_category: nil).sum(:amount)
|
||||
end
|
||||
|
||||
def detail?
|
||||
@opts[:report_subtype] == 'detailed'
|
||||
end
|
||||
|
||||
def tax_type(taxable)
|
||||
taxable.has_tax? ? I18n.t(:report_header_gst_on_income) : I18n.t(:report_header_gst_free_income)
|
||||
end
|
||||
end
|
||||
end
|
||||
57
lib/reporting/line_items.rb
Normal file
57
lib/reporting/line_items.rb
Normal file
@@ -0,0 +1,57 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Reporting
|
||||
# shared code to search and list line items
|
||||
class LineItems
|
||||
def initialize(order_permissions, params, orders_relation = nil)
|
||||
@order_permissions = order_permissions
|
||||
@params = params
|
||||
complete_not_canceled_visible_orders = CompleteVisibleOrders.new(order_permissions).query.not_state(:canceled)
|
||||
@orders_relation = orders_relation || complete_not_canceled_visible_orders
|
||||
end
|
||||
|
||||
def orders
|
||||
@orders ||= search_orders
|
||||
end
|
||||
|
||||
def list(line_item_includes = nil)
|
||||
line_items = order_permissions.visible_line_items.in_orders(orders.result)
|
||||
|
||||
if @params[:supplier_id_in].present?
|
||||
line_items = line_items.supplied_by_any(@params[:supplier_id_in])
|
||||
end
|
||||
|
||||
if line_item_includes.present?
|
||||
line_items = line_items.includes(*line_item_includes).references(:line_items)
|
||||
end
|
||||
|
||||
without_editable_line_items = line_items - editable_line_items(line_items)
|
||||
|
||||
without_editable_line_items.each do |line_item|
|
||||
OrderDataMasker.new(line_item.order).call
|
||||
end
|
||||
|
||||
line_items
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
attr_reader :orders_relation, :order_permissions
|
||||
|
||||
def search_orders
|
||||
orders_relation.ransack(@params[:q])
|
||||
end
|
||||
|
||||
# From the line_items given, returns the ones that are editable by the user
|
||||
def editable_line_items(line_items)
|
||||
editable_line_items_ids = order_permissions.editable_line_items.select(:id)
|
||||
|
||||
# Although merge could take a relation, here we convert line_items to array
|
||||
# because, if we pass a relation, merge will overwrite the conditions on the same field
|
||||
# In this case: the IN clause on spree_line_items.order_id from line_items
|
||||
# overwrites the IN clause on spree_line_items.order_id on editable_line_items_ids
|
||||
# We convert to array the relation with less elements: line_items
|
||||
editable_line_items_ids.merge(line_items.to_a)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,6 +1,6 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module OpenFoodNetwork
|
||||
module Reporting
|
||||
class OrderGrouper
|
||||
def initialize(rules, column_constructors, report = nil)
|
||||
@rules = rules
|
||||
@@ -0,0 +1,67 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Reporting
|
||||
module Reports
|
||||
module BulkCoop
|
||||
class BulkCoopAllocationReport
|
||||
def table_headers
|
||||
[
|
||||
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
|
||||
319
lib/reporting/reports/bulk_coop/bulk_coop_report.rb
Normal file
319
lib/reporting/reports/bulk_coop/bulk_coop_report.rb
Normal file
@@ -0,0 +1,319 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Reporting
|
||||
module Reports
|
||||
module BulkCoop
|
||||
class BulkCoopReport
|
||||
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 table_headers
|
||||
case params[:report_subtype]
|
||||
when "bulk_coop_supplier_report"
|
||||
@supplier_report.table_headers
|
||||
when "bulk_coop_allocation"
|
||||
@allocation_report.table_headers
|
||||
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 table_rows
|
||||
order_grouper = Reporting::OrderGrouper.new rules, columns, self
|
||||
order_grouper.table(table_items)
|
||||
end
|
||||
|
||||
def rules
|
||||
case params[:report_subtype]
|
||||
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_subtype]
|
||||
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 ||= Reporting::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)
|
||||
unit_size = line_items.first.variant.product.group_buy_unit_size || 0.0
|
||||
unit_size / (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
|
||||
65
lib/reporting/reports/bulk_coop/bulk_coop_supplier_report.rb
Normal file
65
lib/reporting/reports/bulk_coop/bulk_coop_supplier_report.rb
Normal file
@@ -0,0 +1,65 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Reporting
|
||||
module Reports
|
||||
module BulkCoop
|
||||
class BulkCoopSupplierReport
|
||||
def table_headers
|
||||
[
|
||||
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
|
||||
105
lib/reporting/reports/customers/customers_report.rb
Normal file
105
lib/reporting/reports/customers/customers_report.rb
Normal file
@@ -0,0 +1,105 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Reporting
|
||||
module Reports
|
||||
module Customers
|
||||
class CustomersReport
|
||||
attr_reader :params
|
||||
|
||||
def initialize(user, params = {}, compile_table = false)
|
||||
@params = params
|
||||
@user = user
|
||||
@compile_table = compile_table
|
||||
end
|
||||
|
||||
def table_headers
|
||||
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_rows
|
||||
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_subtype] == "mailing_list"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,9 +1,9 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module OrderManagement
|
||||
module Reporting
|
||||
module Reports
|
||||
module EnterpriseFeeSummary
|
||||
class Authorizer < ::Reports::Authorizer
|
||||
class Authorizer < Reporting::Reports::EnterpriseFeeSummary::Reports::Authorizer
|
||||
def authorize!
|
||||
authorize_by_distribution!
|
||||
authorize_by_fee!
|
||||
@@ -3,7 +3,7 @@
|
||||
# This module provides EnterpriseFeeSummary::Scope DB result to report mappings for coordinator fees
|
||||
# in an order cycle.
|
||||
|
||||
module OrderManagement
|
||||
module Reporting
|
||||
module Reports
|
||||
module EnterpriseFeeSummary
|
||||
module DataRepresentations
|
||||
@@ -3,7 +3,7 @@
|
||||
# This module provides EnterpriseFeeSummary::Scope DB result to report mappings for exchange fees
|
||||
# that use order-based calculators.
|
||||
|
||||
module OrderManagement
|
||||
module Reporting
|
||||
module Reports
|
||||
module EnterpriseFeeSummary
|
||||
module DataRepresentations
|
||||
@@ -3,7 +3,7 @@
|
||||
# This module provides EnterpriseFeeSummary::Scope DB result to report mappings for incoming
|
||||
# exchange fees that use line item -based calculators.
|
||||
|
||||
module OrderManagement
|
||||
module Reporting
|
||||
module Reports
|
||||
module EnterpriseFeeSummary
|
||||
module DataRepresentations
|
||||
@@ -3,7 +3,7 @@
|
||||
# This module provides EnterpriseFeeSummary::Scope DB result to report mappings for outgoing
|
||||
# exchange fees that use line item -based calculators.
|
||||
|
||||
module OrderManagement
|
||||
module Reporting
|
||||
module Reports
|
||||
module EnterpriseFeeSummary
|
||||
module DataRepresentations
|
||||
@@ -3,7 +3,7 @@
|
||||
# This module provides EnterpriseFeeSummary::Scope DB result to report mappings for payment method
|
||||
# fees.
|
||||
|
||||
module OrderManagement
|
||||
module Reporting
|
||||
module Reports
|
||||
module EnterpriseFeeSummary
|
||||
module DataRepresentations
|
||||
@@ -3,7 +3,7 @@
|
||||
# This module provides EnterpriseFeeSummary::Scope DB result to report mappings for shipping method
|
||||
# fees.
|
||||
|
||||
module OrderManagement
|
||||
module Reporting
|
||||
module Reports
|
||||
module EnterpriseFeeSummary
|
||||
module DataRepresentations
|
||||
@@ -7,7 +7,7 @@
|
||||
# fees. These mappings are not complete and should be supplemented with mappings that are specific
|
||||
# to the way that the enterprise fee is attached to the order cycle.
|
||||
|
||||
module OrderManagement
|
||||
module Reporting
|
||||
module Reports
|
||||
module EnterpriseFeeSummary
|
||||
module DataRepresentations
|
||||
@@ -1,6 +1,6 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module OrderManagement
|
||||
module Reporting
|
||||
module Reports
|
||||
module EnterpriseFeeSummary
|
||||
module DataRepresentations
|
||||
@@ -1,6 +1,6 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module OrderManagement
|
||||
module Reporting
|
||||
module Reports
|
||||
module EnterpriseFeeSummary
|
||||
class EnterpriseFeeSummaryReport
|
||||
@@ -12,7 +12,7 @@ module OrderManagement
|
||||
p['start_at'] = p.delete('completed_at_gt')
|
||||
p['end_at'] = p.delete('completed_at_lt')
|
||||
end
|
||||
@parameters = OrderManagement::Reports::EnterpriseFeeSummary::Parameters.new(p || {})
|
||||
@parameters = Reporting::Reports::EnterpriseFeeSummary::Parameters.new(p || {})
|
||||
@parameters.validate!
|
||||
@user = user
|
||||
@render_table = render_table
|
||||
@@ -0,0 +1,9 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Reporting
|
||||
module Reports
|
||||
module EnterpriseFeeSummary
|
||||
class ParameterNotAllowedError < StandardError; end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,9 +1,9 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module OrderManagement
|
||||
module Reporting
|
||||
module Reports
|
||||
module EnterpriseFeeSummary
|
||||
class Parameters < ::Reports::Parameters::Base
|
||||
class Parameters < Reporting::Reports::EnterpriseFeeSummary::Reports::Parameters::Base
|
||||
include ActiveModel::Validations
|
||||
|
||||
attr_accessor :start_at, :end_at, :distributor_ids, :producer_ids, :order_cycle_ids,
|
||||
@@ -1,6 +1,6 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module OrderManagement
|
||||
module Reporting
|
||||
module Reports
|
||||
module EnterpriseFeeSummary
|
||||
class Permissions
|
||||
@@ -1,10 +1,10 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module OrderManagement
|
||||
module Reporting
|
||||
module Reports
|
||||
module EnterpriseFeeSummary
|
||||
module ReportData
|
||||
class EnterpriseFeeTypeTotal < ::Reports::ReportData::Base
|
||||
class EnterpriseFeeTypeTotal < Reporting::Reports::EnterpriseFeeSummary::Reports::ReportData::Base
|
||||
attr_accessor :fee_type, :enterprise_name, :fee_name, :customer_name, :fee_placement,
|
||||
:fee_calculated_on_transfer_through_name, :tax_category_name, :total_amount
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Reporting
|
||||
module Reports
|
||||
module EnterpriseFeeSummary
|
||||
module Reports
|
||||
class Authorizer
|
||||
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 = Reporting::Reports::EnterpriseFeeSummary::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
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,41 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Reporting
|
||||
module Reports
|
||||
module EnterpriseFeeSummary
|
||||
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
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,19 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Reporting
|
||||
module Reports
|
||||
module EnterpriseFeeSummary
|
||||
module Reports
|
||||
module ReportData
|
||||
class Base
|
||||
def initialize(attributes = {})
|
||||
attributes.each do |key, value|
|
||||
public_send("#{key}=", value)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,6 +1,6 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module OrderManagement
|
||||
module Reporting
|
||||
module Reports
|
||||
module EnterpriseFeeSummary
|
||||
class Scope
|
||||
@@ -1,6 +1,6 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module OrderManagement
|
||||
module Reporting
|
||||
module Reports
|
||||
module EnterpriseFeeSummary
|
||||
class Summarizer
|
||||
@@ -1,6 +1,6 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module OpenFoodNetwork
|
||||
module Reporting
|
||||
module Reports
|
||||
class List
|
||||
def self.all
|
||||
@@ -0,0 +1,170 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Reporting
|
||||
module Reports
|
||||
module OrderCycleManagement
|
||||
class OrderCycleManagementReport
|
||||
DEFAULT_DATE_INTERVAL = { from: -1.month, to: 1.day }.freeze
|
||||
|
||||
attr_reader :params
|
||||
|
||||
def initialize(user, params = {}, render_table = false)
|
||||
@params = sanitize_params(params)
|
||||
@user = user
|
||||
@render_table = render_table
|
||||
end
|
||||
|
||||
def table_headers
|
||||
if is_payment_methods?
|
||||
[
|
||||
I18n.t(:report_header_first_name),
|
||||
I18n.t(:report_header_last_name),
|
||||
I18n.t(:report_header_hub),
|
||||
I18n.t(:report_header_hub_code),
|
||||
I18n.t(:report_header_email),
|
||||
I18n.t(:report_header_phone),
|
||||
I18n.t(:report_header_shipping_method),
|
||||
I18n.t(:report_header_payment_method),
|
||||
I18n.t(:report_header_amount),
|
||||
I18n.t(:report_header_balance),
|
||||
]
|
||||
else
|
||||
[
|
||||
I18n.t(:report_header_first_name),
|
||||
I18n.t(:report_header_last_name),
|
||||
I18n.t(:report_header_hub),
|
||||
I18n.t(:report_header_hub_code),
|
||||
I18n.t(:report_header_delivery_address),
|
||||
I18n.t(:report_header_delivery_postcode),
|
||||
I18n.t(:report_header_phone),
|
||||
I18n.t(:report_header_shipping_method),
|
||||
I18n.t(:report_header_payment_method),
|
||||
I18n.t(:report_header_amount),
|
||||
I18n.t(:report_header_balance),
|
||||
I18n.t(:report_header_temp_controlled_items),
|
||||
I18n.t(:report_header_special_instructions),
|
||||
]
|
||||
end
|
||||
end
|
||||
|
||||
def search
|
||||
Spree::Order.
|
||||
finalized.
|
||||
not_state(:canceled).
|
||||
distributed_by_user(@user).
|
||||
managed_by(@user).
|
||||
ransack(params[:q])
|
||||
end
|
||||
|
||||
def orders
|
||||
search_result = search.result.order(:completed_at)
|
||||
orders_with_balance = OutstandingBalance.new(search_result).
|
||||
query.
|
||||
select('spree_orders.*')
|
||||
|
||||
filter(orders_with_balance)
|
||||
end
|
||||
|
||||
def table_rows
|
||||
return [] unless @render_table
|
||||
|
||||
if is_payment_methods?
|
||||
orders.map { |o| payment_method_row o }
|
||||
else
|
||||
orders.map { |o| delivery_row o }
|
||||
end
|
||||
end
|
||||
|
||||
def filter(search_result)
|
||||
filter_to_payment_method filter_to_shipping_method filter_to_order_cycle search_result
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# This method relies on `balance_value` as a computed DB column. See `CompleteOrdersWithBalance`
|
||||
# for reference.
|
||||
def balance(order)
|
||||
order.balance_value
|
||||
end
|
||||
|
||||
def payment_method_row(order)
|
||||
ba = order.billing_address
|
||||
[ba&.firstname,
|
||||
ba&.lastname,
|
||||
order.distributor&.name,
|
||||
customer_code(order.email),
|
||||
order.email,
|
||||
ba&.phone,
|
||||
order.shipping_method&.name,
|
||||
order.payments.last&.payment_method&.name,
|
||||
order.total,
|
||||
balance(order)]
|
||||
end
|
||||
|
||||
def delivery_row(order)
|
||||
sa = order.shipping_address
|
||||
[sa.firstname,
|
||||
sa.lastname,
|
||||
order.distributor&.name,
|
||||
customer_code(order.email),
|
||||
"#{sa.address1} #{sa.address2} #{sa.city}",
|
||||
sa.zipcode,
|
||||
sa.phone,
|
||||
order.shipping_method&.name,
|
||||
order.payments.first&.payment_method&.name,
|
||||
order.total,
|
||||
balance(order),
|
||||
has_temperature_controlled_items?(order),
|
||||
order.special_instructions]
|
||||
end
|
||||
|
||||
def filter_to_payment_method(orders)
|
||||
if params[:payment_method_in].present?
|
||||
orders.joins(payments: :payment_method).where(spree_payments: { payment_method_id: params[:payment_method_in] })
|
||||
else
|
||||
orders
|
||||
end
|
||||
end
|
||||
|
||||
def filter_to_shipping_method(orders)
|
||||
if params[:shipping_method_in].present?
|
||||
orders.joins(shipments: :shipping_rates).where(spree_shipping_rates: { selected: true,
|
||||
shipping_method_id: params[:shipping_method_in] })
|
||||
else
|
||||
orders
|
||||
end
|
||||
end
|
||||
|
||||
def filter_to_order_cycle(orders)
|
||||
if params[:order_cycle_id].present?
|
||||
orders.where(order_cycle_id: params[:order_cycle_id])
|
||||
else
|
||||
orders
|
||||
end
|
||||
end
|
||||
|
||||
def has_temperature_controlled_items?(order)
|
||||
order.line_items.any? { |line_item|
|
||||
line_item.product.shipping_category&.temperature_controlled
|
||||
}
|
||||
end
|
||||
|
||||
def is_payment_methods?
|
||||
params[:report_subtype] == "payment_methods"
|
||||
end
|
||||
|
||||
def customer_code(email)
|
||||
customer = Customer.where(email: email).first
|
||||
customer.nil? ? "" : customer.code
|
||||
end
|
||||
|
||||
def sanitize_params(params)
|
||||
params[:q] ||= {}
|
||||
params[:q][:completed_at_gt] ||= Time.zone.today + DEFAULT_DATE_INTERVAL[:from]
|
||||
params[:q][:completed_at_lt] ||= Time.zone.today + DEFAULT_DATE_INTERVAL[:to]
|
||||
params
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,116 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Reporting
|
||||
module Reports
|
||||
module OrdersAndDistributors
|
||||
class OrdersAndDistributorsReport
|
||||
def initialize(user, params = {}, render_table = false)
|
||||
@params = params
|
||||
@user = user
|
||||
@render_table = render_table
|
||||
|
||||
@permissions = ::Permissions::Order.new(user, @params[:q])
|
||||
end
|
||||
|
||||
def table_headers
|
||||
[
|
||||
I18n.t(:report_header_order_date),
|
||||
I18n.t(:report_header_order_id),
|
||||
I18n.t(:report_header_customer_name),
|
||||
I18n.t(:report_header_customer_email),
|
||||
I18n.t(:report_header_customer_phone),
|
||||
I18n.t(:report_header_customer_city),
|
||||
I18n.t(:report_header_sku),
|
||||
I18n.t(:report_header_item_name),
|
||||
I18n.t(:report_header_variant),
|
||||
I18n.t(:report_header_quantity),
|
||||
I18n.t(:report_header_max_quantity),
|
||||
I18n.t(:report_header_cost),
|
||||
I18n.t(:report_header_shipping_cost),
|
||||
I18n.t(:report_header_payment_method),
|
||||
I18n.t(:report_header_distributor),
|
||||
I18n.t(:report_header_distributor_address),
|
||||
I18n.t(:report_header_distributor_city),
|
||||
I18n.t(:report_header_distributor_postcode),
|
||||
I18n.t(:report_header_shipping_method),
|
||||
I18n.t(:report_header_shipping_instructions)
|
||||
]
|
||||
end
|
||||
|
||||
def search
|
||||
@permissions.visible_orders.select("DISTINCT spree_orders.*").
|
||||
complete.not_state(:canceled).
|
||||
ransack(@params[:q])
|
||||
end
|
||||
|
||||
def table_rows
|
||||
return [] unless @render_table
|
||||
|
||||
orders = search.result
|
||||
|
||||
orders.select{ |order| orders_with_hidden_details(orders).include? order }.each do |order|
|
||||
OrderDataMasker.new(order).call
|
||||
end
|
||||
|
||||
line_item_details orders
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def orders_with_hidden_details(orders)
|
||||
# If empty array is passed in, the where clause will return all line_items, which is bad
|
||||
if @permissions.editable_orders.empty?
|
||||
orders
|
||||
else
|
||||
orders.
|
||||
where('spree_orders.id NOT IN (?)',
|
||||
@permissions.editable_orders.select(&:id))
|
||||
end
|
||||
end
|
||||
|
||||
def line_item_details(orders)
|
||||
order_and_distributor_details = []
|
||||
|
||||
orders.each do |order|
|
||||
order.line_items.each do |line_item|
|
||||
order_and_distributor_details << row_for(line_item, order)
|
||||
end
|
||||
end
|
||||
|
||||
order_and_distributor_details
|
||||
end
|
||||
|
||||
# Returns a row with the data to display for the specified line_item and
|
||||
# its order
|
||||
#
|
||||
# @param line_item [Spree::LineItem]
|
||||
# @param order [Spree::Order]
|
||||
# @return [Array]
|
||||
def row_for(line_item, order)
|
||||
[
|
||||
order.completed_at.strftime("%F %T"),
|
||||
order.id,
|
||||
order.bill_address.full_name,
|
||||
order.email,
|
||||
order.bill_address.phone,
|
||||
order.bill_address.city,
|
||||
line_item.product.sku,
|
||||
line_item.product.name,
|
||||
line_item.options_text,
|
||||
line_item.quantity,
|
||||
line_item.max_quantity,
|
||||
line_item.price * line_item.quantity,
|
||||
line_item.distribution_fee,
|
||||
order.payments.first&.payment_method&.name,
|
||||
order.distributor&.name,
|
||||
order.distributor.address.address1,
|
||||
order.distributor.address.city,
|
||||
order.distributor.address.zipcode,
|
||||
order.shipping_method&.name,
|
||||
order.special_instructions
|
||||
]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,223 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# rubocop:disable Metrics/ClassLength
|
||||
module Reporting
|
||||
module Reports
|
||||
module OrdersAndFulfillment
|
||||
class CustomerTotalsReport
|
||||
REPORT_TYPE = "order_cycle_customer_totals"
|
||||
|
||||
attr_reader :context
|
||||
|
||||
delegate :line_item_name, to: :context
|
||||
delegate :variant_scoper_for, to: :context
|
||||
|
||||
def initialize(context)
|
||||
@context = context
|
||||
@scopers_by_distributor_id = {}
|
||||
end
|
||||
|
||||
# rubocop:disable Metrics/AbcSize
|
||||
def table_headers
|
||||
[I18n.t(:report_header_hub), I18n.t(:report_header_customer), I18n.t(:report_header_email),
|
||||
I18n.t(:report_header_phone), I18n.t(:report_header_producer),
|
||||
I18n.t(:report_header_product), I18n.t(:report_header_variant),
|
||||
I18n.t(:report_header_quantity),
|
||||
I18n.t(:report_header_item_price, currency: currency_symbol),
|
||||
I18n.t(:report_header_item_fees_price, currency: currency_symbol),
|
||||
I18n.t(:report_header_admin_handling_fees, currency: currency_symbol),
|
||||
I18n.t(:report_header_ship_price, currency: currency_symbol),
|
||||
I18n.t(:report_header_pay_fee_price, currency: currency_symbol),
|
||||
I18n.t(:report_header_total_price, currency: currency_symbol),
|
||||
I18n.t(:report_header_paid), I18n.t(:report_header_shipping),
|
||||
I18n.t(:report_header_delivery), I18n.t(:report_header_ship_street),
|
||||
I18n.t(:report_header_ship_street_2), I18n.t(:report_header_ship_city),
|
||||
I18n.t(:report_header_ship_postcode), I18n.t(:report_header_ship_state),
|
||||
I18n.t(:report_header_comments), I18n.t(:report_header_sku),
|
||||
I18n.t(:report_header_order_cycle), I18n.t(:report_header_payment_method),
|
||||
I18n.t(:report_header_customer_code), I18n.t(:report_header_tags),
|
||||
I18n.t(:report_header_billing_street), I18n.t(:report_header_billing_street_2),
|
||||
I18n.t(:report_header_billing_city), I18n.t(:report_header_billing_postcode),
|
||||
I18n.t(:report_header_billing_state),
|
||||
I18n.t(:report_header_order_number),
|
||||
I18n.t(:report_header_date)]
|
||||
end
|
||||
|
||||
# rubocop:enable Metrics/AbcSize
|
||||
# rubocop:disable Metrics/AbcSize
|
||||
# rubocop:disable Metrics/MethodLength
|
||||
def rules
|
||||
[
|
||||
{
|
||||
group_by: proc { |line_item| line_item.order.distributor },
|
||||
sort_by: proc { |distributor| distributor.name }
|
||||
},
|
||||
{
|
||||
group_by: proc { |line_item| line_item.order },
|
||||
sort_by: proc { |order| order.bill_address.full_name_reverse },
|
||||
summary_columns: [
|
||||
proc { |line_items| line_items.first.order.distributor.name },
|
||||
proc { |line_items| line_items.first.order.bill_address.full_name },
|
||||
proc { |_line_items| "" },
|
||||
proc { |_line_items| "" },
|
||||
proc { |_line_items| "" },
|
||||
proc { |_line_items| I18n.t('admin.reports.total') },
|
||||
proc { |_line_items| "" },
|
||||
|
||||
proc { |_line_items| "" },
|
||||
proc { |line_items| line_items.sum(&:amount) },
|
||||
proc { |line_items| line_items.sum(&:amount_with_adjustments) },
|
||||
proc { |line_items| line_items.first.order.admin_and_handling_total },
|
||||
proc { |line_items| line_items.first.order.ship_total },
|
||||
proc { |line_items| line_items.first.order.payment_fee },
|
||||
proc { |line_items| line_items.first.order.total },
|
||||
proc { |line_items| line_items.first.order.paid? ? I18n.t(:yes) : I18n.t(:no) },
|
||||
|
||||
proc { |_line_items| "" },
|
||||
proc { |_line_items| "" },
|
||||
|
||||
proc { |_line_items| "" },
|
||||
proc { |_line_items| "" },
|
||||
proc { |_line_items| "" },
|
||||
proc { |_line_items| "" },
|
||||
proc { |_line_items| "" },
|
||||
|
||||
proc { |line_items| line_items.first.order.special_instructions },
|
||||
proc { |_line_items| "" },
|
||||
|
||||
proc { |line_items| line_items.first.order.order_cycle&.name },
|
||||
proc { |line_items|
|
||||
line_items.first.order.payments.first&.payment_method&.name
|
||||
},
|
||||
proc { |_line_items| "" },
|
||||
proc { |_line_items| "" },
|
||||
proc { |_line_items| "" },
|
||||
proc { |_line_items| "" },
|
||||
proc { |_line_items| "" },
|
||||
proc { |_line_items| "" },
|
||||
proc { |_line_items| "" },
|
||||
proc { |line_items| line_items.first.order.number },
|
||||
proc { |line_items| line_items.first.order.completed_at.strftime("%F %T") },
|
||||
]
|
||||
},
|
||||
{
|
||||
group_by: proc { |line_item| line_item.variant.product },
|
||||
sort_by: proc { |product| product.name }
|
||||
},
|
||||
{
|
||||
group_by: proc { |line_item| line_item.variant },
|
||||
sort_by: proc { |variant| variant.full_name }
|
||||
},
|
||||
{
|
||||
group_by: line_item_name,
|
||||
sort_by: proc { |full_name| full_name }
|
||||
}
|
||||
]
|
||||
end
|
||||
# rubocop:enable Metrics/AbcSize
|
||||
# rubocop:enable Metrics/MethodLength
|
||||
|
||||
# rubocop:disable Metrics/AbcSize
|
||||
# rubocop:disable Metrics/MethodLength
|
||||
# rubocop:disable Metrics/PerceivedComplexity
|
||||
def columns
|
||||
rsa = proc { |line_items| shipping_method(line_items)&.delivery? }
|
||||
[
|
||||
proc { |line_items| line_items.first.order.distributor.name },
|
||||
proc { |line_items|
|
||||
bill_address = line_items.first.order.bill_address
|
||||
"#{bill_address.firstname} #{bill_address.lastname}"
|
||||
},
|
||||
proc { |line_items| line_items.first.order.email },
|
||||
proc { |line_items| line_items.first.order.bill_address.phone },
|
||||
proc { |line_items| line_items.first.variant.product.supplier.name },
|
||||
proc { |line_items| line_items.first.variant.product.name },
|
||||
proc { |line_items| line_items.first.variant.full_name },
|
||||
|
||||
proc { |line_items| line_items.to_a.sum(&:quantity) },
|
||||
proc { |line_items| line_items.sum(&:amount) },
|
||||
proc { |line_items| line_items.sum(&:amount_with_adjustments) },
|
||||
proc { |_line_items| "" },
|
||||
proc { |_line_items| "" },
|
||||
proc { |_line_items| "" },
|
||||
proc { |_line_items| "" },
|
||||
proc { |line_items|
|
||||
line_items.all? { |li| li.order.paid? } ? I18n.t(:yes) : I18n.t(:no)
|
||||
},
|
||||
|
||||
proc { |line_items| shipping_method(line_items)&.name },
|
||||
proc { |line_items| rsa.call(line_items) ? I18n.t(:yes) : I18n.t(:no) },
|
||||
|
||||
proc { |line_items|
|
||||
line_items.first.order.ship_address&.address1 if rsa.call(line_items)
|
||||
},
|
||||
proc { |line_items|
|
||||
line_items.first.order.ship_address&.address2 if rsa.call(line_items)
|
||||
},
|
||||
proc { |line_items|
|
||||
line_items.first.order.ship_address&.city if rsa.call(line_items)
|
||||
},
|
||||
proc { |line_items|
|
||||
line_items.first.order.ship_address&.zipcode if rsa.call(line_items)
|
||||
},
|
||||
proc { |line_items|
|
||||
line_items.first.order.ship_address&.state if rsa.call(line_items)
|
||||
},
|
||||
|
||||
proc { |_line_items| "" },
|
||||
proc do |line_items|
|
||||
line_item = line_items.first
|
||||
variant_scoper_for(line_item.order.distributor_id).scope(line_item.variant)
|
||||
line_item.variant.sku
|
||||
end,
|
||||
|
||||
proc { |line_items| line_items.first.order.order_cycle&.name },
|
||||
proc { |line_items|
|
||||
payment = line_items.first.order.payments.first
|
||||
payment&.payment_method&.name
|
||||
},
|
||||
proc { |line_items|
|
||||
distributor = line_items.first.order.distributor
|
||||
user = line_items.first.order.user
|
||||
user&.customer_of(distributor)&.code
|
||||
},
|
||||
proc { |line_items|
|
||||
distributor = line_items.first.order.distributor
|
||||
user = line_items.first.order.user
|
||||
user&.customer_of(distributor)&.tags&.join(', ')
|
||||
},
|
||||
|
||||
proc { |line_items| line_items.first.order.bill_address&.address1 },
|
||||
proc { |line_items| line_items.first.order.bill_address&.address2 },
|
||||
proc { |line_items| line_items.first.order.bill_address&.city },
|
||||
proc { |line_items| line_items.first.order.bill_address&.zipcode },
|
||||
proc { |line_items| line_items.first.order.bill_address&.state },
|
||||
proc { |line_items| line_items.first.order.number },
|
||||
proc { |line_items| line_items.first.order.completed_at.strftime("%F %T") },
|
||||
]
|
||||
end
|
||||
# rubocop:enable Metrics/AbcSize
|
||||
# rubocop:enable Metrics/MethodLength
|
||||
# rubocop:enable Metrics/PerceivedComplexity
|
||||
|
||||
def line_item_includes
|
||||
[{ variant: [{ option_values: :option_type }, { product: :supplier }],
|
||||
order: [:bill_address, :ship_address, :order_cycle, :adjustments, :payments,
|
||||
:user, :distributor, :shipments] }]
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def shipping_method(line_items)
|
||||
shipping_rates = line_items.first.order.shipments.first&.shipping_rates
|
||||
|
||||
return unless shipping_rates
|
||||
|
||||
shipping_rate = shipping_rates.find(&:selected) || shipping_rates.first
|
||||
shipping_rate.try(:shipping_method)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
# rubocop:enable Metrics/ClassLength
|
||||
@@ -0,0 +1,66 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Reporting
|
||||
module Reports
|
||||
module OrdersAndFulfillment
|
||||
class DefaultReport
|
||||
delegate :line_item_name, :supplier_name, :product_name, :line_items_name, to: :context
|
||||
|
||||
def initialize(context)
|
||||
@context = context
|
||||
end
|
||||
|
||||
def table_headers
|
||||
[
|
||||
I18n.t(:report_header_producer),
|
||||
I18n.t(:report_header_product),
|
||||
I18n.t(:report_header_variant),
|
||||
I18n.t(:report_header_quantity),
|
||||
I18n.t(:report_header_curr_cost_per_unit),
|
||||
I18n.t(:report_header_total_cost),
|
||||
I18n.t(:report_header_status),
|
||||
I18n.t(:report_header_incoming_transport)
|
||||
]
|
||||
end
|
||||
|
||||
def rules
|
||||
[
|
||||
{
|
||||
group_by: proc { |line_item| line_item.variant.product.supplier },
|
||||
sort_by: proc { |supplier| supplier.name }
|
||||
},
|
||||
{
|
||||
group_by: proc { |line_item| line_item.variant.product },
|
||||
sort_by: proc { |product| product.name }
|
||||
},
|
||||
{
|
||||
group_by: line_item_name,
|
||||
sort_by: proc { |full_name| full_name }
|
||||
}
|
||||
]
|
||||
end
|
||||
|
||||
def columns
|
||||
[
|
||||
supplier_name,
|
||||
product_name,
|
||||
line_items_name,
|
||||
proc { |line_items| line_items.to_a.sum(&:quantity) },
|
||||
proc { |line_items| line_items.first.price },
|
||||
proc { |line_items| line_items.sum { |li| li.quantity * li.price } },
|
||||
proc { |_line_items| "" },
|
||||
proc { |_line_items| I18n.t(:report_header_incoming_transport) }
|
||||
]
|
||||
end
|
||||
|
||||
def line_item_includes
|
||||
[]
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
attr_reader :context
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,80 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Reporting
|
||||
module Reports
|
||||
module OrdersAndFulfillment
|
||||
class DistributorTotalsBySupplierReport
|
||||
REPORT_TYPE = "order_cycle_distributor_totals_by_supplier"
|
||||
|
||||
attr_reader :context
|
||||
|
||||
def initialize(context)
|
||||
@context = context
|
||||
end
|
||||
|
||||
def table_headers
|
||||
[I18n.t(:report_header_hub), I18n.t(:report_header_producer),
|
||||
I18n.t(:report_header_product), I18n.t(:report_header_variant),
|
||||
I18n.t(:report_header_quantity), I18n.t(:report_header_curr_cost_per_unit),
|
||||
I18n.t(:report_header_total_cost), I18n.t(:report_header_total_shipping_cost),
|
||||
I18n.t(:report_header_shipping_method)]
|
||||
end
|
||||
|
||||
# rubocop:disable Metrics/AbcSize
|
||||
# rubocop:disable Metrics/MethodLength
|
||||
def rules
|
||||
[
|
||||
{
|
||||
group_by: proc { |line_item| line_item.order.distributor },
|
||||
sort_by: proc { |distributor| distributor.name },
|
||||
summary_columns: [
|
||||
proc { |_line_items| "" },
|
||||
proc { |_line_items| I18n.t('admin.reports.total') },
|
||||
proc { |_line_items| "" },
|
||||
proc { |_line_items| "" },
|
||||
proc { |_line_items| "" },
|
||||
proc { |_line_items| "" },
|
||||
proc { |line_items| line_items.sum(&:amount) },
|
||||
proc { |line_items| line_items.map(&:order).uniq.sum(&:ship_total) },
|
||||
proc { |_line_items| "" }
|
||||
]
|
||||
},
|
||||
{
|
||||
group_by: proc { |line_item| line_item.variant.product.supplier },
|
||||
sort_by: proc { |supplier| supplier.name }
|
||||
},
|
||||
{
|
||||
group_by: proc { |line_item| line_item.variant.product },
|
||||
sort_by: proc { |product| product.name }
|
||||
},
|
||||
{
|
||||
group_by: proc { |line_item| line_item.variant.full_name },
|
||||
sort_by: proc { |full_name| full_name }
|
||||
}
|
||||
]
|
||||
end
|
||||
# rubocop:enable Metrics/AbcSize
|
||||
# rubocop:enable Metrics/MethodLength
|
||||
|
||||
# rubocop:disable Metrics/AbcSize
|
||||
def columns
|
||||
[proc { |line_items| line_items.first.order.distributor.name },
|
||||
proc { |line_items| line_items.first.variant.product.supplier.name },
|
||||
proc { |line_items| line_items.first.variant.product.name },
|
||||
proc { |line_items| line_items.first.variant.full_name },
|
||||
proc { |line_items| line_items.to_a.sum(&:quantity) },
|
||||
proc { |line_items| line_items.first.price },
|
||||
proc { |line_items| line_items.sum(&:amount) },
|
||||
proc { |_line_items| "" },
|
||||
proc { |_line_items| I18n.t(:report_header_shipping_method) }]
|
||||
end
|
||||
# rubocop:enable Metrics/AbcSize
|
||||
|
||||
def line_item_includes
|
||||
[{ order: [:distributor, :adjustments, { shipments: { shipping_rates: :shipping_method } }],
|
||||
variant: [{ option_values: :option_type }, { product: :supplier }] }]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,116 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
include Spree::ReportsHelper
|
||||
|
||||
module Reporting
|
||||
module Reports
|
||||
module OrdersAndFulfillment
|
||||
class OrdersAndFulfillmentReport
|
||||
attr_reader :options, :report_type
|
||||
|
||||
delegate :table_headers, :rules, :columns, to: :report
|
||||
|
||||
def initialize(user, options = {}, render_table = false)
|
||||
@user = user
|
||||
@options = options
|
||||
@report_type = options[:report_subtype]
|
||||
@render_table = render_table
|
||||
@variant_scopers_by_distributor_id = {}
|
||||
end
|
||||
|
||||
def search
|
||||
report_line_items.orders
|
||||
end
|
||||
|
||||
def table_items
|
||||
return [] unless @render_table
|
||||
|
||||
report_line_items.list(report.line_item_includes)
|
||||
end
|
||||
|
||||
def table_rows
|
||||
order_grouper = Reporting::OrderGrouper.new report.rules, report.columns, report
|
||||
order_grouper.table(table_items)
|
||||
end
|
||||
|
||||
def line_item_name
|
||||
proc { |line_item| line_item.variant.full_name }
|
||||
end
|
||||
|
||||
def line_items_name
|
||||
proc { |line_items| line_items.first.variant.full_name }
|
||||
end
|
||||
|
||||
def supplier_name
|
||||
proc { |line_items| line_items.first.variant.product.supplier.name }
|
||||
end
|
||||
|
||||
def product_name
|
||||
proc { |line_items| line_items.first.variant.product.name }
|
||||
end
|
||||
|
||||
def total_units(line_items)
|
||||
return " " if not_all_have_unit?(line_items)
|
||||
|
||||
total_units = line_items.sum do |li|
|
||||
product = li.variant.product
|
||||
li.quantity * li.unit_value / scale_factor(product)
|
||||
end
|
||||
|
||||
total_units.round(3)
|
||||
end
|
||||
|
||||
def variant_scoper_for(distributor_id)
|
||||
@variant_scopers_by_distributor_id[distributor_id] ||=
|
||||
OpenFoodNetwork::ScopeVariantToHub.new(
|
||||
distributor_id,
|
||||
report_variant_overrides[distributor_id] || {},
|
||||
)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def report
|
||||
@report ||= report_klass.new(self)
|
||||
end
|
||||
|
||||
def report_klass
|
||||
case report_type
|
||||
when SupplierTotalsReport::REPORT_TYPE then SupplierTotalsReport
|
||||
when SupplierTotalsByDistributorReport::REPORT_TYPE then SupplierTotalsByDistributorReport
|
||||
when DistributorTotalsBySupplierReport::REPORT_TYPE then DistributorTotalsBySupplierReport
|
||||
when CustomerTotalsReport::REPORT_TYPE then CustomerTotalsReport
|
||||
else
|
||||
DefaultReport
|
||||
end
|
||||
end
|
||||
|
||||
def not_all_have_unit?(line_items)
|
||||
line_items.map { |li| li.unit_value.nil? }.any?
|
||||
end
|
||||
|
||||
def scale_factor(product)
|
||||
product.variant_unit == 'weight' ? 1000 : 1
|
||||
end
|
||||
|
||||
def order_permissions
|
||||
return @order_permissions unless @order_permissions.nil?
|
||||
|
||||
@order_permissions = ::Permissions::Order.new(@user, options[:q])
|
||||
end
|
||||
|
||||
def report_line_items
|
||||
@report_line_items ||= Reporting::LineItems.new(order_permissions, options)
|
||||
end
|
||||
|
||||
def report_variant_overrides
|
||||
@report_variant_overrides ||=
|
||||
VariantOverridesIndexed.new(
|
||||
order_permissions.visible_line_items.select('DISTINCT variant_id'),
|
||||
report_line_items.orders.result.select('DISTINCT distributor_id'),
|
||||
).indexed
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,79 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Reporting
|
||||
module Reports
|
||||
module OrdersAndFulfillment
|
||||
class SupplierTotalsByDistributorReport
|
||||
REPORT_TYPE = "order_cycle_supplier_totals_by_distributor"
|
||||
|
||||
attr_reader :context
|
||||
|
||||
delegate :supplier_name, to: :context
|
||||
|
||||
def initialize(context)
|
||||
@context = context
|
||||
end
|
||||
|
||||
def table_headers
|
||||
[I18n.t(:report_header_producer), I18n.t(:report_header_product),
|
||||
I18n.t(:report_header_variant), I18n.t(:report_header_to_hub),
|
||||
I18n.t(:report_header_quantity), I18n.t(:report_header_curr_cost_per_unit),
|
||||
I18n.t(:report_header_total_cost), I18n.t(:report_header_shipping_method)]
|
||||
end
|
||||
|
||||
# rubocop:disable Metrics/AbcSize
|
||||
# rubocop:disable Metrics/MethodLength
|
||||
def rules
|
||||
[
|
||||
{
|
||||
group_by: proc { |line_item| line_item.variant.product.supplier },
|
||||
sort_by: proc { |supplier| supplier.name }
|
||||
},
|
||||
{
|
||||
group_by: proc { |line_item| line_item.variant.product },
|
||||
sort_by: proc { |product| product.name }
|
||||
},
|
||||
{
|
||||
group_by: proc { |line_item| line_item.variant.full_name },
|
||||
sort_by: proc { |full_name| full_name },
|
||||
summary_columns: [
|
||||
proc { |_line_items| "" },
|
||||
proc { |_line_items| "" },
|
||||
proc { |_line_items| "" },
|
||||
proc { |_line_items| I18n.t('admin.reports.total') },
|
||||
proc { |_line_items| "" },
|
||||
proc { |_line_items| "" },
|
||||
proc { |line_items| line_items.sum(&:amount) },
|
||||
proc { |_line_items| "" }
|
||||
]
|
||||
},
|
||||
{
|
||||
group_by: proc { |line_item| line_item.order.distributor },
|
||||
sort_by: proc { |distributor| distributor.name }
|
||||
}
|
||||
]
|
||||
end
|
||||
# rubocop:enable Metrics/AbcSize
|
||||
# rubocop:enable Metrics/MethodLength
|
||||
|
||||
def columns
|
||||
[
|
||||
supplier_name,
|
||||
proc { |line_items| line_items.first.variant.product.name },
|
||||
proc { |line_items| line_items.first.variant.full_name },
|
||||
proc { |line_items| line_items.first.order.distributor.name },
|
||||
proc { |line_items| line_items.to_a.sum(&:quantity) },
|
||||
proc { |line_items| line_items.first.price },
|
||||
proc { |line_items| line_items.sum(&:amount) },
|
||||
proc { |_line_items| I18n.t(:report_header_shipping_method) }
|
||||
]
|
||||
end
|
||||
|
||||
def line_item_includes
|
||||
[{ order: :distributor,
|
||||
variant: [{ option_values: :option_type }, { product: :supplier }] }]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,62 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Reporting
|
||||
module Reports
|
||||
module OrdersAndFulfillment
|
||||
class SupplierTotalsReport
|
||||
REPORT_TYPE = "order_cycle_supplier_totals"
|
||||
|
||||
attr_reader :context
|
||||
|
||||
delegate :supplier_name, :product_name, :line_items_name, :total_units, to: :context
|
||||
|
||||
def initialize(context)
|
||||
@context = context
|
||||
end
|
||||
|
||||
def table_headers
|
||||
[I18n.t(:report_header_producer), I18n.t(:report_header_product),
|
||||
I18n.t(:report_header_variant), I18n.t(:report_header_quantity),
|
||||
I18n.t(:report_header_total_units), I18n.t(:report_header_curr_cost_per_unit),
|
||||
I18n.t(:report_header_total_cost), I18n.t(:report_header_status),
|
||||
I18n.t(:report_header_incoming_transport)]
|
||||
end
|
||||
|
||||
def rules
|
||||
[
|
||||
{
|
||||
group_by: proc { |line_item| line_item.variant.product.supplier },
|
||||
sort_by: proc { |supplier| supplier.name }
|
||||
},
|
||||
{
|
||||
group_by: proc { |line_item| line_item.variant.product },
|
||||
sort_by: proc { |product| product.name }
|
||||
},
|
||||
{
|
||||
group_by: proc { |line_item| line_item.variant.full_name },
|
||||
sort_by: proc { |full_name| full_name }
|
||||
}
|
||||
]
|
||||
end
|
||||
|
||||
def columns
|
||||
[
|
||||
supplier_name,
|
||||
product_name,
|
||||
line_items_name,
|
||||
proc { |line_items| line_items.to_a.sum(&:quantity) },
|
||||
proc { |line_items| total_units(line_items) },
|
||||
proc { |line_items| line_items.first.price },
|
||||
proc { |line_items| line_items.sum(&:amount) },
|
||||
proc { |_line_items| "" },
|
||||
proc { |_line_items| I18n.t(:report_header_incoming_transport) }
|
||||
]
|
||||
end
|
||||
|
||||
def line_item_includes
|
||||
[{ variant: [{ option_values: :option_type }, { product: :supplier }] }]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
150
lib/reporting/reports/payments/payments_report.rb
Normal file
150
lib/reporting/reports/payments/payments_report.rb
Normal file
@@ -0,0 +1,150 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Reporting
|
||||
module Reports
|
||||
module Payments
|
||||
class PaymentsReport
|
||||
attr_reader :params
|
||||
|
||||
def initialize(user, params = {}, render_table = false)
|
||||
@params = params
|
||||
@user = user
|
||||
@render_table = render_table
|
||||
end
|
||||
|
||||
def table_headers
|
||||
case params[:report_subtype]
|
||||
when "payments_by_payment_type"
|
||||
I18n.t(:report_header_payment_type)
|
||||
[I18n.t(:report_header_payment_state), I18n.t(:report_header_distributor), I18n.t(:report_header_payment_type),
|
||||
I18n.t(:report_header_total_price, currency: currency_symbol)]
|
||||
when "itemised_payment_totals"
|
||||
[I18n.t(:report_header_payment_state), I18n.t(:report_header_distributor),
|
||||
I18n.t(:report_header_product_total_price, currency: currency_symbol),
|
||||
I18n.t(:report_header_shipping_total_price, currency: currency_symbol),
|
||||
I18n.t(:report_header_outstanding_balance_price, currency: currency_symbol),
|
||||
I18n.t(:report_header_total_price, currency: currency_symbol)]
|
||||
when "payment_totals"
|
||||
[I18n.t(:report_header_payment_state), I18n.t(:report_header_distributor),
|
||||
I18n.t(:report_header_product_total_price, currency: currency_symbol),
|
||||
I18n.t(:report_header_shipping_total_price, currency: currency_symbol),
|
||||
I18n.t(:report_header_total_price, currency: currency_symbol),
|
||||
I18n.t(:report_header_eft_price, currency: currency_symbol),
|
||||
I18n.t(:report_header_paypal_price, currency: currency_symbol),
|
||||
I18n.t(:report_header_outstanding_balance_price, currency: currency_symbol)]
|
||||
else
|
||||
[I18n.t(:report_header_payment_state), I18n.t(:report_header_distributor), I18n.t(:report_header_payment_type),
|
||||
I18n.t(:report_header_total_price, currency: currency_symbol)]
|
||||
end
|
||||
end
|
||||
|
||||
def search
|
||||
Spree::Order.complete.not_state(:canceled).managed_by(@user).ransack(params[:q])
|
||||
end
|
||||
|
||||
def table_items
|
||||
return [] unless @render_table
|
||||
|
||||
orders = search.result
|
||||
payments = orders.includes(:payments).map do |order|
|
||||
order.payments.select(&:completed?)
|
||||
end.flatten
|
||||
|
||||
case params[:report_subtype]
|
||||
when "payments_by_payment_type"
|
||||
payments
|
||||
when "itemised_payment_totals"
|
||||
orders
|
||||
when "payment_totals"
|
||||
orders
|
||||
else
|
||||
payments
|
||||
end
|
||||
end
|
||||
|
||||
def table_rows
|
||||
order_grouper = Reporting::OrderGrouper.new rules, columns, self
|
||||
order_grouper.table(table_items)
|
||||
end
|
||||
|
||||
def rules
|
||||
case params[:report_subtype]
|
||||
when "payments_by_payment_type"
|
||||
[{ group_by: proc { |payment| payment.order.payment_state },
|
||||
sort_by: proc { |payment_state| payment_state } },
|
||||
{ group_by: proc { |payment| payment.order.distributor },
|
||||
sort_by: proc { |distributor| distributor.name } },
|
||||
{ group_by: proc { |payment|
|
||||
Spree::PaymentMethod.unscoped {
|
||||
payment.payment_method
|
||||
}
|
||||
},
|
||||
sort_by: proc { |method| method.name } }]
|
||||
when "itemised_payment_totals"
|
||||
[{ group_by: proc { |order| order.payment_state },
|
||||
sort_by: proc { |payment_state| payment_state } },
|
||||
{ group_by: proc { |order| order.distributor },
|
||||
sort_by: proc { |distributor| distributor.name } }]
|
||||
when "payment_totals"
|
||||
[{ group_by: proc { |order| order.payment_state },
|
||||
sort_by: proc { |payment_state| payment_state } },
|
||||
{ group_by: proc { |order| order.distributor },
|
||||
sort_by: proc { |distributor| distributor.name } }]
|
||||
else
|
||||
[{ group_by: proc { |payment| payment.order.payment_state },
|
||||
sort_by: proc { |payment_state| payment_state } },
|
||||
{ group_by: proc { |payment| payment.order.distributor },
|
||||
sort_by: proc { |distributor| distributor.name } },
|
||||
{ group_by: proc { |payment| payment.payment_method },
|
||||
sort_by: proc { |method| method.name } }]
|
||||
end
|
||||
end
|
||||
|
||||
def columns
|
||||
case params[:report_subtype]
|
||||
when "payments_by_payment_type"
|
||||
[proc { |payments| payments.first.order.payment_state },
|
||||
proc { |payments| payments.first.order.distributor.name },
|
||||
proc { |payments| payments.first.payment_method.name },
|
||||
proc { |payments| payments.sum(&:amount) }]
|
||||
when "itemised_payment_totals"
|
||||
[proc { |orders| orders.first.payment_state },
|
||||
proc { |orders| orders.first.distributor.name },
|
||||
proc { |orders| orders.to_a.sum(&:item_total) },
|
||||
proc { |orders| orders.sum(&:ship_total) },
|
||||
proc { |orders| orders.sum{ |order| order.outstanding_balance.to_f } },
|
||||
proc { |orders| orders.map(&:total).sum }]
|
||||
when "payment_totals"
|
||||
[proc { |orders| orders.first.payment_state },
|
||||
proc { |orders| orders.first.distributor.name },
|
||||
proc { |orders| orders.to_a.sum(&:item_total) },
|
||||
proc { |orders| orders.sum(&:ship_total) },
|
||||
proc { |orders| orders.map(&:total).sum },
|
||||
proc { |orders|
|
||||
orders.sum { |o|
|
||||
o.payments.select { |payment|
|
||||
payment.completed? &&
|
||||
(payment.payment_method.name.to_s.include? "EFT")
|
||||
}.sum(&:amount)
|
||||
}
|
||||
},
|
||||
proc { |orders|
|
||||
orders.sum { |o|
|
||||
o.payments.select { |payment|
|
||||
payment.completed? &&
|
||||
(payment.payment_method.name.to_s.include? "PayPal")
|
||||
}.sum(&:amount)
|
||||
}
|
||||
},
|
||||
proc { |orders| orders.sum{ |order| order.outstanding_balance.to_f } }]
|
||||
else
|
||||
[proc { |payments| payments.first.order.payment_state },
|
||||
proc { |payments| payments.first.order.distributor.name },
|
||||
proc { |payments| payments.first.payment_method.name },
|
||||
proc { |payments| payments.sum(&:amount) }]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,93 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# require 'variant_units/option_value_namer'
|
||||
|
||||
module Reporting
|
||||
module Reports
|
||||
module ProductsAndInventory
|
||||
class LettuceShareReport
|
||||
attr_reader :context
|
||||
|
||||
delegate :variants, :render_table, to: :context
|
||||
|
||||
def initialize(context)
|
||||
@context = context
|
||||
end
|
||||
|
||||
def table_headers
|
||||
# NOTE: These are NOT to be translated, they need to be in this exact format to work with LettucShare
|
||||
[
|
||||
"PRODUCT",
|
||||
"Description",
|
||||
"Qty",
|
||||
"Pack Size",
|
||||
"Unit",
|
||||
"Unit Price",
|
||||
"Total",
|
||||
"GST incl.",
|
||||
"Grower and growing method",
|
||||
"Taxon"
|
||||
]
|
||||
end
|
||||
|
||||
def table_rows
|
||||
return [] unless render_table
|
||||
|
||||
variants.select(&:in_stock?)
|
||||
.map do |variant|
|
||||
[
|
||||
variant.product.name,
|
||||
variant.full_name,
|
||||
'',
|
||||
VariantUnits::OptionValueNamer.new(variant).value,
|
||||
VariantUnits::OptionValueNamer.new(variant).unit,
|
||||
variant.price,
|
||||
'',
|
||||
gst(variant),
|
||||
grower_and_method(variant),
|
||||
variant.product.primary_taxon.name
|
||||
]
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def gst(variant)
|
||||
tax_category = variant.product.tax_category
|
||||
if tax_category && tax_category.tax_rates.present?
|
||||
tax_rate = tax_category.tax_rates.first
|
||||
line_item = mock_line_item(variant)
|
||||
tax_rate.calculator.compute line_item
|
||||
else
|
||||
0
|
||||
end
|
||||
end
|
||||
|
||||
def mock_line_item(variant)
|
||||
line_item = Spree::LineItem.new quantity: 1
|
||||
line_item.define_singleton_method(:product) { variant.product }
|
||||
line_item.define_singleton_method(:price) { variant.price }
|
||||
line_item
|
||||
end
|
||||
|
||||
def grower_and_method(variant)
|
||||
cert = certification(variant)
|
||||
|
||||
result = producer_name(variant)
|
||||
result += " (#{cert})" if cert.present?
|
||||
result
|
||||
end
|
||||
|
||||
def producer_name(variant)
|
||||
variant.product.supplier.name
|
||||
end
|
||||
|
||||
def certification(variant)
|
||||
variant.product.properties_including_inherited.map do |p|
|
||||
"#{p[:name]} - #{p[:value]}"
|
||||
end.join(', ')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,57 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Reporting
|
||||
module Reports
|
||||
module ProductsAndInventory
|
||||
class ProductsAndInventoryDefaultReport
|
||||
attr_reader :context
|
||||
|
||||
delegate :variants, :render_table, to: :context
|
||||
|
||||
def initialize(context)
|
||||
@context = context
|
||||
end
|
||||
|
||||
def table_headers
|
||||
[
|
||||
I18n.t(:report_header_supplier),
|
||||
I18n.t(:report_header_producer_suburb),
|
||||
I18n.t(:report_header_product),
|
||||
I18n.t(:report_header_product_properties),
|
||||
I18n.t(:report_header_taxons),
|
||||
I18n.t(:report_header_variant_value),
|
||||
I18n.t(:report_header_price),
|
||||
I18n.t(:report_header_group_buy_unit_quantity),
|
||||
I18n.t(:report_header_amount),
|
||||
I18n.t(:report_header_sku)
|
||||
]
|
||||
end
|
||||
|
||||
def table_rows
|
||||
return [] unless render_table
|
||||
|
||||
variants.map do |variant|
|
||||
[
|
||||
variant.product.supplier.name,
|
||||
variant.product.supplier.address.city,
|
||||
variant.product.name,
|
||||
variant.product.properties.map(&:name).join(", "),
|
||||
variant.product.taxons.map(&:name).join(", "),
|
||||
variant.full_name,
|
||||
variant.price,
|
||||
variant.product.group_buy_unit_size,
|
||||
"",
|
||||
sku_for(variant)
|
||||
]
|
||||
end
|
||||
end
|
||||
|
||||
def sku_for(variant)
|
||||
return variant.sku if variant.sku.present?
|
||||
|
||||
variant.product.sku
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,102 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'open_food_network/scope_variant_to_hub'
|
||||
|
||||
module Reporting
|
||||
module Reports
|
||||
module ProductsAndInventory
|
||||
class ProductsAndInventoryReport
|
||||
attr_reader :params, :render_table
|
||||
|
||||
delegate :table_rows, :table_headers, :rules, :columns, :sku_for, to: :report
|
||||
|
||||
def initialize(user, params = {}, render_table = false)
|
||||
@user = user
|
||||
@params = params
|
||||
@render_table = render_table
|
||||
end
|
||||
|
||||
def report
|
||||
@report ||= report_klass.new(self)
|
||||
end
|
||||
|
||||
def report_type
|
||||
params[:report_subtype]
|
||||
end
|
||||
|
||||
def report_klass
|
||||
if report_type == 'lettuce_share'
|
||||
LettuceShareReport
|
||||
else
|
||||
ProductsAndInventoryDefaultReport
|
||||
end
|
||||
end
|
||||
|
||||
def permissions
|
||||
@permissions ||= OpenFoodNetwork::Permissions.new(@user)
|
||||
end
|
||||
|
||||
def visible_products
|
||||
@visible_products ||= permissions.visible_products
|
||||
end
|
||||
|
||||
def variants
|
||||
filter(child_variants)
|
||||
end
|
||||
|
||||
def child_variants
|
||||
Spree::Variant.
|
||||
where(is_master: false).
|
||||
includes(option_values: :option_type).
|
||||
joins(:product).
|
||||
merge(visible_products).
|
||||
order('spree_products.name')
|
||||
end
|
||||
|
||||
def filter(variants)
|
||||
filter_on_hand filter_to_distributor filter_to_order_cycle filter_to_supplier variants
|
||||
end
|
||||
|
||||
# Using the `in_stock?` method allows overrides by distributors.
|
||||
def filter_on_hand(variants)
|
||||
if report_type == 'inventory'
|
||||
variants.select(&:in_stock?)
|
||||
else
|
||||
variants
|
||||
end
|
||||
end
|
||||
|
||||
def filter_to_supplier(variants)
|
||||
if params[:supplier_id].to_i > 0
|
||||
variants.where("spree_products.supplier_id = ?", params[:supplier_id])
|
||||
else
|
||||
variants
|
||||
end
|
||||
end
|
||||
|
||||
def filter_to_distributor(variants)
|
||||
if params[:distributor_id].to_i > 0
|
||||
distributor = Enterprise.find params[:distributor_id]
|
||||
scoper = OpenFoodNetwork::ScopeVariantToHub.new(distributor)
|
||||
variants.in_distributor(distributor).each { |v| scoper.scope(v) }
|
||||
else
|
||||
variants
|
||||
end
|
||||
end
|
||||
|
||||
def filter_to_order_cycle(variants)
|
||||
if params[:order_cycle_id].to_i > 0
|
||||
order_cycle = OrderCycle.find params[:order_cycle_id]
|
||||
variant_ids = Exchange.in_order_cycle(order_cycle).
|
||||
joins("INNER JOIN exchange_variants ON exchanges.id = exchange_variants.exchange_id").
|
||||
select("DISTINCT exchange_variants.variant_id")
|
||||
|
||||
variants.where("spree_variants.id IN (#{variant_ids.to_sql})")
|
||||
else
|
||||
variants
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
114
lib/reporting/reports/sales_tax/sales_tax_report.rb
Normal file
114
lib/reporting/reports/sales_tax/sales_tax_report.rb
Normal file
@@ -0,0 +1,114 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Reporting
|
||||
module Reports
|
||||
module SalesTax
|
||||
class SalesTaxReport
|
||||
include Spree::ReportsHelper
|
||||
attr_accessor :user, :params
|
||||
|
||||
def initialize(user, params, render_table)
|
||||
@user = user
|
||||
@params = params
|
||||
@render_table = render_table
|
||||
end
|
||||
|
||||
def table_headers
|
||||
case params[:report_subtype]
|
||||
when "tax_rates"
|
||||
[I18n.t(:report_header_order_number),
|
||||
I18n.t(:report_header_total_excl_vat, currency_symbol: currency_symbol)] +
|
||||
relevant_rates.map { |rate|
|
||||
"%.1f%% (%s)" % [rate.amount.to_f * 100, currency_symbol]
|
||||
} +
|
||||
[I18n.t(:report_header_total_tax, currency_symbol: currency_symbol),
|
||||
I18n.t(:report_header_total_incl_vat, currency_symbol: currency_symbol)]
|
||||
else
|
||||
[I18n.t(:report_header_order_number),
|
||||
I18n.t(:report_header_date),
|
||||
I18n.t(:report_header_items),
|
||||
I18n.t(:report_header_items_total, currency_symbol: currency_symbol),
|
||||
I18n.t(:report_header_taxable_items_total, currency_symbol: currency_symbol),
|
||||
I18n.t(:report_header_sales_tax, currency_symbol: currency_symbol),
|
||||
I18n.t(:report_header_delivery_charge, currency_symbol: currency_symbol),
|
||||
I18n.t(:report_header_tax_on_delivery, currency_symbol: currency_symbol),
|
||||
I18n.t(:report_header_tax_on_fees, currency_symbol: currency_symbol),
|
||||
I18n.t(:report_header_total_tax, currency_symbol: currency_symbol),
|
||||
I18n.t(:report_header_customer),
|
||||
I18n.t(:report_header_distributor)]
|
||||
end
|
||||
end
|
||||
|
||||
def search
|
||||
permissions = ::Permissions::Order.new(user)
|
||||
permissions.editable_orders.complete.not_state(:canceled).ransack(params[:q])
|
||||
end
|
||||
|
||||
def orders
|
||||
search.result
|
||||
end
|
||||
|
||||
def table_rows
|
||||
return [] unless @render_table
|
||||
|
||||
case params[:report_subtype]
|
||||
when "tax_rates"
|
||||
orders.map do |order|
|
||||
[order.number, order.total - order.total_tax] +
|
||||
relevant_rates.map { |rate|
|
||||
OrderTaxAdjustmentsFetcher.new(order).totals.fetch(rate, 0)
|
||||
} + [order.total_tax, order.total]
|
||||
end
|
||||
else
|
||||
orders.map do |order|
|
||||
totals = totals_of order.line_items
|
||||
shipping_cost = shipping_cost_for order
|
||||
|
||||
[order.number, order.completed_at.strftime("%F %T"), totals[:items], totals[:items_total],
|
||||
totals[:taxable_total], totals[:sales_tax], shipping_cost, order.shipping_tax, order.enterprise_fee_tax, order.total_tax,
|
||||
order.bill_address.full_name, order.distributor&.name]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def relevant_rates
|
||||
return @relevant_rates unless @relevant_rates.nil?
|
||||
|
||||
@relevant_rates = Spree::TaxRate.distinct
|
||||
end
|
||||
|
||||
def totals_of(line_items)
|
||||
totals = { items: 0, items_total: 0.0, taxable_total: 0.0, sales_tax: 0.0 }
|
||||
|
||||
line_items.each do |line_item|
|
||||
totals[:items] += line_item.quantity
|
||||
totals[:items_total] += line_item.amount
|
||||
|
||||
sales_tax = tax_included_in line_item
|
||||
|
||||
if sales_tax > 0
|
||||
totals[:taxable_total] += line_item.amount
|
||||
totals[:sales_tax] += sales_tax
|
||||
end
|
||||
end
|
||||
|
||||
totals.each_pair do |k, _v|
|
||||
totals[k] = totals[k].round(2)
|
||||
end
|
||||
|
||||
totals
|
||||
end
|
||||
|
||||
def shipping_cost_for(order)
|
||||
order.shipments.first&.cost || 0.0
|
||||
end
|
||||
|
||||
def tax_included_in(line_item)
|
||||
line_item.adjustments.tax.inclusive.sum(:amount)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,143 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Reporting
|
||||
module Reports
|
||||
module UsersAndEnterprises
|
||||
class UsersAndEnterprisesReport
|
||||
attr_reader :params
|
||||
|
||||
def initialize(user, params = {}, compile_table = false)
|
||||
@user = user
|
||||
@params = params
|
||||
@compile_table = compile_table
|
||||
|
||||
# Convert arrays of ids to comma delimited strings
|
||||
if @params[:enterprise_id_in].is_a? Array
|
||||
@params[:enterprise_id_in] = @params[:enterprise_id_in].join(',')
|
||||
end
|
||||
@params[:user_id_in] = @params[:user_id_in].join(',') if @params[:user_id_in].is_a? Array
|
||||
end
|
||||
|
||||
def table_headers
|
||||
[
|
||||
I18n.t(:report_header_user),
|
||||
I18n.t(:report_header_relationship),
|
||||
I18n.t(:report_header_enterprise),
|
||||
I18n.t(:report_header_is_producer),
|
||||
I18n.t(:report_header_sells),
|
||||
I18n.t(:report_header_visible),
|
||||
I18n.t(:report_header_confirmation_date),
|
||||
]
|
||||
end
|
||||
|
||||
def table_rows
|
||||
return [] unless @compile_table
|
||||
|
||||
users_and_enterprises.map do |uae|
|
||||
[
|
||||
uae["user_email"],
|
||||
uae["relationship_type"],
|
||||
uae["name"],
|
||||
to_bool(uae["is_primary_producer"]),
|
||||
uae["sells"],
|
||||
uae["visible"],
|
||||
to_local_datetime(uae["created_at"])
|
||||
]
|
||||
end
|
||||
end
|
||||
|
||||
def owners_and_enterprises
|
||||
query = Enterprise.joins("LEFT JOIN spree_users AS owner ON enterprises.owner_id = owner.id")
|
||||
.where("enterprises.id IS NOT NULL")
|
||||
|
||||
query = filter_by_int_list_if_present(query, "enterprises.id", params[:enterprise_id_in])
|
||||
query = filter_by_int_list_if_present(query, "owner.id", params[:user_id_in])
|
||||
|
||||
query_helper(query, :owner, :owns)
|
||||
end
|
||||
|
||||
def managers_and_enterprises
|
||||
query = Enterprise
|
||||
.joins("LEFT JOIN enterprise_roles ON enterprises.id = enterprise_roles.enterprise_id")
|
||||
.joins("LEFT JOIN spree_users AS managers ON enterprise_roles.user_id = managers.id")
|
||||
.where("enterprise_id IS NOT NULL")
|
||||
.where("user_id IS NOT NULL")
|
||||
|
||||
query = filter_by_int_list_if_present(query, "enterprise_id", params[:enterprise_id_in])
|
||||
query = filter_by_int_list_if_present(query, "user_id", params[:user_id_in])
|
||||
|
||||
query_helper(query, :managers, :manages)
|
||||
end
|
||||
|
||||
def query_helper(query, email_user, relationship_type)
|
||||
query.order("enterprises.created_at DESC")
|
||||
.select(["enterprises.name",
|
||||
"enterprises.sells",
|
||||
"enterprises.visible",
|
||||
"enterprises.is_primary_producer",
|
||||
"enterprises.created_at",
|
||||
"#{email_user}.email AS user_email"])
|
||||
.to_a
|
||||
.map { |x|
|
||||
{
|
||||
name: x.name,
|
||||
sells: x.sells,
|
||||
visible: (x.visible ? 't' : 'f'),
|
||||
is_primary_producer: (x.is_primary_producer ? 't' : 'f'),
|
||||
created_at: x.created_at.utc.iso8601,
|
||||
relationship_type: relationship_type,
|
||||
user_email: x.user_email
|
||||
}.stringify_keys
|
||||
}
|
||||
end
|
||||
|
||||
def users_and_enterprises
|
||||
sort( owners_and_enterprises.concat(managers_and_enterprises) )
|
||||
end
|
||||
|
||||
def filter_by_int_list_if_present(query, filtered_field_name, int_list)
|
||||
if int_list.present?
|
||||
query = query.where("#{filtered_field_name} IN (?)", split_int_list(int_list))
|
||||
end
|
||||
query
|
||||
end
|
||||
|
||||
def split_int_list(int_list)
|
||||
int_list.split(',').map(&:to_i)
|
||||
end
|
||||
|
||||
def sort(results)
|
||||
results.sort do |a, b|
|
||||
if a["created_at"].nil? || b["created_at"].nil?
|
||||
[(a["created_at"].nil? ? 0 : 1), a["name"], b["relationship_type"],
|
||||
a["user_email"]] <=>
|
||||
[(b["created_at"].nil? ? 0 : 1), b["name"], a["relationship_type"], b["user_email"]]
|
||||
else
|
||||
[
|
||||
DateTime.parse(b["created_at"]).in_time_zone,
|
||||
a["name"],
|
||||
b["relationship_type"],
|
||||
a["user_email"]
|
||||
] <=> [
|
||||
DateTime.parse(a["created_at"]).in_time_zone,
|
||||
b["name"],
|
||||
a["relationship_type"],
|
||||
b["user_email"]
|
||||
]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def to_bool(value)
|
||||
ActiveRecord::Type::Boolean.new.cast(value)
|
||||
end
|
||||
|
||||
def to_local_datetime(date)
|
||||
return "" if date.nil?
|
||||
|
||||
date.to_datetime.in_time_zone.strftime "%Y-%m-%d %H:%M"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
239
lib/reporting/reports/xero_invoices/xero_invoices_report.rb
Normal file
239
lib/reporting/reports/xero_invoices/xero_invoices_report.rb
Normal file
@@ -0,0 +1,239 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Reporting
|
||||
module Reports
|
||||
module XeroInvoices
|
||||
class XeroInvoicesReport
|
||||
def initialize(user, opts = {}, compile_table = false)
|
||||
@user = user
|
||||
|
||||
@opts = opts.
|
||||
symbolize_keys.
|
||||
reject { |_k, v| v.blank? }.
|
||||
reverse_merge( report_subtype: 'summary',
|
||||
invoice_date: Time.zone.today,
|
||||
due_date: Time.zone.today + 1.month,
|
||||
account_code: 'food sales' )
|
||||
@compile_table = compile_table
|
||||
end
|
||||
|
||||
def table_headers
|
||||
# NOTE: These are NOT to be translated, they need to be in this exact format to work with Xero
|
||||
%w(*ContactName EmailAddress POAddressLine1 POAddressLine2 POAddressLine3 POAddressLine4
|
||||
POCity PORegion POPostalCode POCountry *InvoiceNumber Reference *InvoiceDate *DueDate InventoryItemCode *Description *Quantity *UnitAmount Discount *AccountCode *TaxType TrackingName1 TrackingOption1 TrackingName2 TrackingOption2 Currency BrandingTheme Paid?)
|
||||
end
|
||||
|
||||
def search
|
||||
permissions = ::Permissions::Order.new(@user)
|
||||
permissions.editable_orders.complete.not_state(:canceled).ransack(@opts[:q])
|
||||
end
|
||||
|
||||
def orders
|
||||
search.result.reorder('id DESC')
|
||||
end
|
||||
|
||||
def table_rows
|
||||
return [] unless @compile_table
|
||||
|
||||
rows = []
|
||||
|
||||
orders.each_with_index do |order, i|
|
||||
invoice_number = invoice_number_for(order, i)
|
||||
rows += detail_rows_for_order(order, invoice_number, @opts) if detail?
|
||||
rows += summary_rows_for_order(order, invoice_number, @opts)
|
||||
end
|
||||
|
||||
rows.compact
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def report_options
|
||||
@opts.merge(line_item_includes: line_item_includes)
|
||||
end
|
||||
|
||||
def line_item_includes
|
||||
[:bill_address, :adjustments,
|
||||
{ line_items: { variant: [{ option_values: :option_type }, { product: :supplier }] } }]
|
||||
end
|
||||
|
||||
def detail_rows_for_order(order, invoice_number, opts)
|
||||
rows = []
|
||||
|
||||
rows += line_item_detail_rows(order, invoice_number, opts)
|
||||
rows += adjustment_detail_rows(order, invoice_number, opts)
|
||||
|
||||
rows
|
||||
end
|
||||
|
||||
def line_item_detail_rows(order, invoice_number, opts)
|
||||
order.line_items.map do |line_item|
|
||||
line_item_detail_row(line_item, invoice_number, opts)
|
||||
end
|
||||
end
|
||||
|
||||
def line_item_detail_row(line_item, invoice_number, opts)
|
||||
row(line_item.order,
|
||||
line_item.variant.sku,
|
||||
line_item.product_and_full_name,
|
||||
line_item.quantity.to_s,
|
||||
line_item.price.to_s,
|
||||
invoice_number,
|
||||
tax_type(line_item),
|
||||
opts)
|
||||
end
|
||||
|
||||
def adjustment_detail_rows(order, invoice_number, opts)
|
||||
admin_adjustments(order).map do |adjustment|
|
||||
adjustment_detail_row(adjustment, invoice_number, opts)
|
||||
end
|
||||
end
|
||||
|
||||
def adjustment_detail_row(adjustment, invoice_number, opts)
|
||||
row(adjustment_order(adjustment),
|
||||
'',
|
||||
adjustment.label,
|
||||
1,
|
||||
adjustment.amount,
|
||||
invoice_number,
|
||||
tax_type(adjustment),
|
||||
opts)
|
||||
end
|
||||
|
||||
def summary_rows_for_order(order, invoice_number, opts)
|
||||
rows = []
|
||||
|
||||
rows += produce_summary_rows(order, invoice_number, opts) unless detail?
|
||||
rows += fee_summary_rows(order, invoice_number, opts)
|
||||
rows += shipping_summary_rows(order, invoice_number, opts)
|
||||
rows += payment_summary_rows(order, invoice_number, opts)
|
||||
rows += admin_adjustment_summary_rows(order, invoice_number, opts) unless detail?
|
||||
|
||||
rows
|
||||
end
|
||||
|
||||
def produce_summary_rows(order, invoice_number, opts)
|
||||
[summary_row(order, I18n.t(:report_header_total_untaxable_produce), total_untaxable_products(order), invoice_number, I18n.t(:report_header_gst_free_income), opts),
|
||||
summary_row(order, I18n.t(:report_header_total_taxable_produce),
|
||||
total_taxable_products(order), invoice_number, I18n.t(:report_header_gst_on_income), opts)]
|
||||
end
|
||||
|
||||
def fee_summary_rows(order, invoice_number, opts)
|
||||
[summary_row(order, I18n.t(:report_header_total_untaxable_fees), total_untaxable_fees(order), invoice_number, I18n.t(:report_header_gst_free_income), opts),
|
||||
summary_row(order, I18n.t(:report_header_total_taxable_fees), total_taxable_fees(order),
|
||||
invoice_number, I18n.t(:report_header_gst_on_income), opts)]
|
||||
end
|
||||
|
||||
def shipping_summary_rows(order, invoice_number, opts)
|
||||
[summary_row(order, I18n.t(:report_header_delivery_shipping_cost), total_shipping(order),
|
||||
invoice_number, tax_on_shipping_s(order), opts)]
|
||||
end
|
||||
|
||||
def payment_summary_rows(order, invoice_number, opts)
|
||||
[summary_row(order, I18n.t(:report_header_transaction_fee), total_transaction(order),
|
||||
invoice_number, I18n.t(:report_header_gst_free_income), opts)]
|
||||
end
|
||||
|
||||
def admin_adjustment_summary_rows(order, invoice_number, opts)
|
||||
[summary_row(order, I18n.t(:report_header_total_untaxable_admin), total_untaxable_admin_adjustments(order), invoice_number, I18n.t(:report_header_gst_free_income), opts),
|
||||
summary_row(order, I18n.t(:report_header_total_taxable_admin),
|
||||
total_taxable_admin_adjustments(order), invoice_number, I18n.t(:report_header_gst_on_income), opts)]
|
||||
end
|
||||
|
||||
def summary_row(order, description, amount, invoice_number, tax_type, opts = {})
|
||||
row order, '', description, '1', amount, invoice_number, tax_type, opts
|
||||
end
|
||||
|
||||
def row(order, sku, description, quantity, amount, invoice_number, tax_type, opts = {})
|
||||
return nil if amount == 0
|
||||
|
||||
[order.bill_address&.full_name,
|
||||
order.email,
|
||||
order.bill_address&.address1,
|
||||
order.bill_address&.address2,
|
||||
'',
|
||||
'',
|
||||
order.bill_address&.city,
|
||||
order.bill_address&.state,
|
||||
order.bill_address&.zipcode,
|
||||
order.bill_address&.country&.name,
|
||||
invoice_number,
|
||||
order.number,
|
||||
opts[:invoice_date],
|
||||
opts[:due_date],
|
||||
sku,
|
||||
description,
|
||||
quantity,
|
||||
amount,
|
||||
'',
|
||||
opts[:account_code],
|
||||
tax_type,
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
Spree::Config.currency,
|
||||
'',
|
||||
order.paid? ? I18n.t(:y) : I18n.t(:n)]
|
||||
end
|
||||
|
||||
def admin_adjustments(order)
|
||||
order.adjustments.admin
|
||||
end
|
||||
|
||||
def adjustment_order(adjustment)
|
||||
adjustment.adjustable.is_a?(Spree::Order) ? adjustment.adjustable : nil
|
||||
end
|
||||
|
||||
def invoice_number_for(order, idx)
|
||||
@opts[:initial_invoice_number] ? @opts[:initial_invoice_number].to_i + idx : order.number
|
||||
end
|
||||
|
||||
def total_untaxable_products(order)
|
||||
order.line_items.without_tax.to_a.sum(&:amount)
|
||||
end
|
||||
|
||||
def total_taxable_products(order)
|
||||
order.line_items.with_tax.to_a.sum(&:amount)
|
||||
end
|
||||
|
||||
def total_untaxable_fees(order)
|
||||
order.all_adjustments.enterprise_fee.where(tax_category: nil).sum(:amount)
|
||||
end
|
||||
|
||||
def total_taxable_fees(order)
|
||||
order.all_adjustments.enterprise_fee.where.not(tax_category: nil).sum(:amount)
|
||||
end
|
||||
|
||||
def total_shipping(order)
|
||||
order.all_adjustments.shipping.sum(:amount)
|
||||
end
|
||||
|
||||
def total_transaction(order)
|
||||
order.all_adjustments.payment_fee.sum(:amount)
|
||||
end
|
||||
|
||||
def tax_on_shipping_s(order)
|
||||
tax_on_shipping = order.shipments.sum("additional_tax_total + included_tax_total").positive?
|
||||
tax_on_shipping ? I18n.t(:report_header_gst_on_income) : I18n.t(:report_header_gst_free_income)
|
||||
end
|
||||
|
||||
def total_untaxable_admin_adjustments(order)
|
||||
order.adjustments.admin.where(tax_category: nil).sum(:amount)
|
||||
end
|
||||
|
||||
def total_taxable_admin_adjustments(order)
|
||||
order.adjustments.admin.where.not(tax_category: nil).sum(:amount)
|
||||
end
|
||||
|
||||
def detail?
|
||||
@opts[:report_subtype] == 'detailed'
|
||||
end
|
||||
|
||||
def tax_type(taxable)
|
||||
taxable.has_tax? ? I18n.t(:report_header_gst_on_income) : I18n.t(:report_header_gst_free_income)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -237,7 +237,7 @@ describe Spree::Admin::ReportsController, type: :controller do
|
||||
end
|
||||
|
||||
it "creates a ProductAndInventoryReport" do
|
||||
expect(OpenFoodNetwork::ProductsAndInventoryReport).to receive(:new)
|
||||
expect(Reporting::Reports::ProductsAndInventory::ProductsAndInventoryReport).to receive(:new)
|
||||
.with(@admin_user,
|
||||
{ "test" => "foo", "controller" => "spree/admin/reports", "report" => {},
|
||||
"action" => "products_and_inventory", "use_route" => "main_app" }, false)
|
||||
@@ -290,7 +290,7 @@ describe Spree::Admin::ReportsController, type: :controller do
|
||||
end
|
||||
|
||||
it "creates a CustomersReport" do
|
||||
expect(OpenFoodNetwork::CustomersReport).to receive(:new)
|
||||
expect(Reporting::Reports::Customers::CustomersReport).to receive(:new)
|
||||
.with(@admin_user, { "test" => "foo", "controller" => "spree/admin/reports",
|
||||
"action" => "customers", "use_route" => "main_app",
|
||||
"report" => {} }, false)
|
||||
|
||||
@@ -1,163 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
require 'open_food_network/customers_report'
|
||||
|
||||
module OpenFoodNetwork
|
||||
describe CustomersReport do
|
||||
context "as a site admin" do
|
||||
let(:user) do
|
||||
user = create(:user)
|
||||
user.spree_roles << Spree::Role.find_or_create_by!(name: 'admin')
|
||||
user
|
||||
end
|
||||
subject { CustomersReport.new user, {}, true }
|
||||
|
||||
describe "mailing list report" do
|
||||
before do
|
||||
allow(subject).to receive(:params).and_return(report_subtype: "mailing_list")
|
||||
end
|
||||
|
||||
it "returns headers for mailing_list" do
|
||||
expect(subject.table_headers).to eq(["Email", "First Name", "Last Name", "Suburb"])
|
||||
end
|
||||
|
||||
it "builds a table from a list of variants" do
|
||||
order = double(:order, email: "test@test.com")
|
||||
address = double(:billing_address, firstname: "Firsty",
|
||||
lastname: "Lasty", city: "Suburbia")
|
||||
allow(order).to receive(:billing_address).and_return address
|
||||
allow(subject).to receive(:orders).and_return [order]
|
||||
|
||||
expect(subject.table_rows).to eq([[
|
||||
"test@test.com", "Firsty", "Lasty", "Suburbia"
|
||||
]])
|
||||
end
|
||||
end
|
||||
|
||||
describe "addresses report" do
|
||||
before do
|
||||
allow(subject).to receive(:params).and_return(report_subtype: "addresses")
|
||||
end
|
||||
|
||||
it "returns headers for addresses" do
|
||||
expect(subject.table_headers).to eq(["First Name", "Last Name", "Billing Address", "Email",
|
||||
"Phone", "Hub", "Hub Address", "Shipping Method"])
|
||||
end
|
||||
|
||||
it "builds a table from a list of variants" do
|
||||
a = create(:address)
|
||||
d = create(:distributor_enterprise)
|
||||
o = create(:order, distributor: d, bill_address: a)
|
||||
o.shipments << create(:shipment)
|
||||
|
||||
allow(subject).to receive(:orders).and_return [o]
|
||||
expect(subject.table_rows).to eq([[
|
||||
a.firstname, a.lastname,
|
||||
[a.address1, a.address2, a.city].join(" "),
|
||||
o.email, a.phone, d.name,
|
||||
[d.address.address1, d.address.address2, d.address.city].join(" "),
|
||||
o.shipping_method.name
|
||||
]])
|
||||
end
|
||||
end
|
||||
|
||||
describe "fetching orders" do
|
||||
it "fetches completed orders" do
|
||||
o1 = create(:order)
|
||||
o2 = create(:order, completed_at: 1.day.ago)
|
||||
expect(subject.orders).to eq([o2])
|
||||
end
|
||||
|
||||
it "does not show cancelled orders" do
|
||||
o1 = create(:order, state: "canceled", completed_at: 1.day.ago)
|
||||
o2 = create(:order, completed_at: 1.day.ago)
|
||||
expect(subject.orders).to eq([o2])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "as an enterprise user" do
|
||||
let(:user) do
|
||||
user = create(:user)
|
||||
user.spree_roles = []
|
||||
user.save!
|
||||
user
|
||||
end
|
||||
|
||||
subject { CustomersReport.new user, {}, true }
|
||||
|
||||
describe "fetching orders" do
|
||||
let(:supplier) { create(:supplier_enterprise) }
|
||||
let(:product) { create(:simple_product, supplier: supplier) }
|
||||
let(:order) { create(:order, completed_at: 1.day.ago) }
|
||||
|
||||
it "only shows orders managed by the current user" do
|
||||
d1 = create(:distributor_enterprise)
|
||||
d1.enterprise_roles.build(user: user).save
|
||||
d2 = create(:distributor_enterprise)
|
||||
d2.enterprise_roles.build(user: create(:user)).save
|
||||
|
||||
o1 = create(:order, distributor: d1, completed_at: 1.day.ago)
|
||||
o2 = create(:order, distributor: d2, completed_at: 1.day.ago)
|
||||
|
||||
expect(subject).to receive(:filter).with([o1]).and_return([o1])
|
||||
expect(subject.orders).to eq([o1])
|
||||
end
|
||||
|
||||
it "does not show orders through a hub that the current user does not manage" do
|
||||
# Given a supplier enterprise with an order for one of its products
|
||||
supplier.enterprise_roles.build(user: user).save
|
||||
order.line_items << create(:line_item_with_shipment, product: product)
|
||||
|
||||
# When I fetch orders, I should see no orders
|
||||
expect(subject).to receive(:filter).with([]).and_return([])
|
||||
expect(subject.orders).to eq([])
|
||||
end
|
||||
end
|
||||
|
||||
describe "filtering orders" do
|
||||
let(:orders) { Spree::Order.where(nil) }
|
||||
let(:supplier) { create(:supplier_enterprise) }
|
||||
|
||||
it "returns all orders sans-params" do
|
||||
expect(subject.filter(orders)).to eq(orders)
|
||||
end
|
||||
|
||||
it "returns orders with a specific supplier" do
|
||||
supplier = create(:supplier_enterprise)
|
||||
supplier2 = create(:supplier_enterprise)
|
||||
product1 = create(:simple_product, supplier: supplier)
|
||||
product2 = create(:simple_product, supplier: supplier2)
|
||||
order1 = create(:order)
|
||||
order2 = create(:order)
|
||||
order1.line_items << create(:line_item, product: product1)
|
||||
order2.line_items << create(:line_item, product: product2)
|
||||
|
||||
allow(subject).to receive(:params).and_return(supplier_id: supplier.id)
|
||||
expect(subject.filter(orders)).to eq([order1])
|
||||
end
|
||||
|
||||
it "filters to a specific distributor" do
|
||||
d1 = create(:distributor_enterprise)
|
||||
d2 = create(:distributor_enterprise)
|
||||
order1 = create(:order, distributor: d1)
|
||||
order2 = create(:order, distributor: d2)
|
||||
|
||||
allow(subject).to receive(:params).and_return(distributor_id: d1.id)
|
||||
expect(subject.filter(orders)).to eq([order1])
|
||||
end
|
||||
|
||||
it "filters to a specific cycle" do
|
||||
oc1 = create(:simple_order_cycle)
|
||||
oc2 = create(:simple_order_cycle)
|
||||
order1 = create(:order, order_cycle: oc1)
|
||||
order2 = create(:order, order_cycle: oc2)
|
||||
|
||||
allow(subject).to receive(:params).and_return(order_cycle_id: oc1.id)
|
||||
expect(subject.filter(orders)).to eq([order1])
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,90 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
require 'open_food_network/products_and_inventory_report'
|
||||
|
||||
module OpenFoodNetwork
|
||||
describe LettuceShareReport do
|
||||
let(:user) { create(:user) }
|
||||
let(:base_report) {
|
||||
ProductsAndInventoryReport.new(user, { report_subtype: 'lettuce_share' }, true)
|
||||
}
|
||||
let(:report) { base_report.report }
|
||||
let(:variant) { create(:variant) }
|
||||
|
||||
describe "grower and method" do
|
||||
it "shows just the producer when there is no certification" do
|
||||
allow(report).to receive(:producer_name) { "Producer" }
|
||||
allow(report).to receive(:certification) { "" }
|
||||
|
||||
expect(report.send(:grower_and_method, variant)).to eq("Producer")
|
||||
end
|
||||
|
||||
it "shows producer and certification when a certification is present" do
|
||||
allow(report).to receive(:producer_name) { "Producer" }
|
||||
allow(report).to receive(:certification) { "Method" }
|
||||
|
||||
expect(report.send(:grower_and_method, variant)).to eq("Producer (Method)")
|
||||
end
|
||||
end
|
||||
|
||||
describe "gst" do
|
||||
it "handles tax category without rates" do
|
||||
expect(report.send(:gst, variant)).to eq(0)
|
||||
end
|
||||
end
|
||||
|
||||
describe "table" do
|
||||
it "handles no items" do
|
||||
expect(report.table_rows).to eq []
|
||||
end
|
||||
|
||||
describe "lists" do
|
||||
let(:variant2) { create(:variant) }
|
||||
let(:variant3) { create(:variant) }
|
||||
let(:variant4) { create(:variant, on_hand: 0, on_demand: true) }
|
||||
let(:hub_address) {
|
||||
create(:address, address1: "distributor address", city: 'The Shire', zipcode: "1234")
|
||||
}
|
||||
let(:hub) { create(:distributor_enterprise, address: hub_address) }
|
||||
let(:variant2_override) { create(:variant_override, hub: hub, variant: variant2) }
|
||||
let(:variant3_override) {
|
||||
create(:variant_override, hub: hub, variant: variant3, count_on_hand: 0)
|
||||
}
|
||||
|
||||
it "all items" do
|
||||
allow(base_report).to receive(:child_variants) {
|
||||
Spree::Variant.where(id: [variant, variant2, variant3])
|
||||
}
|
||||
expect(report.table_rows.count).to eq 3
|
||||
end
|
||||
|
||||
it "only available items" do
|
||||
variant.on_hand = 0
|
||||
allow(base_report).to receive(:child_variants) {
|
||||
Spree::Variant.where(id: [variant, variant2, variant3, variant4])
|
||||
}
|
||||
expect(report.table_rows.count).to eq 3
|
||||
end
|
||||
|
||||
it "only available items considering overrides" do
|
||||
create(:exchange, incoming: false, receiver_id: hub.id,
|
||||
variants: [variant, variant2, variant3])
|
||||
# create the overrides
|
||||
variant2_override
|
||||
variant3_override
|
||||
allow(base_report).to receive(:child_variants) {
|
||||
Spree::Variant.where(id: [variant, variant2, variant3])
|
||||
}
|
||||
allow(base_report).to receive(:params) {
|
||||
{ distributor_id: hub.id, report_subtype: 'lettuce_share' }
|
||||
}
|
||||
rows = report.table_rows
|
||||
expect(rows.count).to eq 2
|
||||
expect(rows.map{ |row| row[0] }).to include variant.product.name, variant2.product.name
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,213 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
require 'open_food_network/order_cycle_management_report'
|
||||
|
||||
module OpenFoodNetwork
|
||||
describe OrderCycleManagementReport do
|
||||
context "as a site admin" do
|
||||
subject { OrderCycleManagementReport.new(user, params, true) }
|
||||
let(:params) { {} }
|
||||
|
||||
let(:user) do
|
||||
user = create(:user)
|
||||
user.spree_roles << Spree::Role.find_or_create_by!(name: "admin")
|
||||
user
|
||||
end
|
||||
|
||||
describe "fetching orders" do
|
||||
let(:customers_with_balance) { instance_double(CustomersWithBalance) }
|
||||
|
||||
it 'calls the OutstandingBalance query object' do
|
||||
outstanding_balance = instance_double(OutstandingBalance, query: Spree::Order.none)
|
||||
expect(OutstandingBalance).to receive(:new).and_return(outstanding_balance)
|
||||
|
||||
subject.orders
|
||||
end
|
||||
|
||||
it "fetches completed orders" do
|
||||
o1 = create(:order)
|
||||
o2 = create(:order, completed_at: 1.day.ago, state: 'complete')
|
||||
expect(subject.orders).to eq([o2])
|
||||
end
|
||||
|
||||
it 'fetches resumed orders' do
|
||||
order = create(:order, state: 'resumed', completed_at: 1.day.ago)
|
||||
expect(subject.orders).to eq([order])
|
||||
end
|
||||
|
||||
it 'orders them by id' do
|
||||
order1 = create(:order, completed_at: 1.day.ago, state: 'complete')
|
||||
order2 = create(:order, completed_at: 2.days.ago, state: 'complete')
|
||||
|
||||
expect(subject.orders.pluck(:id)).to eq([order2.id, order1.id])
|
||||
end
|
||||
|
||||
it "does not show cancelled orders" do
|
||||
o1 = create(:order, state: 'canceled', completed_at: 1.day.ago)
|
||||
o2 = create(:order, state: 'complete', completed_at: 1.day.ago)
|
||||
expect(subject.orders).to eq([o2])
|
||||
end
|
||||
|
||||
context "default date range" do
|
||||
it "fetches orders completed in the past month" do
|
||||
o1 = create(:order, state: 'complete', completed_at: 1.month.ago - 1.day)
|
||||
o2 = create(:order, state: 'complete', completed_at: 1.month.ago + 1.day)
|
||||
expect(subject.orders).to eq([o2])
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "as an enterprise user" do
|
||||
let!(:user) { create(:user) }
|
||||
|
||||
subject { OrderCycleManagementReport.new user, {}, true }
|
||||
|
||||
describe "fetching orders" do
|
||||
let(:supplier) { create(:supplier_enterprise) }
|
||||
let(:product) { create(:simple_product, supplier: supplier) }
|
||||
let(:order) { create(:order, completed_at: 1.day.ago) }
|
||||
|
||||
it "only shows orders managed by the current user" do
|
||||
d1 = create(:distributor_enterprise)
|
||||
d1.enterprise_roles.create!(user: user)
|
||||
d2 = create(:distributor_enterprise)
|
||||
d2.enterprise_roles.create!(user: create(:user))
|
||||
|
||||
o1 = create(:order, distributor: d1, state: 'complete', completed_at: 1.day.ago)
|
||||
o2 = create(:order, distributor: d2, state: 'complete', completed_at: 1.day.ago)
|
||||
|
||||
expect(subject).to receive(:filter).with([o1]).and_return([o1])
|
||||
expect(subject.orders).to eq([o1])
|
||||
end
|
||||
|
||||
it "does not show orders through a hub that the current user does not manage" do
|
||||
# Given a supplier enterprise with an order for one of its products
|
||||
supplier.enterprise_roles.create!(user: user)
|
||||
order.line_items << create(:line_item_with_shipment, product: product)
|
||||
|
||||
# When I fetch orders, I should see no orders
|
||||
expect(subject).to receive(:filter).with([]).and_return([])
|
||||
expect(subject.orders).to eq([])
|
||||
end
|
||||
end
|
||||
|
||||
describe "filtering orders" do
|
||||
let!(:orders) { Spree::Order.where(nil) }
|
||||
let!(:supplier) { create(:supplier_enterprise) }
|
||||
|
||||
let!(:oc1) { create(:simple_order_cycle) }
|
||||
let!(:pm1) { create(:payment_method, name: "PM1") }
|
||||
let!(:sm1) { create(:shipping_method, name: "ship1") }
|
||||
let!(:s1) { create(:shipment_with, :shipping_method, shipping_method: sm1) }
|
||||
let!(:order1) { create(:order, shipments: [s1], order_cycle: oc1) }
|
||||
let!(:payment1) { create(:payment, order: order1, payment_method: pm1) }
|
||||
|
||||
it "returns all orders sans-params" do
|
||||
expect(subject.filter(orders)).to eq(orders)
|
||||
end
|
||||
|
||||
it "filters to a specific order cycle" do
|
||||
oc2 = create(:simple_order_cycle)
|
||||
order2 = create(:order, order_cycle: oc2)
|
||||
|
||||
allow(subject).to receive(:params).and_return(order_cycle_id: oc1.id)
|
||||
expect(subject.filter(orders)).to eq([order1])
|
||||
end
|
||||
|
||||
it "filters to a payment method" do
|
||||
pm2 = create(:payment_method, name: "PM2")
|
||||
pm3 = create(:payment_method, name: "PM3")
|
||||
order2 = create(:order, payments: [create(:payment, payment_method: pm2)])
|
||||
order3 = create(:order, payments: [create(:payment, payment_method: pm3)])
|
||||
|
||||
allow(subject).to receive(:params).and_return(payment_method_in: [pm1.id, pm3.id] )
|
||||
expect(subject.filter(orders)).to match_array [order1, order3]
|
||||
end
|
||||
|
||||
it "filters to a shipping method" do
|
||||
sm2 = create(:shipping_method, name: "ship2")
|
||||
sm3 = create(:shipping_method, name: "ship3")
|
||||
s2 = create(:shipment_with, :shipping_method, shipping_method: sm2)
|
||||
s3 = create(:shipment_with, :shipping_method, shipping_method: sm3)
|
||||
order2 = create(:order, shipments: [s2])
|
||||
order3 = create(:order, shipments: [s3])
|
||||
|
||||
allow(subject).to receive(:params).and_return(shipping_method_in: [sm1.id, sm3.id])
|
||||
expect(subject.filter(orders)).to match_array [order1, order3]
|
||||
end
|
||||
|
||||
it "should do all the filters at once" do
|
||||
allow(subject).to receive(:params).and_return(order_cycle_id: oc1.id,
|
||||
shipping_method_name: sm1.name,
|
||||
payment_method_name: pm1.name)
|
||||
expect(subject.filter(orders)).to eq([order1])
|
||||
end
|
||||
end
|
||||
|
||||
describe '#table_rows' do
|
||||
subject { OrderCycleManagementReport.new(user, params, true) }
|
||||
|
||||
let(:distributor) { create(:distributor_enterprise) }
|
||||
before { distributor.enterprise_roles.create!(user: user) }
|
||||
|
||||
context 'when the report type is payment_methods' do
|
||||
let(:params) { { report_subtype: 'payment_methods' } }
|
||||
|
||||
let!(:order) do
|
||||
create(
|
||||
:completed_order_with_totals,
|
||||
distributor: distributor,
|
||||
completed_at: 1.day.ago
|
||||
)
|
||||
end
|
||||
|
||||
it 'returns rows with payment information' do
|
||||
expect(subject.table_rows).to eq([[
|
||||
order.billing_address.firstname,
|
||||
order.billing_address.lastname,
|
||||
order.distributor.name,
|
||||
'',
|
||||
order.email,
|
||||
order.billing_address.phone,
|
||||
order.shipment.shipping_method.name,
|
||||
nil,
|
||||
order.total,
|
||||
-order.total
|
||||
]])
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the report type is not payment_methods' do
|
||||
let(:params) { {} }
|
||||
let!(:order) do
|
||||
create(
|
||||
:completed_order_with_totals,
|
||||
distributor: distributor,
|
||||
completed_at: 1.day.ago
|
||||
)
|
||||
end
|
||||
|
||||
it 'returns rows with delivery information' do
|
||||
expect(subject.table_rows).to eq([[
|
||||
order.ship_address.firstname,
|
||||
order.ship_address.lastname,
|
||||
order.distributor.name,
|
||||
"",
|
||||
"#{order.ship_address.address1} #{order.ship_address.address2} #{order.ship_address.city}",
|
||||
order.ship_address.zipcode,
|
||||
order.ship_address.phone,
|
||||
order.shipment.shipping_method.name,
|
||||
nil,
|
||||
order.total,
|
||||
-order.total,
|
||||
false,
|
||||
order.special_instructions
|
||||
]])
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,87 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
require 'open_food_network/orders_and_distributors_report'
|
||||
|
||||
module OpenFoodNetwork
|
||||
describe OrdersAndDistributorsReport do
|
||||
describe 'orders and distributors report' do
|
||||
it 'should return a header row describing the report' do
|
||||
subject = OrdersAndDistributorsReport.new nil
|
||||
|
||||
expect(subject.table_headers).to eq(
|
||||
[
|
||||
'Order date', 'Order Id',
|
||||
'Customer Name', 'Customer Email', 'Customer Phone', 'Customer City',
|
||||
'SKU', 'Item name', 'Variant', 'Quantity', 'Max Quantity', 'Cost', 'Shipping Cost',
|
||||
'Payment Method',
|
||||
'Distributor', 'Distributor address', 'Distributor city', 'Distributor postcode',
|
||||
'Shipping Method', 'Shipping instructions'
|
||||
]
|
||||
)
|
||||
end
|
||||
|
||||
context 'with completed order' do
|
||||
let(:bill_address) { create(:address) }
|
||||
let(:distributor) { create(:distributor_enterprise) }
|
||||
let(:product) { create(:product) }
|
||||
let(:shipping_method) { create(:shipping_method) }
|
||||
let(:shipping_instructions) { 'pick up on thursday please!' }
|
||||
let(:order) {
|
||||
create(:order,
|
||||
state: 'complete', completed_at: Time.zone.now,
|
||||
distributor: distributor, bill_address: bill_address,
|
||||
special_instructions: shipping_instructions)
|
||||
}
|
||||
let(:payment_method) { create(:payment_method, distributors: [distributor]) }
|
||||
let(:payment) { create(:payment, payment_method: payment_method, order: order) }
|
||||
let(:line_item) { create(:line_item_with_shipment, product: product, order: order) }
|
||||
|
||||
before do
|
||||
order.select_shipping_method(shipping_method.id)
|
||||
order.payments << payment
|
||||
order.line_items << line_item
|
||||
end
|
||||
|
||||
it 'should denormalise order and distributor details for display as csv' do
|
||||
subject = OrdersAndDistributorsReport.new create(:admin_user), {}, true
|
||||
|
||||
table = subject.table_rows
|
||||
|
||||
expect(table.size).to eq 1
|
||||
expect(table[0]).to eq([
|
||||
order.reload.completed_at.strftime("%F %T"),
|
||||
order.id,
|
||||
bill_address.full_name,
|
||||
order.email,
|
||||
bill_address.phone,
|
||||
bill_address.city,
|
||||
line_item.product.sku,
|
||||
line_item.product.name,
|
||||
line_item.options_text,
|
||||
line_item.quantity,
|
||||
line_item.max_quantity,
|
||||
line_item.price * line_item.quantity,
|
||||
line_item.distribution_fee,
|
||||
payment_method.name,
|
||||
distributor.name,
|
||||
distributor.address.address1,
|
||||
distributor.address.city,
|
||||
distributor.address.zipcode,
|
||||
shipping_method.name,
|
||||
shipping_instructions
|
||||
])
|
||||
end
|
||||
|
||||
it "prints one row per line item" do
|
||||
create(:line_item_with_shipment, order: order)
|
||||
|
||||
subject = OrdersAndDistributorsReport.new(create(:admin_user), {}, true)
|
||||
|
||||
table = subject.table_rows
|
||||
expect(table.size).to eq 2
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,127 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "spec_helper"
|
||||
require 'open_food_network/orders_and_fulfillment_report'
|
||||
require 'open_food_network/orders_and_fulfillment_report/customer_totals_report'
|
||||
|
||||
RSpec.describe OpenFoodNetwork::OrdersAndFulfillmentReport::CustomerTotalsReport do
|
||||
let!(:distributor) { create(:distributor_enterprise) }
|
||||
let!(:customer) { create(:customer, enterprise: distributor) }
|
||||
let(:current_user) { distributor.owner }
|
||||
|
||||
let(:report) do
|
||||
report_options = { report_subtype: described_class::REPORT_TYPE }
|
||||
OpenFoodNetwork::OrdersAndFulfillmentReport.new(current_user, report_options, true)
|
||||
end
|
||||
|
||||
let(:report_table) do
|
||||
report.table_rows
|
||||
end
|
||||
|
||||
context "viewing the report" do
|
||||
let!(:order) do
|
||||
create(:completed_order_with_totals, line_items_count: 1, user: customer.user,
|
||||
customer: customer, distributor: distributor)
|
||||
end
|
||||
|
||||
it "generates the report" do
|
||||
expect(report_table.length).to eq(2)
|
||||
end
|
||||
|
||||
it "has a line item row" do
|
||||
distributor_name_field = report_table.first[0]
|
||||
expect(distributor_name_field).to eq distributor.name
|
||||
|
||||
customer_name_field = report_table.first[1]
|
||||
expect(customer_name_field).to eq order.bill_address.full_name
|
||||
|
||||
total_field = report_table.last[5]
|
||||
expect(total_field).to eq I18n.t("admin.reports.total")
|
||||
end
|
||||
|
||||
it 'includes the order number and date in item rows' do
|
||||
order_number_and_date_fields = report_table.first[33..34]
|
||||
expect(order_number_and_date_fields).to eq([
|
||||
order.number,
|
||||
order.completed_at.strftime("%F %T"),
|
||||
])
|
||||
end
|
||||
|
||||
it 'includes the order number and date in total rows' do
|
||||
order_number_and_date_fields = report_table.last[33..34]
|
||||
expect(order_number_and_date_fields).to eq([
|
||||
order.number,
|
||||
order.completed_at.strftime("%F %T"),
|
||||
])
|
||||
end
|
||||
end
|
||||
|
||||
context "loading shipping methods" do
|
||||
let!(:shipping_method1) {
|
||||
create(:shipping_method, distributors: [distributor], name: "First")
|
||||
}
|
||||
let!(:shipping_method2) {
|
||||
create(:shipping_method, distributors: [distributor], name: "Second")
|
||||
}
|
||||
let!(:shipping_method3) {
|
||||
create(:shipping_method, distributors: [distributor], name: "Third")
|
||||
}
|
||||
let!(:order) do
|
||||
create(:completed_order_with_totals, line_items_count: 1, user: customer.user,
|
||||
customer: customer, distributor: distributor)
|
||||
end
|
||||
|
||||
before do
|
||||
order.shipments.each(&:refresh_rates)
|
||||
order.select_shipping_method(shipping_method2.id)
|
||||
end
|
||||
|
||||
it "displays the correct shipping_method" do
|
||||
shipping_method_name_field = report_table.first[15]
|
||||
expect(shipping_method_name_field).to eq shipping_method2.name
|
||||
end
|
||||
end
|
||||
|
||||
context "displaying payment fees" do
|
||||
context "with both failed and completed payments present" do
|
||||
let!(:order) {
|
||||
create(:order_ready_to_ship, user: customer.user,
|
||||
customer: customer, distributor: distributor)
|
||||
}
|
||||
let(:completed_payment) { order.payments.completed.first }
|
||||
let!(:failed_payment) { create(:payment, order: order, state: "failed") }
|
||||
|
||||
before do
|
||||
completed_payment.adjustment.update amount: 123.00
|
||||
failed_payment.adjustment.update amount: 456.00, eligible: false, state: "finalized"
|
||||
end
|
||||
|
||||
it "shows the correct payment fee amount for the order" do
|
||||
payment_fee_field = report_table.last[12]
|
||||
expect(payment_fee_field).to eq completed_payment.adjustment.amount
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when a variant override applies' do
|
||||
let!(:order) do
|
||||
create(:completed_order_with_totals, line_items_count: 1, user: customer.user,
|
||||
customer: customer, distributor: distributor)
|
||||
end
|
||||
let(:overidden_sku) { 'magical_sku' }
|
||||
|
||||
before do
|
||||
create(
|
||||
:variant_override,
|
||||
hub: distributor,
|
||||
variant: order.line_items.first.variant,
|
||||
sku: overidden_sku
|
||||
)
|
||||
end
|
||||
|
||||
it 'uses the sku from the variant override' do
|
||||
sku_field = report_table.first[23]
|
||||
expect(sku_field).to eq overidden_sku
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,39 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
require 'open_food_network/orders_and_fulfillment_report/distributor_totals_by_supplier_report'
|
||||
|
||||
RSpec.describe OpenFoodNetwork::OrdersAndFulfillmentReport::DistributorTotalsBySupplierReport do
|
||||
let!(:distributor) { create(:distributor_enterprise) }
|
||||
|
||||
let!(:order) do
|
||||
create(:completed_order_with_totals, line_items_count: 1, distributor: distributor)
|
||||
end
|
||||
|
||||
let(:current_user) { distributor.owner }
|
||||
|
||||
let(:report) do
|
||||
report_options = { report_subtype: described_class::REPORT_TYPE }
|
||||
OpenFoodNetwork::OrdersAndFulfillmentReport.new(current_user, report_options, true)
|
||||
end
|
||||
|
||||
let(:report_table) do
|
||||
report.table_rows
|
||||
end
|
||||
|
||||
it "generates the report" do
|
||||
expect(report_table.length).to eq(2)
|
||||
end
|
||||
|
||||
it "has a variant row under the distributor" do
|
||||
distributor_name_field = report_table.first[0]
|
||||
expect(distributor_name_field).to eq distributor.name
|
||||
|
||||
supplier = order.line_items.first.variant.product.supplier
|
||||
supplier_name_field = report_table.first[1]
|
||||
expect(supplier_name_field).to eq supplier.name
|
||||
|
||||
total_field = report_table.last[1]
|
||||
expect(total_field).to eq I18n.t("admin.reports.total")
|
||||
end
|
||||
end
|
||||
@@ -1,39 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
require 'open_food_network/orders_and_fulfillment_report/supplier_totals_by_distributor_report'
|
||||
|
||||
RSpec.describe OpenFoodNetwork::OrdersAndFulfillmentReport::SupplierTotalsByDistributorReport do
|
||||
let!(:distributor) { create(:distributor_enterprise) }
|
||||
|
||||
let!(:order) do
|
||||
create(:completed_order_with_totals, line_items_count: 1, distributor: distributor)
|
||||
end
|
||||
|
||||
let(:current_user) { distributor.owner }
|
||||
|
||||
let(:report) do
|
||||
report_options = { report_subtype: described_class::REPORT_TYPE }
|
||||
OpenFoodNetwork::OrdersAndFulfillmentReport.new(current_user, report_options, true)
|
||||
end
|
||||
|
||||
let(:report_table) do
|
||||
report.table_rows
|
||||
end
|
||||
|
||||
it "generates the report" do
|
||||
expect(report_table.length).to eq(2)
|
||||
end
|
||||
|
||||
it "has a variant row under the distributor" do
|
||||
supplier = order.line_items.first.variant.product.supplier
|
||||
supplier_name_field = report_table.first[0]
|
||||
expect(supplier_name_field).to eq supplier.name
|
||||
|
||||
distributor_name_field = report_table.first[3]
|
||||
expect(distributor_name_field).to eq distributor.name
|
||||
|
||||
total_field = report_table.last[3]
|
||||
expect(total_field).to eq I18n.t("admin.reports.total")
|
||||
end
|
||||
end
|
||||
@@ -1,33 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
require 'open_food_network/orders_and_fulfillment_report/supplier_totals_report'
|
||||
|
||||
RSpec.describe OpenFoodNetwork::OrdersAndFulfillmentReport::SupplierTotalsReport do
|
||||
let!(:distributor) { create(:distributor_enterprise) }
|
||||
|
||||
let!(:order) do
|
||||
create(:completed_order_with_totals, line_items_count: 1, distributor: distributor)
|
||||
end
|
||||
|
||||
let(:current_user) { distributor.owner }
|
||||
|
||||
let(:report) do
|
||||
report_options = { report_subtype: described_class::REPORT_TYPE }
|
||||
OpenFoodNetwork::OrdersAndFulfillmentReport.new(current_user, report_options, true)
|
||||
end
|
||||
|
||||
let(:report_table) do
|
||||
report.table_rows
|
||||
end
|
||||
|
||||
it "generates the report" do
|
||||
expect(report_table.length).to eq(1)
|
||||
end
|
||||
|
||||
it "has a variant row" do
|
||||
supplier = order.line_items.first.variant.product.supplier
|
||||
supplier_name_field = report_table.first[0]
|
||||
expect(supplier_name_field).to eq supplier.name
|
||||
end
|
||||
end
|
||||
@@ -1,277 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
require 'open_food_network/orders_and_fulfillment_report'
|
||||
require 'open_food_network/order_grouper'
|
||||
|
||||
describe OpenFoodNetwork::OrdersAndFulfillmentReport do
|
||||
include AuthenticationHelper
|
||||
|
||||
let(:distributor) { create(:distributor_enterprise) }
|
||||
let(:order_cycle) { create(:simple_order_cycle) }
|
||||
let(:address) { create(:address) }
|
||||
let(:order) {
|
||||
create(
|
||||
:order,
|
||||
completed_at: 1.day.ago,
|
||||
order_cycle: order_cycle,
|
||||
distributor: distributor,
|
||||
bill_address: address
|
||||
)
|
||||
}
|
||||
let(:line_item) { build(:line_item_with_shipment) }
|
||||
let(:user) { create(:user) }
|
||||
let(:admin_user) { create(:admin_user) }
|
||||
|
||||
describe "fetching orders" do
|
||||
before { order.line_items << line_item }
|
||||
|
||||
context "as a site admin" do
|
||||
subject { described_class.new(admin_user, {}, true) }
|
||||
|
||||
it "fetches completed orders" do
|
||||
o2 = create(:order)
|
||||
o2.line_items << build(:line_item)
|
||||
expect(subject.table_items).to eq([line_item])
|
||||
end
|
||||
|
||||
it "does not show cancelled orders" do
|
||||
o2 = create(:order, state: "canceled", completed_at: 1.day.ago)
|
||||
o2.line_items << build(:line_item_with_shipment)
|
||||
expect(subject.table_items).to eq([line_item])
|
||||
end
|
||||
end
|
||||
|
||||
context "as a manager of a supplier" do
|
||||
subject { described_class.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) {
|
||||
create(
|
||||
:order,
|
||||
distributor: distributor,
|
||||
completed_at: 1.day.ago,
|
||||
bill_address: create(:address),
|
||||
ship_address: create(:address)
|
||||
)
|
||||
}
|
||||
let(:li2) {
|
||||
build(:line_item_with_shipment, product: create(:simple_product, supplier: s1))
|
||||
}
|
||||
|
||||
before do
|
||||
o2.line_items << li2
|
||||
create(
|
||||
:enterprise_relationship,
|
||||
parent: s1,
|
||||
child: distributor,
|
||||
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
|
||||
|
||||
context "where the distributor allows suppliers to see customer names" do
|
||||
before do
|
||||
distributor.update_columns show_customer_names_to_suppliers: true
|
||||
end
|
||||
|
||||
it "shows line items supplied by my producers, with names shown" do
|
||||
expect(subject.table_items).to eq([li2])
|
||||
expect(subject.table_items.first.order.bill_address.firstname).
|
||||
to eq(order.bill_address.firstname)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "that has not granted P-OC to the distributor" do
|
||||
let(:o2) {
|
||||
create(
|
||||
:order,
|
||||
distributor: distributor,
|
||||
completed_at: 1.day.ago,
|
||||
bill_address: create(:address),
|
||||
ship_address: create(:address)
|
||||
)
|
||||
}
|
||||
let(:li2) {
|
||||
build(:line_item_with_shipment, product: create(:simple_product, supplier: s1))
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
context "where the distributor allows suppliers to see customer names" do
|
||||
before do
|
||||
distributor.show_customer_names_to_suppliers = true
|
||||
end
|
||||
|
||||
it "does not show line items supplied by my producers" do
|
||||
expect(subject.table_items).to eq([])
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "as a manager of a distributor" do
|
||||
subject { described_class.new(user, {}, true) }
|
||||
|
||||
before do
|
||||
distributor.enterprise_roles.create!(user: user)
|
||||
end
|
||||
|
||||
it "only shows line items distributed by enterprises managed by the current user" do
|
||||
d2 = create(:distributor_enterprise)
|
||||
d2.enterprise_roles.create!(user: create(:user))
|
||||
o2 = create(:order, distributor: d2, completed_at: 1.day.ago)
|
||||
o2.line_items << build(:line_item_with_shipment)
|
||||
expect(subject.table_items).to eq([line_item])
|
||||
end
|
||||
|
||||
it "only shows the selected order cycle" do
|
||||
oc2 = create(:simple_order_cycle)
|
||||
o2 = create(:order, distributor: distributor, order_cycle: oc2)
|
||||
o2.line_items << build(:line_item)
|
||||
allow(subject).to receive(:params).and_return(order_cycle_id_in: order_cycle.id)
|
||||
expect(subject.table_items).to eq([line_item])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "columns are aligned" do
|
||||
it 'has aligned columsn' do
|
||||
report_types = [
|
||||
"",
|
||||
"order_cycle_supplier_totals",
|
||||
"order_cycle_supplier_totals_by_distributor",
|
||||
"order_cycle_distributor_totals_by_supplier",
|
||||
"order_cycle_customer_totals"
|
||||
]
|
||||
|
||||
report_types.each do |report_type|
|
||||
report = described_class.new(admin_user, report_subtype: report_type)
|
||||
expect(report.table_headers.size).to eq(report.columns.size)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "order_cycle_customer_totals" do
|
||||
let!(:product) { line_item.product }
|
||||
let!(:fuji) do
|
||||
create(:variant, product: product, display_name: "Fuji", sku: "FUJI", on_hand: 100)
|
||||
end
|
||||
let!(:gala) do
|
||||
create(:variant, product: product, display_name: "Gala", sku: "GALA", on_hand: 100)
|
||||
end
|
||||
|
||||
let(:items) {
|
||||
described_class.new(admin_user, { report_subtype: "order_cycle_customer_totals" }, true)
|
||||
.table_rows
|
||||
}
|
||||
|
||||
before do
|
||||
# Clear price so it will be computed based on quantity and variant price.
|
||||
order.line_items << build(:line_item_with_shipment, variant: fuji, price: nil, quantity: 1)
|
||||
order.line_items << build(:line_item_with_shipment, variant: gala, price: nil, quantity: 2)
|
||||
end
|
||||
|
||||
it "has a product row" do
|
||||
product_name_field = items.first[5]
|
||||
expect(product_name_field).to eq product.name
|
||||
end
|
||||
|
||||
it "has a summary row" do
|
||||
product_name_field = items.last[5]
|
||||
expect(product_name_field).to eq "TOTAL"
|
||||
end
|
||||
|
||||
# Expected Report for Scenario:
|
||||
#
|
||||
# Row 1: Armstrong Amari, Fuji Apple, price: 8
|
||||
# Row 2: SUMMARY
|
||||
# Row 3: Bartoletti Brooklyn, Fuji Apple, price: 1 + 4
|
||||
# Row 4: Bartoletti Brooklyn, Gala Apple, price: 2
|
||||
# Row 5: SUMMARY
|
||||
describe "grouping of line items" do
|
||||
let!(:address) { create(:address, last_name: "Bartoletti", first_name: "Brooklyn") }
|
||||
|
||||
let!(:second_address) { create(:address, last_name: "Armstrong", first_name: "Amari") }
|
||||
let!(:second_order) do
|
||||
create(:order, completed_at: 1.day.ago, order_cycle: order_cycle, distributor: distributor,
|
||||
bill_address: second_address)
|
||||
end
|
||||
|
||||
before do
|
||||
# Add a second line item for Fuji variant to the order, to test grouping in this edge case.
|
||||
order.line_items << build(:line_item_with_shipment, variant: fuji, price: nil, quantity: 4)
|
||||
|
||||
second_order.line_items << build(:line_item_with_shipment, variant: fuji, price: nil,
|
||||
quantity: 8)
|
||||
end
|
||||
|
||||
it "groups line items by variant and order" do
|
||||
expect(items.length).to eq(5)
|
||||
|
||||
# Row 1: Armstrong Amari, Fuji Apple, price: 8
|
||||
row_data = items[0]
|
||||
expect(customer_name(row_data)).to eq(second_address.full_name)
|
||||
expect(amount(row_data)).to eq(fuji.price * 8)
|
||||
expect(variant_sku(row_data)).to eq(fuji.sku)
|
||||
|
||||
# Row 2: SUMMARY
|
||||
row_data = items[1]
|
||||
expect(totals_row?(row_data)).to eq(true)
|
||||
expect(customer_name(row_data)).to eq(second_address.full_name)
|
||||
expect(amount(row_data)).to eq(fuji.price * 8)
|
||||
|
||||
# Row 3: Bartoletti Brooklyn, Fuji Apple, price: 1 + 4
|
||||
row_data = items[2]
|
||||
expect(customer_name(row_data)).to eq(address.full_name)
|
||||
expect(amount(row_data)).to eq(fuji.price * 5)
|
||||
expect(variant_sku(row_data)).to eq(fuji.sku)
|
||||
|
||||
# Row 4: Bartoletti Brooklyn, Gala Apple, price: 2
|
||||
row_data = items[3]
|
||||
expect(customer_name(row_data)).to eq(address.full_name)
|
||||
expect(amount(row_data)).to eq(gala.price * 2)
|
||||
expect(variant_sku(row_data)).to eq(gala.sku)
|
||||
|
||||
# Row 5: SUMMARY
|
||||
row_data = items[4]
|
||||
expect(totals_row?(row_data)).to eq(true)
|
||||
expect(customer_name(row_data)).to eq(address.full_name)
|
||||
expect(amount(row_data)).to eq(fuji.price * 5 + gala.price * 2)
|
||||
end
|
||||
end
|
||||
|
||||
def totals_row?(row_data)
|
||||
row_data[5] == I18n.t("admin.reports.total")
|
||||
end
|
||||
|
||||
def customer_name(row_data)
|
||||
row_data[1]
|
||||
end
|
||||
|
||||
def amount(row_data)
|
||||
row_data[8]
|
||||
end
|
||||
|
||||
def variant_sku(row_data)
|
||||
row_data[23]
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,261 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
require 'open_food_network/products_and_inventory_report'
|
||||
|
||||
describe OpenFoodNetwork::ProductsAndInventoryDefaultReport do
|
||||
context "As a site admin" do
|
||||
let(:user) do
|
||||
user = create(:user)
|
||||
user.spree_roles << Spree::Role.find_or_create_by!(name: 'admin')
|
||||
user
|
||||
end
|
||||
subject do
|
||||
OpenFoodNetwork::ProductsAndInventoryReport.new user, {}, true
|
||||
end
|
||||
|
||||
it "Should return headers" do
|
||||
expect(subject.table_headers).to eq([
|
||||
"Supplier",
|
||||
"Producer Suburb",
|
||||
"Product",
|
||||
"Product Properties",
|
||||
"Taxons",
|
||||
"Variant Value",
|
||||
"Price",
|
||||
"Group Buy Unit Quantity",
|
||||
"Amount",
|
||||
"SKU"
|
||||
])
|
||||
end
|
||||
|
||||
it "should build a table from a list of variants" do
|
||||
variant = double(:variant, sku: "sku",
|
||||
full_name: "Variant Name",
|
||||
count_on_hand: 10,
|
||||
price: 100)
|
||||
allow(variant).to receive_message_chain(:product, :supplier, :name).and_return("Supplier")
|
||||
allow(variant).to receive_message_chain(:product, :supplier, :address,
|
||||
:city).and_return("A city")
|
||||
allow(variant).to receive_message_chain(:product, :name).and_return("Product Name")
|
||||
allow(variant).to receive_message_chain(:product,
|
||||
:properties).and_return [double(name: "property1"),
|
||||
double(name: "property2")]
|
||||
allow(variant).to receive_message_chain(:product,
|
||||
:taxons).and_return [double(name: "taxon1"),
|
||||
double(name: "taxon2")]
|
||||
allow(variant).to receive_message_chain(:product, :group_buy_unit_size).and_return(21)
|
||||
allow(subject).to receive(:variants).and_return [variant]
|
||||
|
||||
expect(subject.table_rows).to eq([[
|
||||
"Supplier",
|
||||
"A city",
|
||||
"Product Name",
|
||||
"property1, property2",
|
||||
"taxon1, taxon2",
|
||||
"Variant Name",
|
||||
100,
|
||||
21,
|
||||
"",
|
||||
"sku"
|
||||
]])
|
||||
end
|
||||
|
||||
it "fetches variants for some params" do
|
||||
expect(subject).to receive(:child_variants).and_return ["children"]
|
||||
expect(subject).to receive(:filter).with(['children']).and_return ["filter_children"]
|
||||
expect(subject.variants).to eq(["filter_children"])
|
||||
end
|
||||
end
|
||||
|
||||
context "As an enterprise user" do
|
||||
let(:supplier) { create(:supplier_enterprise) }
|
||||
let(:enterprise_user) do
|
||||
user = create(:user)
|
||||
user.enterprise_roles.create(enterprise: supplier)
|
||||
user.spree_roles = []
|
||||
user.save!
|
||||
user
|
||||
end
|
||||
|
||||
subject do
|
||||
OpenFoodNetwork::ProductsAndInventoryReport.new enterprise_user, {}, true
|
||||
end
|
||||
|
||||
describe "fetching child variants" do
|
||||
it "returns some variants" do
|
||||
product1 = create(:simple_product, supplier: supplier)
|
||||
variant1 = product1.variants.first
|
||||
variant2 = create(:variant, product: product1)
|
||||
|
||||
expect(subject.child_variants).to match_array [variant1, variant2]
|
||||
end
|
||||
|
||||
it "should only return variants managed by the user" do
|
||||
product1 = create(:simple_product, supplier: create(:supplier_enterprise))
|
||||
product2 = create(:simple_product, supplier: supplier)
|
||||
variant1 = product1.variants.first
|
||||
variant2 = product2.variants.first
|
||||
|
||||
expect(subject.child_variants).to eq([variant2])
|
||||
end
|
||||
end
|
||||
|
||||
describe "Filtering variants" do
|
||||
let(:variants) { Spree::Variant.where(nil).joins(:product).where(is_master: false) }
|
||||
|
||||
describe "based on report type" do
|
||||
it "returns only variants on hand" do
|
||||
product1 = create(:simple_product, supplier: supplier, on_hand: 99)
|
||||
product2 = create(:simple_product, supplier: supplier, on_hand: 0)
|
||||
|
||||
allow(subject).to receive(:params).and_return(report_subtype: 'inventory')
|
||||
expect(subject.filter(variants)).to eq([product1.variants.first])
|
||||
end
|
||||
end
|
||||
it "filters to a specific supplier" do
|
||||
supplier2 = create(:supplier_enterprise)
|
||||
product1 = create(:simple_product, supplier: supplier)
|
||||
product2 = create(:simple_product, supplier: supplier2)
|
||||
|
||||
allow(subject).to receive(:params).and_return(supplier_id: supplier.id)
|
||||
expect(subject.filter(variants)).to eq([product1.variants.first])
|
||||
end
|
||||
it "filters to a specific distributor" do
|
||||
distributor = create(:distributor_enterprise)
|
||||
product1 = create(:simple_product, supplier: supplier)
|
||||
product2 = create(:simple_product, supplier: supplier)
|
||||
order_cycle = create(:simple_order_cycle, suppliers: [supplier],
|
||||
distributors: [distributor],
|
||||
variants: [product2.variants.first])
|
||||
|
||||
allow(subject).to receive(:params).and_return(distributor_id: distributor.id)
|
||||
expect(subject.filter(variants)).to eq([product2.variants.first])
|
||||
end
|
||||
|
||||
it "ignores variant overrides without filter" do
|
||||
distributor = create(:distributor_enterprise)
|
||||
product = create(:simple_product, supplier: supplier, price: 5)
|
||||
variant = product.variants.first
|
||||
order_cycle = create(:simple_order_cycle, suppliers: [supplier],
|
||||
distributors: [distributor],
|
||||
variants: [product.variants.first])
|
||||
create(:variant_override, hub: distributor, variant: variant, price: 2)
|
||||
|
||||
result = subject.filter(variants)
|
||||
|
||||
expect(result.first.price).to eq 5
|
||||
end
|
||||
|
||||
it "considers variant overrides with distributor" do
|
||||
distributor = create(:distributor_enterprise)
|
||||
product = create(:simple_product, supplier: supplier, price: 5)
|
||||
variant = product.variants.first
|
||||
order_cycle = create(:simple_order_cycle, suppliers: [supplier],
|
||||
distributors: [distributor],
|
||||
variants: [product.variants.first])
|
||||
create(:variant_override, hub: distributor, variant: variant, price: 2)
|
||||
|
||||
allow(subject).to receive(:params).and_return(distributor_id: distributor.id)
|
||||
result = subject.filter(variants)
|
||||
|
||||
expect(result.first.price).to eq 2
|
||||
end
|
||||
|
||||
it "filters to a specific order cycle" do
|
||||
distributor = create(:distributor_enterprise)
|
||||
product1 = create(:simple_product, supplier: supplier)
|
||||
product2 = create(:simple_product, supplier: supplier)
|
||||
order_cycle = create(:simple_order_cycle, suppliers: [supplier],
|
||||
distributors: [distributor],
|
||||
variants: [product1.variants.first])
|
||||
|
||||
allow(subject).to receive(:params).and_return(order_cycle_id: order_cycle.id)
|
||||
expect(subject.filter(variants)).to eq([product1.variants.first])
|
||||
end
|
||||
|
||||
it "should do all the filters at once" do
|
||||
# The following data ensures that this spec fails if any of the
|
||||
# filters fail. It's testing the filters are not impacting each other.
|
||||
distributor = create(:distributor_enterprise)
|
||||
other_distributor = create(:distributor_enterprise)
|
||||
other_supplier = create(:supplier_enterprise)
|
||||
not_filtered_variant = create(:simple_product, supplier: supplier).variants.first
|
||||
variant_filtered_by_order_cycle = create(:simple_product,
|
||||
supplier: supplier).variants.first
|
||||
variant_filtered_by_distributor = create(:simple_product,
|
||||
supplier: supplier).variants.first
|
||||
variant_filtered_by_supplier = create(:simple_product,
|
||||
supplier: other_supplier).variants.first
|
||||
variant_filtered_by_stock = create(:simple_product, supplier: supplier,
|
||||
on_hand: 0).variants.first
|
||||
|
||||
# This OC contains all products except the one that should be filtered
|
||||
# by order cycle. We create a separate OC further down to proof that
|
||||
# the product is passing all other filters.
|
||||
order_cycle = create(
|
||||
:simple_order_cycle,
|
||||
suppliers: [supplier, other_supplier],
|
||||
distributors: [distributor, other_distributor],
|
||||
variants: [
|
||||
not_filtered_variant,
|
||||
variant_filtered_by_distributor,
|
||||
variant_filtered_by_supplier,
|
||||
variant_filtered_by_stock
|
||||
]
|
||||
)
|
||||
|
||||
# Remove the distribution of one product for one distributor but still
|
||||
# sell it through the other distributor.
|
||||
order_cycle.exchanges.outgoing.find_by(receiver_id: distributor.id).
|
||||
exchange_variants.
|
||||
find_by(variant_id: variant_filtered_by_distributor).
|
||||
destroy
|
||||
|
||||
# Make product available to be filtered later. See OC comment above.
|
||||
create(
|
||||
:simple_order_cycle,
|
||||
suppliers: [supplier],
|
||||
distributors: [distributor, other_distributor],
|
||||
variants: [
|
||||
variant_filtered_by_order_cycle
|
||||
]
|
||||
)
|
||||
|
||||
allow(subject).to receive(:params).and_return(
|
||||
order_cycle_id: order_cycle.id,
|
||||
supplier_id: supplier.id,
|
||||
distributor_id: distributor.id,
|
||||
report_subtype: 'inventory'
|
||||
)
|
||||
|
||||
expect(subject.filter(variants)).to match_array [not_filtered_variant]
|
||||
|
||||
# And it integrates with the ordering of the `variants` method.
|
||||
expect(subject.variants).to match_array [not_filtered_variant]
|
||||
end
|
||||
end
|
||||
|
||||
describe "fetching SKU for a variant" do
|
||||
let(:variant) { create(:variant) }
|
||||
let(:product) { variant.product }
|
||||
|
||||
before { product.update_attribute(:sku, "Product SKU") }
|
||||
|
||||
context "when the variant has an SKU set" do
|
||||
before { variant.update_attribute(:sku, "Variant SKU") }
|
||||
it "returns it" do
|
||||
expect(subject.__send__(:sku_for, variant)).to eq "Variant SKU"
|
||||
end
|
||||
end
|
||||
|
||||
context "when the variant has bo SKU set" do
|
||||
before { variant.update_attribute(:sku, "") }
|
||||
|
||||
it "returns the product's SKU" do
|
||||
expect(subject.__send__(:sku_for, variant)).to eq "Product SKU"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,64 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
require 'open_food_network/sales_tax_report'
|
||||
|
||||
module OpenFoodNetwork
|
||||
describe SalesTaxReport do
|
||||
let(:user) { create(:user) }
|
||||
let(:report) { SalesTaxReport.new(user, {}, true) }
|
||||
|
||||
describe "calculating totals for line items" do
|
||||
let(:li1) { double(:line_item, quantity: 1, amount: 12) }
|
||||
let(:li2) { double(:line_item, quantity: 2, amount: 24) }
|
||||
let(:totals) { report.send(:totals_of, [li1, li2]) }
|
||||
|
||||
before do
|
||||
allow(report).to receive(:tax_included_in).and_return(2, 4)
|
||||
end
|
||||
|
||||
it "calculates total quantity" do
|
||||
expect(totals[:items]).to eq(3)
|
||||
end
|
||||
|
||||
it "calculates total price" do
|
||||
expect(totals[:items_total]).to eq(36)
|
||||
end
|
||||
|
||||
context "when floating point math would result in fractional cents" do
|
||||
let(:li1) { double(:line_item, quantity: 1, amount: 0.11) }
|
||||
let(:li2) { double(:line_item, quantity: 2, amount: 0.12) }
|
||||
|
||||
it "rounds to the nearest cent" do
|
||||
expect(totals[:items_total]).to eq(0.23)
|
||||
end
|
||||
end
|
||||
|
||||
it "calculates the taxable total price" do
|
||||
expect(totals[:taxable_total]).to eq(36)
|
||||
end
|
||||
|
||||
it "calculates sales tax" do
|
||||
expect(totals[:sales_tax]).to eq(6)
|
||||
end
|
||||
|
||||
context "when there is no tax on a line item" do
|
||||
before do
|
||||
allow(report).to receive(:tax_included_in) { 0 }
|
||||
end
|
||||
|
||||
it "does not appear in taxable total" do
|
||||
expect(totals[:taxable_total]).to eq(0)
|
||||
end
|
||||
|
||||
it "still appears on items total" do
|
||||
expect(totals[:items_total]).to eq(36)
|
||||
end
|
||||
|
||||
it "does not register sales tax" do
|
||||
expect(totals[:sales_tax]).to eq(0)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,125 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
require 'open_food_network/users_and_enterprises_report'
|
||||
|
||||
module OpenFoodNetwork
|
||||
describe UsersAndEnterprisesReport do
|
||||
describe "users_and_enterprises" do
|
||||
let!(:owners_and_enterprises) { double(:owners_and_enterprises) }
|
||||
let!(:managers_and_enterprises) { double(:managers_and_enterprises) }
|
||||
let!(:subject) { OpenFoodNetwork::UsersAndEnterprisesReport.new(nil, {}, true) }
|
||||
|
||||
before do
|
||||
allow(subject).to receive(:owners_and_enterprises) { owners_and_enterprises }
|
||||
allow(subject).to receive(:managers_and_enterprises) { managers_and_enterprises }
|
||||
end
|
||||
|
||||
it "should concatenate owner and manager queries" do
|
||||
expect(subject).to receive(:owners_and_enterprises).once
|
||||
expect(subject).to receive(:managers_and_enterprises).once
|
||||
expect(owners_and_enterprises).to receive(:concat).with(managers_and_enterprises).and_return []
|
||||
expect(subject).to receive(:sort).with []
|
||||
subject.users_and_enterprises
|
||||
end
|
||||
end
|
||||
|
||||
describe "sorting results" do
|
||||
let!(:subject) { OpenFoodNetwork::UsersAndEnterprisesReport.new(nil, {}, true) }
|
||||
|
||||
it "sorts by creation date" do
|
||||
uae_mock = [
|
||||
{ "created_at" => "2015-01-01", "name" => "bbb" },
|
||||
{ "created_at" => "2015-01-02", "name" => "aaa" }
|
||||
]
|
||||
expect(subject.sort(uae_mock)).to eq [uae_mock[1], uae_mock[0]]
|
||||
end
|
||||
|
||||
it "then sorts by name" do
|
||||
uae_mock = [
|
||||
{ "name" => "aaa", "relationship_type" => "bbb", "user_email" => "bbb" },
|
||||
{ "name" => "bbb", "relationship_type" => "aaa", "user_email" => "aaa" }
|
||||
]
|
||||
expect(subject.sort(uae_mock)).to eq [uae_mock[0], uae_mock[1]]
|
||||
end
|
||||
|
||||
it "then sorts by relationship type (reveresed)" do
|
||||
uae_mock = [
|
||||
{ "name" => "aaa", "relationship_type" => "bbb", "user_email" => "bbb" },
|
||||
{ "name" => "aaa", "relationship_type" => "aaa", "user_email" => "aaa" },
|
||||
{ "name" => "aaa", "relationship_type" => "bbb", "user_email" => "aaa" }
|
||||
]
|
||||
expect(subject.sort(uae_mock)).to eq [uae_mock[2], uae_mock[0], uae_mock[1]]
|
||||
end
|
||||
|
||||
it "then sorts by user_email" do
|
||||
uae_mock = [
|
||||
{ "name" => "aaa", "relationship_type" => "bbb", "user_email" => "aaa" },
|
||||
{ "name" => "aaa", "relationship_type" => "aaa", "user_email" => "aaa" },
|
||||
{ "name" => "aaa", "relationship_type" => "aaa", "user_email" => "bbb" }
|
||||
]
|
||||
expect(subject.sort(uae_mock)).to eq [uae_mock[0], uae_mock[1], uae_mock[2]]
|
||||
end
|
||||
end
|
||||
|
||||
describe "filtering results" do
|
||||
let!(:enterprise1) { create(:enterprise, owner: create(:user) ) }
|
||||
let!(:enterprise2) { create(:enterprise, owner: create(:user) ) }
|
||||
|
||||
describe "for owners and enterprises" do
|
||||
describe "by enterprise id" do
|
||||
let!(:params) { { enterprise_id_in: [enterprise1.id.to_s] } }
|
||||
let!(:subject) { OpenFoodNetwork::UsersAndEnterprisesReport.new nil, params, true }
|
||||
|
||||
it "excludes enterprises that are not explicitly requested" do
|
||||
results = subject.owners_and_enterprises.to_a.map{ |oae| oae["name"] }
|
||||
expect(results).to include enterprise1.name
|
||||
expect(results).to_not include enterprise2.name
|
||||
end
|
||||
end
|
||||
|
||||
describe "by user id" do
|
||||
let!(:params) { { user_id_in: [enterprise1.owner.id.to_s] } }
|
||||
let!(:subject) { OpenFoodNetwork::UsersAndEnterprisesReport.new nil, params, true }
|
||||
|
||||
it "excludes enterprises that are not explicitly requested" do
|
||||
results = subject.owners_and_enterprises.to_a.map{ |oae| oae["name"] }
|
||||
expect(results).to include enterprise1.name
|
||||
expect(results).to_not include enterprise2.name
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "for managers and enterprises" do
|
||||
describe "by enterprise id" do
|
||||
let!(:params) { { enterprise_id_in: [enterprise1.id.to_s] } }
|
||||
let!(:subject) { OpenFoodNetwork::UsersAndEnterprisesReport.new nil, params, true }
|
||||
|
||||
it "excludes enterprises that are not explicitly requested" do
|
||||
results = subject.managers_and_enterprises.to_a.map{ |mae| mae["name"] }
|
||||
expect(results).to include enterprise1.name
|
||||
expect(results).to_not include enterprise2.name
|
||||
end
|
||||
end
|
||||
|
||||
describe "by user id" do
|
||||
let!(:manager1) { create(:user) }
|
||||
let!(:manager2) { create(:user) }
|
||||
let!(:params) { { user_id_in: [manager1.id.to_s] } }
|
||||
let!(:subject) { OpenFoodNetwork::UsersAndEnterprisesReport.new nil, params, true }
|
||||
|
||||
before do
|
||||
enterprise1.enterprise_roles.build(user: manager1).save
|
||||
enterprise2.enterprise_roles.build(user: manager2).save
|
||||
end
|
||||
|
||||
it "excludes enterprises whose managers are not explicitly requested" do
|
||||
results = subject.managers_and_enterprises.to_a.map{ |mae| mae["name"] }
|
||||
expect(results).to include enterprise1.name
|
||||
expect(results).to_not include enterprise2.name
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,96 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
require 'open_food_network/xero_invoices_report'
|
||||
|
||||
module OpenFoodNetwork
|
||||
describe XeroInvoicesReport do
|
||||
subject { XeroInvoicesReport.new user, {}, true }
|
||||
|
||||
let(:user) { create(:user) }
|
||||
|
||||
describe "option defaults" do
|
||||
let(:report) {
|
||||
XeroInvoicesReport.new user, initial_invoice_number: '', invoice_date: '', due_date: '',
|
||||
account_code: ''
|
||||
}
|
||||
|
||||
around { |example| Timecop.travel(Time.zone.local(2015, 5, 5, 14, 0, 0)) { example.run } }
|
||||
|
||||
it "uses defaults when blank params are passed" do
|
||||
expect(report.instance_variable_get(:@opts)).to eq( invoice_date: Date.civil(2015, 5, 5),
|
||||
due_date: Date.civil(2015, 6, 5),
|
||||
account_code: 'food sales',
|
||||
report_subtype: 'summary' )
|
||||
end
|
||||
end
|
||||
|
||||
describe "summary rows" do
|
||||
let(:report) {
|
||||
XeroInvoicesReport.new user, initial_invoice_number: '', invoice_date: '', due_date: '',
|
||||
account_code: ''
|
||||
}
|
||||
let(:order) { double(:order) }
|
||||
let(:summary_rows) { report.send(:summary_rows_for_order, order, 1, {}) }
|
||||
|
||||
before do
|
||||
allow(report).to receive(:produce_summary_rows) { ['produce'] }
|
||||
allow(report).to receive(:fee_summary_rows) { ['fee'] }
|
||||
allow(report).to receive(:shipping_summary_rows) { ['shipping'] }
|
||||
allow(report).to receive(:payment_summary_rows) { ['payment'] }
|
||||
allow(report).to receive(:admin_adjustment_summary_rows) { ['admin'] }
|
||||
end
|
||||
|
||||
it "displays produce summary rows when summary report" do
|
||||
allow(report).to receive(:detail?) { false }
|
||||
expect(summary_rows).to include 'produce'
|
||||
end
|
||||
|
||||
it "does not display produce summary rows when detail report" do
|
||||
allow(report).to receive(:detail?) { true }
|
||||
expect(summary_rows).not_to include 'produce'
|
||||
end
|
||||
|
||||
it "displays fee summary rows when summary report" do
|
||||
allow(report).to receive(:detail?) { false }
|
||||
expect(summary_rows).to include 'fee'
|
||||
end
|
||||
|
||||
it "displays fee summary rows when detail report" do
|
||||
allow(report).to receive(:detail?) { true }
|
||||
expect(summary_rows).to include 'fee'
|
||||
end
|
||||
|
||||
it "always displays shipping summary rows" do
|
||||
expect(summary_rows).to include 'shipping'
|
||||
end
|
||||
|
||||
it "displays admin adjustment summary rows when summary report" do
|
||||
expect(summary_rows).to include 'admin'
|
||||
end
|
||||
|
||||
it "does not display admin adjustment summary rows when detail report" do
|
||||
allow(report).to receive(:detail?) { true }
|
||||
expect(summary_rows).not_to include 'admin'
|
||||
end
|
||||
end
|
||||
|
||||
describe "generating invoice numbers" do
|
||||
let(:order) { double(:order, number: 'R731032860') }
|
||||
|
||||
describe "when no initial invoice number is given" do
|
||||
it "returns the order number" do
|
||||
expect(subject.send(:invoice_number_for, order, 123)).to eq('R731032860')
|
||||
end
|
||||
end
|
||||
|
||||
describe "when an initial invoice number is given" do
|
||||
subject { XeroInvoicesReport.new user, initial_invoice_number: '123' }
|
||||
|
||||
it "increments the number by the index" do
|
||||
expect(subject.send(:invoice_number_for, order, 456)).to eq(579)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,10 +1,9 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
require 'open_food_network/bulk_coop_report'
|
||||
|
||||
describe OpenFoodNetwork::BulkCoopReport do
|
||||
subject { OpenFoodNetwork::BulkCoopReport.new user, params, true }
|
||||
describe Reporting::Reports::BulkCoop::BulkCoopReport do
|
||||
subject { Reporting::Reports::BulkCoop::BulkCoopReport.new user, params, true }
|
||||
let(:user) { create(:admin_user) }
|
||||
|
||||
describe '#table_items' do
|
||||
@@ -62,15 +61,15 @@ describe OpenFoodNetwork::BulkCoopReport do
|
||||
li2 = build(:line_item_with_shipment)
|
||||
o2.line_items << li2
|
||||
|
||||
report = OpenFoodNetwork::BulkCoopReport.new user, {}, true
|
||||
report = Reporting::Reports::BulkCoop::BulkCoopReport.new user, {}, true
|
||||
expect(report.table_items).to match_array [li1, li2]
|
||||
|
||||
report = OpenFoodNetwork::BulkCoopReport.new(
|
||||
report = Reporting::Reports::BulkCoop::BulkCoopReport.new(
|
||||
user, { q: { completed_at_gt: 2.days.ago } }, true
|
||||
)
|
||||
expect(report.table_items).to eq([li1])
|
||||
|
||||
report = OpenFoodNetwork::BulkCoopReport.new(
|
||||
report = Reporting::Reports::BulkCoop::BulkCoopReport.new(
|
||||
user, { q: { completed_at_lt: 2.days.ago } }, true
|
||||
)
|
||||
expect(report.table_items).to eq([li2])
|
||||
@@ -86,15 +85,15 @@ describe OpenFoodNetwork::BulkCoopReport do
|
||||
li2 = build(:line_item_with_shipment)
|
||||
o2.line_items << li2
|
||||
|
||||
report = OpenFoodNetwork::BulkCoopReport.new user, {}, true
|
||||
report = Reporting::Reports::BulkCoop::BulkCoopReport.new user, {}, true
|
||||
expect(report.table_items).to match_array [li1, li2]
|
||||
|
||||
report = OpenFoodNetwork::BulkCoopReport.new(
|
||||
report = Reporting::Reports::BulkCoop::BulkCoopReport.new(
|
||||
user, { q: { distributor_id_in: [d1.id] } }, true
|
||||
)
|
||||
expect(report.table_items).to eq([li1])
|
||||
|
||||
report = OpenFoodNetwork::BulkCoopReport.new(
|
||||
report = Reporting::Reports::BulkCoop::BulkCoopReport.new(
|
||||
user, { q: { distributor_id_in: [d2.id] } }, true
|
||||
)
|
||||
expect(report.table_items).to eq([li2])
|
||||
@@ -103,7 +102,7 @@ describe OpenFoodNetwork::BulkCoopReport do
|
||||
|
||||
context "as a manager of a supplier" do
|
||||
let!(:user) { create(:user) }
|
||||
subject { OpenFoodNetwork::BulkCoopReport.new user, {}, true }
|
||||
subject { Reporting::Reports::BulkCoop::BulkCoopReport.new user, {}, true }
|
||||
|
||||
let(:s1) { create(:supplier_enterprise) }
|
||||
|
||||
@@ -171,7 +170,7 @@ describe OpenFoodNetwork::BulkCoopReport do
|
||||
end
|
||||
|
||||
# Yes, I know testing a private method is bad practice but report's design, tighly coupling
|
||||
# OpenFoodNetwork::OrderGrouper and OpenFoodNetwork::BulkCoopReport, makes it
|
||||
# Reporting::OrderGrouper and Reporting::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) { {} }
|
||||
166
spec/lib/reports/customers_report_spec.rb
Normal file
166
spec/lib/reports/customers_report_spec.rb
Normal file
@@ -0,0 +1,166 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
module Reporting
|
||||
module Reports
|
||||
module Customers
|
||||
describe CustomersReport do
|
||||
context "as a site admin" do
|
||||
let(:user) do
|
||||
user = create(:user)
|
||||
user.spree_roles << Spree::Role.find_or_create_by!(name: 'admin')
|
||||
user
|
||||
end
|
||||
subject { CustomersReport.new user, {}, true }
|
||||
|
||||
describe "mailing list report" do
|
||||
before do
|
||||
allow(subject).to receive(:params).and_return(report_subtype: "mailing_list")
|
||||
end
|
||||
|
||||
it "returns headers for mailing_list" do
|
||||
expect(subject.table_headers).to eq(["Email", "First Name", "Last Name", "Suburb"])
|
||||
end
|
||||
|
||||
it "builds a table from a list of variants" do
|
||||
order = double(:order, email: "test@test.com")
|
||||
address = double(:billing_address, firstname: "Firsty",
|
||||
lastname: "Lasty", city: "Suburbia")
|
||||
allow(order).to receive(:billing_address).and_return address
|
||||
allow(subject).to receive(:orders).and_return [order]
|
||||
|
||||
expect(subject.table_rows).to eq([[
|
||||
"test@test.com", "Firsty", "Lasty", "Suburbia"
|
||||
]])
|
||||
end
|
||||
end
|
||||
|
||||
describe "addresses report" do
|
||||
before do
|
||||
allow(subject).to receive(:params).and_return(report_subtype: "addresses")
|
||||
end
|
||||
|
||||
it "returns headers for addresses" do
|
||||
expect(subject.table_headers).to eq(["First Name", "Last Name", "Billing Address", "Email",
|
||||
"Phone", "Hub", "Hub Address", "Shipping Method"])
|
||||
end
|
||||
|
||||
it "builds a table from a list of variants" do
|
||||
a = create(:address)
|
||||
d = create(:distributor_enterprise)
|
||||
o = create(:order, distributor: d, bill_address: a)
|
||||
o.shipments << create(:shipment)
|
||||
|
||||
allow(subject).to receive(:orders).and_return [o]
|
||||
expect(subject.table_rows).to eq([[
|
||||
a.firstname, a.lastname,
|
||||
[a.address1, a.address2, a.city].join(" "),
|
||||
o.email, a.phone, d.name,
|
||||
[d.address.address1, d.address.address2, d.address.city].join(" "),
|
||||
o.shipping_method.name
|
||||
]])
|
||||
end
|
||||
end
|
||||
|
||||
describe "fetching orders" do
|
||||
it "fetches completed orders" do
|
||||
o1 = create(:order)
|
||||
o2 = create(:order, completed_at: 1.day.ago)
|
||||
expect(subject.orders).to eq([o2])
|
||||
end
|
||||
|
||||
it "does not show cancelled orders" do
|
||||
o1 = create(:order, state: "canceled", completed_at: 1.day.ago)
|
||||
o2 = create(:order, completed_at: 1.day.ago)
|
||||
expect(subject.orders).to eq([o2])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "as an enterprise user" do
|
||||
let(:user) do
|
||||
user = create(:user)
|
||||
user.spree_roles = []
|
||||
user.save!
|
||||
user
|
||||
end
|
||||
|
||||
subject { CustomersReport.new user, {}, true }
|
||||
|
||||
describe "fetching orders" do
|
||||
let(:supplier) { create(:supplier_enterprise) }
|
||||
let(:product) { create(:simple_product, supplier: supplier) }
|
||||
let(:order) { create(:order, completed_at: 1.day.ago) }
|
||||
|
||||
it "only shows orders managed by the current user" do
|
||||
d1 = create(:distributor_enterprise)
|
||||
d1.enterprise_roles.build(user: user).save
|
||||
d2 = create(:distributor_enterprise)
|
||||
d2.enterprise_roles.build(user: create(:user)).save
|
||||
|
||||
o1 = create(:order, distributor: d1, completed_at: 1.day.ago)
|
||||
o2 = create(:order, distributor: d2, completed_at: 1.day.ago)
|
||||
|
||||
expect(subject).to receive(:filter).with([o1]).and_return([o1])
|
||||
expect(subject.orders).to eq([o1])
|
||||
end
|
||||
|
||||
it "does not show orders through a hub that the current user does not manage" do
|
||||
# Given a supplier enterprise with an order for one of its products
|
||||
supplier.enterprise_roles.build(user: user).save
|
||||
order.line_items << create(:line_item_with_shipment, product: product)
|
||||
|
||||
# When I fetch orders, I should see no orders
|
||||
expect(subject).to receive(:filter).with([]).and_return([])
|
||||
expect(subject.orders).to eq([])
|
||||
end
|
||||
end
|
||||
|
||||
describe "filtering orders" do
|
||||
let(:orders) { Spree::Order.where(nil) }
|
||||
let(:supplier) { create(:supplier_enterprise) }
|
||||
|
||||
it "returns all orders sans-params" do
|
||||
expect(subject.filter(orders)).to eq(orders)
|
||||
end
|
||||
|
||||
it "returns orders with a specific supplier" do
|
||||
supplier = create(:supplier_enterprise)
|
||||
supplier2 = create(:supplier_enterprise)
|
||||
product1 = create(:simple_product, supplier: supplier)
|
||||
product2 = create(:simple_product, supplier: supplier2)
|
||||
order1 = create(:order)
|
||||
order2 = create(:order)
|
||||
order1.line_items << create(:line_item, product: product1)
|
||||
order2.line_items << create(:line_item, product: product2)
|
||||
|
||||
allow(subject).to receive(:params).and_return(supplier_id: supplier.id)
|
||||
expect(subject.filter(orders)).to eq([order1])
|
||||
end
|
||||
|
||||
it "filters to a specific distributor" do
|
||||
d1 = create(:distributor_enterprise)
|
||||
d2 = create(:distributor_enterprise)
|
||||
order1 = create(:order, distributor: d1)
|
||||
order2 = create(:order, distributor: d2)
|
||||
|
||||
allow(subject).to receive(:params).and_return(distributor_id: d1.id)
|
||||
expect(subject.filter(orders)).to eq([order1])
|
||||
end
|
||||
|
||||
it "filters to a specific cycle" do
|
||||
oc1 = create(:simple_order_cycle)
|
||||
oc2 = create(:simple_order_cycle)
|
||||
order1 = create(:order, order_cycle: oc1)
|
||||
order2 = create(:order, order_cycle: oc2)
|
||||
|
||||
allow(subject).to receive(:params).and_return(order_cycle_id: oc1.id)
|
||||
expect(subject.filter(orders)).to eq([order1])
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
179
spec/lib/reports/enterprise_fee_summary/authorizer_spec.rb
Normal file
179
spec/lib/reports/enterprise_fee_summary/authorizer_spec.rb
Normal file
@@ -0,0 +1,179 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "spec_helper"
|
||||
|
||||
module Reporting
|
||||
module Reports
|
||||
module EnterpriseFeeSummary
|
||||
describe Authorizer do
|
||||
let(:user) { create(:user) }
|
||||
|
||||
let(:parameters) { Parameters.new(params) }
|
||||
let(:permissions) { Permissions.new(user) }
|
||||
let(:authorizer) { Authorizer.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(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(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(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(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(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(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
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -3,8 +3,8 @@
|
||||
require "spec_helper"
|
||||
|
||||
# rubocop:disable Layout/LineLength
|
||||
# describe OrderManagement::Reports::EnterpriseFeeSummary::EnterpriseFeeSummaryReport do
|
||||
# let(:report_klass) { OrderManagement::Reports::EnterpriseFeeSummary }
|
||||
# describe Reporting::Reports::EnterpriseFeeSummary::EnterpriseFeeSummaryReport do
|
||||
# let(:report_klass) { Reporting::Reports::EnterpriseFeeSummary }
|
||||
|
||||
# # Basic data.
|
||||
# let!(:shipping_method) do
|
||||
92
spec/lib/reports/enterprise_fee_summary/parameters_spec.rb
Normal file
92
spec/lib/reports/enterprise_fee_summary/parameters_spec.rb
Normal file
@@ -0,0 +1,92 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "spec_helper"
|
||||
|
||||
require "date_time_string_validator"
|
||||
|
||||
module Reporting
|
||||
module Reports
|
||||
module EnterpriseFeeSummary
|
||||
describe 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
|
||||
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(ParameterNotAllowedError)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
require "spec_helper"
|
||||
|
||||
describe OrderManagement::Reports::EnterpriseFeeSummary::Permissions do
|
||||
describe Reporting::Reports::EnterpriseFeeSummary::Permissions do
|
||||
let!(:order_cycle) { create(:simple_order_cycle) }
|
||||
let!(:incoming_exchange) { create(:exchange, incoming: true, order_cycle: order_cycle) }
|
||||
let!(:outgoing_exchange) { create(:exchange, incoming: false, order_cycle: order_cycle) }
|
||||
@@ -2,8 +2,8 @@
|
||||
|
||||
require "spec_helper"
|
||||
|
||||
# describe OrderManagement::Reports::EnterpriseFeeSummary::Renderers::CsvRenderer do
|
||||
# let(:report_klass) { OrderManagement::Reports::EnterpriseFeeSummary }
|
||||
# describe Reporting::Reports::EnterpriseFeeSummary::Renderers::CsvRenderer do
|
||||
# let(:report_klass) { Reporting::Reports::EnterpriseFeeSummary }
|
||||
|
||||
# let!(:permissions) { report_klass::Permissions.new(current_user) }
|
||||
# let!(:parameters) { report_klass::Parameters.new }
|
||||
@@ -2,12 +2,12 @@
|
||||
|
||||
require "spec_helper"
|
||||
|
||||
# describe OrderManagement::Reports::EnterpriseFeeSummary::Renderers::HtmlRenderer do
|
||||
# let(:report_klass) { OrderManagement::Reports::EnterpriseFeeSummary }
|
||||
# describe Reporting::Reports::EnterpriseFeeSummary::Renderers::HtmlRenderer do
|
||||
# let(:report_klass) { Reporting::Reports::EnterpriseFeeSummary }
|
||||
|
||||
# let!(:permissions) { report_klass::Permissions.new(current_user) }
|
||||
# let!(:parameters) { report_klass::Parameters.new }
|
||||
# let!(:controller) { OrderManagement::Reports::EnterpriseFeeSummariesController.new }
|
||||
# let!(:controller) { Reporting::Reports::EnterpriseFeeSummariesController.new }
|
||||
# let!(:service) { report_klass::ReportService.new(permissions, parameters) }
|
||||
# let!(:renderer) { described_class.new(service) }
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
require "spec_helper"
|
||||
|
||||
describe OrderManagement::Reports::EnterpriseFeeSummary::ReportData::EnterpriseFeeTypeTotal do
|
||||
describe Reporting::Reports::EnterpriseFeeSummary::ReportData::EnterpriseFeeTypeTotal do
|
||||
it "sorts instances according to their attributes" do
|
||||
instance_a = described_class.new(
|
||||
fee_type: "sales",
|
||||
95
spec/lib/reports/lettuce_share_report_spec.rb
Normal file
95
spec/lib/reports/lettuce_share_report_spec.rb
Normal file
@@ -0,0 +1,95 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
module Reporting
|
||||
module Reports
|
||||
module ProductsAndInventory
|
||||
describe LettuceShareReport do
|
||||
let(:user) { create(:user) }
|
||||
let(:base_report) {
|
||||
ProductsAndInventoryReport.new(user, { report_subtype: 'lettuce_share' }, true)
|
||||
}
|
||||
let(:report) { base_report.report }
|
||||
let(:variant) { create(:variant) }
|
||||
|
||||
describe "grower and method" do
|
||||
it "shows just the producer when there is no certification" do
|
||||
allow(report).to receive(:producer_name) { "Producer" }
|
||||
allow(report).to receive(:certification) { "" }
|
||||
|
||||
expect(report.__send__(:grower_and_method, variant)).to eq("Producer")
|
||||
end
|
||||
|
||||
it "shows producer and certification when a certification is present" do
|
||||
allow(report).to receive(:producer_name) { "Producer" }
|
||||
allow(report).to receive(:certification) { "Method" }
|
||||
|
||||
expect(report.__send__(:grower_and_method, variant)).to eq("Producer (Method)")
|
||||
end
|
||||
end
|
||||
|
||||
describe "gst" do
|
||||
it "handles tax category without rates" do
|
||||
expect(report.__send__(:gst, variant)).to eq(0)
|
||||
end
|
||||
end
|
||||
|
||||
describe "table" do
|
||||
it "handles no items" do
|
||||
expect(report.table_rows).to eq []
|
||||
end
|
||||
|
||||
describe "lists" do
|
||||
let(:variant2) { create(:variant) }
|
||||
let(:variant3) { create(:variant) }
|
||||
let(:variant4) { create(:variant, on_hand: 0, on_demand: true) }
|
||||
let(:hub_address) {
|
||||
create(:address, address1: "distributor address", city: 'The Shire', zipcode: "1234")
|
||||
}
|
||||
let(:hub) { create(:distributor_enterprise, address: hub_address) }
|
||||
let(:variant2_override) { create(:variant_override, hub: hub, variant: variant2) }
|
||||
let(:variant3_override) {
|
||||
create(:variant_override, hub: hub, variant: variant3, count_on_hand: 0)
|
||||
}
|
||||
|
||||
it "all items" do
|
||||
allow(base_report).to receive(:child_variants) {
|
||||
Spree::Variant.where(id: [variant, variant2, variant3])
|
||||
}
|
||||
expect(report.table_rows.count).to eq 3
|
||||
end
|
||||
|
||||
it "only available items" do
|
||||
variant.on_hand = 0
|
||||
allow(base_report).to receive(:child_variants) {
|
||||
Spree::Variant.where(id: [variant, variant2, variant3,
|
||||
variant4])
|
||||
}
|
||||
expect(report.table_rows.count).to eq 3
|
||||
end
|
||||
|
||||
it "only available items considering overrides" do
|
||||
create(:exchange, incoming: false, receiver_id: hub.id,
|
||||
variants: [variant, variant2, variant3])
|
||||
# create the overrides
|
||||
variant2_override
|
||||
variant3_override
|
||||
allow(base_report).to receive(:child_variants) {
|
||||
Spree::Variant.where(id: [variant, variant2, variant3])
|
||||
}
|
||||
allow(base_report).to receive(:params) {
|
||||
{ distributor_id: hub.id, report_subtype: 'lettuce_share' }
|
||||
}
|
||||
rows = report.table_rows
|
||||
expect(rows.count).to eq 2
|
||||
expect(rows.map{ |row|
|
||||
row[0]
|
||||
} ).to include variant.product.name, variant2.product.name
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,9 +1,8 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
require 'open_food_network/reports/line_items'
|
||||
|
||||
describe OpenFoodNetwork::Reports::LineItems do
|
||||
describe Reporting::LineItems do
|
||||
subject(:reports_line_items) { described_class.new(order_permissions, params) }
|
||||
|
||||
# This object lets us add some test coverage despite the very deep coupling between the class
|
||||
216
spec/lib/reports/order_cycle_management_report_spec.rb
Normal file
216
spec/lib/reports/order_cycle_management_report_spec.rb
Normal file
@@ -0,0 +1,216 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
module Reporting
|
||||
module Reports
|
||||
module OrderCycleManagement
|
||||
describe OrderCycleManagementReport do
|
||||
context "as a site admin" do
|
||||
subject { OrderCycleManagementReport.new(user, params, true) }
|
||||
let(:params) { {} }
|
||||
|
||||
let(:user) do
|
||||
user = create(:user)
|
||||
user.spree_roles << Spree::Role.find_or_create_by!(name: "admin")
|
||||
user
|
||||
end
|
||||
|
||||
describe "fetching orders" do
|
||||
let(:customers_with_balance) { instance_double(CustomersWithBalance) }
|
||||
|
||||
it 'calls the OutstandingBalance query object' do
|
||||
outstanding_balance = instance_double(OutstandingBalance, query: Spree::Order.none)
|
||||
expect(OutstandingBalance).to receive(:new).and_return(outstanding_balance)
|
||||
|
||||
subject.orders
|
||||
end
|
||||
|
||||
it "fetches completed orders" do
|
||||
o1 = create(:order)
|
||||
o2 = create(:order, completed_at: 1.day.ago, state: 'complete')
|
||||
expect(subject.orders).to eq([o2])
|
||||
end
|
||||
|
||||
it 'fetches resumed orders' do
|
||||
order = create(:order, state: 'resumed', completed_at: 1.day.ago)
|
||||
expect(subject.orders).to eq([order])
|
||||
end
|
||||
|
||||
it 'orders them by id' do
|
||||
order1 = create(:order, completed_at: 1.day.ago, state: 'complete')
|
||||
order2 = create(:order, completed_at: 2.days.ago, state: 'complete')
|
||||
|
||||
expect(subject.orders.pluck(:id)).to eq([order2.id, order1.id])
|
||||
end
|
||||
|
||||
it "does not show cancelled orders" do
|
||||
o1 = create(:order, state: 'canceled', completed_at: 1.day.ago)
|
||||
o2 = create(:order, state: 'complete', completed_at: 1.day.ago)
|
||||
expect(subject.orders).to eq([o2])
|
||||
end
|
||||
|
||||
context "default date range" do
|
||||
it "fetches orders completed in the past month" do
|
||||
o1 = create(:order, state: 'complete', completed_at: 1.month.ago - 1.day)
|
||||
o2 = create(:order, state: 'complete', completed_at: 1.month.ago + 1.day)
|
||||
expect(subject.orders).to eq([o2])
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "as an enterprise user" do
|
||||
let!(:user) { create(:user) }
|
||||
|
||||
subject { OrderCycleManagementReport.new user, {}, true }
|
||||
|
||||
describe "fetching orders" do
|
||||
let(:supplier) { create(:supplier_enterprise) }
|
||||
let(:product) { create(:simple_product, supplier: supplier) }
|
||||
let(:order) { create(:order, completed_at: 1.day.ago) }
|
||||
|
||||
it "only shows orders managed by the current user" do
|
||||
d1 = create(:distributor_enterprise)
|
||||
d1.enterprise_roles.create!(user: user)
|
||||
d2 = create(:distributor_enterprise)
|
||||
d2.enterprise_roles.create!(user: create(:user))
|
||||
|
||||
o1 = create(:order, distributor: d1, state: 'complete', completed_at: 1.day.ago)
|
||||
o2 = create(:order, distributor: d2, state: 'complete', completed_at: 1.day.ago)
|
||||
|
||||
expect(subject).to receive(:filter).with([o1]).and_return([o1])
|
||||
expect(subject.orders).to eq([o1])
|
||||
end
|
||||
|
||||
it "does not show orders through a hub that the current user does not manage" do
|
||||
# Given a supplier enterprise with an order for one of its products
|
||||
supplier.enterprise_roles.create!(user: user)
|
||||
order.line_items << create(:line_item_with_shipment, product: product)
|
||||
|
||||
# When I fetch orders, I should see no orders
|
||||
expect(subject).to receive(:filter).with([]).and_return([])
|
||||
expect(subject.orders).to eq([])
|
||||
end
|
||||
end
|
||||
|
||||
describe "filtering orders" do
|
||||
let!(:orders) { Spree::Order.where(nil) }
|
||||
let!(:supplier) { create(:supplier_enterprise) }
|
||||
|
||||
let!(:oc1) { create(:simple_order_cycle) }
|
||||
let!(:pm1) { create(:payment_method, name: "PM1") }
|
||||
let!(:sm1) { create(:shipping_method, name: "ship1") }
|
||||
let!(:s1) { create(:shipment_with, :shipping_method, shipping_method: sm1) }
|
||||
let!(:order1) { create(:order, shipments: [s1], order_cycle: oc1) }
|
||||
let!(:payment1) { create(:payment, order: order1, payment_method: pm1) }
|
||||
|
||||
it "returns all orders sans-params" do
|
||||
expect(subject.filter(orders)).to eq(orders)
|
||||
end
|
||||
|
||||
it "filters to a specific order cycle" do
|
||||
oc2 = create(:simple_order_cycle)
|
||||
order2 = create(:order, order_cycle: oc2)
|
||||
|
||||
allow(subject).to receive(:params).and_return(order_cycle_id: oc1.id)
|
||||
expect(subject.filter(orders)).to eq([order1])
|
||||
end
|
||||
|
||||
it "filters to a payment method" do
|
||||
pm2 = create(:payment_method, name: "PM2")
|
||||
pm3 = create(:payment_method, name: "PM3")
|
||||
order2 = create(:order, payments: [create(:payment, payment_method: pm2)])
|
||||
order3 = create(:order, payments: [create(:payment, payment_method: pm3)])
|
||||
|
||||
allow(subject).to receive(:params).and_return(payment_method_in: [pm1.id, pm3.id] )
|
||||
expect(subject.filter(orders)).to match_array [order1, order3]
|
||||
end
|
||||
|
||||
it "filters to a shipping method" do
|
||||
sm2 = create(:shipping_method, name: "ship2")
|
||||
sm3 = create(:shipping_method, name: "ship3")
|
||||
s2 = create(:shipment_with, :shipping_method, shipping_method: sm2)
|
||||
s3 = create(:shipment_with, :shipping_method, shipping_method: sm3)
|
||||
order2 = create(:order, shipments: [s2])
|
||||
order3 = create(:order, shipments: [s3])
|
||||
|
||||
allow(subject).to receive(:params).and_return(shipping_method_in: [sm1.id, sm3.id])
|
||||
expect(subject.filter(orders)).to match_array [order1, order3]
|
||||
end
|
||||
|
||||
it "should do all the filters at once" do
|
||||
allow(subject).to receive(:params).and_return(order_cycle_id: oc1.id,
|
||||
shipping_method_name: sm1.name,
|
||||
payment_method_name: pm1.name)
|
||||
expect(subject.filter(orders)).to eq([order1])
|
||||
end
|
||||
end
|
||||
|
||||
describe '#table_rows' do
|
||||
subject { OrderCycleManagementReport.new(user, params, true) }
|
||||
|
||||
let(:distributor) { create(:distributor_enterprise) }
|
||||
before { distributor.enterprise_roles.create!(user: user) }
|
||||
|
||||
context 'when the report type is payment_methods' do
|
||||
let(:params) { { report_subtype: 'payment_methods' } }
|
||||
|
||||
let!(:order) do
|
||||
create(
|
||||
:completed_order_with_totals,
|
||||
distributor: distributor,
|
||||
completed_at: 1.day.ago
|
||||
)
|
||||
end
|
||||
|
||||
it 'returns rows with payment information' do
|
||||
expect(subject.table_rows).to eq([[
|
||||
order.billing_address.firstname,
|
||||
order.billing_address.lastname,
|
||||
order.distributor.name,
|
||||
'',
|
||||
order.email,
|
||||
order.billing_address.phone,
|
||||
order.shipment.shipping_method.name,
|
||||
nil,
|
||||
order.total,
|
||||
-order.total
|
||||
]])
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the report type is not payment_methods' do
|
||||
let(:params) { {} }
|
||||
let!(:order) do
|
||||
create(
|
||||
:completed_order_with_totals,
|
||||
distributor: distributor,
|
||||
completed_at: 1.day.ago
|
||||
)
|
||||
end
|
||||
|
||||
it 'returns rows with delivery information' do
|
||||
expect(subject.table_rows).to eq([[
|
||||
order.ship_address.firstname,
|
||||
order.ship_address.lastname,
|
||||
order.distributor.name,
|
||||
"",
|
||||
"#{order.ship_address.address1} #{order.ship_address.address2} #{order.ship_address.city}",
|
||||
order.ship_address.zipcode,
|
||||
order.ship_address.phone,
|
||||
order.shipment.shipping_method.name,
|
||||
nil,
|
||||
order.total,
|
||||
-order.total,
|
||||
false,
|
||||
order.special_instructions
|
||||
]])
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,9 +1,8 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
require 'open_food_network/order_grouper'
|
||||
|
||||
module OpenFoodNetwork
|
||||
module Reporting
|
||||
describe OrderGrouper do
|
||||
before(:each) do
|
||||
@items = [1, 2, 3, 4]
|
||||
@@ -84,7 +83,7 @@ module OpenFoodNetwork
|
||||
subject = OrderGrouper.new @rules, @columns
|
||||
|
||||
grouped_tree = double(:grouped_tree)
|
||||
expect(subject).to receive(:group_and_sort).with(@rule1, @rules[1..-1],
|
||||
expect(subject).to receive(:group_and_sort).with(@rule1, @rules[1..],
|
||||
@items).and_return(grouped_tree)
|
||||
|
||||
expect(subject.build_tree(@items, @rules)).to eq(grouped_tree)
|
||||
@@ -101,7 +100,7 @@ module OpenFoodNetwork
|
||||
expect(@items).to receive(:group_by).and_return(groups)
|
||||
sorted_groups = {}
|
||||
1.upto(number_of_categories) { |i|
|
||||
sorted_groups[i] = double(:group, name: "Group " + i.to_s )
|
||||
sorted_groups[i] = double(:group, name: "Group #{i}" )
|
||||
}
|
||||
expect(groups).to receive(:sort_by).and_return(sorted_groups)
|
||||
group = { group1: 1, group2: 2, group3: 3 }
|
||||
90
spec/lib/reports/orders_and_distributors_report_spec.rb
Normal file
90
spec/lib/reports/orders_and_distributors_report_spec.rb
Normal file
@@ -0,0 +1,90 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
module Reporting
|
||||
module Reports
|
||||
module OrdersAndDistributors
|
||||
describe OrdersAndDistributorsReport do
|
||||
describe 'orders and distributors report' do
|
||||
it 'should return a header row describing the report' do
|
||||
subject = OrdersAndDistributorsReport.new nil
|
||||
|
||||
expect(subject.table_headers).to eq(
|
||||
[
|
||||
'Order date', 'Order Id',
|
||||
'Customer Name', 'Customer Email', 'Customer Phone', 'Customer City',
|
||||
'SKU', 'Item name', 'Variant', 'Quantity', 'Max Quantity', 'Cost', 'Shipping Cost',
|
||||
'Payment Method',
|
||||
'Distributor', 'Distributor address', 'Distributor city', 'Distributor postcode',
|
||||
'Shipping Method', 'Shipping instructions'
|
||||
]
|
||||
)
|
||||
end
|
||||
|
||||
context 'with completed order' do
|
||||
let(:bill_address) { create(:address) }
|
||||
let(:distributor) { create(:distributor_enterprise) }
|
||||
let(:product) { create(:product) }
|
||||
let(:shipping_method) { create(:shipping_method) }
|
||||
let(:shipping_instructions) { 'pick up on thursday please!' }
|
||||
let(:order) {
|
||||
create(:order,
|
||||
state: 'complete', completed_at: Time.zone.now,
|
||||
distributor: distributor, bill_address: bill_address,
|
||||
special_instructions: shipping_instructions)
|
||||
}
|
||||
let(:payment_method) { create(:payment_method, distributors: [distributor]) }
|
||||
let(:payment) { create(:payment, payment_method: payment_method, order: order) }
|
||||
let(:line_item) { create(:line_item_with_shipment, product: product, order: order) }
|
||||
|
||||
before do
|
||||
order.select_shipping_method(shipping_method.id)
|
||||
order.payments << payment
|
||||
order.line_items << line_item
|
||||
end
|
||||
|
||||
it 'should denormalise order and distributor details for display as csv' do
|
||||
subject = OrdersAndDistributorsReport.new create(:admin_user), {}, true
|
||||
|
||||
table = subject.table_rows
|
||||
|
||||
expect(table.size).to eq 1
|
||||
expect(table[0]).to eq([
|
||||
order.reload.completed_at.strftime("%F %T"),
|
||||
order.id,
|
||||
bill_address.full_name,
|
||||
order.email,
|
||||
bill_address.phone,
|
||||
bill_address.city,
|
||||
line_item.product.sku,
|
||||
line_item.product.name,
|
||||
line_item.options_text,
|
||||
line_item.quantity,
|
||||
line_item.max_quantity,
|
||||
line_item.price * line_item.quantity,
|
||||
line_item.distribution_fee,
|
||||
payment_method.name,
|
||||
distributor.name,
|
||||
distributor.address.address1,
|
||||
distributor.address.city,
|
||||
distributor.address.zipcode,
|
||||
shipping_method.name,
|
||||
shipping_instructions
|
||||
])
|
||||
end
|
||||
|
||||
it "prints one row per line item" do
|
||||
create(:line_item_with_shipment, order: order)
|
||||
|
||||
subject = OrdersAndDistributorsReport.new(create(:admin_user), {}, true)
|
||||
|
||||
table = subject.table_rows
|
||||
expect(table.size).to eq 2
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,131 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "spec_helper"
|
||||
|
||||
module Reporting
|
||||
module Reports
|
||||
module OrdersAndFulfillment
|
||||
describe CustomerTotalsReport do
|
||||
let!(:distributor) { create(:distributor_enterprise) }
|
||||
let!(:customer) { create(:customer, enterprise: distributor) }
|
||||
let(:current_user) { distributor.owner }
|
||||
|
||||
let(:report) do
|
||||
report_options = { report_subtype: described_class::REPORT_TYPE }
|
||||
OrdersAndFulfillmentReport.new(current_user, report_options, true)
|
||||
end
|
||||
|
||||
let(:report_table) do
|
||||
report.table_rows
|
||||
end
|
||||
|
||||
context "viewing the report" do
|
||||
let!(:order) do
|
||||
create(:completed_order_with_totals, line_items_count: 1, user: customer.user,
|
||||
customer: customer, distributor: distributor)
|
||||
end
|
||||
|
||||
it "generates the report" do
|
||||
expect(report_table.length).to eq(2)
|
||||
end
|
||||
|
||||
it "has a line item row" do
|
||||
distributor_name_field = report_table.first[0]
|
||||
expect(distributor_name_field).to eq distributor.name
|
||||
|
||||
customer_name_field = report_table.first[1]
|
||||
expect(customer_name_field).to eq order.bill_address.full_name
|
||||
|
||||
total_field = report_table.last[5]
|
||||
expect(total_field).to eq I18n.t("admin.reports.total")
|
||||
end
|
||||
|
||||
it 'includes the order number and date in item rows' do
|
||||
order_number_and_date_fields = report_table.first[33..34]
|
||||
expect(order_number_and_date_fields).to eq([
|
||||
order.number,
|
||||
order.completed_at.strftime("%F %T"),
|
||||
])
|
||||
end
|
||||
|
||||
it 'includes the order number and date in total rows' do
|
||||
order_number_and_date_fields = report_table.last[33..34]
|
||||
expect(order_number_and_date_fields).to eq([
|
||||
order.number,
|
||||
order.completed_at.strftime("%F %T"),
|
||||
])
|
||||
end
|
||||
end
|
||||
|
||||
context "loading shipping methods" do
|
||||
let!(:shipping_method1) {
|
||||
create(:shipping_method, distributors: [distributor], name: "First")
|
||||
}
|
||||
let!(:shipping_method2) {
|
||||
create(:shipping_method, distributors: [distributor], name: "Second")
|
||||
}
|
||||
let!(:shipping_method3) {
|
||||
create(:shipping_method, distributors: [distributor], name: "Third")
|
||||
}
|
||||
let!(:order) do
|
||||
create(:completed_order_with_totals, line_items_count: 1, user: customer.user,
|
||||
customer: customer, distributor: distributor)
|
||||
end
|
||||
|
||||
before do
|
||||
order.shipments.each(&:refresh_rates)
|
||||
order.select_shipping_method(shipping_method2.id)
|
||||
end
|
||||
|
||||
it "displays the correct shipping_method" do
|
||||
shipping_method_name_field = report_table.first[15]
|
||||
expect(shipping_method_name_field).to eq shipping_method2.name
|
||||
end
|
||||
end
|
||||
|
||||
context "displaying payment fees" do
|
||||
context "with both failed and completed payments present" do
|
||||
let!(:order) {
|
||||
create(:order_ready_to_ship, user: customer.user,
|
||||
customer: customer, distributor: distributor)
|
||||
}
|
||||
let(:completed_payment) { order.payments.completed.first }
|
||||
let!(:failed_payment) { create(:payment, order: order, state: "failed") }
|
||||
|
||||
before do
|
||||
completed_payment.adjustment.update amount: 123.00
|
||||
failed_payment.adjustment.update amount: 456.00, eligible: false, state: "finalized"
|
||||
end
|
||||
|
||||
it "shows the correct payment fee amount for the order" do
|
||||
payment_fee_field = report_table.last[12]
|
||||
expect(payment_fee_field).to eq completed_payment.adjustment.amount
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when a variant override applies' do
|
||||
let!(:order) do
|
||||
create(:completed_order_with_totals, line_items_count: 1, user: customer.user,
|
||||
customer: customer, distributor: distributor)
|
||||
end
|
||||
let(:overidden_sku) { 'magical_sku' }
|
||||
|
||||
before do
|
||||
create(
|
||||
:variant_override,
|
||||
hub: distributor,
|
||||
variant: order.line_items.first.variant,
|
||||
sku: overidden_sku
|
||||
)
|
||||
end
|
||||
|
||||
it 'uses the sku from the variant override' do
|
||||
sku_field = report_table.first[23]
|
||||
expect(sku_field).to eq overidden_sku
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user