mirror of
https://github.com/openfoodfoundation/openfoodnetwork
synced 2026-01-24 20:36:49 +00:00
sales_tax_totals_by_order report
This commit is contained in:
@@ -0,0 +1,13 @@
|
||||
.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})
|
||||
|
||||
.row
|
||||
.alpha.two.columns= label_tag nil, t(:report_customers)
|
||||
.omega.fourteen.columns
|
||||
= f.select(:customer_id_in, customer_email_options(@data.order_customers), {selected: params.dig(:q, :customer_id_in)}, {class: "select2 fullwidth", multiple: true})
|
||||
@@ -1416,6 +1416,7 @@ en:
|
||||
payment_methods: Payment Methods Report
|
||||
delivery: Delivery Report
|
||||
sales_tax_totals_by_producer: Sales Tax Totals By Producer
|
||||
sales_tax_totals_by_order: Sales Tax Totals By Order
|
||||
tax_types: Tax Types
|
||||
tax_rates: Tax Rates
|
||||
pack_by_customer: Pack By Customer
|
||||
|
||||
@@ -71,7 +71,8 @@ module Reporting
|
||||
[
|
||||
[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_producer"), :sales_tax_totals_by_producer],
|
||||
[i18n_translate("sales_tax_totals_by_order"), :sales_tax_totals_by_order]
|
||||
]
|
||||
end
|
||||
|
||||
|
||||
194
lib/reporting/reports/sales_tax/sales_tax_totals_by_order.rb
Normal file
194
lib/reporting/reports/sales_tax/sales_tax_totals_by_order.rb
Normal file
@@ -0,0 +1,194 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# rubocop:disable Metrics/ClassLength
|
||||
module Reporting
|
||||
module Reports
|
||||
module SalesTax
|
||||
class SalesTaxTotalsByOrder < Base
|
||||
def search
|
||||
report_line_items.orders
|
||||
end
|
||||
|
||||
def order_permissions
|
||||
@order_permissions ||= ::Permissions::Order.new(user, ransack_params)
|
||||
end
|
||||
|
||||
def report_line_items
|
||||
# Needed to filter by supplier_id
|
||||
@report_line_items ||= Reporting::LineItems.new(order_permissions, params)
|
||||
end
|
||||
|
||||
def query_result
|
||||
# We'll group the line items by
|
||||
# [tax_rate_id, order_id]
|
||||
orders = report_line_items.list.map(&:order).uniq
|
||||
orders.flat_map(&join_tax_rate)
|
||||
.group_by(&group_key)
|
||||
.map(&change_root_to_order)
|
||||
end
|
||||
|
||||
def join_tax_rate
|
||||
proc do |order|
|
||||
tax_rate_ids = order.all_adjustments.tax.pluck("distinct(originator_id)")
|
||||
tax_rate_ids << nil if tax_rate_ids.empty?
|
||||
tax_rate_ids.map do |tax_rate_id|
|
||||
{
|
||||
tax_rate_id: tax_rate_id,
|
||||
order: order
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def group_key
|
||||
proc do |hash|
|
||||
[
|
||||
hash[:tax_rate_id],
|
||||
hash[:order].id
|
||||
]
|
||||
end
|
||||
end
|
||||
|
||||
def change_root_to_order
|
||||
proc do |key, value|
|
||||
[key, value.first[:order]]
|
||||
end
|
||||
end
|
||||
|
||||
def columns
|
||||
{
|
||||
distributor: :distributor,
|
||||
order_cycle: :order_cycle,
|
||||
order_number: :order_number,
|
||||
tax_category: :tax_category,
|
||||
tax_rate_name: :tax_rate_name,
|
||||
tax_rate: :tax_rate_amount,
|
||||
total_excl_tax: :total_excl_tax,
|
||||
tax: :tax_rate_total,
|
||||
total_incl_tax: :total_incl_tax,
|
||||
first_name: :first_name,
|
||||
last_name: :last_name,
|
||||
code: :code,
|
||||
email: :email,
|
||||
}
|
||||
end
|
||||
|
||||
def columns_format
|
||||
{
|
||||
tax_rate: :percentage
|
||||
}
|
||||
end
|
||||
|
||||
def rules
|
||||
[
|
||||
{
|
||||
group_by: :distributor,
|
||||
},
|
||||
{
|
||||
group_by: :order_cycle,
|
||||
},
|
||||
{
|
||||
group_by: :order_number,
|
||||
summary_row: proc do |_key, items, _rows|
|
||||
order = items.first.second
|
||||
{
|
||||
total_excl_tax: order.total - order.total_tax,
|
||||
tax: order.total_tax,
|
||||
total_incl_tax: order.total,
|
||||
first_name: order.customer&.first_name,
|
||||
last_name: order.customer&.last_name,
|
||||
code: order.customer&.code,
|
||||
email: order.email
|
||||
}
|
||||
end
|
||||
}
|
||||
]
|
||||
end
|
||||
|
||||
def distributor(query_result_row)
|
||||
order(query_result_row).distributor&.name
|
||||
end
|
||||
|
||||
def order_cycle(query_result_row)
|
||||
order(query_result_row).order_cycle&.name
|
||||
end
|
||||
|
||||
def order_number(query_result_row)
|
||||
order(query_result_row).number
|
||||
end
|
||||
|
||||
def tax_category(query_result_row)
|
||||
tax_rate(query_result_row)&.tax_category&.name
|
||||
end
|
||||
|
||||
def tax_rate_name(query_result_row)
|
||||
tax_rate(query_result_row)&.name
|
||||
end
|
||||
|
||||
def tax_rate_amount(query_result_row)
|
||||
tax_rate(query_result_row)&.amount
|
||||
end
|
||||
|
||||
def total_excl_tax(query_result_row)
|
||||
order(query_result_row).total - order(query_result_row).total_tax
|
||||
end
|
||||
|
||||
def tax_rate_total(query_result_row)
|
||||
order(query_result_row).all_adjustments
|
||||
.tax
|
||||
.where(originator_id: tax_rate_id(query_result_row))
|
||||
.pluck('sum(amount)').first || 0
|
||||
end
|
||||
|
||||
def total_incl_tax(query_result_row)
|
||||
order(query_result_row).total -
|
||||
order(query_result_row).total_tax +
|
||||
tax_rate_total(query_result_row)
|
||||
end
|
||||
|
||||
def first_name(query_result_row)
|
||||
order(query_result_row).customer&.first_name
|
||||
end
|
||||
|
||||
def last_name(query_result_row)
|
||||
order(query_result_row).customer&.last_name
|
||||
end
|
||||
|
||||
def code(query_result_row)
|
||||
order(query_result_row).customer&.code
|
||||
end
|
||||
|
||||
def email(query_result_row)
|
||||
order(query_result_row).email
|
||||
end
|
||||
|
||||
def tax_rate(query_result_row)
|
||||
targeted_tax_rate_id = tax_rate_id(query_result_row)
|
||||
tax_rates(query_result_row).find do |tax_rate|
|
||||
tax_rate.id == targeted_tax_rate_id
|
||||
end
|
||||
end
|
||||
|
||||
def tax_rate_id(query_result_row)
|
||||
key(query_result_row).first
|
||||
end
|
||||
|
||||
def tax_rates(query_result_row)
|
||||
order(query_result_row).all_adjustments
|
||||
.tax
|
||||
.select("distinct(originator_id)", "originator_type")
|
||||
.map(&:originator)
|
||||
end
|
||||
|
||||
def key(query_result_row)
|
||||
query_result_row.first
|
||||
end
|
||||
|
||||
def order(query_result_row)
|
||||
query_result_row.second
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
# rubocop:enable Metrics/ClassLength
|
||||
@@ -0,0 +1,424 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'system_helper'
|
||||
|
||||
describe "Sales Tax Totals By order" do
|
||||
# Scenarion 1: added tax
|
||||
# 1 producer
|
||||
# 1 distributor
|
||||
# product that costs 100$
|
||||
# shipping costs 10$
|
||||
# the packaging cost is 5$
|
||||
# 1 order with 1 line item
|
||||
# the line item match 2 tax rates: country (2.5%) and state (1.5%)
|
||||
let!(:table_header){
|
||||
[
|
||||
"Distributor",
|
||||
"Order Cycle",
|
||||
"Order Number",
|
||||
"Tax Category",
|
||||
"Tax Rate Name",
|
||||
"Tax Rate",
|
||||
"Total excl. Tax ($)",
|
||||
"Tax",
|
||||
"Total incl. Tax ($)",
|
||||
"First Name",
|
||||
"Last Name",
|
||||
"Code",
|
||||
"Email"
|
||||
].join(" ").upcase
|
||||
}
|
||||
let!(:state_zone){ create(:zone_with_state_member) }
|
||||
let!(:country_zone){ create(:zone_with_member) }
|
||||
let!(:tax_category){ create(:tax_category, name: 'tax_category') }
|
||||
let!(:state_tax_rate){
|
||||
create(:tax_rate, zone: state_zone, tax_category: tax_category,
|
||||
name: 'State', amount: 0.015)
|
||||
}
|
||||
let!(:country_tax_rate){
|
||||
create(:tax_rate, zone: country_zone, tax_category: tax_category,
|
||||
name: 'Country', amount: 0.025)
|
||||
}
|
||||
let!(:ship_address){
|
||||
create(:ship_address,
|
||||
state: state_zone.members.first.zoneable,
|
||||
country: country_zone.members.first.zoneable)
|
||||
}
|
||||
|
||||
let!(:variant){ create(:variant) }
|
||||
let!(:product){ variant.product }
|
||||
let!(:supplier){ create(:supplier_enterprise, name: 'Supplier', charges_sales_tax: true) }
|
||||
let!(:distributor){
|
||||
create(:distributor_enterprise_with_tax, name: 'Distributor', charges_sales_tax: true)
|
||||
}
|
||||
let!(:distributor_fee){
|
||||
create(:enterprise_fee, :flat_rate, amount: 5,
|
||||
tax_category_id: tax_category.id,
|
||||
enterprise_id: distributor.id)
|
||||
}
|
||||
let!(:payment_method){ create(:payment_method, :flat_rate) }
|
||||
let!(:shipping_method){
|
||||
create(:shipping_method, :flat_rate, amount: 10, tax_category_id: tax_category.id)
|
||||
}
|
||||
|
||||
let!(:order){ create(:order_with_distributor, distributor: distributor) }
|
||||
let!(:order_cycle){
|
||||
create(:simple_order_cycle, name: 'oc1', suppliers: [supplier], distributors: [distributor],
|
||||
variants: [variant])
|
||||
}
|
||||
let!(:customer1){
|
||||
create(:customer, enterprise: create(:enterprise),
|
||||
user: create(:user),
|
||||
first_name: 'cfname', last_name: 'clname', code: 'ABC123')
|
||||
}
|
||||
|
||||
let(:admin){ create(:admin_user) }
|
||||
|
||||
before do
|
||||
order_cycle.cached_outgoing_exchanges.first.enterprise_fees << distributor_fee
|
||||
distributor.shipping_methods << shipping_method
|
||||
distributor.payment_methods << payment_method
|
||||
|
||||
product.update!(
|
||||
tax_category_id: tax_category.id,
|
||||
supplier_id: supplier.id
|
||||
)
|
||||
|
||||
order.update!(
|
||||
number: 'ORDER_NUMBER_1',
|
||||
order_cycle_id: order_cycle.id,
|
||||
ship_address_id: ship_address.id,
|
||||
customer_id: customer1.id,
|
||||
email: 'order1@example.com'
|
||||
)
|
||||
order.line_items.create(variant: variant, quantity: 1, price: 100)
|
||||
end
|
||||
|
||||
context 'added tax' do
|
||||
before do
|
||||
# the enterprise fees can be known only when the user selects the variants
|
||||
# we'll need to create them by calling recreate_all_fees!
|
||||
order.recreate_all_fees!
|
||||
OrderWorkflow.new(order).complete!
|
||||
end
|
||||
|
||||
it "generates the report" do
|
||||
login_as admin
|
||||
visit admin_reports_path
|
||||
click_on I18n.t("admin.reports.sales_tax_totals_by_order")
|
||||
|
||||
expect(page).to have_button("Go")
|
||||
click_on "Go"
|
||||
expect(page.find("table.report__table thead tr").text).to have_content(table_header)
|
||||
|
||||
expect(page.find("table.report__table tbody").text).to have_content([
|
||||
"Distributor",
|
||||
"oc1",
|
||||
"ORDER_NUMBER_1",
|
||||
"tax_category",
|
||||
"State",
|
||||
"1.5 %",
|
||||
"115.0",
|
||||
"1.73",
|
||||
"116.73",
|
||||
"cfname",
|
||||
"clname",
|
||||
"ABC123",
|
||||
"order1@example.com"
|
||||
].join(" "))
|
||||
|
||||
expect(page.find("table.report__table tbody").text).to have_content([
|
||||
"Distributor",
|
||||
"oc1",
|
||||
"ORDER_NUMBER_1",
|
||||
"tax_category",
|
||||
"Country",
|
||||
"2.5 %",
|
||||
"115.0",
|
||||
"2.88",
|
||||
"117.88",
|
||||
"cfname",
|
||||
"clname",
|
||||
"ABC123",
|
||||
"order1@example.com"
|
||||
].join(" "))
|
||||
|
||||
expect(page.find("table.report__table tbody").text).to have_content([
|
||||
"TOTAL",
|
||||
"115.0",
|
||||
"4.61",
|
||||
"119.61",
|
||||
"cfname",
|
||||
"clname",
|
||||
"ABC123",
|
||||
"order1@example.com"
|
||||
].join(" "))
|
||||
end
|
||||
end
|
||||
|
||||
context 'included tax' do
|
||||
before do
|
||||
state_tax_rate.update!({ included_in_price: true })
|
||||
country_tax_rate.update!({ included_in_price: true })
|
||||
|
||||
order.recreate_all_fees!
|
||||
OrderWorkflow.new(order).complete!
|
||||
end
|
||||
it "generates the report" do
|
||||
login_as admin
|
||||
visit admin_reports_path
|
||||
click_on I18n.t("admin.reports.sales_tax_totals_by_order")
|
||||
|
||||
expect(page).to have_button("Go")
|
||||
click_on "Go"
|
||||
expect(page.find("table.report__table thead tr").text).to have_content(table_header)
|
||||
|
||||
expect(page.find("table.report__table tbody").text).to have_content([
|
||||
"Distributor",
|
||||
"oc1",
|
||||
"ORDER_NUMBER_1",
|
||||
"tax_category",
|
||||
"State",
|
||||
"1.5 %",
|
||||
"110.5",
|
||||
"1.7",
|
||||
"112.2",
|
||||
"cfname",
|
||||
"clname",
|
||||
"ABC123",
|
||||
"order1@example.com"
|
||||
].join(" "))
|
||||
|
||||
expect(page.find("table.report__table tbody").text).to have_content([
|
||||
"Distributor",
|
||||
"oc1",
|
||||
"ORDER_NUMBER_1",
|
||||
"tax_category",
|
||||
"Country",
|
||||
"2.5 %",
|
||||
"110.5",
|
||||
"2.8",
|
||||
"113.3",
|
||||
"cfname",
|
||||
"clname",
|
||||
"ABC123",
|
||||
"order1@example.com"
|
||||
].join(" "))
|
||||
|
||||
expect(page.find("table.report__table tbody").text).to have_content([
|
||||
"TOTAL",
|
||||
"110.5",
|
||||
"4.5",
|
||||
"115.0",
|
||||
"cfname",
|
||||
"clname",
|
||||
"ABC123",
|
||||
"order1@example.com"
|
||||
].join(" "))
|
||||
end
|
||||
end
|
||||
|
||||
context 'should filter by customer' do
|
||||
let!(:order2){ create(:order_with_distributor, distributor: distributor) }
|
||||
let!(:customer2){ create(:customer, enterprise: create(:enterprise), user: create(:user)) }
|
||||
let!(:customer_email_dropdown_selector){ "#s2id_q_customer_id_in" }
|
||||
let!(:table_raw_selector){ "table.report__table tbody tr" }
|
||||
let(:customer1_country_tax_rate_row){
|
||||
[
|
||||
"Distributor",
|
||||
"oc1",
|
||||
"ORDER_NUMBER_1",
|
||||
"tax_category",
|
||||
"Country",
|
||||
"2.5 %",
|
||||
"115.0",
|
||||
"2.88",
|
||||
"117.88",
|
||||
"cfname",
|
||||
"clname",
|
||||
"ABC123",
|
||||
"order1@example.com"
|
||||
].join(" ")
|
||||
}
|
||||
let(:customer1_state_tax_rate_row){
|
||||
[
|
||||
"Distributor",
|
||||
"oc1",
|
||||
"ORDER_NUMBER_1",
|
||||
"tax_category",
|
||||
"State",
|
||||
"1.5 %",
|
||||
"115.0",
|
||||
"1.73",
|
||||
"116.73",
|
||||
"cfname",
|
||||
"clname",
|
||||
"ABC123",
|
||||
"order1@example.com"
|
||||
].join(" ")
|
||||
}
|
||||
let(:customer1_summary_row){
|
||||
[
|
||||
"TOTAL",
|
||||
"115.0",
|
||||
"4.61",
|
||||
"119.61",
|
||||
"cfname",
|
||||
"clname",
|
||||
"ABC123",
|
||||
"order1@example.com"
|
||||
].join(" ")
|
||||
}
|
||||
|
||||
let(:customer2_country_tax_rate_row){
|
||||
[
|
||||
"Distributor",
|
||||
"oc1",
|
||||
"ORDER_NUMBER_2",
|
||||
"tax_category",
|
||||
"Country",
|
||||
"2.5 %",
|
||||
"215.0",
|
||||
"5.38",
|
||||
"220.38",
|
||||
"c2fname",
|
||||
"c2lname",
|
||||
"DEF456",
|
||||
"order2@example.com"
|
||||
].join(" ")
|
||||
}
|
||||
let(:customer2_state_tax_rate_row){
|
||||
[
|
||||
"Distributor",
|
||||
"oc1",
|
||||
"ORDER_NUMBER_2",
|
||||
"tax_category",
|
||||
"State",
|
||||
"1.5 %",
|
||||
"215.0",
|
||||
"3.23",
|
||||
"218.23",
|
||||
"c2fname",
|
||||
"c2lname",
|
||||
"DEF456",
|
||||
"order2@example.com"
|
||||
].join(" ")
|
||||
}
|
||||
let(:customer2_summary_row){
|
||||
[
|
||||
"TOTAL",
|
||||
"215.0",
|
||||
"8.61",
|
||||
"223.61",
|
||||
"c2fname",
|
||||
"c2lname",
|
||||
"DEF456",
|
||||
"order2@example.com"
|
||||
].join(" ")
|
||||
}
|
||||
|
||||
before do
|
||||
order.recreate_all_fees!
|
||||
OrderWorkflow.new(order).complete!
|
||||
|
||||
customer2.update!({ first_name: 'c2fname', last_name: 'c2lname', code: 'DEF456' })
|
||||
order2.line_items.create({ variant: variant, quantity: 1, price: 200 })
|
||||
order2.update!({
|
||||
order_cycle_id: order_cycle.id,
|
||||
ship_address_id: customer2.bill_address_id,
|
||||
customer_id: customer2.id,
|
||||
number: 'ORDER_NUMBER_2',
|
||||
email: 'order2@example.com'
|
||||
})
|
||||
order2.recreate_all_fees!
|
||||
OrderWorkflow.new(order2).complete!
|
||||
|
||||
login_as admin
|
||||
visit admin_reports_path
|
||||
click_on I18n.t("admin.reports.sales_tax_totals_by_order")
|
||||
end
|
||||
|
||||
it "should load all the orders" do
|
||||
expect(page).to have_button("Go")
|
||||
click_on "Go"
|
||||
|
||||
expect(page.find("table.report__table thead tr").text).to have_content(table_header)
|
||||
expect(
|
||||
page.find("table.report__table tbody").text
|
||||
).to have_content(customer1_country_tax_rate_row)
|
||||
expect(
|
||||
page.find("table.report__table tbody").text
|
||||
).to have_content(customer1_state_tax_rate_row)
|
||||
expect(page.find("table.report__table tbody").text).to have_content(customer1_summary_row)
|
||||
expect(
|
||||
page.find("table.report__table tbody").text
|
||||
).to have_content(customer2_country_tax_rate_row)
|
||||
expect(
|
||||
page.find("table.report__table tbody").text
|
||||
).to have_content(customer2_state_tax_rate_row)
|
||||
expect(page.find("table.report__table tbody").text).to have_content(customer2_summary_row)
|
||||
expect(page).to have_selector(table_raw_selector, count: 6)
|
||||
end
|
||||
|
||||
it "should filter customer1 orders" do
|
||||
page.find(customer_email_dropdown_selector).click
|
||||
find('li', text: customer1.email).click
|
||||
|
||||
expect(page).to have_button("Go")
|
||||
click_on "Go"
|
||||
|
||||
expect(page.find("table.report__table thead tr").text).to have_content(table_header)
|
||||
expect(
|
||||
page.find("table.report__table tbody").text
|
||||
).to have_content(customer1_country_tax_rate_row)
|
||||
expect(
|
||||
page.find("table.report__table tbody").text
|
||||
).to have_content(customer1_state_tax_rate_row)
|
||||
expect(page.find("table.report__table tbody").text).to have_content(customer1_summary_row)
|
||||
expect(page).to have_selector(table_raw_selector, count: 3)
|
||||
end
|
||||
|
||||
it "should filter customer2 orders" do
|
||||
page.find(customer_email_dropdown_selector).click
|
||||
find('li', text: customer2.email).click
|
||||
|
||||
expect(page).to have_button("Go")
|
||||
click_on "Go"
|
||||
|
||||
expect(page.find("table.report__table thead tr").text).to have_content(table_header)
|
||||
expect(
|
||||
page.find("table.report__table tbody").text
|
||||
).to have_content(customer2_country_tax_rate_row)
|
||||
expect(
|
||||
page.find("table.report__table tbody").text
|
||||
).to have_content(customer2_state_tax_rate_row)
|
||||
expect(page.find("table.report__table tbody").text).to have_content(customer2_summary_row)
|
||||
expect(page).to have_selector(table_raw_selector, count: 3)
|
||||
end
|
||||
|
||||
it "should filter customer1 and customer2 orders" do
|
||||
page.find(customer_email_dropdown_selector).click
|
||||
find('li', text: customer1.email).click
|
||||
page.find(customer_email_dropdown_selector).click
|
||||
find('li', text: customer2.email).click
|
||||
click_on "Go"
|
||||
|
||||
expect(page.find("table.report__table thead tr").text).to have_content(table_header)
|
||||
expect(
|
||||
page.find("table.report__table tbody").text
|
||||
).to have_content(customer1_country_tax_rate_row)
|
||||
expect(
|
||||
page.find("table.report__table tbody").text
|
||||
).to have_content(customer1_state_tax_rate_row)
|
||||
expect(page.find("table.report__table tbody").text).to have_content(customer1_summary_row)
|
||||
expect(
|
||||
page.find("table.report__table tbody").text
|
||||
).to have_content(customer2_country_tax_rate_row)
|
||||
expect(
|
||||
page.find("table.report__table tbody").text
|
||||
).to have_content(customer2_state_tax_rate_row)
|
||||
expect(page.find("table.report__table tbody").text).to have_content(customer2_summary_row)
|
||||
expect(page).to have_selector(table_raw_selector, count: 6)
|
||||
end
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user