From 7cb28fd064ef955c482e60fea0aaff1db7ac2bd5 Mon Sep 17 00:00:00 2001 From: Ahmed Ejaz Date: Sun, 29 Sep 2024 17:31:08 +0500 Subject: [PATCH] 12776: add supplier report --- .../reports/filters/_suppliers.html.haml | 14 ++ config/locales/en.yml | 12 +- lib/reporting/reports/suppliers/base.rb | 100 +++++++++++++++ .../suppliers/helpers/columns_helper.rb | 121 ++++++++++++++++++ .../helpers/line_items_access_helper.rb | 64 +++++++++ 5 files changed, 310 insertions(+), 1 deletion(-) create mode 100644 app/views/admin/reports/filters/_suppliers.html.haml create mode 100644 lib/reporting/reports/suppliers/base.rb create mode 100644 lib/reporting/reports/suppliers/helpers/columns_helper.rb create mode 100644 lib/reporting/reports/suppliers/helpers/line_items_access_helper.rb diff --git a/app/views/admin/reports/filters/_suppliers.html.haml b/app/views/admin/reports/filters/_suppliers.html.haml new file mode 100644 index 0000000000..1c10ffd6ce --- /dev/null +++ b/app/views/admin/reports/filters/_suppliers.html.haml @@ -0,0 +1,14 @@ += render 'admin/reports/date_range_form', f: f + +.row + .alpha.two.columns= label_tag nil, t(:report_hubs) + .omega.fourteen.columns= f.collection_select(:distributor_id_in, @data.orders_distributors, :id, :name, {}, {class: "select2 fullwidth", multiple: true}) + +.row + .alpha.two.columns= label_tag nil, t(:report_producers) + .omega.fourteen.columns= select_tag(:supplier_id_in, options_from_collection_for_select(@data.orders_suppliers, :id, :name, params[:supplier_id_in]), {class: "select2 fullwidth", multiple: true}) + +.row + .alpha.two.columns= label_tag nil, t(:report_customers_cycle) + .omega.fourteen.columns + = f.select(:order_cycle_id_in, report_order_cycle_options(@data.order_cycles), {selected: params.dig(:q, :order_cycle_id_in)}, {class: "select2 fullwidth", multiple: true}) \ No newline at end of file diff --git a/config/locales/en.yml b/config/locales/en.yml index 7f03a407a0..07969c9cd8 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -3174,7 +3174,9 @@ See the %{link} to find out more about %{sitename}'s features and to start using report_print: Print Report report_render_options: Rendering Options report_header_ofn_uid: OFN UID - report_header_order_cycle: Order Cycle + report_header_order_cycle: Order Cycle (OC) + report_header_order_cycle_start_date: OC Start Date + report_header_order_cycle_end_date: OC End Date report_header_user: User report_header_email: Email report_header_status: Status @@ -3195,6 +3197,7 @@ See the %{link} to find out more about %{sitename}'s features and to start using report_header_hub_legal_name: "Hub Legal Name" report_header_hub_contact_name: "Hub Contact Name" report_header_hub_email: "Hub Public Email" + report_header_hub_contact_email: Hub Contact Email report_header_hub_owner_email: Hub Owner Email report_header_hub_phone: "Hub Phone Number" report_header_hub_address_line1: "Hub Address Line 1" @@ -3255,6 +3258,7 @@ See the %{link} to find out more about %{sitename}'s features and to start using report_header_quantity: Quantity report_header_max_quantity: Max Quantity report_header_variant: Variant + report_header_variant_unit_name: Variant Unit Name report_header_variant_value: Variant Value report_header_variant_unit: Variant Unit report_header_total_available: Total available @@ -3266,6 +3270,8 @@ See the %{link} to find out more about %{sitename}'s features and to start using report_header_producer_suburb: Producer Suburb report_header_producer_tax_status: Producer Tax Status report_header_producer_charges_sales_tax?: GST/VAT Registered + report_header_producer_abn_acn: Producer ABN/ACN + report_header_producer_address: Producer Address report_header_unit: Unit report_header_group_buy_unit_quantity: Group Buy Unit Quantity report_header_cost: Cost @@ -3326,7 +3332,11 @@ See the %{link} to find out more about %{sitename}'s features and to start using report_header_total_units: Total Units report_header_sum_max_total: "Sum Max Total" report_header_total_excl_vat: "Total excl. tax (%{currency_symbol})" + report_header_total_fees_excl_vat: "Total fees excl. tax (%{currency_symbol})" + report_header_total_vat_on_fees: "Total tax on fees (%{currency_symbol})" + report_header_total: "Total (%{currency_symbol})" report_header_total_incl_vat: "Total incl. tax (%{currency_symbol})" + report_header_total_excl_vat_and_fees: "Total excl. fees and tax (%{currency_symbol})" report_header_temp_controlled: TempControlled? report_header_is_producer: Producer? report_header_not_confirmed: Not Confirmed diff --git a/lib/reporting/reports/suppliers/base.rb b/lib/reporting/reports/suppliers/base.rb new file mode 100644 index 0000000000..28fa3bc45e --- /dev/null +++ b/lib/reporting/reports/suppliers/base.rb @@ -0,0 +1,100 @@ +# frozen_string_literal: true + +module Reporting + module Reports + module Suppliers + class Base < ReportTemplate + include Helpers::ColumnsHelper + + def default_params + { + q: { + completed_at_gt: 1.month.ago.beginning_of_day, + completed_at_lt: 1.day.from_now.beginning_of_day + } + } + end + + def search + report_line_items.orders + end + + def query_result + report_line_items.list(line_item_includes).group_by { |e| + [e.variant_id, e.price, e.order.distributor_id] + }.values + end + + def columns + { + producer:, + producer_address:, + producer_abn_acn:, + email:, + hub:, + hub_address:, + hub_contact_email:, + order_number:, + order_date:, + order_cycle:, + order_cycle_start_date:, + order_cycle_end_date:, + product:, + variant_unit_name:, + quantity:, + total_excl_vat_and_fees:, + total_excl_vat:, + total_fees_excl_vat:, + total_vat_on_fees:, + total_tax:, + total:, + } + end + + def rules + [ + { + group_by: :producer, + header: true, + summary_row: proc do |_key, items| + line_items = items.flatten + + { + total_excl_vat_and_fees: total_excl_vat_and_fees.call(line_items), + total_excl_vat: total_excl_vat.call(line_items), + total_fees_excl_vat: total_fees_excl_vat.call(line_items), + total_vat_on_fees: total_vat_on_fees.call(line_items), + total_tax: total_tax.call(line_items), + total: total.call(line_items), + } + end + } + ] + end + + private + + def order_permissions + return @order_permissions unless @order_permissions.nil? + + @order_permissions = ::Permissions::Order.new(@user, ransack_params) + end + + def report_line_items + @report_line_items ||= Reporting::LineItems.new(order_permissions, params) + end + + def line_item_includes + [{ + order: [ + :distributor, + :adjustments, + { shipments: { shipping_rates: :shipping_method } } + ], + variant: [:product, :supplier] + }] + end + end + end + end +end diff --git a/lib/reporting/reports/suppliers/helpers/columns_helper.rb b/lib/reporting/reports/suppliers/helpers/columns_helper.rb new file mode 100644 index 0000000000..a5bd1002e9 --- /dev/null +++ b/lib/reporting/reports/suppliers/helpers/columns_helper.rb @@ -0,0 +1,121 @@ +# frozen_string_literal: true + +module Reporting + module Reports + module Suppliers + module Helpers + module ColumnsHelper + include LineItemsAccessHelper + + def producer + proc { |line_items| supplier(line_items).name } + end + + def producer_address + proc { |line_items| supplier(line_items).address&.full_address } + end + + def producer_abn_acn + proc do |line_items| + supplier = supplier(line_items) + + supplier.abn.presence || supplier.acn + end + end + + def email + proc { |line_items| supplier(line_items).email_address } + end + + def hub + proc { |line_items| distributor(line_items).name } + end + + def hub_address + proc { |line_items| distributor(line_items).address&.full_address } + end + + def hub_contact_email + proc { |line_items| distributor(line_items).email_address } + end + + def order_number + proc { |line_items| order(line_items).number } + end + + def order_date + proc { |line_items| order(line_items).completed_at.strftime("%d/%m/%Y") } + end + + def order_cycle + proc { |line_items| item_order_cycle(line_items).name } + end + + def order_cycle_start_date + proc { |line_items| item_order_cycle(line_items).orders_open_at.strftime("%d/%m/%Y") } + end + + def order_cycle_end_date + proc { |line_items| item_order_cycle(line_items).orders_close_at.strftime("%d/%m/%Y") } + end + + def product + proc { |line_items| variant(line_items).product.name } + end + + def variant_unit_name + proc { |line_items| variant(line_items).full_name } + end + + def quantity + proc { |line_items| line_items.to_a.sum(&:quantity) } + end + + def total_excl_vat_and_fees + proc do |line_items| + included_tax = adjustments_by_type(line_items, :tax, included: true) + line_items.sum(&:amount) - included_tax + end + end + + def total_excl_vat + proc do |line_items| + total_fees = adjustments_by_type(line_items, :fees) + total_excl_vat_and_fees.call(line_items) + total_fees + end + end + + def total_fees_excl_vat + proc do |line_items| + included_tax = tax_on_fees(line_items, included: true) + adjustments_by_type(line_items, :fees) - included_tax + end + end + + def total_vat_on_fees + proc { |line_items| tax_on_fees(line_items) } + end + + def total_tax + proc do |line_items| + excluded_tax = adjustments_by_type(line_items, :tax) + included_tax = adjustments_by_type(line_items, :tax, included: true) + + excluded_tax + included_tax + end + end + + def total + proc do |line_items| + total_price = total_excl_vat_and_fees.call(line_items) + total_fees = total_fees_excl_vat.call(line_items) + tax = total_tax.call(line_items) + + total_price + total_fees + tax + end + end + end + end + end + end +end diff --git a/lib/reporting/reports/suppliers/helpers/line_items_access_helper.rb b/lib/reporting/reports/suppliers/helpers/line_items_access_helper.rb new file mode 100644 index 0000000000..363989d441 --- /dev/null +++ b/lib/reporting/reports/suppliers/helpers/line_items_access_helper.rb @@ -0,0 +1,64 @@ +# frozen_string_literal: true + +module Reporting + module Reports + module Suppliers + module Helpers + module LineItemsAccessHelper + def variant(line_items) + line_items.first.variant + end + + def order(line_items) + line_items.first.order + end + + def supplier(line_items) + variant(line_items).supplier + end + + def distributor(line_items) + order(line_items).distributor + end + + def item_order_cycle(line_items) + line_items.first.order_cycle + end + + def adjustments_by_type(line_items, type, included: false) + total_amount = 0.0 + adjustment_type = type == :tax ? 'Spree::TaxRate' : 'EnterpriseFee' + adjustments = line_items.flat_map(&:adjustments) + + adjustments.each do |adjustment| + if adjustment.originator_type == adjustment_type + amount = included == adjustment.included ? adjustment.amount : 0.0 + total_amount += amount + end + end + + total_amount + end + + def tax_on_fees(line_items, included: false) + total_amount = 0.0 + adjustments = line_items.flat_map(&:adjustments) + + adjustments.each do |adjustment| + next unless adjustment.originator_type == 'EnterpriseFee' + + adjustment.adjustments.each do |fee_adjustment| + next unless adjustment.originator_type == 'Spree::TaxRate' + + amount = included == fee_adjustment.included ? fee_adjustment.amount : 0.0 + total_amount += amount + end + end + + total_amount + end + end + end + end + end +end