diff --git a/app/models/spree/ability.rb b/app/models/spree/ability.rb index 3233e58c6d9..572efddd8ce 100644 --- a/app/models/spree/ability.rb +++ b/app/models/spree/ability.rb @@ -244,7 +244,7 @@ def add_product_management_abilities(user) can [:admin, :index, :show, :create], ::Admin::ReportsController can [:admin, :show, :create, :customers, :orders_and_distributors, :group_buys, :payments, :orders_and_fulfillment, :products_and_inventory, :order_cycle_management, - :packing, :enterprise_fee_summary, :bulk_coop], :report + :packing, :enterprise_fee_summary, :bulk_coop, :suppliers], :report end def add_order_cycle_management_abilities(user) 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 00000000000..0a9963544ff --- /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}) diff --git a/config/locales/en.yml b/config/locales/en.yml index 80b5a5e90b3..44834a3d6f5 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -1768,6 +1768,7 @@ en: pack_by_customer: Pack By Customer pack_by_supplier: Pack By Supplier pack_by_product: Pack By Product + pay_your_suppliers: Pay your suppliers display: report_is_big: "This report is big and may slow down your device." display_anyway: "Display anyway" @@ -1814,6 +1815,8 @@ en: enterprise_fee_summary: name: "Enterprise Fee Summary" description: "Summary of Enterprise Fees collected" + suppliers: + name: Suppliers enterprise_fees_with_tax_report_by_order: "Enterprise Fees With Tax Report By Order" enterprise_fees_with_tax_report_by_producer: "Enterprise Fees With Tax Report By Producer" errors: @@ -3172,6 +3175,8 @@ See the %{link} to find out more about %{sitename}'s features and to start using report_render_options: Rendering Options report_header_ofn_uid: OFN UID report_header_order_cycle: Order Cycle + 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 @@ -3192,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" @@ -3252,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 @@ -3263,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 @@ -3323,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_tax: "Total fees excl. tax (%{currency_symbol})" + report_header_total_tax_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_fees_and_tax: "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/list.rb b/lib/reporting/reports/list.rb index a2dd38efc32..95d5b38abfa 100644 --- a/lib/reporting/reports/list.rb +++ b/lib/reporting/reports/list.rb @@ -3,6 +3,8 @@ module Reporting module Reports class List + include ReportTypes + def self.all new.all end @@ -22,98 +24,9 @@ def all xero_invoices: xero_report_types, packing: packing_report_types, revenues_by_hub: [], + suppliers: suppliers_report_types, } end - - protected - - def orders_and_fulfillment_report_types - [ - [i18n_translate("supplier_totals"), :order_cycle_supplier_totals], - [i18n_translate("supplier_totals_by_distributor"), - :order_cycle_supplier_totals_by_distributor], - [i18n_translate("totals_by_supplier"), :order_cycle_distributor_totals_by_supplier], - [i18n_translate("customer_totals"), :order_cycle_customer_totals] - ] - end - - def products_and_inventory_report_types - [ - [i18n_translate("all_products"), :all_products], - [i18n_translate("inventory"), :inventory, { deprecated: true }], - [i18n_translate("lettuce_share"), :lettuce_share] - ] - end - - def payments_report_types - [ - [I18n.t(:report_payment_by), :payments_by_payment_type], - [I18n.t(:report_itemised_payment), :itemised_payment_totals], - [I18n.t(:report_payment_totals), :payment_totals] - ] - end - - def enterprise_fee_summary - [ - [i18n_translate('enterprise_fee_summary.name'), :fee_summary], - [ - i18n_translate('enterprise_fees_with_tax_report_by_order'), - :enterprise_fees_with_tax_report_by_order - ], - [ - i18n_translate('enterprise_fees_with_tax_report_by_producer'), - :enterprise_fees_with_tax_report_by_producer - ], - ] - end - - def order_cycle_management_report_types - [ - [i18n_translate("payment_methods"), :payment_methods], - [i18n_translate("delivery"), :delivery] - ] - end - - def sales_tax_report_types - [ - [i18n_translate("tax_types"), :tax_types], - [i18n_translate("tax_rates"), :tax_rates], - [i18n_translate("sales_tax_totals_by_producer"), :sales_tax_totals_by_producer], - [i18n_translate("sales_tax_totals_by_order"), :sales_tax_totals_by_order] - ] - end - - def packing_report_types - [ - [i18n_translate("pack_by_customer"), :customer], - [i18n_translate("pack_by_supplier"), :supplier], - [i18n_translate("pack_by_product"), :product] - ] - end - - def xero_report_types - [ - [I18n.t(:summary), 'summary'], - [I18n.t(:detailed), 'detailed'] - ] - end - - def bulk_coop_report_types - [ - bulk_coop_item(:supplier_report), - bulk_coop_item(:allocation), - bulk_coop_item(:packing_sheets), - bulk_coop_item(:customer_payments) - ] - end - - def bulk_coop_item(key) - [I18n.t("order_management.reports.bulk_coop.filters.bulk_coop_#{key}"), key] - end - - def i18n_translate(key) - I18n.t(key, scope: "admin.reports") - end end end end diff --git a/lib/reporting/reports/report_types.rb b/lib/reporting/reports/report_types.rb new file mode 100644 index 00000000000..d68225722ea --- /dev/null +++ b/lib/reporting/reports/report_types.rb @@ -0,0 +1,103 @@ +# frozen_string_literal: true + +module Reporting + module Reports + module ReportTypes + protected + + def orders_and_fulfillment_report_types + [ + [i18n_translate("supplier_totals"), :order_cycle_supplier_totals], + [i18n_translate("supplier_totals_by_distributor"), + :order_cycle_supplier_totals_by_distributor], + [i18n_translate("totals_by_supplier"), :order_cycle_distributor_totals_by_supplier], + [i18n_translate("customer_totals"), :order_cycle_customer_totals] + ] + end + + def products_and_inventory_report_types + [ + [i18n_translate("all_products"), :all_products], + [i18n_translate("inventory"), :inventory, { deprecated: true }], + [i18n_translate("lettuce_share"), :lettuce_share] + ] + end + + def payments_report_types + [ + [I18n.t(:report_payment_by), :payments_by_payment_type], + [I18n.t(:report_itemised_payment), :itemised_payment_totals], + [I18n.t(:report_payment_totals), :payment_totals] + ] + end + + def enterprise_fee_summary + [ + [i18n_translate('enterprise_fee_summary.name'), :fee_summary], + [ + i18n_translate('enterprise_fees_with_tax_report_by_order'), + :enterprise_fees_with_tax_report_by_order + ], + [ + i18n_translate('enterprise_fees_with_tax_report_by_producer'), + :enterprise_fees_with_tax_report_by_producer + ], + ] + end + + def order_cycle_management_report_types + [ + [i18n_translate("payment_methods"), :payment_methods], + [i18n_translate("delivery"), :delivery] + ] + end + + def sales_tax_report_types + [ + [i18n_translate("tax_types"), :tax_types], + [i18n_translate("tax_rates"), :tax_rates], + [i18n_translate("sales_tax_totals_by_producer"), :sales_tax_totals_by_producer], + [i18n_translate("sales_tax_totals_by_order"), :sales_tax_totals_by_order] + ] + end + + def packing_report_types + [ + [i18n_translate("pack_by_customer"), :customer], + [i18n_translate("pack_by_supplier"), :supplier], + [i18n_translate("pack_by_product"), :product] + ] + end + + def xero_report_types + [ + [I18n.t(:summary), 'summary'], + [I18n.t(:detailed), 'detailed'] + ] + end + + def bulk_coop_report_types + [ + bulk_coop_item(:supplier_report), + bulk_coop_item(:allocation), + bulk_coop_item(:packing_sheets), + bulk_coop_item(:customer_payments) + ] + end + + def suppliers_report_types + [ + [i18n_translate(:pay_your_suppliers), :pay_your_suppliers] + ] + end + + def bulk_coop_item(key) + [I18n.t("order_management.reports.bulk_coop.filters.bulk_coop_#{key}"), key] + end + + def i18n_translate(key) + I18n.t(key, scope: "admin.reports") + end + end + end +end diff --git a/lib/reporting/reports/suppliers/base.rb b/lib/reporting/reports/suppliers/base.rb new file mode 100644 index 00000000000..f6723ffdc89 --- /dev/null +++ b/lib/reporting/reports/suppliers/base.rb @@ -0,0 +1,99 @@ +# 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) + 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_fees_and_tax:, + total_excl_vat:, + total_fees_excl_tax:, + total_tax_on_fees:, + total_tax:, + total:, + } + end + + def rules + [ + { + group_by: :producer, + header: true, + summary_row: proc do |_key, line_items| + summary_hash = Hash.new(0) + + line_items.each do |line_item| + summary_hash[:total_excl_fees_and_tax] += total_excl_fees_and_tax.call(line_item) + summary_hash[:total_excl_vat] += total_excl_vat.call(line_item) + summary_hash[:total_fees_excl_tax] += total_fees_excl_tax.call(line_item) + summary_hash[:total_tax_on_fees] += total_tax_on_fees.call(line_item) + summary_hash[:total_tax] += total_tax.call(line_item) + summary_hash[:total] += total.call(line_item) + end + + summary_hash + 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, + ], + 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 00000000000..2405a5442aa --- /dev/null +++ b/lib/reporting/reports/suppliers/helpers/columns_helper.rb @@ -0,0 +1,122 @@ +# frozen_string_literal: true + +module Reporting + module Reports + module Suppliers + module Helpers + module ColumnsHelper + include LineItemsAccessHelper + + def producer + proc { |line_item| supplier(line_item).name } + end + + def producer_address + proc { |line_item| supplier(line_item).address&.full_address } + end + + def producer_abn_acn + proc do |line_items| + supplier = supplier(line_items) + # return nil if both abn and acn are nil so that it can be converted to "none" + [supplier.abn, supplier.acn].compact_blank.join("/").presence + end + end + + def email + proc { |line_item| supplier(line_item).email_address } + end + + def hub + proc { |line_item| distributor(line_item).name } + end + + def hub_address + proc { |line_item| distributor(line_item).address&.full_address } + end + + def hub_contact_email + proc { |line_item| distributor(line_item).email_address } + end + + def order_number + proc { |line_item| order(line_item).number } + end + + def order_date + proc { |line_item| order(line_item).completed_at.to_date } + end + + def order_cycle + proc { |line_item| item_order_cycle(line_item).name } + end + + def order_cycle_start_date + proc { |line_item| item_order_cycle(line_item).orders_open_at.to_date } + end + + def order_cycle_end_date + proc { |line_item| item_order_cycle(line_item).orders_close_at.to_date } + end + + def product + proc { |line_item| variant(line_item).product.name } + end + + def variant_unit_name + proc { |line_item| variant(line_item).full_name } + end + + def quantity + proc { |line_item| line_item.quantity } + end + + def total_excl_fees_and_tax + proc do |line_item| + included_tax = adjustments_by_type(line_item, :tax, included: true) + line_item.amount - included_tax + end + end + + def total_excl_vat + proc do |line_item| + total_fees = adjustments_by_type(line_item, :fees) + total_excl_fees_and_tax.call(line_item) + total_fees + end + end + + def total_fees_excl_tax + proc do |line_item| + included_tax = tax_on_fees(line_item, included: true) + adjustments_by_type(line_item, :fees) - included_tax + end + end + + def total_tax_on_fees + proc { |line_item| tax_on_fees(line_item) + tax_on_fees(line_item, included: true) } + end + + def total_tax + proc do |line_item| + excluded_tax = adjustments_by_type(line_item, :tax) + included_tax = adjustments_by_type(line_item, :tax, included: true) + + excluded_tax + included_tax + end + end + + def total + proc do |line_item| + total_price = total_excl_fees_and_tax.call(line_item) + total_fees = total_fees_excl_tax.call(line_item) + total_fees_tax = total_tax_on_fees.call(line_item) + tax = total_tax.call(line_item) + + total_price + total_fees + total_fees_tax + 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 00000000000..3152cac7dd0 --- /dev/null +++ b/lib/reporting/reports/suppliers/helpers/line_items_access_helper.rb @@ -0,0 +1,66 @@ +# frozen_string_literal: true + +module Reporting + module Reports + module Suppliers + module Helpers + module LineItemsAccessHelper + def variant(line_item) + line_item.variant + end + + def order(line_item) + line_item.order + end + + def supplier(line_item) + variant(line_item).supplier + end + + def distributor(line_item) + order(line_item).distributor + end + + def item_order_cycle(line_item) + line_item.order_cycle + end + + def suppliers_adjustments(line_item, adjustment_type = 'EnterpriseFee') + adjustments = line_item.adjustments + return adjustments.tax if adjustment_type == 'Spree::TaxRate' + + supplier_id = line_item.supplier_id + adjustments.enterprise_fee.select do |adjustment| + label = adjustment.label + adjustment_enterprise_id = adjustment.originator.enterprise_id + label.include?('supplier') && adjustment_enterprise_id == supplier_id + end + end + + def adjustments_by_type(line_item, type, included: false) + total_amount = 0.0 + adjustment_type = type == :tax ? 'Spree::TaxRate' : 'EnterpriseFee' + suppliers_adjustments(line_item, adjustment_type).each do |adjustment| + amount = included == adjustment.included ? adjustment.amount : 0.0 + total_amount += amount + end + + total_amount + end + + def tax_on_fees(line_item, included: false) + total_amount = 0.0 + suppliers_adjustments(line_item).each do |adjustment| + adjustment.adjustments.tax.each do |fee_adjustment| + amount = included == fee_adjustment.included ? fee_adjustment.amount : 0.0 + total_amount += amount + end + end + + total_amount + end + end + end + end + end +end diff --git a/spec/lib/reports/suppliers/pay_your_suppliers_report_spec.rb b/spec/lib/reports/suppliers/pay_your_suppliers_report_spec.rb new file mode 100644 index 00000000000..9494342ee06 --- /dev/null +++ b/spec/lib/reports/suppliers/pay_your_suppliers_report_spec.rb @@ -0,0 +1,105 @@ +# frozen_string_literal: true + +require "spec_helper" + +RSpec.describe "Pay Your Suppliers Report" do + let(:hub) { create(:distributor_enterprise) } + let(:order_cycle) { create(:open_order_cycle, distributors: [hub]) } + let(:product) { order.products.first } + let(:variant) { product.variants.first } + let(:supplier) { variant.supplier } + let(:current_user) { hub.owner } + let!(:order) do + create(:completed_order_with_totals, distributor: hub, order_cycle:, line_items_count: 1) + end + let(:params) { { display_summary_row: true } } + let(:report) { Reporting::Reports::Suppliers::Base.new(current_user, { q: params }) } + let(:report_table_rows) { report.rows } + + context "without fees and taxes" do + it "Generates the report" do + expect(report_table_rows.length).to eq(1) + table_row = report_table_rows.first + + expect(table_row.producer).to eq(supplier.name) + expect(table_row.producer_address).to eq(supplier.address.full_address) + expect(table_row.producer_abn_acn).to eq("none") + expect(table_row.email).to eq("none") + expect(table_row.hub).to eq(hub.name) + expect(table_row.hub_address).to eq(hub.address.full_address) + expect(table_row.hub_contact_email).to eq("none") + expect(table_row.order_number).to eq(order.number) + expect(table_row.order_date).to eq(order.completed_at.to_date.to_s) + expect(table_row.order_cycle).to eq(order_cycle.name) + expect(table_row.order_cycle_start_date).to eq( + order_cycle.orders_open_at.to_date.to_s + ) + expect(table_row.order_cycle_end_date).to eq(order_cycle.orders_close_at.to_date.to_s) + expect(table_row.product).to eq(product.name) + expect(table_row.variant_unit_name).to eq(variant.full_name) + expect(table_row.quantity).to eq(1) + expect(table_row.total_excl_fees_and_tax.to_f).to eq(10.0) + expect(table_row.total_excl_vat.to_f).to eq(10.0) + expect(table_row.total_fees_excl_tax.to_f).to eq(0.0) + expect(table_row.total_tax_on_fees.to_f).to eq(0.0) + expect(table_row.total_tax.to_f).to eq(0.0) + expect(table_row.total.to_f).to eq(10.0) + end + end + + context "with taxes and fees" do + let(:line_item) { order.line_items.first } + let(:tax_category) { + create( + :tax_category, + tax_rates: [ + create( + :tax_rate, + zone: create(:zone_with_member) + ) + ] + ) + } + let!(:enterprise_fee) do + create( + :enterprise_fee, + enterprise: supplier, + fee_type: 'sales', + amount: 0.1, + tax_category: + ) + end + + before do + # Prepare order or line_item to have respective tax adjustments + hub.update!(charges_sales_tax: true) + supplier.update!(charges_sales_tax: true) + line_item.variant.update!(tax_category:) + line_item.copy_tax_category + + exchange = order_cycle.exchanges.take + exchange.enterprise_fees << enterprise_fee + exchange.exchange_variants.build(variant: line_item.variant) + exchange.incoming = true + exchange.save! + + OpenFoodNetwork::EnterpriseFeeCalculator + .new(hub, order_cycle) + .create_line_item_adjustments_for(line_item) + + order.create_tax_charge! + end + + it "Generates the report" do + expect(report_table_rows.length).to eq(1) + table_row = report_table_rows.first + + expect(table_row.total_excl_fees_and_tax.to_f).to eq(10.0) + expect(table_row.total_excl_vat.to_f).to eq(10.1) + expect(table_row.total_fees_excl_tax.to_f).to eq(0.1) + expect(table_row.total_tax_on_fees.to_f).to eq(0.01) + expect(table_row.total_tax.to_f).to eq(1.0) + expect(table_row.total.to_f).to eq(11.11) + end + end +end diff --git a/spec/system/admin/reports/pay_your_suppliers_spec.rb b/spec/system/admin/reports/pay_your_suppliers_spec.rb new file mode 100644 index 00000000000..a7b7ac99667 --- /dev/null +++ b/spec/system/admin/reports/pay_your_suppliers_spec.rb @@ -0,0 +1,141 @@ +# frozen_string_literal: true + +require "system_helper" + +RSpec.describe "Pay Your Suppliers Report" do + include ReportsHelper + + let(:owner) { create(:user) } + let(:hub1) { create(:enterprise, owner:) } + let(:order_cycle1) { create(:open_order_cycle, distributors: [hub1]) } + let!(:order1) do + create( + :completed_order_with_totals, + distributor: hub1, + order_cycle: order_cycle1, + line_items_count: 2 + ) + end + + let(:hub2) { create(:enterprise, owner:) } + let(:product2) { order1.products.first } + let(:variant2) { product2.variants.first } + let(:supplier2) { variant2.supplier } + let(:order_cycle2) { create(:open_order_cycle, distributors: [hub2]) } + let!(:order2) do + create( + :completed_order_with_totals, + distributor: hub2, + order_cycle: order_cycle2, + line_items_count: 3 + ) + end + + before do + login_as owner + visit admin_reports_path + end + + context "on Reports page" do + it "should generate 'Pay Your Suppliers' report" do + click_on 'Pay your suppliers' + expect(page).to have_button("Go") + run_report + + expect(page.find("table.report__table thead tr").text).to have_content([ + "Producer", + "Producer Address", + "Producer ABN/ACN", + "Email", + "Hub", + "Hub Address", + "Hub Contact Email", + "Order number", + "Order date", + "Order Cycle", + "OC Start Date", + "OC End Date", + "Product", + "Variant Unit Name", + "Quantity", + "Total excl. fees and tax ($)", + "Total excl. tax ($)", + "Total fees excl. tax ($)", + "Total tax on fees ($)", + "Total Tax ($)", + "Total ($)" + ].join(" ")) + + lines = page.all('table.report__table tbody tr').map(&:text) + # 5 line_item rows + 1 summary row = 6 rows + expect(lines.count).to be(6) + + hub1_rows = lines.select { |line| line.include?(hub1.name) } + order1.line_items.each_with_index do |line_item, index| + variant = line_item.variant + supplier = line_item.supplier + product = line_item.variant.product + line = hub1_rows[index] + + expect(line).to have_content([ + supplier.name, + supplier.address.full_address, + "none", + "none", + hub1.name, + hub1.address.full_address, + "none", + order1.number, + order1.completed_at.to_date.to_s, + order_cycle1.name, + order_cycle1.orders_open_at.to_date.to_s, + order_cycle1.orders_close_at.to_date.to_s, + product.name, + variant.full_name, + 1, + 10.0, + 10.0, + 0.0, + 0.0, + 0.0, + 10.0, + ].compact.join(" ")) + end + + hub2_rows = lines.select { |line| line.include?(hub2.name) } + order2.line_items.each_with_index do |line_item, index| + variant = line_item.variant + supplier = line_item.supplier + product = line_item.variant.product + line = hub2_rows[index] + + expect(line).to have_content([ + supplier.name, + supplier.address.full_address, + "none", + "none", + hub2.name, + hub2.address.full_address, + "none", + order2.number, + order2.completed_at.to_date.to_s, + order_cycle2.name, + order_cycle2.orders_open_at.to_date.to_s, + order_cycle2.orders_close_at.to_date.to_s, + product.name, + variant.full_name, + 1, + 10.0, + 10.0, + 0.0, + 0.0, + 0.0, + 10.0, + ].compact.join(" ")) + end + + # summary row + expect(lines.last).to have_content("TOTAL 50.0 50.0 0.0 0.0 0.0 50.0") + end + end +end