mirror of
https://github.com/openfoodfoundation/openfoodnetwork
synced 2026-01-24 20:36:49 +00:00
Merge pull request #11254 from rioug/11130-add-product-filter-order-report
11130 add product filter order report
This commit is contained in:
@@ -55,6 +55,15 @@ module Admin
|
||||
@report_title = report_title
|
||||
@rendering_options = rendering_options
|
||||
@data = Reporting::FrontendData.new(spree_current_user)
|
||||
|
||||
variant_id_in = params[:variant_id_in]&.compact_blank
|
||||
load_selected_variant if variant_id_in.present?
|
||||
end
|
||||
|
||||
# Orders and Fulfillment Reports include a per product filter, load any selected product
|
||||
def load_selected_variant
|
||||
variant = Spree::Variant.find(params[:variant_id_in][0])
|
||||
@variant_serialized = Api::Admin::VariantSerializer.new(variant)
|
||||
end
|
||||
|
||||
def render_data?
|
||||
|
||||
@@ -12,3 +12,9 @@
|
||||
.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 :add_variant_id, Spree.t(:name_or_sku)
|
||||
.omega.fourteen.columns
|
||||
- variant_json = @variant_serialized.present? ? @variant_serialized.to_json() : {}
|
||||
= select_tag(:variant_id_in, params[:variant_id_in], { class: "fullwidth", multiple: true , data: { controller: "select-variant", "select-variant-selected-value": "#{variant_json}" } })
|
||||
|
||||
|
||||
90
app/webpacker/controllers/select_variant_controller.js
Normal file
90
app/webpacker/controllers/select_variant_controller.js
Normal file
@@ -0,0 +1,90 @@
|
||||
import TomSelectController from "./tom_select_controller";
|
||||
|
||||
// This is simalar to the "variantAutocomplete" directive that uses "select2", but it doesn't
|
||||
// have all the same feature
|
||||
//
|
||||
export default class extends TomSelectController {
|
||||
static values = { options: Object, distributor: Number, selected: Object };
|
||||
|
||||
connect() {
|
||||
const options = {
|
||||
valueField: "id",
|
||||
searchField: ["name", "sku"],
|
||||
load: this.#load.bind(this),
|
||||
shouldLoad: (query) => query.length > 2,
|
||||
render: {
|
||||
option: this.#renderOption.bind(this),
|
||||
item: this.#renderItem.bind(this),
|
||||
},
|
||||
};
|
||||
super.connect(options);
|
||||
// Add the selected value if any and select it.
|
||||
// It will need to include data used in the templates below:
|
||||
// - id
|
||||
// - image
|
||||
// - name
|
||||
// - producer_name
|
||||
// - sku
|
||||
// - on_demand
|
||||
// - on_hand
|
||||
// - options_text
|
||||
//
|
||||
if (this.hasSelectedValue && Object.keys(this.selectedValue).length > 0) {
|
||||
this.control.addOption(this.selectedValue);
|
||||
this.control.addItem(this.selectedValue.id);
|
||||
}
|
||||
}
|
||||
|
||||
// private
|
||||
|
||||
#load(query, callback) {
|
||||
const url = "/admin/variants/search.json?q=" + encodeURIComponent(query);
|
||||
fetch(url)
|
||||
.then((response) => response.json())
|
||||
.then((json) => {
|
||||
callback(json);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.log(error);
|
||||
callback();
|
||||
});
|
||||
}
|
||||
|
||||
#renderOption(variant, escape) {
|
||||
return `<div class='variant-autocomplete-item'>
|
||||
<figure class='variant-image'>
|
||||
${variant.image ? `<img src='${variant.image}' />` : "<img src='/noimage/mini.png' />"}
|
||||
</figure>
|
||||
<div class='variant-details'>
|
||||
<h6 class="variant-name">${escape(variant.name)}</h6>
|
||||
<ul>
|
||||
<li>
|
||||
<strong> ${I18n.t("spree.admin.variants.autocomplete.producer_name")}: </strong>
|
||||
${escape(variant.producer_name)}
|
||||
</li>
|
||||
</ul>
|
||||
<ul class='variant-data'>
|
||||
<li class='variant-sku'>
|
||||
<strong>${I18n.t("admin.sku")}: </strong>
|
||||
${escape(variant.sku)}
|
||||
</li>
|
||||
${
|
||||
variant.on_demand
|
||||
? `<li class='variant-on_demand'><strong>${I18n.t("on_demand")}</strong></li>`
|
||||
: `<li class='variant-on_hand'>
|
||||
<strong>${I18n.t("on_hand")}:</strong> ${escape(variant.on_hand)}
|
||||
</li>`
|
||||
}
|
||||
<li class='variant-options_text'>
|
||||
<strong> ${I18n.t("spree.admin.variants.autocomplete.unit")}: </strong>
|
||||
${escape(variant.options_text)}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
#renderItem(variant, escape) {
|
||||
return `<span>${escape(variant.name)}</span>`;
|
||||
}
|
||||
}
|
||||
@@ -23,6 +23,12 @@ module Reporting
|
||||
line_items = line_items.supplied_by_any(@params[:supplier_id_in])
|
||||
end
|
||||
|
||||
# Filter by product
|
||||
variant_id_in = @params[:variant_id_in]&.compact_blank
|
||||
if variant_id_in.present?
|
||||
line_items = line_items.where('spree_line_items.variant_id': variant_id_in)
|
||||
end
|
||||
|
||||
if line_item_includes.present?
|
||||
line_items = line_items.includes(*line_item_includes).references(:line_items)
|
||||
end
|
||||
|
||||
@@ -9,13 +9,13 @@ describe Reporting::LineItems do
|
||||
# under test and the various objects it depends on. Other more common moking strategies where very
|
||||
# hard.
|
||||
class FakeOrderPermissions
|
||||
def initialize(line_item, orders_relation)
|
||||
@relation = Spree::LineItem.where(id: line_item.id)
|
||||
def initialize(line_items, orders_relation)
|
||||
@relations = Spree::LineItem.where(id: line_items.map(&:id))
|
||||
@orders_relation = orders_relation
|
||||
end
|
||||
|
||||
def visible_line_items
|
||||
relation
|
||||
relations
|
||||
end
|
||||
|
||||
def editable_line_items
|
||||
@@ -29,7 +29,7 @@ describe Reporting::LineItems do
|
||||
|
||||
private
|
||||
|
||||
attr_reader :relation, :orders_relation
|
||||
attr_reader :relations, :orders_relation
|
||||
end
|
||||
|
||||
describe '#list' do
|
||||
@@ -41,15 +41,39 @@ describe Reporting::LineItems do
|
||||
shipments: [build(:shipment)]
|
||||
)
|
||||
end
|
||||
let!(:line_item) { create(:line_item, order: order) }
|
||||
let!(:line_item1) { create(:line_item, order: order) }
|
||||
|
||||
let(:orders_relation) { Spree::Order.where(id: order.id) }
|
||||
let(:order_permissions) { FakeOrderPermissions.new(line_item, orders_relation) }
|
||||
let(:order_permissions) { FakeOrderPermissions.new([line_item1], orders_relation) }
|
||||
let(:params) { {} }
|
||||
|
||||
it 'returns masked data' do
|
||||
line_items = reports_line_items.list
|
||||
expect(line_items.first.order.email).to eq('HIDDEN')
|
||||
end
|
||||
|
||||
context "when filtering by product" do
|
||||
subject(:line_items) { reports_line_items.list }
|
||||
|
||||
let!(:line_item2) { create(:line_item, order: order) }
|
||||
let!(:line_item3) { create(:line_item, order: order) }
|
||||
let(:order_permissions) do
|
||||
FakeOrderPermissions.new([line_item1, line_item2, line_item3], orders_relation)
|
||||
end
|
||||
let(:params) { { variant_id_in: [line_item3.variant.id, line_item1.variant.id] } }
|
||||
|
||||
context "with an empty array" do
|
||||
let(:params) { { variant_id_in: [""] } }
|
||||
|
||||
it "does not filter" do
|
||||
expect(line_items).to include(line_item1, line_item2, line_item3)
|
||||
end
|
||||
end
|
||||
|
||||
it "includes selected products" do
|
||||
expect(line_items).to include(line_item1, line_item3)
|
||||
expect(line_items).not_to include(line_item2)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -176,6 +176,36 @@ describe "Orders And Fulfillment" do
|
||||
expect(rows[3]).to have_content "Ave Zebu"
|
||||
end
|
||||
end
|
||||
|
||||
context "When filtering by product" do
|
||||
let(:variant1) { create(:variant, product: product, unit_description: "Big") }
|
||||
let(:variant3) { create(:variant) }
|
||||
|
||||
before do
|
||||
create(:line_item_with_shipment, variant: variant1, quantity: 1, order: order1)
|
||||
create(:line_item_with_shipment, variant: variant2, quantity: 3, order: order1)
|
||||
create(:line_item_with_shipment, variant: variant1, quantity: 2, order: order2)
|
||||
create(:line_item_with_shipment, variant: variant3, quantity: 1, order: order2)
|
||||
end
|
||||
|
||||
it "includes only selected product" do
|
||||
tomselect_search_and_select(variant3.sku, from: "variant_id_in[]")
|
||||
click_button 'Go'
|
||||
|
||||
rows = find("table.report__table").all("tbody tr")
|
||||
table = rows.map { |r| r.all("td").map { |c| c.text.strip } }
|
||||
expect(table).to have_content(variant3.product.name)
|
||||
expect(table).not_to have_content(product.name)
|
||||
|
||||
# Check the product dropdown still show the selected product
|
||||
selected_product = page
|
||||
.find("[name='variant_id_in[]']")
|
||||
.sibling(".ts-wrapper")
|
||||
.first(".ts-control")
|
||||
.first(".item")
|
||||
expect(selected_product.text).to have_content(variant3.product.name)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "Order Cycle Supplier" do
|
||||
@@ -508,6 +538,7 @@ describe "Orders And Fulfillment" do
|
||||
expect(page).to have_checked_field('Summary Row')
|
||||
end
|
||||
end
|
||||
|
||||
context "Columns to show" do
|
||||
it "should store columns to show for every report separately" do
|
||||
# Step 1: Update report rendering options on two reports
|
||||
@@ -542,6 +573,7 @@ describe "Orders And Fulfillment" do
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "Revisiting a report after logout" do
|
||||
context "Display options" do
|
||||
it "should store display options" do
|
||||
|
||||
Reference in New Issue
Block a user