mirror of
https://github.com/openfoodfoundation/openfoodnetwork
synced 2026-01-24 20:36:49 +00:00
Capybara helpers already wait for the content to show up (and we already have a default of 10 seconds configured), so I don't think waiting more is actually the problem in these specs. But if we wanted to wait more, I think it's better to pass the `:wait` option to capybara matchers, because that's a "maximum waiting value" but we'll still proceed earlier if the content shows up. Using the same idea, I changed the positive assertions to happen first, because negative assertions do spend "max wait time" waiting, while positive assertions only wait until the content shows up.
767 lines
28 KiB
Ruby
767 lines
28 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
require "system_helper"
|
|
|
|
RSpec.describe '
|
|
As an administrator
|
|
I want numbers, all the numbers!
|
|
' do
|
|
include WebHelper
|
|
include AuthenticationHelper
|
|
|
|
context "Permissions for different reports" do
|
|
context "As an enterprise user" do
|
|
let(:user) do
|
|
create(:user, enterprises: [create(:distributor_enterprise)])
|
|
end
|
|
|
|
it "does not show super admin only report" do
|
|
login_as user
|
|
visit admin_reports_path
|
|
expect(page).not_to have_content "Users & Enterprises"
|
|
end
|
|
end
|
|
|
|
context "As an admin user" do
|
|
it "shows the super admin only report" do
|
|
login_to_admin_section
|
|
click_link "Reports"
|
|
expect(page).to have_content "Users & Enterprises"
|
|
end
|
|
end
|
|
end
|
|
|
|
describe "Background processing" do
|
|
it "renders UTF-8 characters" do
|
|
# We had a problem when UTF-8 was in the page and the report because
|
|
# ActiveStorage read ASCII.
|
|
# - https://github.com/openfoodfoundation/openfoodnetwork/issues/10758
|
|
#
|
|
# Create order to inject special characters:
|
|
order = create(:completed_order_with_totals)
|
|
|
|
# Render special characters in the page (filter option):
|
|
order.distributor.update!(name: "Späti")
|
|
|
|
# Render special character within the report:
|
|
order.billing_address.update!(lastname: "Müller")
|
|
|
|
# Run the report:
|
|
login_as_admin
|
|
visit admin_report_path(report_type: :customers)
|
|
run_report
|
|
expect(page).to have_content "Späti"
|
|
expect(page).to have_content "First Name Last Name Billing Address Email"
|
|
expect(page).to have_content "Müller"
|
|
end
|
|
|
|
it "requires confirmation to display big reports" do
|
|
# Mock data is much faster and accurate than creating many orders:
|
|
allow_any_instance_of(Reporting::Reports::Customers::Base)
|
|
.to receive(:columns).and_return(
|
|
{
|
|
first_name: proc { |_| "Little Bobby Tables " * (10**5) }, # 2 MB
|
|
}
|
|
)
|
|
|
|
# We still need an order for the report to render a row:
|
|
create(:completed_order_with_totals)
|
|
|
|
login_as_admin
|
|
visit admin_report_path(report_type: :customers)
|
|
run_report
|
|
|
|
expect(page).to have_content "This report is big"
|
|
expect(page).not_to have_content "Little Bobby Tables"
|
|
|
|
click_on "Display anyway"
|
|
expect(page).to have_content "First Name"
|
|
expect(page).to have_content "Little Bobby Tables"
|
|
end
|
|
|
|
it "displays a friendly timeout message and offers download" do
|
|
login_as_admin
|
|
visit admin_report_path(report_type: :customers)
|
|
stub_const("ReportJob::NOTIFICATION_TIME", 0)
|
|
|
|
run_report
|
|
|
|
# We also get an email.
|
|
perform_enqueued_jobs(only: ActionMailer::MailDeliveryJob)
|
|
email = ActionMailer::Base.deliveries.last
|
|
expect(email.body).to have_link(
|
|
"customers",
|
|
href: %r"^http://.*/rails/active_storage/disk/.*/customers_[0-9]+\.html$"
|
|
)
|
|
|
|
# ActiveStorage links usually expire after 5 minutes.
|
|
# But we want a longer expiry in emailed links.
|
|
parsed_email = Capybara::Node::Simple.new(email.body.to_s)
|
|
report_link = parsed_email.find(:link, "customers")[:href]
|
|
content = URI.parse(report_link).read
|
|
expect(content).to match "<th>\nFirst Name\n</th>"
|
|
|
|
# Let's also check the expiry of the emailed link:
|
|
travel(3.days) do
|
|
content = URI.parse(report_link).read
|
|
expect(content).to match "<th>\nFirst Name\n</th>"
|
|
end
|
|
|
|
# The link should still expire though:
|
|
travel(3.months) do
|
|
expect { URI.parse(report_link).read }
|
|
.to raise_error OpenURI::HTTPError, "404 Not Found"
|
|
end
|
|
end
|
|
|
|
it "allows the report to finish before the loading screen is rendered" do
|
|
login_as_admin
|
|
visit admin_report_path(report_type: :customers)
|
|
|
|
# The controller wants to execute the ReportJob in the background.
|
|
# But we change the logic here, execute it immediately and then wait
|
|
# until the report is displayed in the browser.
|
|
# The controller will still be waiting while the browser is receiving
|
|
# the report via web socket.
|
|
breakpoint = Mutex.new
|
|
breakpoint.lock
|
|
expect(ReportJob).to receive(:perform_later) do |**args|
|
|
ReportJob.perform_now(**args)
|
|
breakpoint.synchronize { "continue after unlocked" }
|
|
end
|
|
|
|
click_on "Go"
|
|
|
|
expect(page).to have_content "First Name Last Name Billing Address Email"
|
|
|
|
# Now that we see the report, we need to make sure that it's not replaced
|
|
# by the "loading" spinner when the controller action finishes.
|
|
# Unlocking the breakpoint will continue execution of the controller.
|
|
breakpoint.unlock
|
|
|
|
# Now the controller response will show the loading spinner again and
|
|
# the fallback mechanism will render the report later.
|
|
expect(page).to have_selector ".loading"
|
|
expect(page).to have_content "First Name Last Name Billing Address Email"
|
|
expect(page).not_to have_selector ".loading"
|
|
end
|
|
end
|
|
|
|
describe "Order cycle management report" do
|
|
before do
|
|
login_as_admin
|
|
visit admin_reports_path
|
|
end
|
|
|
|
it "payment method report" do
|
|
click_link "Payment Methods Report"
|
|
run_report
|
|
rows = find("table.report__table").all("thead tr")
|
|
table = rows.map { |r| r.all("th").map { |c| c.text.strip } }
|
|
expect(table.sort).to eq([
|
|
["First Name", "Last Name", "Hub", "Customer Code", "Email", "Phone", "Shipping Method",
|
|
"Payment Method", "Amount", "Balance"]
|
|
].sort)
|
|
end
|
|
|
|
it "delivery report" do
|
|
click_link "Delivery Report"
|
|
run_report
|
|
rows = find("table.report__table").all("thead tr")
|
|
table = rows.map { |r| r.all("th").map { |c| c.text.strip } }
|
|
expect(table.sort).to eq([
|
|
["First Name", "Last Name", "Hub", "Customer Code", "Delivery Address", "Delivery Postcode",
|
|
"Phone", "Shipping Method", "Payment Method", "Amount", "Balance",
|
|
"Temp Controlled Items?", "Special Instructions"]
|
|
].sort)
|
|
end
|
|
end
|
|
|
|
context "for a complete, paid order" do
|
|
let!(:ready_to_ship_order) { create(:order_ready_to_ship) }
|
|
|
|
before do
|
|
login_as_admin
|
|
visit admin_reports_path
|
|
end
|
|
|
|
it "generates the payments reports" do
|
|
click_link 'Payments By Type'
|
|
run_report
|
|
|
|
rows = find("table.report__table").all("thead tr")
|
|
table_headers = rows.map { |r| r.all("th").map { |c| c.text.strip } }
|
|
|
|
expect(table_headers).to eq([
|
|
['Payment State',
|
|
'Distributor',
|
|
'Payment Type',
|
|
"Total (%s)" % currency_symbol]
|
|
])
|
|
|
|
expect(all('table.report__table tbody tr').count).to eq(
|
|
Spree::Payment.where(
|
|
order_id: ready_to_ship_order.id # Total rows should equal number of payments, per order
|
|
).count
|
|
)
|
|
end
|
|
end
|
|
|
|
context "sales tax report" do
|
|
let(:distributor1) {
|
|
create(:distributor_enterprise, with_payment_and_shipping: true, charges_sales_tax: true)
|
|
}
|
|
let(:distributor2) {
|
|
create(:distributor_enterprise, with_payment_and_shipping: true, charges_sales_tax: true)
|
|
}
|
|
let(:user1) { create(:user, enterprises: [distributor1]) }
|
|
let(:user2) { create(:user, enterprises: [distributor2]) }
|
|
let(:shipping_tax_rate) { create(:tax_rate, amount: 0.20, included_in_price: true, zone:) }
|
|
let(:shipping_tax_category) { create(:tax_category, tax_rates: [shipping_tax_rate]) }
|
|
let!(:shipping_method) {
|
|
create(:shipping_method_with, :expensive_name, distributors: [distributor1],
|
|
tax_category: shipping_tax_category)
|
|
}
|
|
let(:enterprise_fee) {
|
|
create(:enterprise_fee, enterprise: user1.enterprises.first,
|
|
tax_category: product2.variants.first.tax_category,
|
|
calculator: Calculator::FlatRate.new(preferred_amount: 120.0))
|
|
}
|
|
let(:order_cycle) {
|
|
create(:simple_order_cycle, coordinator: distributor1,
|
|
coordinator_fees: [enterprise_fee], distributors: [distributor1],
|
|
variants: [product1.variants.first, product2.variants.first])
|
|
}
|
|
|
|
let!(:zone) { create(:zone_with_member) }
|
|
let(:address) { create(:address) }
|
|
let(:order1) {
|
|
create(:order, order_cycle:, distributor: user1.enterprises.first,
|
|
ship_address: address, bill_address: address)
|
|
}
|
|
let(:product1) {
|
|
create(:taxed_product, zone:, price: 12.54, tax_rate_amount: 0, included_in_price: true)
|
|
}
|
|
let(:product2) {
|
|
create(:taxed_product, zone:, price: 500.15, tax_rate_amount: 0.2,
|
|
included_in_price: true)
|
|
}
|
|
|
|
let!(:line_item1) {
|
|
create(:line_item, variant: product1.variants.first, price: 12.54, quantity: 1, order: order1)
|
|
}
|
|
let!(:line_item2) {
|
|
create(:line_item, variant: product2.variants.first, price: 500.15, quantity: 3,
|
|
order: order1)
|
|
}
|
|
|
|
before do
|
|
order1.reload
|
|
while !order1.delivery?
|
|
break unless order1.next!
|
|
end
|
|
|
|
order1.select_shipping_method(shipping_method.id)
|
|
order1.recreate_all_fees!
|
|
order_workflow = Orders::WorkflowService.new(order1)
|
|
order_workflow.advance_to_payment
|
|
|
|
create(:payment, state: "checkout", order: order1, amount: order1.reload.total,
|
|
payment_method: create(:payment_method, distributors: [distributor1]))
|
|
order_workflow.complete!
|
|
|
|
login_as_admin
|
|
visit admin_reports_path
|
|
end
|
|
|
|
it "generate Tax Types reports" do
|
|
click_link "Tax Types"
|
|
run_report
|
|
|
|
# Then it should give me access only to managed enterprises
|
|
expect(page).to have_select 'q_distributor_id_eq',
|
|
with_options: [user1.enterprises.first.name]
|
|
expect(page).not_to have_select 'q_distributor_id_eq',
|
|
with_options: [user2.enterprises.first.name]
|
|
|
|
# When I filter to just one distributor
|
|
select user1.enterprises.first.name, from: 'q_distributor_id_eq'
|
|
run_report
|
|
|
|
# Then I should see the relevant order
|
|
expect(page).to have_content order1.number.to_s
|
|
|
|
# And the totals and sales tax should be correct
|
|
expect(page).to have_content "1512.99" # items total
|
|
expect(page).to have_content "1500.45" # taxable items total
|
|
expect(page).to have_content "250.08" # sales tax
|
|
expect(page).to have_content "20.0" # enterprise fee tax
|
|
|
|
# And the shipping cost and tax should be correct
|
|
expect(page).to have_content "100.55" # shipping cost
|
|
expect(page).to have_content "16.76" # shipping tax
|
|
|
|
# And the total tax should be correct
|
|
expect(page).to have_content "286.84" # total tax
|
|
end
|
|
|
|
it "generate Tax Rates report" do
|
|
click_link "Tax Rates"
|
|
run_report
|
|
|
|
expect(page).to have_css(".report__table thead th", text: "20.0% ($)")
|
|
expect(page).to have_css(".report__table thead th", text: "0.0% ($)")
|
|
expect(page).to have_table_row [order1.number.to_s, "1446.7", "16.76", "0", "270.08",
|
|
"286.84", "1733.54"]
|
|
end
|
|
end
|
|
|
|
describe "products and inventory report" do
|
|
let(:supplier) { create(:supplier_enterprise, name: 'Supplier Name') }
|
|
let(:taxon) { create(:taxon, name: 'Taxon Name') }
|
|
let(:product1) {
|
|
create(:simple_product, name: "Product Name", price: 100, primary_taxon_id: taxon.id,
|
|
supplier_id: supplier.id)
|
|
}
|
|
let(:product2) {
|
|
create(:simple_product, name: "Product 2", price: 99.0, variant_unit: 'weight',
|
|
variant_unit_scale: 1, unit_value: '100',
|
|
primary_taxon_id: taxon.id, sku: "product_sku",
|
|
supplier_id: supplier.id)
|
|
}
|
|
let(:variant1) { product1.variants.first }
|
|
let(:variant2) {
|
|
create(:variant, product: product1, price: 80.0, primary_taxon: taxon, supplier:)
|
|
}
|
|
let(:variant3) { product2.variants.first }
|
|
|
|
before do
|
|
product1.set_property 'Organic', 'NASAA 12345'
|
|
product2.set_property 'Organic', 'NASAA 12345'
|
|
variant1.on_hand = 10
|
|
variant1.update!(sku: "sku1")
|
|
variant2.on_hand = 20
|
|
variant2.update!(sku: "sku2")
|
|
variant3.on_hand = 9
|
|
variant3.update!(sku: "")
|
|
end
|
|
|
|
it "shows report error at the bottom of page" do
|
|
login_as_admin
|
|
visit admin_reports_path
|
|
|
|
click_link 'All products'
|
|
report = Reporting::Reports::ProductsAndInventory::AllProducts
|
|
|
|
click_on "Go"
|
|
|
|
allow(report).to receive(:new).and_raise(StandardError, 'Provoked error for testing')
|
|
perform_enqueued_jobs(only: ReportJob)
|
|
|
|
expect(page).not_to have_selector ".loading"
|
|
expect(page).to have_button "Go", disabled: false
|
|
expect(page).to have_content 'This report failed. It may be too big to process. ' \
|
|
'We will look into it but please let us know ' \
|
|
'if the problem persists.'
|
|
|
|
# Admin shoulb be able to make some changes and retry
|
|
allow(report).to receive(:new).and_call_original
|
|
|
|
run_report
|
|
expect(page).to have_content "Supplier"
|
|
end
|
|
|
|
it "shows products and inventory report" do
|
|
login_as_admin
|
|
visit admin_reports_path
|
|
|
|
expect(page).to have_content "All products"
|
|
expect(page).to have_content "Inventory (on hand)"
|
|
|
|
click_link 'All products'
|
|
run_report
|
|
expect(page).to have_content "Supplier"
|
|
expect(page).to have_table_row ["Supplier", "Producer Suburb", "Product",
|
|
"Product Properties", "Taxons", "Variant Value", "Price",
|
|
"Group Buy Unit Quantity", "Amount", "SKU",
|
|
"On Demand?", "On Hand"]
|
|
expect(page).to have_table_row [supplier.name, supplier.address.city,
|
|
"Product Name",
|
|
product1.properties.map(&:presentation).join(", "),
|
|
taxon.name, "1g", "100.0",
|
|
"none", "", "sku1", "No", "10"]
|
|
expect(page).to have_table_row [supplier.name, supplier.address.city,
|
|
"Product Name",
|
|
product1.properties.map(&:presentation).join(", "),
|
|
taxon.name, "1g", "80.0",
|
|
"none", "", "sku2", "No", "20"]
|
|
expect(page).to have_table_row [supplier.name, supplier.address.city,
|
|
"Product 2",
|
|
product1.properties.map(&:presentation).join(", "),
|
|
taxon.name, "100g", "99.0",
|
|
"none", "", "product_sku", "No", "9"]
|
|
end
|
|
|
|
it "shows the LettuceShare report" do
|
|
login_as_admin
|
|
visit admin_reports_path
|
|
click_link 'LettuceShare'
|
|
run_report
|
|
|
|
expect(page).to have_table_row ['PRODUCT', 'Description', 'Qty', 'Pack Size', 'Unit',
|
|
'Unit Price', 'Total', 'GST incl.',
|
|
'Grower and growing method', 'Taxon']
|
|
expect(page).to have_table_row ['Product 2', '100g', '', '100', 'g', '99.0', '', '0',
|
|
'Supplier Name (Organic - NASAA 12345)', 'Taxon Name']
|
|
end
|
|
end
|
|
|
|
describe "users and enterprises report" do
|
|
let!(:enterprise1) { create( :enterprise, owner: create(:user) ) }
|
|
let!(:enterprise2) { create( :enterprise, owner: create(:user) ) }
|
|
let!(:enterprise3) { create( :enterprise, owner: create(:user) ) }
|
|
|
|
before do
|
|
enterprise3.enterprise_roles.build( user: enterprise1.owner ).save
|
|
|
|
login_as_admin
|
|
visit admin_reports_path
|
|
|
|
click_link 'Users & Enterprises'
|
|
end
|
|
|
|
it "shows users and enterprises report" do
|
|
run_report
|
|
|
|
rows = find("table.report__table").all("tr")
|
|
table = rows.map { |r| r.all("th,td").map { |c| c.text.strip }[0..2] }
|
|
|
|
expect(table.sort).to eq([
|
|
["User", "Relationship", "Enterprise"],
|
|
[enterprise1.owner.email, "owns", enterprise1.name],
|
|
[enterprise1.owner.email, "manages", enterprise1.name],
|
|
[enterprise2.owner.email, "owns", enterprise2.name],
|
|
[enterprise2.owner.email, "manages", enterprise2.name],
|
|
[enterprise3.owner.email, "owns", enterprise3.name],
|
|
[enterprise3.owner.email, "manages", enterprise3.name],
|
|
[enterprise1.owner.email, "manages", enterprise3.name]
|
|
].sort)
|
|
end
|
|
|
|
it "filters the list" do
|
|
select enterprise3.name, from: "enterprise_id_in"
|
|
select enterprise1.owner.email, from: "user_id_in"
|
|
|
|
run_report
|
|
|
|
rows = find("table.report__table").all("tr")
|
|
table = rows.map { |r| r.all("th,td").map { |c| c.text.strip }[0..2] }
|
|
|
|
expect(table.sort).to eq([
|
|
["User", "Relationship", "Enterprise"],
|
|
[enterprise1.owner.email, "manages", enterprise3.name]
|
|
].sort)
|
|
end
|
|
end
|
|
|
|
describe 'bulk coop report' do
|
|
let!(:order) { create(:completed_order_with_totals) }
|
|
|
|
before do
|
|
login_as_admin
|
|
visit admin_reports_path
|
|
end
|
|
|
|
it "generating Bulk Co-op Supplier Report" do
|
|
click_link "Bulk Co-op Supplier Report"
|
|
run_report
|
|
|
|
expect(page).to have_table_row [
|
|
"Supplier",
|
|
"Product",
|
|
"Bulk Unit Size",
|
|
"Variant",
|
|
"Variant Value",
|
|
"Variant Unit",
|
|
"Weight",
|
|
"Sum Total",
|
|
"Units Required",
|
|
"Unallocated",
|
|
"Max Quantity Excess"
|
|
]
|
|
end
|
|
|
|
it "generating Bulk Co-op Allocation report" do
|
|
click_link "Bulk Co-op Allocation"
|
|
run_report
|
|
|
|
expect(page).to have_table_row [
|
|
"Customer",
|
|
"Product",
|
|
"Bulk Unit Size",
|
|
"Variant",
|
|
"Variant Value",
|
|
"Variant Unit",
|
|
"Weight",
|
|
"Sum Total",
|
|
"Total available",
|
|
"Unallocated",
|
|
"Max Quantity Excess"
|
|
]
|
|
end
|
|
|
|
it "generating Bulk Co-op Packing Sheets report" do
|
|
click_link "Bulk Co-op Packing Sheets"
|
|
run_report
|
|
|
|
expect(page).to have_table_row [
|
|
"Customer",
|
|
"Product",
|
|
"Variant",
|
|
"Sum Total"
|
|
]
|
|
end
|
|
|
|
it "generating Bulk Co-op Customer Payments report" do
|
|
click_link "Bulk Co-op Customer Payments"
|
|
run_report
|
|
|
|
expect(page).to have_table_row [
|
|
"Customer",
|
|
"Date of Order",
|
|
"Total Cost",
|
|
"Amount Owing",
|
|
"Amount Paid"
|
|
]
|
|
end
|
|
end
|
|
|
|
describe "Xero invoices report" do
|
|
let(:distributor1) {
|
|
create(:distributor_enterprise, with_payment_and_shipping: true, charges_sales_tax: true)
|
|
}
|
|
let(:distributor2) {
|
|
create(:distributor_enterprise, with_payment_and_shipping: true, charges_sales_tax: true)
|
|
}
|
|
let(:user1) { create(:user, enterprises: [distributor1]) }
|
|
let(:user2) { create(:user, enterprises: [distributor2]) }
|
|
let(:shipping_method) { create(:shipping_method_with, :expensive_name) }
|
|
let(:shipment) { create(:shipment_with, :shipping_method, shipping_method:) }
|
|
|
|
let(:enterprise_fee1) {
|
|
create(:enterprise_fee, enterprise: user1.enterprises.first,
|
|
tax_category: product2.variants.first.tax_category,
|
|
calculator: Calculator::FlatRate.new(preferred_amount: 10))
|
|
}
|
|
let(:enterprise_fee2) {
|
|
create(:enterprise_fee, enterprise: user1.enterprises.first,
|
|
tax_category: product2.variants.first.tax_category,
|
|
calculator: Calculator::FlatRate.new(preferred_amount: 20))
|
|
}
|
|
let(:order_cycle) {
|
|
create(:simple_order_cycle, coordinator: distributor1,
|
|
coordinator_fees: [enterprise_fee1, enterprise_fee2],
|
|
distributors: [distributor1], variants: [product1.variants.first])
|
|
}
|
|
|
|
let!(:zone) { create(:zone_with_member) }
|
|
let(:bill_address) {
|
|
create(:address, firstname: 'Customer', lastname: 'Name', address1: 'customer l1',
|
|
address2: '', city: 'customer city', zipcode: 1234)
|
|
}
|
|
let(:order1) {
|
|
create(:order, order_cycle:, distributor: user1.enterprises.first,
|
|
shipments: [shipment], bill_address:, state: 'payment')
|
|
}
|
|
let(:product1) {
|
|
create(:taxed_product, zone:, price: 12.54, tax_rate_amount: 0, sku: 'sku1')
|
|
}
|
|
let(:product2) {
|
|
create(:taxed_product, zone:, price: 500.15, tax_rate_amount: 0.2, sku: 'sku2')
|
|
}
|
|
|
|
describe "with adjustments" do
|
|
let!(:line_item1) {
|
|
create(:line_item, variant: product1.variants.first, price: 12.54, quantity: 1,
|
|
order: order1)
|
|
}
|
|
let!(:line_item2) {
|
|
create(:line_item, variant: product2.variants.first, price: 500.15, quantity: 3,
|
|
order: order1)
|
|
}
|
|
|
|
let!(:tax_category) { create(:tax_category) }
|
|
let!(:tax_rate) { create(:tax_rate, tax_category:) }
|
|
let!(:adj_shipping) {
|
|
create(:adjustment, order: order1, adjustable: order1, label: "Shipping",
|
|
originator: shipping_method, amount: 100.55)
|
|
}
|
|
let!(:adj_fee1) {
|
|
create(:adjustment, order: order1, adjustable: order1, originator: enterprise_fee1,
|
|
label: "Enterprise fee untaxed", amount: 10)
|
|
}
|
|
let!(:adj_fee2) {
|
|
create(:adjustment, order: order1, adjustable: order1, originator: enterprise_fee2,
|
|
label: "Enterprise fee taxed", amount: 20, tax_category:)
|
|
}
|
|
let!(:adj_fee2_tax) {
|
|
create(:adjustment, order: order1, adjustable: adj_fee2, originator: tax_rate, amount: 3,
|
|
state: "closed")
|
|
}
|
|
let!(:adj_admin1) {
|
|
create(:adjustment, order: order1, adjustable: order1, originator: nil,
|
|
label: "Manual adjustment", amount: 30)
|
|
}
|
|
let!(:adj_admin2) {
|
|
create(:adjustment, order: order1, adjustable: order1, originator: nil,
|
|
label: "Manual adjustment", amount: 40, tax_category:)
|
|
}
|
|
|
|
before do
|
|
order1.update_order!
|
|
order1.update!(email: 'customer@email.com')
|
|
order1.shipment.update(included_tax_total: 10.06)
|
|
travel_to(Time.zone.local(2021, 4, 25, 14, 0, 0)) { order1.finalize! }
|
|
order1.reload
|
|
order1.create_tax_charge!
|
|
end
|
|
|
|
before do
|
|
travel_to(Time.zone.local(2021, 4, 26, 14, 0, 0))
|
|
end
|
|
|
|
context "summary report" do
|
|
before do
|
|
login_as_admin
|
|
visit admin_reports_path
|
|
click_link "Summary"
|
|
run_report
|
|
end
|
|
|
|
it "shows Xero invoices report" do
|
|
expect(xero_invoice_table).to match_table [
|
|
xero_invoice_header,
|
|
xero_invoice_summary_row('Total untaxable produce (no tax)', 12.54,
|
|
'GST Free Income'),
|
|
xero_invoice_summary_row('Total taxable produce (tax inclusive)', 1500.45,
|
|
'GST on Income'),
|
|
xero_invoice_summary_row('Total untaxable fees (no tax)', 10.0,
|
|
'GST Free Income'),
|
|
xero_invoice_summary_row('Total taxable fees (tax inclusive)', 20.0,
|
|
'GST on Income'),
|
|
xero_invoice_summary_row('Delivery Shipping Cost (tax inclusive)', 100.55,
|
|
'GST on Income'),
|
|
xero_invoice_summary_row('Total untaxable admin adjustments (no tax)', 30.0,
|
|
'GST Free Income'),
|
|
xero_invoice_summary_row('Total taxable admin adjustments (tax inclusive)', 40.0,
|
|
'GST on Income')
|
|
]
|
|
end
|
|
|
|
it "can customise a number of fields" do
|
|
fill_in 'initial_invoice_number', with: '5'
|
|
|
|
pick_datetime '#invoice_date', Date.new(2021, 2, 12)
|
|
pick_datetime '#due_date', Date.new(2021, 3, 12)
|
|
|
|
fill_in 'account_code', with: 'abc123'
|
|
run_report
|
|
|
|
opts = { invoice_number: '5', invoice_date: '2021-02-12',
|
|
due_date: '2021-03-12', account_code: 'abc123' }
|
|
|
|
expect(xero_invoice_table).to match_table [
|
|
xero_invoice_header,
|
|
xero_invoice_summary_row('Total untaxable produce (no tax)', 12.54,
|
|
'GST Free Income', opts),
|
|
xero_invoice_summary_row('Total taxable produce (tax inclusive)', 1500.45,
|
|
'GST on Income', opts),
|
|
xero_invoice_summary_row('Total untaxable fees (no tax)', 10.0,
|
|
'GST Free Income', opts),
|
|
xero_invoice_summary_row('Total taxable fees (tax inclusive)', 20.0,
|
|
'GST on Income', opts),
|
|
xero_invoice_summary_row('Delivery Shipping Cost (tax inclusive)', 100.55,
|
|
'GST on Income', opts),
|
|
xero_invoice_summary_row('Total untaxable admin adjustments (no tax)', 30.0,
|
|
'GST Free Income', opts),
|
|
xero_invoice_summary_row('Total taxable admin adjustments (tax inclusive)', 40.0,
|
|
'GST on Income', opts)
|
|
]
|
|
end
|
|
end
|
|
|
|
context "detailed report" do
|
|
it "generates a detailed report" do
|
|
login_as_admin
|
|
visit admin_reports_path
|
|
click_link "Detailed"
|
|
run_report
|
|
|
|
opts = {}
|
|
|
|
expect(xero_invoice_table).to match_table [
|
|
xero_invoice_header,
|
|
xero_invoice_li_row(line_item1),
|
|
xero_invoice_li_row(line_item2),
|
|
xero_invoice_adjustment_row(adj_admin1),
|
|
xero_invoice_adjustment_row(adj_admin2),
|
|
xero_invoice_summary_row('Total untaxable fees (no tax)', 10.0,
|
|
'GST Free Income', opts),
|
|
xero_invoice_summary_row('Total taxable fees (tax inclusive)', 20.0,
|
|
'GST on Income', opts),
|
|
xero_invoice_summary_row('Delivery Shipping Cost (tax inclusive)', 100.55,
|
|
'GST on Income', opts)
|
|
]
|
|
end
|
|
end
|
|
end
|
|
|
|
private
|
|
|
|
def xero_invoice_table
|
|
find("table.report__table")
|
|
end
|
|
|
|
def xero_invoice_header
|
|
%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 xero_invoice_summary_row(description, amount, tax_type, opts = {})
|
|
xero_invoice_row '', description, amount, '1', tax_type, opts
|
|
end
|
|
|
|
def xero_invoice_li_row(line_item, opts = {})
|
|
tax_type = line_item.has_tax? ? 'GST on Income' : 'GST Free Income'
|
|
xero_invoice_row line_item.variant.sku, line_item.product_and_full_name,
|
|
line_item.price.to_s, line_item.quantity.to_s, tax_type, opts
|
|
end
|
|
|
|
def xero_invoice_adjustment_row(adjustment, opts = {})
|
|
tax_type = adjustment.has_tax? ? 'GST on Income' : 'GST Free Income'
|
|
xero_invoice_row('', adjustment.label, adjustment.amount, '1', tax_type, opts)
|
|
end
|
|
|
|
def xero_invoice_row(sku, description, amount, quantity, tax_type, opts = {})
|
|
opts.reverse_merge!(customer_name: 'Customer Name', address1: 'customer l1',
|
|
city: 'customer city', state: 'Victoria', zipcode: '1234',
|
|
country: 'Australia', invoice_number: order1.number,
|
|
order_number: order1.number, invoice_date: '2021-04-26',
|
|
due_date: '2021-05-26', account_code: 'food sales')
|
|
|
|
[opts[:customer_name], 'customer@email.com', opts[:address1], '', '', '',
|
|
opts[:city], opts[:state], opts[:zipcode], opts[:country], opts[:invoice_number],
|
|
opts[:order_number], opts[:invoice_date], opts[:due_date],
|
|
|
|
sku,
|
|
description,
|
|
quantity,
|
|
amount.to_s, '', opts[:account_code], tax_type, '', '', '', '', "AUD",
|
|
'', 'N']
|
|
end
|
|
end
|
|
end
|