mirror of
https://github.com/openfoodfoundation/openfoodnetwork
synced 2026-01-27 21:06:49 +00:00
Merge pull request #11369 from Matt-Yorkley/product-taxon
[Product Refactor] Primary Taxon
This commit is contained in:
@@ -48,7 +48,7 @@ angular.module("ofn.admin").controller "AdminProductEditCtrl", ($scope, $timeout
|
||||
params = {
|
||||
'q[name_cont]': $scope.q.query,
|
||||
'q[supplier_id_eq]': $scope.q.producerFilter,
|
||||
'q[primary_taxon_id_eq]': $scope.q.categoryFilter,
|
||||
'q[variants_primary_taxon_id_eq]': $scope.q.categoryFilter,
|
||||
'q[s]': $scope.sorting,
|
||||
import_date: $scope.q.importDateFilter,
|
||||
page: $scope.page,
|
||||
@@ -136,6 +136,7 @@ angular.module("ofn.admin").controller "AdminProductEditCtrl", ($scope, $timeout
|
||||
on_hand: null
|
||||
price: null
|
||||
tax_category_id: null
|
||||
category_id: null
|
||||
DisplayProperties.setShowVariants product.id, true
|
||||
|
||||
|
||||
@@ -217,7 +218,7 @@ angular.module("ofn.admin").controller "AdminProductEditCtrl", ($scope, $timeout
|
||||
filters:
|
||||
'q[name_cont]': $scope.q.query
|
||||
'q[supplier_id_eq]': $scope.q.producerFilter
|
||||
'q[primary_taxon_id_eq]': $scope.q.categoryFilter
|
||||
'q[variants_primary_taxon_id_eq]': $scope.q.categoryFilter
|
||||
'q[s]': $scope.sorting
|
||||
import_date: $scope.q.importDateFilter
|
||||
page: $scope.page
|
||||
@@ -332,9 +333,6 @@ filterSubmitProducts = (productsToFilter) ->
|
||||
if product.hasOwnProperty("on_demand") and filteredVariants.length == 0 #only update if no variants present
|
||||
filteredProduct.on_demand = product.on_demand
|
||||
hasUpdatableProperty = true
|
||||
if product.hasOwnProperty("category_id")
|
||||
filteredProduct.primary_taxon_id = product.category_id
|
||||
hasUpdatableProperty = true
|
||||
if product.hasOwnProperty("inherits_properties")
|
||||
filteredProduct.inherits_properties = product.inherits_properties
|
||||
hasUpdatableProperty = true
|
||||
@@ -375,6 +373,9 @@ filterSubmitVariant = (variant) ->
|
||||
if variant.hasOwnProperty("tax_category_id")
|
||||
filteredVariant.tax_category_id = variant.tax_category_id
|
||||
hasUpdatableProperty = true
|
||||
if variant.hasOwnProperty("category_id")
|
||||
filteredVariant.primary_taxon_id = variant.category_id
|
||||
hasUpdatableProperty = true
|
||||
if variant.hasOwnProperty("display_as")
|
||||
filteredVariant.display_as = variant.display_as
|
||||
hasUpdatableProperty = true
|
||||
|
||||
@@ -68,7 +68,7 @@ angular.module('Darkswarm').controller "ProductsCtrl", ($scope, $sce, $filter, $
|
||||
per_page: $scope.per_page,
|
||||
'q[name_or_meta_keywords_or_variants_display_as_or_variants_display_name_or_supplier_name_cont]': $scope.query,
|
||||
'q[with_properties][]': $scope.activeProperties,
|
||||
'q[primary_taxon_id_in_any][]': $scope.activeTaxons
|
||||
'q[variants_primary_taxon_id_in_any][]': $scope.activeTaxons
|
||||
}
|
||||
|
||||
$scope.searchKeypress = (e)->
|
||||
|
||||
@@ -70,22 +70,7 @@ module Api
|
||||
end
|
||||
|
||||
def search_params
|
||||
permitted_search_params = params.slice :q, :page, :per_page
|
||||
|
||||
if permitted_search_params.key? :q
|
||||
permitted_search_params[:q].slice!(*permitted_ransack_params)
|
||||
end
|
||||
|
||||
permitted_search_params
|
||||
end
|
||||
|
||||
def permitted_ransack_params
|
||||
[
|
||||
"#{[:name, :meta_keywords, :variants_display_as,
|
||||
:variants_display_name, :supplier_name]
|
||||
.join('_or_')}_cont",
|
||||
:with_properties, :primary_taxon_id_in_any
|
||||
]
|
||||
params.slice :q, :page, :per_page
|
||||
end
|
||||
|
||||
def distributor
|
||||
|
||||
@@ -24,7 +24,6 @@ module ProductImport
|
||||
|
||||
def self.non_updatable_fields
|
||||
{
|
||||
category: :primary_taxon_id,
|
||||
description: :description,
|
||||
unit_type: :variant_unit_scale,
|
||||
variant_unit_name: :variant_unit_name,
|
||||
@@ -69,7 +68,7 @@ module ProductImport
|
||||
def mark_as_new_variant(entry, product_id)
|
||||
variant_attributes = entry.assignable_attributes.except(
|
||||
'id', 'product_id', 'on_hand', 'on_demand', 'variant_unit', 'variant_unit_name',
|
||||
'variant_unit_scale', 'primary_taxon_id'
|
||||
'variant_unit_scale'
|
||||
)
|
||||
# Variant needs a product. Product needs to be assigned first in order for
|
||||
# delegate to work. name= will fail otherwise.
|
||||
@@ -398,7 +397,7 @@ module ProductImport
|
||||
def mark_as_existing_variant(entry, existing_variant)
|
||||
existing_variant.assign_attributes(
|
||||
entry.assignable_attributes.except('id', 'product_id', 'variant_unit', 'variant_unit_name',
|
||||
'variant_unit_scale', 'primary_taxon_id')
|
||||
'variant_unit_scale')
|
||||
)
|
||||
check_on_hand_nil(entry, existing_variant)
|
||||
|
||||
|
||||
@@ -28,16 +28,11 @@ module Spree
|
||||
|
||||
acts_as_paranoid
|
||||
|
||||
after_create :ensure_standard_variant
|
||||
around_destroy :destruction
|
||||
after_save :update_units
|
||||
|
||||
searchable_attributes :supplier_id, :primary_taxon_id, :meta_keywords, :sku
|
||||
searchable_associations :supplier, :properties, :primary_taxon, :variants
|
||||
searchable_attributes :supplier_id, :meta_keywords, :sku
|
||||
searchable_associations :supplier, :properties, :variants
|
||||
searchable_scopes :active, :with_properties
|
||||
|
||||
belongs_to :supplier, class_name: 'Enterprise', optional: false, touch: true
|
||||
belongs_to :primary_taxon, class_name: 'Spree::Taxon', optional: false, touch: true
|
||||
|
||||
has_one :image, class_name: "Spree::Image", as: :viewable, dependent: :destroy
|
||||
|
||||
@@ -77,7 +72,11 @@ module Spree
|
||||
# Transient attributes used temporarily when creating a new product,
|
||||
# these values are persisted on the product's variant
|
||||
attr_accessor :price, :display_as, :unit_value, :unit_description, :tax_category_id,
|
||||
:shipping_category_id
|
||||
:shipping_category_id, :primary_taxon_id
|
||||
|
||||
after_create :ensure_standard_variant
|
||||
around_destroy :destruction
|
||||
after_save :update_units
|
||||
|
||||
scope :with_properties, ->(*property_ids) {
|
||||
left_outer_joins(:product_properties).
|
||||
@@ -284,6 +283,7 @@ module Spree
|
||||
variant.unit_description = unit_description
|
||||
variant.tax_category_id = tax_category_id
|
||||
variant.shipping_category_id = shipping_category_id
|
||||
variant.primary_taxon_id = primary_taxon_id
|
||||
variants << variant
|
||||
end
|
||||
|
||||
|
||||
@@ -7,9 +7,12 @@ module Spree
|
||||
acts_as_nested_set dependent: :destroy
|
||||
|
||||
belongs_to :taxonomy, class_name: 'Spree::Taxonomy', touch: true
|
||||
has_many :products, class_name: "Spree::Product", foreign_key: "primary_taxon_id",
|
||||
|
||||
has_many :variants, class_name: "Spree::Variant", foreign_key: "primary_taxon_id",
|
||||
inverse_of: :primary_taxon, dependent: :restrict_with_error
|
||||
|
||||
has_many :products, through: :variants, dependent: nil
|
||||
|
||||
before_create :set_permalink
|
||||
|
||||
validates :name, presence: true
|
||||
@@ -77,7 +80,7 @@ module Spree
|
||||
|
||||
taxons = Spree::Taxon
|
||||
.select("DISTINCT spree_taxons.id, ents_and_vars.enterprise_id")
|
||||
.joins(products: :variants)
|
||||
.joins(:variants)
|
||||
.joins("
|
||||
INNER JOIN (#{ents_and_vars.to_sql}) AS ents_and_vars
|
||||
ON spree_variants.id = ents_and_vars.variant_id")
|
||||
|
||||
@@ -13,8 +13,8 @@ module Spree
|
||||
|
||||
acts_as_paranoid
|
||||
|
||||
searchable_attributes :sku, :display_as, :display_name
|
||||
searchable_associations :product, :default_price
|
||||
searchable_attributes :sku, :display_as, :display_name, :primary_taxon_id
|
||||
searchable_associations :product, :default_price, :primary_taxon
|
||||
searchable_scopes :active, :deleted
|
||||
|
||||
NAME_FIELDS = ["display_name", "display_as", "weight", "unit_value", "unit_description"].freeze
|
||||
@@ -28,6 +28,7 @@ module Spree
|
||||
belongs_to :product, -> { with_deleted }, touch: true, class_name: 'Spree::Product'
|
||||
belongs_to :tax_category, class_name: 'Spree::TaxCategory'
|
||||
belongs_to :shipping_category, class_name: 'Spree::ShippingCategory', optional: false
|
||||
belongs_to :primary_taxon, class_name: 'Spree::Taxon', touch: true, optional: false
|
||||
|
||||
delegate :name, :name=, :description, :description=, :meta_keywords, to: :product
|
||||
|
||||
@@ -82,6 +83,7 @@ module Spree
|
||||
before_validation :ensure_unit_value
|
||||
before_validation :update_weight_from_unit_value, if: ->(v) { v.product.present? }
|
||||
before_validation :convert_variant_weight_to_decimal
|
||||
before_validation :assign_related_taxon, if: ->(v) { v.primary_taxon.blank? }
|
||||
|
||||
before_save :assign_units, if: ->(variant) {
|
||||
variant.new_record? || variant.changed_attributes.keys.intersection(NAME_FIELDS).any?
|
||||
@@ -208,6 +210,10 @@ module Spree
|
||||
|
||||
private
|
||||
|
||||
def assign_related_taxon
|
||||
self.primary_taxon ||= product.variants.last&.primary_taxon
|
||||
end
|
||||
|
||||
def check_currency
|
||||
return unless currency.nil?
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ class ProductScopeQuery
|
||||
|
||||
product_query.
|
||||
ransack(query_params_with_defaults).
|
||||
result
|
||||
result(distinct: true)
|
||||
end
|
||||
|
||||
def find_product
|
||||
|
||||
@@ -152,7 +152,7 @@ class ProductsReflex < ApplicationReflex
|
||||
|
||||
def fetch_products
|
||||
product_query = OpenFoodNetwork::Permissions.new(current_user)
|
||||
.editable_products.merge(product_scope).ransack(ransack_query).result
|
||||
.editable_products.merge(product_scope).ransack(ransack_query).result(distinct: true)
|
||||
@pagy, @products = pagy(product_query.order(:name), items: @per_page, page: @page,
|
||||
size: [1, 2, 2, 1])
|
||||
end
|
||||
@@ -173,7 +173,7 @@ class ProductsReflex < ApplicationReflex
|
||||
if @search_term.present?
|
||||
query.merge!(Spree::Variant::SEARCH_KEY => @search_term)
|
||||
end
|
||||
query.merge!(primary_taxon_id_in: @category_id) if @category_id.present?
|
||||
query.merge!(variants_primary_taxon_id_in: @category_id) if @category_id.present?
|
||||
query
|
||||
end
|
||||
|
||||
|
||||
@@ -8,7 +8,6 @@ module Api
|
||||
:thumb_url, :variants
|
||||
|
||||
has_one :supplier, key: :producer_id, embed: :id
|
||||
has_one :primary_taxon, key: :category_id, embed: :id
|
||||
|
||||
def variants
|
||||
ActiveModel::ArraySerializer.new(
|
||||
|
||||
@@ -8,6 +8,8 @@ module Api
|
||||
:display_as, :display_name, :name_to_display, :variant_overrides_count,
|
||||
:price, :on_demand, :on_hand, :in_stock, :stock_location_id, :stock_location_name
|
||||
|
||||
has_one :primary_taxon, key: :category_id, embed: :id
|
||||
|
||||
def name
|
||||
if object.full_name.present?
|
||||
"#{object.name} - #{object.full_name}"
|
||||
|
||||
@@ -9,8 +9,6 @@ class Api::ProductSerializer < ActiveModel::Serializer
|
||||
|
||||
has_many :variants, serializer: Api::VariantSerializer
|
||||
|
||||
has_one :primary_taxon, serializer: Api::TaxonSerializer
|
||||
|
||||
has_one :image, serializer: Api::ImageSerializer
|
||||
has_one :supplier, serializer: Api::IdSerializer
|
||||
|
||||
|
||||
@@ -15,6 +15,22 @@ module OrderCycles
|
||||
Spree::Product.where(id: stocked_products).group("spree_products.id")
|
||||
end
|
||||
|
||||
# Joins on the first product variant to allow us to filter product by taxon. This is so
|
||||
# enterprise can display product sorted by category in a custom order on their shopfront.
|
||||
#
|
||||
# Caveat, the category sorting won't work properly if there are multiple variant with different
|
||||
# category for a given product.
|
||||
#
|
||||
def products_taxons_relation
|
||||
Spree::Product.where(id: stocked_products).
|
||||
joins("LEFT JOIN (
|
||||
SELECT DISTINCT ON(product_id) id, product_id, primary_taxon_id
|
||||
FROM spree_variants WHERE deleted_at IS NULL
|
||||
) first_variant ON spree_products.id = first_variant.product_id").
|
||||
select("spree_products.*, first_variant.primary_taxon_id").
|
||||
group("spree_products.id, first_variant.primary_taxon_id")
|
||||
end
|
||||
|
||||
def variants_relation
|
||||
order_cycle.
|
||||
variants_distributed_by(distributor).
|
||||
|
||||
@@ -7,7 +7,7 @@ module PermittedAttributes
|
||||
:id, :sku, :on_hand, :on_demand, :shipping_category_id,
|
||||
:price, :unit_value, :unit_description,
|
||||
:display_name, :display_as, :tax_category_id,
|
||||
:weight, :height, :width, :depth
|
||||
:weight, :height, :width, :depth, :taxon_ids, :primary_taxon_id
|
||||
]
|
||||
end
|
||||
end
|
||||
|
||||
@@ -35,7 +35,7 @@ class ProductsRenderer
|
||||
|
||||
@products ||= begin
|
||||
results = distributed_products.
|
||||
products_relation.
|
||||
products_taxons_relation.
|
||||
order(Arel.sql(products_order))
|
||||
|
||||
filter_and_paginate(results).
|
||||
@@ -54,7 +54,7 @@ class ProductsRenderer
|
||||
def filter_and_paginate(query)
|
||||
results = query.ransack(args[:q]).result
|
||||
|
||||
_pagy, paginated_results = pagy(
|
||||
_pagy, paginated_results = pagy_arel(
|
||||
results,
|
||||
page: args[:page] || 1,
|
||||
items: args[:per_page] || DEFAULT_PER_PAGE
|
||||
@@ -78,7 +78,7 @@ class ProductsRenderer
|
||||
distributor.preferred_shopfront_taxon_order.present?
|
||||
distributor
|
||||
.preferred_shopfront_taxon_order
|
||||
.split(",").map { |id| "spree_products.primary_taxon_id=#{id} DESC" }
|
||||
.split(",").map { |id| "first_variant.primary_taxon_id=#{id} DESC" }
|
||||
.join(", ") + ", spree_products.name ASC, spree_products.id ASC"
|
||||
else
|
||||
"spree_products.name ASC, spree_products.id"
|
||||
|
||||
@@ -46,18 +46,12 @@ module Sets
|
||||
# variant.update( { price: xx.x } )
|
||||
#
|
||||
def update_product_attributes(attributes)
|
||||
split_taxon_ids!(attributes)
|
||||
|
||||
product = find_model(@collection, attributes[:id])
|
||||
return if product.nil?
|
||||
|
||||
update_product(product, attributes)
|
||||
end
|
||||
|
||||
def split_taxon_ids!(attributes)
|
||||
attributes[:taxon_ids] = attributes[:taxon_ids].split(',') if attributes[:taxon_ids].present?
|
||||
end
|
||||
|
||||
def update_product(product, attributes)
|
||||
return false unless update_product_only_attributes(product, attributes)
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
%td.align-left
|
||||
.content= product.supplier&.name
|
||||
%td.align-left
|
||||
.content= product.primary_taxon&.name
|
||||
-# empty
|
||||
%td.align-left
|
||||
%td.align-left
|
||||
.content= product.inherits_properties ? 'YES' : 'NO' #TODO: consider using https://github.com/RST-J/human_attribute_values, else use I18n.t (also below)
|
||||
|
||||
@@ -42,7 +42,7 @@
|
||||
%td.align-left
|
||||
.content= variant.product.supplier&.name # same as product
|
||||
%td.align-left
|
||||
-# empty
|
||||
.content= variant.primary_taxon&.name
|
||||
%td.align-left
|
||||
.content= variant.tax_category&.name || "None" # TODO: convert to dropdown, else translate hardcoded string.
|
||||
%td.align-left
|
||||
|
||||
@@ -27,8 +27,6 @@
|
||||
= f.text_field :variant_unit_name, {placeholder: t('admin.products.unit_name_placeholder')}
|
||||
= f.error_message_on :variant_unit_name
|
||||
|
||||
= render 'spree/admin/products/primary_taxon_form', f: f
|
||||
|
||||
= f.field_container :supplier do
|
||||
= f.label :supplier, t(:spree_admin_supplier)
|
||||
%br
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
= f.field_container :primary_taxon do
|
||||
= f.label :primary_taxon, t('.product_category')
|
||||
= f.label :primary_taxon_id, t('.product_category')
|
||||
%span.required *
|
||||
%br
|
||||
= f.collection_select(:primary_taxon_id, Spree::Taxon.order(:name), :id, :name, {:include_blank => true}, {:class => "select2 fullwidth"})
|
||||
= f.error_message_on :primary_taxon
|
||||
= f.error_message_on :primary_taxon_id
|
||||
|
||||
@@ -24,7 +24,6 @@
|
||||
%td.on_demand{ 'ng-show' => 'columns.on_demand.visible' }
|
||||
%input.field{ 'ng-model' => 'product.on_demand', :name => 'on_demand', 'ofn-track-product' => 'on_demand', :type => 'checkbox', 'ng-hide' => 'hasVariants(product)' }
|
||||
%td.category{ 'ng-if' => 'columns.category.visible' }
|
||||
%input.fullwidth{ :type => 'text', id: "p{{product.id}}_category_id", 'ng-model' => 'product.category_id', 'ofn-taxon-autocomplete' => '', 'ofn-track-product' => 'category_id', 'multiple-selection' => 'false', placeholder: 'Category' }
|
||||
%td.tax_category{ 'ng-if' => 'columns.tax_category.visible' }
|
||||
%td.inherits_properties{ 'ng-show' => 'columns.inherits_properties.visible' }
|
||||
%input{ 'ng-model' => 'product.inherits_properties', :name => 'inherits_properties', 'ofn-track-product' => 'inherits_properties', type: "checkbox" }
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
%td{ 'ng-show' => 'columns.on_demand.visible' }
|
||||
%input.field{ 'ng-model' => 'variant.on_demand', :name => 'variant_on_demand', 'ofn-track-variant' => 'on_demand', :type => 'checkbox' }
|
||||
%td{ 'ng-show' => 'columns.category.visible' }
|
||||
%input.fullwidth{ type: 'text', id: "p{{product.id}}_category_id", 'ng-model' => 'variant.category_id', 'ofn-taxon-autocomplete' => '', 'ofn-track-variant' => 'category_id', 'multiple-selection' => 'false', placeholder: 'Category' }
|
||||
%td{ 'ng-show' => 'columns.tax_category.visible' }
|
||||
%select.select2{ name: 'variant_tax_category_id', "ofn-track-variant": 'tax_category_id', "ng-model": 'variant.tax_category_id', "ng-options": 'tax_category.id as tax_category.name for tax_category in tax_categories' }
|
||||
%option{ value: '' }= t(:none)
|
||||
|
||||
@@ -72,4 +72,8 @@
|
||||
= f.label :shipping_category_id, t(:shipping_categories)
|
||||
= f.collection_select(:shipping_category_id, @shipping_categories, :id, :name, {}, { class: 'select2 fullwidth' })
|
||||
|
||||
.field
|
||||
= f.label :primary_taxon, t('spree.admin.products.primary_taxon_form.product_category')
|
||||
= f.collection_select(:primary_taxon_id, Spree::Taxon.order(:name), :id, :name, { include_blank: true }, { class: "select2 fullwidth" })
|
||||
|
||||
.clear
|
||||
|
||||
@@ -1,15 +1,18 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'pagy/extras/arel'
|
||||
require 'pagy/extras/items'
|
||||
require 'pagy/extras/overflow'
|
||||
|
||||
|
||||
# Pagy Variables
|
||||
# See https://ddnexus.github.io/pagy/api/pagy#variables
|
||||
Pagy::DEFAULT[:items] = 100
|
||||
|
||||
# Items extra: Allow the client to request a custom number of items per page with an optional selector UI
|
||||
# See https://ddnexus.github.io/pagy/extras/items
|
||||
require 'pagy/extras/items'
|
||||
Pagy::DEFAULT[:items_param] = :per_page
|
||||
Pagy::DEFAULT[:max_items] = 100
|
||||
|
||||
# For handling requests for non-existant pages eg: page 35 when there are only 4 pages of results
|
||||
require 'pagy/extras/overflow'
|
||||
Pagy::DEFAULT[:overflow] = :empty_page
|
||||
|
||||
@@ -72,6 +72,9 @@ en:
|
||||
variant_unit: "Variant Unit"
|
||||
variant_unit_name: "Variant Unit Name"
|
||||
unit_value: "Unit value"
|
||||
spree/variant:
|
||||
primary_taxon: "Product Category"
|
||||
shipping_category_id: "Shipping Category"
|
||||
spree/credit_card:
|
||||
base: "Credit Card"
|
||||
number: "Number"
|
||||
@@ -1246,6 +1249,7 @@ en:
|
||||
open_date: "Open Date"
|
||||
close_date: "Close Date"
|
||||
display_ordering_in_shopfront: "Display ordering in shopfront:"
|
||||
shopfront_sort_by_product: "By product"
|
||||
shopfront_sort_by_category: "By category"
|
||||
shopfront_sort_by_producer: "By producer"
|
||||
shopfront_sort_by_category_placeholder: "Category"
|
||||
|
||||
5
db/migrate/20230803191831_add_taxons_to_variants.rb
Normal file
5
db/migrate/20230803191831_add_taxons_to_variants.rb
Normal file
@@ -0,0 +1,5 @@
|
||||
class AddTaxonsToVariants < ActiveRecord::Migration[7.0]
|
||||
def change
|
||||
add_reference :spree_variants, :primary_taxon, foreign_key: { to_table: :spree_taxons }
|
||||
end
|
||||
end
|
||||
5
db/migrate/20230807114014_remove_taxon_constraint.rb
Normal file
5
db/migrate/20230807114014_remove_taxon_constraint.rb
Normal file
@@ -0,0 +1,5 @@
|
||||
class RemoveTaxonConstraint < ActiveRecord::Migration[7.0]
|
||||
def change
|
||||
change_column_null :spree_products, :primary_taxon_id, true
|
||||
end
|
||||
end
|
||||
15
db/migrate/20230807122052_migrate_product_taxons.rb
Normal file
15
db/migrate/20230807122052_migrate_product_taxons.rb
Normal file
@@ -0,0 +1,15 @@
|
||||
class MigrateProductTaxons < ActiveRecord::Migration[7.0]
|
||||
def up
|
||||
migrate_primary_taxon
|
||||
end
|
||||
|
||||
def migrate_primary_taxon
|
||||
ActiveRecord::Base.connection.execute(<<-SQL
|
||||
UPDATE spree_variants
|
||||
SET primary_taxon_id = spree_products.primary_taxon_id
|
||||
FROM spree_products
|
||||
WHERE spree_variants.product_id = spree_products.id
|
||||
SQL
|
||||
)
|
||||
end
|
||||
end
|
||||
@@ -698,7 +698,7 @@ ActiveRecord::Schema[7.0].define(version: 2024_02_13_044159) do
|
||||
t.float "variant_unit_scale"
|
||||
t.string "variant_unit_name", limit: 255
|
||||
t.text "notes"
|
||||
t.integer "primary_taxon_id", null: false
|
||||
t.integer "primary_taxon_id"
|
||||
t.boolean "inherits_properties", default: true, null: false
|
||||
t.string "sku", limit: 255, default: "", null: false
|
||||
t.index ["deleted_at"], name: "index_products_on_deleted_at"
|
||||
@@ -979,6 +979,8 @@ ActiveRecord::Schema[7.0].define(version: 2024_02_13_044159) do
|
||||
t.datetime "updated_at", default: -> { "now()" }, null: false
|
||||
t.bigint "tax_category_id"
|
||||
t.bigint "shipping_category_id"
|
||||
t.bigint "primary_taxon_id"
|
||||
t.index ["primary_taxon_id"], name: "index_spree_variants_on_primary_taxon_id"
|
||||
t.index ["product_id"], name: "index_variants_on_product_id"
|
||||
t.index ["shipping_category_id"], name: "index_spree_variants_on_shipping_category_id"
|
||||
t.index ["sku"], name: "index_spree_variants_on_sku"
|
||||
@@ -1225,6 +1227,7 @@ ActiveRecord::Schema[7.0].define(version: 2024_02_13_044159) do
|
||||
add_foreign_key "spree_variants", "spree_products", column: "product_id", name: "spree_variants_product_id_fk"
|
||||
add_foreign_key "spree_variants", "spree_shipping_categories", column: "shipping_category_id"
|
||||
add_foreign_key "spree_variants", "spree_tax_categories", column: "tax_category_id"
|
||||
add_foreign_key "spree_variants", "spree_taxons", column: "primary_taxon_id"
|
||||
add_foreign_key "spree_zone_members", "spree_zones", column: "zone_id", name: "spree_zone_members_zone_id_fk"
|
||||
add_foreign_key "subscription_line_items", "spree_variants", column: "variant_id", name: "subscription_line_items_variant_id_fk"
|
||||
add_foreign_key "subscription_line_items", "subscriptions", name: "subscription_line_items_subscription_id_fk"
|
||||
|
||||
@@ -65,26 +65,25 @@ class SuppliedProductBuilder < DfcBuilder
|
||||
Spree::Product.new(
|
||||
name: supplied_product.name,
|
||||
description: supplied_product.description,
|
||||
price: 0, # will be in DFC Offer
|
||||
primary_taxon: taxon(supplied_product)
|
||||
price: 0 # will be in DFC Offer
|
||||
).tap do |product|
|
||||
QuantitativeValueBuilder.apply(supplied_product.quantity, product)
|
||||
product.ensure_standard_variant
|
||||
product.variants.first.primary_taxon = taxon(supplied_product)
|
||||
end
|
||||
end
|
||||
|
||||
def self.apply(supplied_product, variant)
|
||||
variant.product.assign_attributes(
|
||||
description: supplied_product.description,
|
||||
primary_taxon: taxon(supplied_product)
|
||||
)
|
||||
variant.product.assign_attributes(description: supplied_product.description)
|
||||
|
||||
variant.display_name = supplied_product.name
|
||||
variant.primary_taxon = taxon(supplied_product)
|
||||
QuantitativeValueBuilder.apply(supplied_product.quantity, variant.product)
|
||||
variant.unit_value = variant.product.unit_value
|
||||
end
|
||||
|
||||
def self.product_type(variant)
|
||||
taxon_dfc_id = variant.product.primary_taxon&.dfc_id
|
||||
taxon_dfc_id = variant.primary_taxon&.dfc_id
|
||||
|
||||
DfcProductTypeFactory.for(taxon_dfc_id)
|
||||
end
|
||||
|
||||
@@ -10,11 +10,10 @@ describe "SuppliedProducts", type: :request, swagger_doc: "dfc.yaml", rswag_auto
|
||||
:product_with_image,
|
||||
id: 90_000,
|
||||
supplier: enterprise, name: "Pesto", description: "Basil Pesto",
|
||||
variants: [variant],
|
||||
primary_taxon: taxon
|
||||
variants: [variant]
|
||||
)
|
||||
}
|
||||
let(:variant) { build(:base_variant, id: 10_001, unit_value: 1) }
|
||||
let(:variant) { build(:base_variant, id: 10_001, unit_value: 1, primary_taxon: taxon) }
|
||||
let(:taxon) {
|
||||
build(
|
||||
:taxon,
|
||||
@@ -114,7 +113,7 @@ describe "SuppliedProducts", type: :request, swagger_doc: "dfc.yaml", rswag_auto
|
||||
product = Spree::Product.find(product_id)
|
||||
expect(product.name).to eq "Apple"
|
||||
expect(product.variants).to eq [variant]
|
||||
expect(product.primary_taxon).to eq(non_local_vegetable)
|
||||
expect(product.variants.first.primary_taxon).to eq(non_local_vegetable)
|
||||
|
||||
# Creates a variant for existing product
|
||||
supplied_product[:'ofn:spree_product_id'] = product_id
|
||||
@@ -244,7 +243,7 @@ describe "SuppliedProducts", type: :request, swagger_doc: "dfc.yaml", rswag_auto
|
||||
}.to change { variant.description }.to("DFC-Pesto updated")
|
||||
.and change { variant.display_name }.to("Pesto novo")
|
||||
.and change { variant.unit_value }.to(17)
|
||||
.and change { variant.product.primary_taxon }.to(drink_taxon)
|
||||
.and change { variant.primary_taxon }.to(drink_taxon)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -7,12 +7,10 @@ describe SuppliedProductBuilder do
|
||||
|
||||
subject(:builder) { described_class }
|
||||
let(:variant) {
|
||||
build(:variant, id: 5, product: spree_product)
|
||||
build(:variant, id: 5, product: spree_product, primary_taxon: taxon)
|
||||
}
|
||||
let(:spree_product) {
|
||||
create(:product, id: 6, supplier:).tap do |p|
|
||||
p.primary_taxon = taxon
|
||||
end
|
||||
create(:product, id: 6, supplier:)
|
||||
}
|
||||
let(:supplier) {
|
||||
build(:supplier_enterprise, id: 7)
|
||||
@@ -121,7 +119,7 @@ describe SuppliedProductBuilder do
|
||||
it "assigns the taxon matching the DFC product type" do
|
||||
product = builder.import_product(supplied_product)
|
||||
|
||||
expect(product.primary_taxon).to eq(taxon)
|
||||
expect(product.variants.first.primary_taxon).to eq(taxon)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -191,58 +189,7 @@ describe SuppliedProductBuilder do
|
||||
imported_product = imported_variant.product
|
||||
expect(imported_product.id).to eq(spree_product.id)
|
||||
expect(imported_product.description).to eq("Better Awesome tomato")
|
||||
expect(imported_product.primary_taxon).to eq(new_taxon)
|
||||
end
|
||||
|
||||
it "adds a new variant" do
|
||||
expect(imported_variant.id).to be_nil
|
||||
expect(imported_variant.product).to eq(spree_product)
|
||||
expect(imported_variant.display_name).to eq("Tomato")
|
||||
expect(imported_variant.unit_value).to eq(2000)
|
||||
end
|
||||
end
|
||||
|
||||
context "with spree_product_uri supplied" do
|
||||
let(:imported_variant) { builder.import_variant(supplied_product, supplier) }
|
||||
let(:product_type) { DfcLoader.connector.PRODUCT_TYPES.DRINK.SOFT_DRINK }
|
||||
let!(:new_taxon) {
|
||||
create(
|
||||
:taxon,
|
||||
name: "Soft Drink",
|
||||
dfc_id: "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/productTypes.rdf#soft-drink"
|
||||
)
|
||||
}
|
||||
|
||||
context "when spree_product_uri match the server host" do
|
||||
let(:supplied_product) do
|
||||
variant.save! # referenced in spree_product_id
|
||||
|
||||
DfcProvider::SuppliedProduct.new(
|
||||
"https://example.net/tomato",
|
||||
name: "Tomato",
|
||||
description: "Better Awesome tomato",
|
||||
quantity: DataFoodConsortium::Connector::QuantitativeValue.new(
|
||||
unit: DfcLoader.connector.MEASURES.KILOGRAM,
|
||||
value: 2,
|
||||
),
|
||||
productType: product_type,
|
||||
spree_product_uri: "http://test.host/api/dfc/enterprises/7?spree_product_id=6"
|
||||
)
|
||||
end
|
||||
|
||||
it "update an existing Spree::Product" do
|
||||
imported_product = imported_variant.product
|
||||
expect(imported_product.id).to eq(spree_product.id)
|
||||
expect(imported_product.description).to eq("Better Awesome tomato")
|
||||
expect(imported_product.primary_taxon).to eq(new_taxon)
|
||||
end
|
||||
|
||||
it "adds a new variant" do
|
||||
expect(imported_variant.id).to be_nil
|
||||
expect(imported_variant.product).to eq(spree_product)
|
||||
expect(imported_variant.display_name).to eq("Tomato")
|
||||
expect(imported_variant.unit_value).to eq(2000)
|
||||
end
|
||||
expect(imported_variant.primary_taxon).to eq(new_taxon)
|
||||
end
|
||||
|
||||
context "when spree_product_uri doesn't match the server host" do
|
||||
|
||||
@@ -17,7 +17,7 @@ module Reporting
|
||||
producer_suburb: proc { |variant| variant.product.supplier.address.city },
|
||||
product: proc { |variant| variant.product.name },
|
||||
product_properties: proc { |v| v.product.properties.map(&:name).join(", ") },
|
||||
taxons: proc { |variant| variant.product.primary_taxon.name },
|
||||
taxons: proc { |variant| variant.primary_taxon.name },
|
||||
variant_value: proc { |variant| variant.full_name },
|
||||
price: proc { |variant| variant.price },
|
||||
group_buy_unit_quantity: proc { |variant| variant.product.group_buy_unit_size },
|
||||
|
||||
@@ -32,7 +32,7 @@ module Reporting
|
||||
total: proc { |_variant| '' },
|
||||
gst: proc { |variant| gst(variant) },
|
||||
grower: proc { |variant| grower_and_method(variant) },
|
||||
taxon: proc { |variant| variant.product.primary_taxon.name }
|
||||
taxon: proc { |variant| variant.primary_taxon.name }
|
||||
}
|
||||
end
|
||||
|
||||
|
||||
@@ -126,7 +126,7 @@ module Api
|
||||
context "with taxon filters" do
|
||||
it "filters by taxon" do
|
||||
api_get :products, id: order_cycle.id, distributor: distributor.id,
|
||||
q: { primary_taxon_id_in_any: [taxon2.id] }
|
||||
q: { variants_primary_taxon_id_in_any: [taxon2.id] }
|
||||
|
||||
expect(product_ids).to include product2.id, product3.id
|
||||
expect(product_ids).not_to include product1.id, product4.id
|
||||
|
||||
@@ -25,6 +25,7 @@ describe Api::V0::ProductsController, type: :controller do
|
||||
end
|
||||
|
||||
context "as a normal user" do
|
||||
let(:taxon) { create(:taxon) }
|
||||
let(:attachment) { fixture_file_upload("thinking-cat.jpg") }
|
||||
|
||||
before do
|
||||
@@ -34,9 +35,11 @@ describe Api::V0::ProductsController, type: :controller do
|
||||
|
||||
it "gets a single product" do
|
||||
product.create_image!(attachment:)
|
||||
product.variants.create!(unit_value: "1", unit_description: "thing", price: 1)
|
||||
product.variants.create!(unit_value: "1", unit_description: "thing", price: 1,
|
||||
primary_taxon: taxon)
|
||||
product.variants.first.images.create!(attachment:)
|
||||
product.set_property("spree", "rocks")
|
||||
|
||||
api_get :show, id: product.to_param
|
||||
|
||||
expect(all_attributes.all?{ |attr| json_response.keys.include? attr }).to eq(true)
|
||||
@@ -117,8 +120,7 @@ describe Api::V0::ProductsController, type: :controller do
|
||||
expect(response.status).to eq(422)
|
||||
expect(json_response["error"]).to eq("Invalid resource. Please fix errors and try again.")
|
||||
errors = json_response["errors"]
|
||||
expect(errors.keys).to match_array(["name", "primary_taxon", "supplier", "variant_unit",
|
||||
"price"])
|
||||
expect(errors.keys).to match_array(["name", "supplier", "variant_unit", "price"])
|
||||
end
|
||||
|
||||
it "can update a product" do
|
||||
@@ -266,7 +268,8 @@ describe Api::V0::ProductsController, type: :controller do
|
||||
end
|
||||
|
||||
it "filters results by product category" do
|
||||
api_get :bulk_products, { page: 1, per_page: 15, q: { primary_taxon_id_eq: taxon.id } },
|
||||
api_get :bulk_products,
|
||||
{ page: 1, per_page: 15, q: { variants_primary_taxon_id_eq: taxon.id } },
|
||||
format: :json
|
||||
expect(returned_product_ids).to eq [product3.id, product2.id]
|
||||
end
|
||||
|
||||
@@ -127,6 +127,7 @@ describe Api::V0::VariantsController, type: :controller do
|
||||
|
||||
let(:product) { create(:product) }
|
||||
let(:variant) { product.variants.first }
|
||||
let(:taxon) { create(:taxon) }
|
||||
let!(:variant2) { create(:variant, product:) }
|
||||
|
||||
context "deleted variants" do
|
||||
@@ -144,7 +145,7 @@ describe Api::V0::VariantsController, type: :controller do
|
||||
it "can create a new variant" do
|
||||
original_number_of_variants = variant.product.variants.count
|
||||
api_post :create, variant: { sku: "12345", unit_value: "1",
|
||||
unit_description: "L", price: "1" },
|
||||
unit_description: "L", price: "1", primary_taxon_id: taxon.id },
|
||||
product_id: variant.product.id
|
||||
|
||||
expect(attributes.all?{ |attr| json_response.include? attr.to_s }).to eq(true)
|
||||
|
||||
@@ -93,6 +93,7 @@ describe Spree::Admin::ProductsController, type: :controller do
|
||||
variant_unit_name: nil
|
||||
)
|
||||
end
|
||||
let!(:taxon) { create(:taxon) }
|
||||
|
||||
before { controller_login_as_enterprise_user([producer]) }
|
||||
|
||||
@@ -111,7 +112,8 @@ describe Spree::Admin::ProductsController, type: :controller do
|
||||
"price" => "5.0",
|
||||
"unit_value" => 4,
|
||||
"unit_description" => "",
|
||||
"display_name" => "name"
|
||||
"display_name" => "name",
|
||||
"primary_taxon_id" => taxon.id
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -11,7 +11,9 @@ module Spree
|
||||
describe "deleted variants" do
|
||||
let(:product) { create(:product, name: 'Product A') }
|
||||
let(:deleted_variant) do
|
||||
deleted_variant = product.variants.create(unit_value: "2", price: 1)
|
||||
deleted_variant = product.variants.create(
|
||||
unit_value: "2", price: 1, primary_taxon: create(:taxon)
|
||||
)
|
||||
deleted_variant.delete
|
||||
deleted_variant
|
||||
end
|
||||
|
||||
@@ -3,13 +3,18 @@
|
||||
FactoryBot.define do
|
||||
factory :base_product, class: Spree::Product do
|
||||
sequence(:name) { |n| "Product ##{n} - #{Kernel.rand(9999)}" }
|
||||
|
||||
transient do
|
||||
primary_taxon { nil }
|
||||
end
|
||||
|
||||
primary_taxon_id { |p| (p.primary_taxon || Spree::Taxon.first || create(:taxon)).id }
|
||||
description { generate(:random_description) }
|
||||
price { 19.99 }
|
||||
sku { 'ABC' }
|
||||
deleted_at { nil }
|
||||
|
||||
supplier { Enterprise.is_primary_producer.first || FactoryBot.create(:supplier_enterprise) }
|
||||
primary_taxon { Spree::Taxon.first || FactoryBot.create(:taxon) }
|
||||
|
||||
unit_value { 1 }
|
||||
unit_description { '' }
|
||||
@@ -48,6 +53,7 @@ FactoryBot.define do
|
||||
on_demand { false }
|
||||
on_hand { 5 }
|
||||
end
|
||||
|
||||
after(:create) do |product, evaluator|
|
||||
product.variants.first.on_demand = evaluator.on_demand
|
||||
product.variants.first.on_hand = evaluator.on_hand
|
||||
|
||||
@@ -11,7 +11,8 @@ FactoryBot.define do
|
||||
width { generate(:random_float) }
|
||||
depth { generate(:random_float) }
|
||||
|
||||
product { |p| p.association(:base_product) }
|
||||
primary_taxon { Spree::Taxon.first || FactoryBot.create(:taxon) }
|
||||
product { |p| p.association(:product) }
|
||||
|
||||
# ensure stock item will be created for this variant
|
||||
before(:create) { create(:stock_location) if Spree::StockLocation.count.zero? }
|
||||
@@ -22,7 +23,6 @@ FactoryBot.define do
|
||||
on_hand { 5 }
|
||||
end
|
||||
|
||||
product { |p| p.association(:product) }
|
||||
unit_value { 1 }
|
||||
unit_description { '' }
|
||||
|
||||
|
||||
@@ -803,8 +803,8 @@ describe "AdminProductEditCtrl", ->
|
||||
expect(product).toEqual
|
||||
id: 123
|
||||
variants: [
|
||||
{id: -1, price: null, unit_value: null, tax_category_id: null, unit_description: null, on_demand: false, on_hand: null, display_as: null, display_name: null}
|
||||
{id: -2, price: null, unit_value: null, tax_category_id: null, unit_description: null, on_demand: false, on_hand: null, display_as: null, display_name: null}
|
||||
{id: -1, price: null, unit_value: null, tax_category_id: null, unit_description: null, on_demand: false, on_hand: null, display_as: null, display_name: null, category_id: null}
|
||||
{id: -2, price: null, unit_value: null, tax_category_id: null, unit_description: null, on_demand: false, on_hand: null, display_as: null, display_name: null, category_id: null}
|
||||
]
|
||||
|
||||
it "shows the variant(s)", ->
|
||||
|
||||
@@ -43,7 +43,7 @@ module Reporting
|
||||
allow(variant).to receive_message_chain(:product, :name).and_return("Product Name")
|
||||
allow(variant).to receive_message_chain(:product, :properties)
|
||||
.and_return [double(name: "property1"), double(name: "property2")]
|
||||
allow(variant).to receive_message_chain(:product, :primary_taxon).
|
||||
allow(variant).to receive_message_chain(:primary_taxon).
|
||||
and_return double(name: "taxon1")
|
||||
allow(variant).to receive_message_chain(:product, :group_buy_unit_size).and_return(21)
|
||||
allow(subject).to receive(:query_result).and_return [variant]
|
||||
|
||||
@@ -12,6 +12,7 @@ describe Enterprise do
|
||||
|
||||
describe "with a supplied product" do
|
||||
let(:product) { create(:simple_product, supplier: enterprise, primary_taxon_id: taxon.id) }
|
||||
let(:variant) { product.variants.first }
|
||||
let(:property) { product.product_properties.last }
|
||||
let(:producer_property) { enterprise.producer_properties.last }
|
||||
|
||||
@@ -20,9 +21,9 @@ describe Enterprise do
|
||||
enterprise.set_producer_property 'Biodynamic', 'ASDF 4321'
|
||||
end
|
||||
|
||||
it "touches enterprise when a taxon on that product changes" do
|
||||
it "touches enterprise when a taxon on that variant changes" do
|
||||
expect {
|
||||
later { product.update(primary_taxon_id: taxon2.id) }
|
||||
later { variant.update(primary_taxon_id: taxon2.id) }
|
||||
}.to change { enterprise.reload.updated_at }
|
||||
end
|
||||
|
||||
@@ -47,6 +48,7 @@ describe Enterprise do
|
||||
|
||||
describe "with a distributed product" do
|
||||
let(:product) { create(:simple_product, primary_taxon_id: taxon.id) }
|
||||
let(:variant) { product.variants.first }
|
||||
let(:oc) {
|
||||
create(:simple_order_cycle, distributors: [enterprise],
|
||||
variants: [product.variants.first])
|
||||
@@ -63,9 +65,9 @@ describe Enterprise do
|
||||
context "with an order cycle" do
|
||||
before { oc }
|
||||
|
||||
it "touches enterprise when a taxon on that product changes" do
|
||||
it "touches enterprise when a taxon on that variant changes" do
|
||||
expect {
|
||||
later { product.update(primary_taxon_id: taxon2.id) }
|
||||
later { variant.update(primary_taxon_id: taxon2.id) }
|
||||
}.to change { enterprise.reload.updated_at }
|
||||
end
|
||||
|
||||
|
||||
@@ -284,7 +284,7 @@ describe ProductImport::ProductImporter do
|
||||
carrots = Spree::Product.find_by(name: 'Good Carrots')
|
||||
expect(carrots.on_hand).to eq 5
|
||||
expect(carrots.variants.first.price).to eq 3.20
|
||||
expect(carrots.primary_taxon.name).to eq "Vegetables"
|
||||
expect(carrots.variants.first.primary_taxon.name).to eq "Vegetables"
|
||||
expect(carrots.variants.first.shipping_category).to eq shipping_category
|
||||
expect(carrots.supplier).to eq enterprise
|
||||
expect(carrots.variants.first.unit_presentation).to eq "500g"
|
||||
@@ -562,7 +562,7 @@ describe ProductImport::ProductImporter do
|
||||
let(:csv_data) {
|
||||
CSV.generate do |csv|
|
||||
csv << ["name", "producer", "category", "on_hand", "price", "units", "unit_type"]
|
||||
csv << ["Beetroot", enterprise3.name, "Meat", "5", "3.50", "500", "g"]
|
||||
csv << ["Beetroot", enterprise3.name, "Vegetables", "5", "3.50", "500", "Kg"]
|
||||
csv << ["Tomato", enterprise3.name, "Vegetables", "6", "5.50", "500", "Kg"]
|
||||
end
|
||||
}
|
||||
|
||||
@@ -155,7 +155,6 @@ module Spree
|
||||
|
||||
describe "associations" do
|
||||
it { is_expected.to belong_to(:supplier).required }
|
||||
it { is_expected.to belong_to(:primary_taxon).required }
|
||||
end
|
||||
|
||||
describe "validations and defaults" do
|
||||
@@ -167,10 +166,6 @@ module Spree
|
||||
it { is_expected.to validate_length_of(:name).is_at_most(255) }
|
||||
it { is_expected.to validate_length_of(:sku).is_at_most(255) }
|
||||
|
||||
it "requires a primary taxon" do
|
||||
expect(build(:simple_product, primary_taxon: nil)).not_to be_valid
|
||||
end
|
||||
|
||||
context "unit value" do
|
||||
it "requires a unit value when variant unit is weight" do
|
||||
expect(build(:simple_product, variant_unit: 'weight', variant_unit_name: 'name',
|
||||
@@ -229,10 +224,11 @@ module Spree
|
||||
context "saving a new product" do
|
||||
let!(:product){ Spree::Product.new }
|
||||
let!(:shipping_category){ create(:shipping_category) }
|
||||
let!(:taxon){ create(:taxon) }
|
||||
|
||||
before do
|
||||
create(:stock_location)
|
||||
product.primary_taxon = create(:taxon)
|
||||
product.primary_taxon_id = taxon.id
|
||||
product.supplier = create(:supplier_enterprise)
|
||||
product.name = "Product1"
|
||||
product.variant_unit = "weight"
|
||||
@@ -248,6 +244,7 @@ module Spree
|
||||
standard_variant = product.variants.reload.first
|
||||
expect(standard_variant.price).to eq 4.27
|
||||
expect(standard_variant.shipping_category).to eq shipping_category
|
||||
expect(standard_variant.primary_taxon).to eq taxon
|
||||
end
|
||||
end
|
||||
|
||||
@@ -324,7 +321,7 @@ module Spree
|
||||
let(:product) { create(:simple_product) }
|
||||
|
||||
describe "touching affected enterprises when the product is deleted" do
|
||||
let(:product) { create(:simple_product) }
|
||||
let(:product) { create(:simple_product, supplier: distributor) }
|
||||
let(:supplier) { product.supplier }
|
||||
let(:distributor) { create(:distributor_enterprise) }
|
||||
let!(:oc) {
|
||||
|
||||
@@ -41,11 +41,11 @@ module Spree
|
||||
let!(:taxon1) { create(:taxon) }
|
||||
let!(:taxon2) { create(:taxon) }
|
||||
let!(:product) { create(:simple_product, primary_taxon: taxon1) }
|
||||
let(:variant) { product.variants.first }
|
||||
|
||||
it "is touched when assignment of primary_taxon on a product changes" do
|
||||
it "is touched when assignment of primary_taxon on a variant changes" do
|
||||
expect do
|
||||
product.primary_taxon = taxon2
|
||||
product.save
|
||||
variant.update(primary_taxon: taxon2)
|
||||
end.to change { taxon2.reload.updated_at }
|
||||
end
|
||||
end
|
||||
|
||||
@@ -12,7 +12,7 @@ describe ProductScopeQuery do
|
||||
|
||||
before { current_api_user.enterprise_roles.create(enterprise: supplier2) }
|
||||
|
||||
describe 'bulk update' do
|
||||
describe '#bulk_products' do
|
||||
let!(:product3) { create(:product, supplier: supplier2) }
|
||||
|
||||
it "returns a list of products" do
|
||||
@@ -28,12 +28,29 @@ describe ProductScopeQuery do
|
||||
expect(subject).not_to include(product2, product3)
|
||||
end
|
||||
|
||||
it "filters results by product category" do
|
||||
subject = ProductScopeQuery
|
||||
.new(current_api_user, { q: { primary_taxon_id_eq: taxon.id } }).bulk_products
|
||||
describe "by variant category" do
|
||||
it "filters results by product category" do
|
||||
create(:variant, product: product2, primary_taxon: taxon)
|
||||
|
||||
expect(subject).to include(product, product2)
|
||||
expect(subject).not_to include(product3)
|
||||
subject = ProductScopeQuery
|
||||
.new(current_api_user, { q: { variants_primary_taxon_id_eq: taxon.id } })
|
||||
.bulk_products
|
||||
|
||||
expect(subject).to match_array([product, product2])
|
||||
expect(subject).not_to include(product3)
|
||||
end
|
||||
|
||||
context "with mutiple variant in the same category" do
|
||||
it "doesn't duplicate products" do
|
||||
create(:variant, product: product2, primary_taxon: taxon)
|
||||
|
||||
subject = ProductScopeQuery
|
||||
.new(current_api_user, { q: { variants_primary_taxon_id_eq: taxon.id } })
|
||||
.bulk_products
|
||||
|
||||
expect(subject).to match_array([product, product2])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
it "filters results by import_date" do
|
||||
|
||||
@@ -15,7 +15,7 @@ describe ProductsReflex, type: :reflex, feature: :admin_style_v3 do
|
||||
end
|
||||
|
||||
describe '#fetch' do
|
||||
subject{ build_reflex(method_name: :fetch, **context) }
|
||||
subject { build_reflex(method_name: :fetch, **context) }
|
||||
|
||||
describe "sorting" do
|
||||
let!(:product_z) { create(:simple_product, name: "Zucchini") }
|
||||
@@ -34,6 +34,27 @@ describe ProductsReflex, type: :reflex, feature: :admin_style_v3 do
|
||||
end
|
||||
end
|
||||
|
||||
describe '#filter' do
|
||||
context "when filtering by category" do
|
||||
let!(:product_a) { create(:simple_product, name: "Apples") }
|
||||
let!(:product_z) do
|
||||
create(:simple_product, name: "Zucchini").tap do |p|
|
||||
p.variants.first.update(primary_taxon: category_c)
|
||||
end
|
||||
end
|
||||
let(:category_c) { create(:taxon, name: "Category 1") }
|
||||
|
||||
it "returns product with a variant matching the given category" do
|
||||
# Add a second variant to test we are not returning duplicate product
|
||||
product_z.variants << create(:variant, primary_taxon: category_c)
|
||||
|
||||
reflex = run_reflex(:filter, params: { category_id: category_c.id } )
|
||||
|
||||
expect(reflex.get(:products).to_a).to eq([product_z])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#bulk_update' do
|
||||
let!(:variant_a1) {
|
||||
product_a.variants.first.tap{ |v|
|
||||
|
||||
@@ -28,7 +28,7 @@ describe Api::ProductSerializer do
|
||||
it "serializes various attributes" do
|
||||
expect(serializer.serializable_hash.keys).to eq [
|
||||
:id, :name, :meta_keywords, :group_buy, :notes, :description, :description_html,
|
||||
:properties_with_values, :variants, :primary_taxon, :image, :supplier
|
||||
:properties_with_values, :variants, :image, :supplier
|
||||
]
|
||||
end
|
||||
|
||||
|
||||
@@ -162,12 +162,6 @@ describe ProductsRenderer do
|
||||
expect(products_renderer.products_json).to include "998.0"
|
||||
end
|
||||
|
||||
it "includes the primary taxon" do
|
||||
taxon = create(:taxon)
|
||||
allow_any_instance_of(Spree::Product).to receive(:primary_taxon).and_return taxon
|
||||
expect(products_renderer.products_json).to include taxon.name
|
||||
end
|
||||
|
||||
it "loads tag_list for variants" do
|
||||
VariantOverride.create(variant:, hub: distributor, tag_list: 'lalala')
|
||||
expect(products_renderer.products_json).to include "[\"lalala\"]"
|
||||
|
||||
@@ -344,7 +344,6 @@ describe '
|
||||
within "tr#p_#{p.id}" do
|
||||
expect(page).to have_field "product_name", with: p.name
|
||||
expect(page).to have_select "producer_id", selected: s1.name
|
||||
expect(page).to have_select2 "p#{p.id}_category_id", selected: t2.name
|
||||
expect(page).to have_select "variant_unit_with_scale", selected: "Volume (L)"
|
||||
expect(page).to have_checked_field "inherits_properties"
|
||||
expect(page).to have_field "product_sku", with: p.sku
|
||||
@@ -352,7 +351,6 @@ describe '
|
||||
fill_in "product_name", with: "Big Bag Of Potatoes"
|
||||
select s2.name, from: 'producer_id'
|
||||
select "Weight (kg)", from: "variant_unit_with_scale"
|
||||
select2_select t1.name, from: "p#{p.id}_category_id"
|
||||
uncheck "inherits_properties"
|
||||
fill_in "product_sku", with: "NEW SKU"
|
||||
end
|
||||
@@ -365,7 +363,6 @@ describe '
|
||||
expect(p.supplier).to eq s2
|
||||
expect(p.variant_unit).to eq "weight"
|
||||
expect(p.variant_unit_scale).to eq 1000 # Kg
|
||||
expect(p.primary_taxon.permalink).to eq t1.permalink
|
||||
expect(p.inherits_properties).to be false
|
||||
expect(p.sku).to eq "NEW SKU"
|
||||
end
|
||||
|
||||
@@ -143,7 +143,7 @@ describe '
|
||||
expect(product.variants.first.unit_value).to eq(5000)
|
||||
expect(product.variants.first.unit_description).to eq("")
|
||||
expect(product.variant_unit_name).to eq("")
|
||||
expect(product.primary_taxon_id).to eq(taxon.id)
|
||||
expect(product.variants.first.primary_taxon_id).to eq(taxon.id)
|
||||
expect(product.variants.first.price.to_s).to eq('19.99')
|
||||
expect(product.on_hand).to eq(5)
|
||||
expect(product.variants.first.tax_category_id).to eq(tax_category.id)
|
||||
|
||||
@@ -358,15 +358,15 @@ describe '
|
||||
let(:taxon) { create(:taxon, name: 'Taxon Name') }
|
||||
let(:product1) {
|
||||
create(:simple_product, name: "Product Name", price: 100, supplier:,
|
||||
primary_taxon: taxon)
|
||||
primary_taxon_id: taxon.id)
|
||||
}
|
||||
let(:product2) {
|
||||
create(:simple_product, name: "Product 2", price: 99.0, variant_unit: 'weight',
|
||||
variant_unit_scale: 1, unit_value: '100', supplier:,
|
||||
primary_taxon: taxon, sku: "product_sku")
|
||||
primary_taxon_id: taxon.id, sku: "product_sku")
|
||||
}
|
||||
let(:variant1) { product1.variants.first }
|
||||
let(:variant2) { create(:variant, product: product1, price: 80.0) }
|
||||
let(:variant2) { create(:variant, product: product1, price: 80.0, primary_taxon: taxon) }
|
||||
let(:variant3) { product2.variants.first }
|
||||
|
||||
before do
|
||||
@@ -396,17 +396,17 @@ describe '
|
||||
expect(page).to have_table_row [product1.supplier.name, product1.supplier.address.city,
|
||||
"Product Name",
|
||||
product1.properties.map(&:presentation).join(", "),
|
||||
product1.primary_taxon.name, "1g", "100.0",
|
||||
taxon.name, "1g", "100.0",
|
||||
"none", "", "sku1", "No", "10"]
|
||||
expect(page).to have_table_row [product1.supplier.name, product1.supplier.address.city,
|
||||
"Product Name",
|
||||
product1.properties.map(&:presentation).join(", "),
|
||||
product1.primary_taxon.name, "1g", "80.0",
|
||||
taxon.name, "1g", "80.0",
|
||||
"none", "", "sku2", "No", "20"]
|
||||
expect(page).to have_table_row [product2.supplier.name, product1.supplier.address.city,
|
||||
"Product 2",
|
||||
product1.properties.map(&:presentation).join(", "),
|
||||
product2.primary_taxon.name, "100g", "99.0",
|
||||
taxon.name, "100g", "99.0",
|
||||
"none", "", "product_sku", "No", "9"]
|
||||
end
|
||||
|
||||
|
||||
@@ -9,6 +9,8 @@ describe '
|
||||
include AuthenticationHelper
|
||||
include WebHelper
|
||||
|
||||
let!(:taxon) { create(:taxon) }
|
||||
|
||||
describe "new variant" do
|
||||
it "creating a new variant" do
|
||||
# Given a product with a unit-related option type
|
||||
@@ -21,6 +23,7 @@ describe '
|
||||
|
||||
fill_in 'unit_value_human', with: '1'
|
||||
fill_in 'variant_unit_description', with: 'foo'
|
||||
select taxon.name, from: "variant_primary_taxon_id"
|
||||
click_button 'Create'
|
||||
|
||||
# Then the variant should have been created
|
||||
@@ -61,6 +64,7 @@ describe '
|
||||
# Expect variant_weight to accept 3 decimal places
|
||||
fill_in 'variant_weight', with: '1.234'
|
||||
fill_in 'unit_value_human', with: 1
|
||||
select taxon.name, from: "variant_primary_taxon_id"
|
||||
click_button 'Create'
|
||||
|
||||
# Then the variant should have been created
|
||||
|
||||
@@ -53,8 +53,9 @@ describe "Shops caching", caching: true do
|
||||
let!(:property) { create(:property, presentation: "Cached Property") }
|
||||
let!(:property2) { create(:property, presentation: "New Property") }
|
||||
let!(:product) {
|
||||
create(:product, primary_taxon: taxon, properties: [property])
|
||||
create(:product, primary_taxon_id: taxon.id, properties: [property])
|
||||
}
|
||||
let(:variant) { product.variants.first }
|
||||
let(:exchange) { order_cycle.exchanges.to_enterprises(distributor).outgoing.first }
|
||||
|
||||
let(:test_domain) {
|
||||
@@ -92,7 +93,7 @@ describe "Shops caching", caching: true do
|
||||
expect(page).to have_content taxon.name
|
||||
expect(page).to have_content property.presentation
|
||||
|
||||
product.update_attribute(:primary_taxon, taxon2)
|
||||
variant.update_attribute(:primary_taxon, taxon2)
|
||||
product.update_attribute(:properties, [property2])
|
||||
|
||||
visit enterprise_shop_path(distributor)
|
||||
|
||||
Reference in New Issue
Block a user