diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index e054d5ae51..6e13783512 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -6,13 +6,6 @@ # Note that changes in the inspected code, or installation of new # versions of RuboCop, may require this file to be generated again. -# Offense count: 1 -# This cop supports safe autocorrection (--autocorrect). -# Configuration parameters: EmptyLineBetweenMethodDefs, EmptyLineBetweenClassDefs, EmptyLineBetweenModuleDefs, DefLikeMacros, AllowAdjacentOneLineDefs, NumberOfEmptyLines. -Layout/EmptyLineBetweenDefs: - Exclude: - - 'app/services/products_renderer.rb' - # Offense count: 1 # This cop supports safe autocorrection (--autocorrect). Layout/EmptyLines: @@ -108,7 +101,7 @@ Lint/UselessMethodDefinition: Exclude: - 'app/models/spree/gateway.rb' -# Offense count: 24 +# Offense count: 23 # Configuration parameters: AllowedMethods, AllowedPatterns, CountRepeatedAttributes, Max. Metrics/AbcSize: Exclude: @@ -121,7 +114,6 @@ Metrics/AbcSize: - 'app/helpers/spree/admin/navigation_helper.rb' - 'app/models/enterprise_group.rb' - 'app/models/enterprise_relationship.rb' - - 'app/models/product_import/entry_processor.rb' - 'app/models/spree/ability.rb' - 'app/models/spree/address.rb' - 'app/models/spree/order/checkout.rb' @@ -253,7 +245,7 @@ Metrics/MethodLength: - 'lib/spree/localized_number.rb' - 'lib/tasks/sample_data/product_factory.rb' -# Offense count: 48 +# Offense count: 49 # Configuration parameters: CountComments, Max, CountAsOne. Metrics/ModuleLength: Exclude: @@ -283,6 +275,7 @@ Metrics/ModuleLength: - 'spec/controllers/payment_gateways/stripe_controller_spec.rb' - 'spec/controllers/spree/admin/adjustments_controller_spec.rb' - 'spec/controllers/spree/admin/payment_methods_controller_spec.rb' + - 'spec/controllers/spree/admin/variants_controller_spec.rb' - 'spec/lib/open_food_network/address_finder_spec.rb' - 'spec/lib/open_food_network/enterprise_fee_calculator_spec.rb' - 'spec/lib/open_food_network/order_cycle_form_applicator_spec.rb' @@ -893,7 +886,7 @@ Style/ReturnNilInPredicateMethodDefinition: - 'app/serializers/api/admin/customer_serializer.rb' - 'engines/order_management/app/services/order_management/subscriptions/validator.rb' -# Offense count: 204 +# Offense count: 207 Style/Send: Exclude: - 'spec/controllers/admin/subscriptions_controller_spec.rb' diff --git a/app/assets/javascripts/admin/bulk_product_update.js.coffee b/app/assets/javascripts/admin/bulk_product_update.js.coffee index 5adb65c399..7df51e7711 100644 --- a/app/assets/javascripts/admin/bulk_product_update.js.coffee +++ b/app/assets/javascripts/admin/bulk_product_update.js.coffee @@ -47,7 +47,7 @@ angular.module("ofn.admin").controller "AdminProductEditCtrl", ($scope, $timeout removeClearedValues() params = { 'q[name_cont]': $scope.q.query, - 'q[supplier_id_eq]': $scope.q.producerFilter, + 'q[variants_supplier_id_eq]': $scope.q.producerFilter, 'q[variants_primary_taxon_id_eq]': $scope.q.categoryFilter, 'q[s]': $scope.sorting, import_date: $scope.q.importDateFilter, @@ -217,7 +217,7 @@ angular.module("ofn.admin").controller "AdminProductEditCtrl", ($scope, $timeout products: productsToSubmit filters: 'q[name_cont]': $scope.q.query - 'q[supplier_id_eq]': $scope.q.producerFilter + 'q[variants_supplier_id_eq]': $scope.q.producerFilter 'q[variants_primary_taxon_id_eq]': $scope.q.categoryFilter 'q[s]': $scope.sorting import_date: $scope.q.importDateFilter @@ -314,9 +314,6 @@ filterSubmitProducts = (productsToFilter) -> if product.hasOwnProperty("name") filteredProduct.name = product.name hasUpdatableProperty = true - if product.hasOwnProperty("producer_id") - filteredProduct.supplier_id = product.producer_id - hasUpdatableProperty = true if product.hasOwnProperty("price") filteredProduct.price = product.price hasUpdatableProperty = true @@ -379,6 +376,9 @@ filterSubmitVariant = (variant) -> if variant.hasOwnProperty("display_as") filteredVariant.display_as = variant.display_as hasUpdatableProperty = true + if variant.hasOwnProperty("producer_id") + filteredVariant.supplier_id = variant.producer_id + hasUpdatableProperty = true {filteredVariant: filteredVariant, hasUpdatableProperty: hasUpdatableProperty} diff --git a/app/assets/javascripts/admin/index_utils/filters/attr_filter.js.coffee b/app/assets/javascripts/admin/index_utils/filters/attr_filter.js.coffee index c645b507f1..14255f3f85 100644 --- a/app/assets/javascripts/admin/index_utils/filters/attr_filter.js.coffee +++ b/app/assets/javascripts/admin/index_utils/filters/attr_filter.js.coffee @@ -1,12 +1,16 @@ # Used like a regular angular filter where an object is passed # Adds the additional special case that a value of 0 for the filter # acts as a bypass for that particular attribute + +# NOTE the name doesn't reflect what the filter does, it only fiters on the variant.producer_id angular.module("admin.indexUtils").filter "attrFilter", ($filter) -> return (objects, filters) -> - Object.keys(filters).reduce (filtered, attr) -> - filter = filters[attr] - return filtered if !filter? || filter == 0 - return $filter('filter')(filtered, (object) -> - object[attr] == filter - ) - , objects + filter = filters["producer_id"] + + return objects if !filter? || filter == 0 + + return $filter('filter')(objects, (product) -> + for variant in product.variants + return true if variant["producer_id"] == filter + false + , true) diff --git a/app/assets/javascripts/admin/line_items/controllers/line_items_controller.js.coffee b/app/assets/javascripts/admin/line_items/controllers/line_items_controller.js.coffee index e19c5c7b8b..88479620fa 100644 --- a/app/assets/javascripts/admin/line_items/controllers/line_items_controller.js.coffee +++ b/app/assets/javascripts/admin/line_items/controllers/line_items_controller.js.coffee @@ -27,7 +27,7 @@ angular.module("admin.lineItems").controller 'LineItemsCtrl', ($scope, $timeout, "order_bill_address_full_name_reversed", "order_bill_address_full_name_with_comma", "order_bill_address_full_name_with_comma_reversed", - "variant_product_supplier_name", + "variant_supplier_name", "order_email", "order_number", "product_name"].join("_or_") + "_cont" @@ -81,7 +81,7 @@ angular.module("admin.lineItems").controller 'LineItemsCtrl', ($scope, $timeout, "q[order_shipment_state_not_eq]": "shipped", "q[order_completed_at_not_null]": "true", "q[order_distributor_id_eq]": $scope.distributorFilter, - "q[variant_product_supplier_id_eq]": $scope.supplierFilter, + "q[variant_supplier_id_eq]": $scope.supplierFilter, "q[order_order_cycle_id_eq]": $scope.orderCycleFilter, "q[order_completed_at_gteq]": if formattedStartDate then formattedStartDate else undefined, "q[order_completed_at_lt]": if formattedEndDate then formattedEndDate else undefined, @@ -105,7 +105,7 @@ angular.module("admin.lineItems").controller 'LineItemsCtrl', ($scope, $timeout, Dereferencer.dereferenceAttr $scope.line_items, "supplier", Enterprises.byID $scope.loadOrders() RequestMonitor.load $q.all([$scope.orders.$promise]).then -> - Dereferencer.dereferenceAttr $scope.line_items, "order", Orders.byID + Dereferencer.dereferenceAttr $scope.line_items, "order", Orders.byID Dereferencer.dereferenceAttr $scope.orders, "distributor", Enterprises.byID Dereferencer.dereferenceAttr $scope.orders, "order_cycle", OrderCycles.byID $scope.bulk_order_form.$setPristine() @@ -133,7 +133,7 @@ angular.module("admin.lineItems").controller 'LineItemsCtrl', ($scope, $timeout, return $http( method: 'GET' url: "/admin/orders/#{order.number}/fire?e=cancel&send_cancellation_email=#{sendEmailCancellation}&restock_items=#{restock_items}") - + $scope.deleteLineItem = (lineItem) -> if lineItem.order.item_count == 1 ofnCancelOrderAlert((confirm, sendEmailCancellation, restock_items) -> @@ -167,7 +167,7 @@ angular.module("admin.lineItems").controller 'LineItemsCtrl', ($scope, $timeout, $scope.cancelOrder(order, sendEmailCancellation, restock_items).then(-> $scope.refreshData()) else Promise.all(LineItems.delete(item) for item in items).then(-> $scope.refreshData()) - , "js.admin.deleting_item_will_cancel_order") + , "js.admin.deleting_item_will_cancel_order") else ofnDeleteLineItemsAlert(() -> Promise.all(LineItems.delete(item) for item in lineItemsToDelete).then(-> $scope.refreshData()) @@ -199,7 +199,7 @@ angular.module("admin.lineItems").controller 'LineItemsCtrl', ($scope, $timeout, $scope.refreshData() $scope.getLineItemScale = (lineItem) -> - if lineItem.units_product && lineItem.units_variant && (lineItem.units_product.variant_unit == "weight" || lineItem.units_product.variant_unit == "volume") + if lineItem.units_product && lineItem.units_variant && (lineItem.units_product.variant_unit == "weight" || lineItem.units_product.variant_unit == "volume") lineItem.units_product.variant_unit_scale else 1 @@ -252,7 +252,7 @@ angular.module("admin.lineItems").controller 'LineItemsCtrl', ($scope, $timeout, scale = $scope.getScale(unitsProduct, unitsVariant) if scale $scope.getFormattedValueWithUnitName(value, unitsProduct, unitsVariant, scale) - else + else '' $scope.fulfilled = (sumOfUnitValues) -> diff --git a/app/assets/javascripts/admin/variant_overrides/directives/track_inheritance.js.coffee b/app/assets/javascripts/admin/variant_overrides/directives/track_inheritance.js.coffee index ecc1cdac29..6c908ea641 100644 --- a/app/assets/javascripts/admin/variant_overrides/directives/track_inheritance.js.coffee +++ b/app/assets/javascripts/admin/variant_overrides/directives/track_inheritance.js.coffee @@ -2,10 +2,10 @@ angular.module("admin.variantOverrides").directive "trackInheritance", (VariantO require: "ngModel" link: (scope, element, attrs, ngModel) -> # This is a bit hacky, but it allows us to load the inherit property on the VO, but then not submit it - scope.inherit = angular.equals scope.variantOverrides[scope.hub_id][scope.variant.id], VariantOverrides.newFor scope.hub_id, scope.variant.id + scope.inherit = angular.equals scope.variantOverrides[scope.hub_id][scope.variant.id], VariantOverrides.newFor scope.hub_id, scope.variant ngModel.$parsers.push (viewValue) -> if ngModel.$dirty && viewValue - DirtyVariantOverrides.inherit scope.hub_id, scope.variant.id, scope.variantOverrides[scope.hub_id][scope.variant.id].id + DirtyVariantOverrides.inherit scope.hub_id, scope.variant, scope.variantOverrides[scope.hub_id][scope.variant.id].id scope.displayDirty() viewValue diff --git a/app/assets/javascripts/admin/variant_overrides/filters/hub_permissions_filter.js.coffee b/app/assets/javascripts/admin/variant_overrides/filters/hub_permissions_filter.js.coffee index b1434fb21e..7818fadc78 100644 --- a/app/assets/javascripts/admin/variant_overrides/filters/hub_permissions_filter.js.coffee +++ b/app/assets/javascripts/admin/variant_overrides/filters/hub_permissions_filter.js.coffee @@ -2,4 +2,8 @@ angular.module("admin.variantOverrides").filter "hubPermissions", ($filter) -> return (products, hubPermissions, hub_id) -> return [] if !hub_id return [] if !hubPermissions[hub_id] - return $filter('filter')(products, ((product) -> hubPermissions[hub_id].indexOf(product.producer_id) > -1), true) + + return $filter('filter')(products, ((product) -> + for variant in product.variants + return hubPermissions[hub_id].indexOf(variant.producer_id) > -1 + ), true) diff --git a/app/assets/javascripts/admin/variant_overrides/services/dirty_variant_overrides.js.coffee b/app/assets/javascripts/admin/variant_overrides/services/dirty_variant_overrides.js.coffee index 537fb484dc..0d5512979f 100644 --- a/app/assets/javascripts/admin/variant_overrides/services/dirty_variant_overrides.js.coffee +++ b/app/assets/javascripts/admin/variant_overrides/services/dirty_variant_overrides.js.coffee @@ -12,11 +12,11 @@ angular.module("admin.variantOverrides").factory "DirtyVariantOverrides", ($http @add(hub_id, variant_id, vo_id) @dirtyVariantOverrides[hub_id][variant_id][attr] = value - inherit: (hub_id, variant_id, vo_id) -> - @add(hub_id, variant_id, vo_id) - blankVo = angular.copy(VariantOverrides.inherit(hub_id, variant_id)) + inherit: (hub_id, variant, vo_id) -> + @add(hub_id, variant.id, vo_id) + blankVo = angular.copy(VariantOverrides.inherit(hub_id, variant)) delete blankVo[attr] for attr, value of blankVo when attr not in @requiredAttrs() - @dirtyVariantOverrides[hub_id][variant_id] = blankVo + @dirtyVariantOverrides[hub_id][variant.id] = blankVo count: -> count = 0 diff --git a/app/assets/javascripts/admin/variant_overrides/services/variant_overrides.js.coffee b/app/assets/javascripts/admin/variant_overrides/services/variant_overrides.js.coffee index 94a4d093eb..351dbb89f5 100644 --- a/app/assets/javascripts/admin/variant_overrides/services/variant_overrides.js.coffee +++ b/app/assets/javascripts/admin/variant_overrides/services/variant_overrides.js.coffee @@ -13,17 +13,18 @@ angular.module("admin.variantOverrides").factory "VariantOverrides", (variantOve @variantOverrides[hub.id] ||= {} for product in products for variant in product.variants - @inherit(hub.id, variant.id) unless @variantOverrides[hub.id][variant.id] + @inherit(hub.id, variant) unless @variantOverrides[hub.id][variant.id] - inherit: (hub_id, variant_id) -> + inherit: (hub_id, variant) -> # This method is called from the trackInheritance directive, to reinstate inheritance - @variantOverrides[hub_id][variant_id] ||= {} - angular.extend @variantOverrides[hub_id][variant_id], @newFor hub_id, variant_id + @variantOverrides[hub_id][variant.id] ||= {} + angular.extend @variantOverrides[hub_id][variant.id], @newFor(hub_id, variant) - newFor: (hub_id, variant_id) -> + newFor: (hub_id, variant) -> # These properties need to match those checked in VariantOverrideSet.deletable? hub_id: hub_id - variant_id: variant_id + variant_id: variant.id + producer_id: variant.producer_id sku: null price: null count_on_hand: null diff --git a/app/assets/javascripts/darkswarm/controllers/products_controller.js.coffee b/app/assets/javascripts/darkswarm/controllers/products_controller.js.coffee index 79f1db42ab..96f40509df 100644 --- a/app/assets/javascripts/darkswarm/controllers/products_controller.js.coffee +++ b/app/assets/javascripts/darkswarm/controllers/products_controller.js.coffee @@ -4,15 +4,18 @@ angular.module('Darkswarm').controller "ProductsCtrl", ($scope, $sce, $filter, $ $scope.query = "" $scope.taxonSelectors = FilterSelectorsService.createSelectors() $scope.propertySelectors = FilterSelectorsService.createSelectors() + $scope.producerPropertySelectors = FilterSelectorsService.createSelectors() $scope.filtersActive = true $scope.page = 1 $scope.per_page = 10 $scope.order_cycle = OrderCycle.order_cycle $scope.supplied_taxons = null $scope.supplied_properties = null + $scope.supplied_producer_properties = null $scope.showFilterSidebar = false $scope.activeTaxons = [] $scope.activeProperties = [] + $scope.activeProducerProperties = [] # Update filters after initial load of shop tab $timeout => @@ -45,6 +48,12 @@ angular.module('Darkswarm').controller "ProductsCtrl", ($scope, $sce, $filter, $ $scope.supplied_properties[property.id] = Properties.properties_by_id[property.id] ) + OrderCycleResource.producerProperties params, (data)=> + $scope.supplied_producer_properties = {} + data.map( (property) -> + $scope.supplied_producer_properties[property.id] = Properties.properties_by_id[property.id] + ) + $scope.loadMore = -> if ($scope.page * $scope.per_page) <= Products.products.length $scope.loadMoreProducts() @@ -52,6 +61,7 @@ angular.module('Darkswarm').controller "ProductsCtrl", ($scope, $sce, $filter, $ $scope.$watch 'query', (newValue, oldValue) -> $scope.loadProducts() if newValue != oldValue $scope.$watchCollection 'activeTaxons', (newValue, oldValue) -> $scope.loadProducts() if newValue != oldValue $scope.$watchCollection 'activeProperties', (newValue, oldValue) -> $scope.loadProducts() if newValue != oldValue + $scope.$watchCollection 'activeProducerProperties', (newValue, oldValue) -> $scope.loadProducts() if newValue != oldValue $scope.loadProducts = -> $scope.page = 1 @@ -66,8 +76,9 @@ angular.module('Darkswarm').controller "ProductsCtrl", ($scope, $sce, $filter, $ id: $scope.order_cycle.order_cycle_id, page: page || $scope.page, 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[name_or_meta_keywords_or_variants_display_as_or_variants_display_name_or_variants_supplier_name_cont]': $scope.query, 'q[with_properties][]': $scope.activeProperties, + 'q[with_variants_supplier_properties][]': $scope.activeProducerProperties, 'q[variants_primary_taxon_id_in_any][]': $scope.activeTaxons } @@ -86,6 +97,12 @@ angular.module('Darkswarm').controller "ProductsCtrl", ($scope, $sce, $filter, $ Properties.properties_by_id[property_id].name ).join($scope.filtersJoinWord()) if $scope.activeProperties? + $scope.appliedProducerPropertiesList = -> + $scope.activeProducerProperties.map( (property_id) -> + Properties.properties_by_id[property_id].name + ).join($scope.filtersJoinWord()) if $scope.activeProducerProperties? + + $scope.filtersJoinWord = -> $sce.trustAsHtml(" #{t('products_or')} ") @@ -99,6 +116,7 @@ angular.module('Darkswarm').controller "ProductsCtrl", ($scope, $sce, $filter, $ $scope.clearFilters = -> $scope.taxonSelectors.clearAll() $scope.propertySelectors.clearAll() + $scope.producerPropertySelectors.clearAll() $scope.refreshStaleData = -> # If the products template has already been loaded but the controller is being initialized @@ -109,7 +127,7 @@ angular.module('Darkswarm').controller "ProductsCtrl", ($scope, $sce, $filter, $ $scope.loadProducts() $scope.filtersCount = () -> - $scope.taxonSelectors.totalActive() + $scope.propertySelectors.totalActive() + $scope.taxonSelectors.totalActive() + $scope.propertySelectors.totalActive() + $scope.producerPropertySelectors.totalActive() $scope.toggleFilterSidebar = -> $scope.showFilterSidebar = !$scope.showFilterSidebar diff --git a/app/assets/javascripts/darkswarm/services/order_cycle_resource.js.coffee b/app/assets/javascripts/darkswarm/services/order_cycle_resource.js.coffee index 092ea51345..5eb3e7b420 100644 --- a/app/assets/javascripts/darkswarm/services/order_cycle_resource.js.coffee +++ b/app/assets/javascripts/darkswarm/services/order_cycle_resource.js.coffee @@ -18,4 +18,11 @@ angular.module('Darkswarm').factory 'OrderCycleResource', ($resource) -> url: '/api/v0/order_cycles/:id/properties.json' params: id: '@id' + 'producerProperties': + method: 'GET' + isArray: true + url: '/api/v0/order_cycles/:id/producer_properties.json' + params: + id: '@id' + }) diff --git a/app/assets/javascripts/darkswarm/services/products.js.coffee b/app/assets/javascripts/darkswarm/services/products.js.coffee index 8482c34d57..6334f0cd7f 100644 --- a/app/assets/javascripts/darkswarm/services/products.js.coffee +++ b/app/assets/javascripts/darkswarm/services/products.js.coffee @@ -39,7 +39,7 @@ angular.module('Darkswarm').factory 'Products', (OrderCycleResource, OrderCycle, dereference: -> for product in @fetched_products - product.supplier = Shopfront.producers_by_id[product.supplier.id] + product.supplier = Shopfront.producers_by_id[product.variants[0].supplier.id] Dereferencer.dereference product.taxons, Taxons.taxons_by_id product.properties = angular.copy(product.properties_with_values) diff --git a/app/controllers/admin/enterprises_controller.rb b/app/controllers/admin/enterprises_controller.rb index db67df4406..e5b35473ac 100644 --- a/app/controllers/admin/enterprises_controller.rb +++ b/app/controllers/admin/enterprises_controller.rb @@ -189,10 +189,7 @@ module Admin .visible_enterprises if enterprises.present? - enterprises.includes( - supplied_products: - [:supplier, :variants, :image] - ) + enterprises.includes(supplied_products: [:variants, :image]) end when :index if spree_current_user.admin? diff --git a/app/controllers/admin/products_v3_controller.rb b/app/controllers/admin/products_v3_controller.rb index 28f352009f..327a4a4be9 100644 --- a/app/controllers/admin/products_v3_controller.rb +++ b/app/controllers/admin/products_v3_controller.rb @@ -149,7 +149,7 @@ module Admin def ransack_query query = {} - query.merge!(supplier_id_in: @producer_id) if @producer_id.present? + query.merge!(variants_supplier_id_in: @producer_id) if @producer_id.present? if @search_term.present? query.merge!(Spree::Variant::SEARCH_KEY => @search_term) end @@ -163,13 +163,13 @@ module Admin def product_query_includes [ :image, - :supplier, { variants: [ :default_price, :primary_taxon, :product, :stock_items, :tax_category, + :supplier, ] }, ] end diff --git a/app/controllers/api/v0/order_cycles_controller.rb b/app/controllers/api/v0/order_cycles_controller.rb index 7bb56ecc56..ccf6a24dd6 100644 --- a/app/controllers/api/v0/order_cycles_controller.rb +++ b/app/controllers/api/v0/order_cycles_controller.rb @@ -7,9 +7,11 @@ module Api include ApiActionCaching skip_authorization_check - skip_before_action :authenticate_user, :ensure_api_key, only: [:taxons, :properties] + skip_before_action :authenticate_user, :ensure_api_key, only: [ + :taxons, :properties, :producer_properties + ] - caches_action :taxons, :properties, + caches_action :taxons, :properties, :producer_properties, expires_in: CacheService::FILTERS_EXPIRY, cache_path: proc { |controller| controller.request.url } @@ -41,7 +43,13 @@ module Api def properties render plain: ActiveModel::ArraySerializer.new( - product_properties | producer_properties, each_serializer: Api::PropertySerializer + product_properties, each_serializer: Api::PropertySerializer + ).to_json + end + + def producer_properties + render plain: ActiveModel::ArraySerializer.new( + load_producer_properties, each_serializer: Api::PropertySerializer ).to_json end @@ -58,7 +66,7 @@ module Api select('DISTINCT spree_properties.*') end - def producer_properties + def load_producer_properties producers = Enterprise. joins(:supplied_products). where(spree_products: { id: distributed_products }) @@ -86,8 +94,9 @@ module Api end def distributed_products - OrderCycles::DistributedProductsService.new(distributor, order_cycle, - customer).products_relation + OrderCycles::DistributedProductsService.new( + distributor, order_cycle, customer + ).products_supplier_relation.pluck(:id) end end end diff --git a/app/controllers/api/v0/products_controller.rb b/app/controllers/api/v0/products_controller.rb index a85b1986d9..6df0356cf5 100644 --- a/app/controllers/api/v0/products_controller.rb +++ b/app/controllers/api/v0/products_controller.rb @@ -54,7 +54,7 @@ module Api end def overridable - @products = product_finder.paged_products_for_producers + @products = product_finder.products_for_producers render_paged_products @products, ::Api::Admin::ProductSimpleSerializer end diff --git a/app/controllers/spree/admin/product_properties_controller.rb b/app/controllers/spree/admin/product_properties_controller.rb index e5edc8f3d4..aebdff556e 100644 --- a/app/controllers/spree/admin/product_properties_controller.rb +++ b/app/controllers/spree/admin/product_properties_controller.rb @@ -8,6 +8,7 @@ module Spree before_action :setup_property, only: [:index] def index + @supplier = @product.variants.first.supplier @url_filters = ::ProductFilters.new.extract(request.query_parameters) end diff --git a/app/controllers/spree/admin/products_controller.rb b/app/controllers/spree/admin/products_controller.rb index 32099627d8..3ae92a38bd 100644 --- a/app/controllers/spree/admin/products_controller.rb +++ b/app/controllers/spree/admin/products_controller.rb @@ -12,6 +12,7 @@ module Spree include EnterprisesHelper before_action :load_data + before_action :load_producers, only: [:index, :new] before_action :load_form_data, only: [:index, :new, :create, :edit, :update] before_action :load_spree_api_key, only: [:index, :variant_overrides] before_action :strip_new_properties, only: [:create, :update] @@ -41,6 +42,7 @@ module Spree flash[:success] = flash_message_for(@object, :successfully_created) redirect_after_save else + load_producers # Re-fill the form with deleted params on product @on_hand = request.params[:product][:on_hand] @on_demand = request.params[:product][:on_demand] @@ -52,14 +54,9 @@ module Spree def update @url_filters = ::ProductFilters.new.extract(request.query_parameters) - original_supplier_id = @product.supplier_id delete_stock_params_and_set_after do params[:product] ||= {} if params[:clear_product_properties] if @object.update(permitted_resource_params) - if original_supplier_id != @product.supplier_id - ExchangeVariantDeleter.new.delete(@product) - end - flash[:success] = flash_message_for(@object, :successfully_updated) end redirect_to spree.edit_admin_product_url(@object, @url_filters) @@ -157,12 +154,15 @@ module Spree end def load_form_data - @producers = OpenFoodNetwork::Permissions.new(spree_current_user). - managed_product_enterprises.is_primary_producer.by_name @taxons = Spree::Taxon.order(:name) @import_dates = product_import_dates.uniq.to_json end + def load_producers + @producers = OpenFoodNetwork::Permissions.new(spree_current_user). + managed_product_enterprises.is_primary_producer.by_name + end + def product_import_dates options = [{ id: '0', name: '' }] product_import_dates_query.collect(&:import_date). @@ -173,12 +173,10 @@ module Spree def product_import_dates_query Spree::Variant. - select('DISTINCT spree_variants.import_date'). - joins(:product). - where(spree_products: { supplier_id: editable_enterprises.collect(&:id) }). + select('import_date').distinct. + where(supplier_id: editable_enterprises.collect(&:id)). where.not(spree_variants: { import_date: nil }). - where(spree_variants: { deleted_at: nil }). - order('spree_variants.import_date DESC') + order('import_date DESC') end def strip_new_properties diff --git a/app/controllers/spree/admin/variants_controller.rb b/app/controllers/spree/admin/variants_controller.rb index 47505f7b1d..33f29e1b45 100644 --- a/app/controllers/spree/admin/variants_controller.rb +++ b/app/controllers/spree/admin/variants_controller.rb @@ -46,7 +46,13 @@ module Spree def update @url_filters = ::ProductFilters.new.extract(request.query_parameters) + original_supplier_id = @object.supplier_id + if @object.update(permitted_resource_params) + if original_supplier_id != @object.supplier_id + ExchangeVariantDeleter.new.delete(@object) + end + flash[:success] = flash_message_for(@object, :successfully_updated) redirect_to spree.admin_product_variants_url(params[:product_id], @url_filters) else @@ -113,6 +119,8 @@ module Spree private def load_data + @producers = OpenFoodNetwork::Permissions.new(spree_current_user). + managed_product_enterprises.is_primary_producer.by_name @tax_categories = TaxCategory.order(:name) @shipping_categories = ShippingCategory.order(:name) end diff --git a/app/mailers/producer_mailer.rb b/app/mailers/producer_mailer.rb index 4752141c42..99c5f0f4f5 100644 --- a/app/mailers/producer_mailer.rb +++ b/app/mailers/producer_mailer.rb @@ -60,11 +60,12 @@ class ProducerMailer < ApplicationMailer def line_items_from(order_cycle, producer) @line_items ||= Spree::LineItem. - includes(variant: [:product]). + includes(variant: :product). + joins(variant: :product). from_order_cycle(order_cycle). - sorted_by_name_and_unit_value. - merge(Spree::Product.with_deleted.in_supplier(producer)). - merge(Spree::Order.by_state(["complete", "resumed"])) + merge(Spree::Variant.with_deleted.where(supplier: producer)). + merge(Spree::Order.by_state(["complete", "resumed"])). + sorted_by_name_and_unit_value end def total_from_line_items(line_items) @@ -81,7 +82,7 @@ class ProducerMailer < ApplicationMailer line_items.map do |line_item| { sku: line_item.variant.sku, - supplier_name: line_item.product.supplier.name, + supplier_name: line_item.variant.supplier.name, product_and_full_name: line_item.product_and_full_name, quantity: line_item.quantity, first_name: line_item.order.billing_address.first_name, diff --git a/app/models/enterprise.rb b/app/models/enterprise.rb index 5a1ec12d23..03de14c176 100644 --- a/app/models/enterprise.rb +++ b/app/models/enterprise.rb @@ -39,13 +39,13 @@ class Enterprise < ApplicationRecord class_name: 'EnterpriseGroup' has_many :producer_properties, foreign_key: 'producer_id', dependent: :destroy has_many :properties, through: :producer_properties - has_many :supplied_products, class_name: 'Spree::Product', - foreign_key: 'supplier_id', - dependent: :destroy - has_many :supplied_variants, through: :supplied_products, source: :variants + has_many :supplied_variants, + class_name: 'Spree::Variant', foreign_key: 'supplier_id', dependent: :destroy + has_many :supplied_products, through: :supplied_variants, source: :product has_many :distributed_orders, class_name: 'Spree::Order', foreign_key: 'distributor_id', dependent: :restrict_with_exception + belongs_to :address, class_name: 'Spree::Address' belongs_to :business_address, optional: true, class_name: 'Spree::Address', dependent: :destroy has_many :enterprise_fees, dependent: :restrict_with_exception @@ -167,7 +167,7 @@ class Enterprise < ApplicationRecord scope :is_distributor, -> { where.not(sells: 'none') } scope :is_hub, -> { where(sells: 'any') } scope :supplying_variant_in, lambda { |variants| - joins(supplied_products: :variants). + joins(:supplied_variants). where(spree_variants: { id: variants }). select('DISTINCT enterprises.*') } @@ -205,14 +205,14 @@ class Enterprise < ApplicationRecord select('DISTINCT enterprises.*') } - scope :distributing_products, lambda { |product_ids| + scope :distributing_variants, lambda { |variants_ids| exchanges = joins(" INNER JOIN exchanges - ON (exchanges.receiver_id = enterprises.id AND exchanges.incoming = 'f') + ON (exchanges.receiver_id = enterprises.id AND exchanges.incoming = false) "). joins('INNER JOIN exchange_variants ON (exchange_variants.exchange_id = exchanges.id)'). joins('INNER JOIN spree_variants ON (spree_variants.id = exchange_variants.variant_id)'). - where(spree_variants: { product_id: product_ids }).select('DISTINCT enterprises.id') + where(spree_variants: { id: variants_ids }).select('DISTINCT enterprises.id') where(id: exchanges) } @@ -598,7 +598,7 @@ class Enterprise < ApplicationRecord # Touch distributors without them touching their distributors. # We avoid an infinite loop and don't need to touch the whole distributor tree. def touch_distributors - Enterprise.distributing_products(supplied_products.select(:id)). + Enterprise.distributing_variants(supplied_variants.select(:id)). where.not(enterprises: { id: }). update_all(updated_at: Time.zone.now) end diff --git a/app/models/enterprise_relationship.rb b/app/models/enterprise_relationship.rb index 29cd043f9a..bdc318c08b 100644 --- a/app/models/enterprise_relationship.rb +++ b/app/models/enterprise_relationship.rb @@ -108,6 +108,6 @@ class EnterpriseRelationship < ApplicationRecord def child_variant_overrides VariantOverride.unscoped.for_hubs(child) - .joins(variant: :product).where(spree_products: { supplier_id: parent }) + .joins(:variant).where(spree_variants: { supplier_id: parent } ) end end diff --git a/app/models/product_import/entry_processor.rb b/app/models/product_import/entry_processor.rb index 05bc462eff..8ce73489a9 100644 --- a/app/models/product_import/entry_processor.rb +++ b/app/models/product_import/entry_processor.rb @@ -54,10 +54,7 @@ module ProductImport if settings.importing_into_inventory? VariantOverride.for_hubs([enterprise_id]).count else - Spree::Variant. - joins(:product). - where(spree_products: { supplier_id: enterprise_id }). - count + Spree::Variant.where(supplier_id: enterprise_id).count end @enterprise_products[enterprise_id] = products_count @@ -169,7 +166,6 @@ module ProductImport product.assign_attributes( entry.assignable_attributes.except('id', 'on_hand', 'on_demand', 'display_name') ) - product.supplier_id = entry.producer_id if product.save ensure_variant_updated(product, entry) @@ -228,10 +224,13 @@ module ProductImport # Ensure attributes are correctly copied to a new product's variant variant = product.variants.first variant.display_name = entry.display_name if entry.display_name + variant.import_date = @import_time + variant.supplier_id = entry.producer_id + variant.save + + # on_demand and on_hand require a stock level, which is created after the variant is created variant.on_demand = entry.on_demand if entry.on_demand variant.on_hand = entry.on_hand if entry.on_hand - variant.import_date = @import_time - variant.save end end end diff --git a/app/models/product_import/entry_validator.rb b/app/models/product_import/entry_validator.rb index 34369fe44d..2705ff1126 100644 --- a/app/models/product_import/entry_validator.rb +++ b/app/models/product_import/entry_validator.rb @@ -73,6 +73,7 @@ module ProductImport # Variant needs a product. Product needs to be assigned first in order for # delegate to work. name= will fail otherwise. new_variant = Spree::Variant.new(product_id:, **variant_attributes) + new_variant.supplier_id = entry.producer_id new_variant.save if new_variant.persisted? @@ -287,9 +288,7 @@ module ProductImport end def inventory_validation(entry) - products = Spree::Product.where(supplier_id: entry.producer_id, - name: entry.name, - deleted_at: nil) + products = Spree::Product.in_supplier(entry.producer_id).where(name: entry.name) if products.empty? mark_as_invalid(entry, attribute: 'name', @@ -358,9 +357,7 @@ module ProductImport end def product_validation(entry) - products = Spree::Product.where(supplier_id: entry.enterprise_id, - name: entry.name, - deleted_at: nil) + products = Spree::Product.in_supplier(entry.enterprise_id).where(name: entry.name) if products.empty? mark_as_new_product(entry) @@ -384,7 +381,6 @@ module ProductImport new_product.assign_attributes( entry.assignable_attributes.except('id', 'on_hand', 'on_demand', 'display_name') ) - new_product.supplier_id = entry.producer_id entry.on_hand = 0 if entry.on_hand.nil? if new_product.valid? diff --git a/app/models/spree/ability.rb b/app/models/spree/ability.rb index 159b56dcc7..4e2a8601be 100644 --- a/app/models/spree/ability.rb +++ b/app/models/spree/ability.rb @@ -189,7 +189,9 @@ module Spree :seo, :group_buy_options, :bulk_update, :clone, :delete, :destroy], Spree::Product do |product| - OpenFoodNetwork::Permissions.new(user).managed_product_enterprises.include? product.supplier + OpenFoodNetwork::Permissions.new(user).managed_product_enterprises.include?( + product.variants.first.supplier + ) end can [:admin, :index, :bulk_update, :destroy, :destroy_variant, :clone], :products_v3 @@ -198,11 +200,11 @@ module Spree can [:admin, :index, :read, :edit, :update, :search, :delete, :destroy], Spree::Variant do |variant| OpenFoodNetwork::Permissions.new(user). - managed_product_enterprises.include? variant.product.supplier + managed_product_enterprises.include? variant.supplier end can [:admin, :index, :read, :update, :bulk_update, :bulk_reset], VariantOverride do |vo| - next false unless vo.hub.present? && vo.variant&.product&.supplier.present? + next false unless vo.hub.present? && vo.variant&.supplier.present? hub_auth = OpenFoodNetwork::Permissions.new(user). variant_override_hubs. @@ -210,14 +212,14 @@ module Spree producer_auth = OpenFoodNetwork::Permissions.new(user). variant_override_producers. - include? vo.variant.product.supplier + include? vo.variant.supplier hub_auth && producer_auth end can [:admin, :create, :update], InventoryItem do |ii| next false unless ii.enterprise.present? && - ii.variant&.product&.supplier.present? + ii.variant&.supplier.present? hub_auth = OpenFoodNetwork::Permissions.new(user). variant_override_hubs. @@ -225,7 +227,7 @@ module Spree producer_auth = OpenFoodNetwork::Permissions.new(user). variant_override_producers. - include? ii.variant.product.supplier + include? ii.variant.supplier hub_auth && producer_auth end diff --git a/app/models/spree/line_item.rb b/app/models/spree/line_item.rb index eed6741b31..fc44eb109a 100644 --- a/app/models/spree/line_item.rb +++ b/app/models/spree/line_item.rb @@ -16,7 +16,7 @@ module Spree belongs_to :variant, -> { with_deleted }, class_name: "Spree::Variant" has_one :product, through: :variant - has_one :supplier, through: :product + has_one :supplier, through: :variant belongs_to :tax_category, class_name: "Spree::TaxCategory", optional: true has_many :adjustments, as: :adjustable, dependent: :destroy @@ -85,13 +85,11 @@ module Spree where(order_cycles: { id: order_cycle }) } - # Here we are simply joining the line item to its variant and product - # We dont use joins here to avoid the default scopes, - # and with that, include deleted variants and deleted products + # Here we are simply joining the line item to its variant + # We dont use joins here to avoid the default scopes, and with that, include deleted variants scope :supplied_by_any, lambda { |enterprises| - product_ids = Spree::Product.unscoped.where(supplier_id: enterprises).select(:id) - variant_ids = Spree::Variant.unscoped.where(product_id: product_ids).select(:id) - where(spree_line_items: { variant_id: variant_ids }) + variant_ids = Spree::Variant.unscoped.where(supplier: enterprises).select(:id) + where(variant_id: variant_ids) } scope :with_tax, -> { diff --git a/app/models/spree/product.rb b/app/models/spree/product.rb index 1f79ebf95c..2aa558a5ad 100755 --- a/app/models/spree/product.rb +++ b/app/models/spree/product.rb @@ -5,19 +5,15 @@ require 'open_food_network/property_merge' # PRODUCTS # Products represent an entity for sale in a store. # Products can have variations, called variants -# Products properties include description, permalink, availability, -# shipping category, etc. that do not change by variant. -# -# MASTER VARIANT -# Every product has one master variant, which stores master price and sku, size and weight, etc. -# Price, SKU, size, weight, etc. are all delegated to the master variant. -# Contains on_hand inventory levels only when there are no variants for the product. +# Products properties include description, meta_keywork, etc. that do not change by variant. # # VARIANTS -# All variants can access the product properties directly (via reverse delegation). +# Every product has at least one variant (standard variant), which stores price and availability, +# shipping category, sku, size and weight, etc. +# All variants can access the product name, description, and meta_keyword directly (via reverse +# delegation). # Inventory units are tied to Variant. -# The master variant can have inventory units, but not option values. -# All other variants have option values and may have inventory units. +# All variants have option values and may have inventory units. # Sum of on_hand each variant's inventory level determine "on_hand" level for the product. # module Spree @@ -26,15 +22,14 @@ module Spree include LogDestroyPerformer self.belongs_to_required_by_default = false + self.ignored_columns += [:supplier_id] acts_as_paranoid - searchable_attributes :supplier_id, :meta_keywords, :sku - searchable_associations :supplier, :properties, :variants + searchable_attributes :meta_keywords, :sku + searchable_associations :properties, :variants searchable_scopes :active, :with_properties - belongs_to :supplier, class_name: 'Enterprise', optional: false, touch: true - has_one :image, class_name: "Spree::Image", as: :viewable, dependent: :destroy has_many :product_properties, dependent: :destroy @@ -45,7 +40,6 @@ module Spree has_many :prices, -> { order('spree_variants.id, currency') }, through: :variants has_many :stock_items, through: :variants - has_many :supplier_properties, through: :supplier, source: :properties has_many :variant_images, -> { order(:position) }, source: :images, through: :variants @@ -68,28 +62,27 @@ module Spree accepts_nested_attributes_for :image accepts_nested_attributes_for :product_properties, allow_destroy: true, - reject_if: lambda { |pp| pp[:property_name].blank? } + reject_if: ->(pp) { pp[:property_name].blank? } # 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, :primary_taxon_id + :shipping_category_id, :primary_taxon_id, :supplier_id after_create :ensure_standard_variant + after_update :touch_supplier, if: :saved_change_to_primary_taxon_id? around_destroy :destruction after_save :update_units + after_touch :touch_supplier + # -- Scopes scope :with_properties, ->(*property_ids) { left_outer_joins(:product_properties). - left_outer_joins(:supplier_properties). where(inherits_properties: true). - where(producer_properties: { property_id: property_ids }). - or( - where(spree_product_properties: { property_id: property_ids }) - ) + where(spree_product_properties: { property_id: property_ids }) } - scope :with_order_cycles_outer, -> { + scope :with_order_cycles_outer, lambda { joins(" LEFT OUTER JOIN spree_variants AS o_spree_variants ON (o_spree_variants.product_id = spree_products.id)"). @@ -111,9 +104,7 @@ module Spree where(import_date: import_date.all_day)) } - scope :with_order_cycles_inner, -> { - joins(variants: { exchanges: :order_cycle }) - } + scope :with_order_cycles_inner, -> { joins(variants: { exchanges: :order_cycle }) } scope :visible_for, lambda { |enterprise| joins(' @@ -126,8 +117,9 @@ module Spree distinct } - # -- Scopes - scope :in_supplier, lambda { |supplier| where(supplier_id: supplier) } + scope :in_supplier, lambda { |supplier| + distinct.joins(:variants).where(spree_variants: { supplier: }) + } # Products distributed via the given distributor through an OC scope :in_distributor, lambda { |distributor| @@ -144,18 +136,6 @@ module Spree distinct } - # Products supplied by a given enterprise or distributed via that enterprise through an OC - scope :in_supplier_or_distributor, lambda { |enterprise| - enterprise = enterprise.respond_to?(:id) ? enterprise.id : enterprise.to_i - - with_order_cycles_outer. - where(" - spree_products.supplier_id = ? - OR (o_exchanges.incoming = ? AND o_exchanges.receiver_id = ?) - ", enterprise, false, enterprise). - select('distinct spree_products.*') - } - # Products distributed by the given order cycle scope :in_order_cycle, lambda { |order_cycle| with_order_cycles_inner. @@ -170,27 +150,17 @@ module Spree where.not(order_cycles: { id: nil }) } - scope :by_producer, -> { joins(:supplier).order('enterprises.name') } - scope :by_name, -> { order('name') } + scope :by_producer, -> { joins(variants: :supplier).order('enterprises.name') } + scope :by_name, -> { order('spree_products.name') } scope :managed_by, lambda { |user| if user.has_spree_role?('admin') where(nil) else - where(supplier_id: user.enterprises.select("enterprises.id")) + in_supplier(user.enterprises) end } - scope :stockable_by, lambda { |enterprise| - return where('1=0') if enterprise.blank? - - permitted_producer_ids = EnterpriseRelationship.joins(:parent).permitting(enterprise.id) - .with_permission(:add_to_order_cycle) - .where(enterprises: { is_primary_producer: true }) - .pluck(:parent_id) - where(spree_products: { supplier_id: [enterprise.id] | permitted_producer_ids }) - } - scope :active, lambda { where(spree_products: { deleted_at: nil }) } def self.group_by_products_id @@ -236,7 +206,10 @@ module Spree ps = product_properties.all if inherits_properties - ps = OpenFoodNetwork::PropertyMerge.merge(ps, supplier.producer_properties) + # NOTE: Set the supplier as the first variant supplier. If variants have different supplier, + # result might not be correct + supplier = variants.first.supplier + ps = OpenFoodNetwork::PropertyMerge.merge(ps, supplier&.producer_properties || []) end ps. @@ -263,8 +236,6 @@ module Spree def destruction transaction do - touch_distributors - ExchangeVariant. where(exchange_variants: { variant_id: variants.with_deleted. select(:id) }).destroy_all @@ -285,6 +256,7 @@ module Spree variant.tax_category_id = tax_category_id variant.shipping_category_id = shipping_category_id variant.primary_taxon_id = primary_taxon_id + variant.supplier_id = supplier_id variants << variant end @@ -319,11 +291,26 @@ module Spree def update_units return unless saved_change_to_variant_unit? || saved_change_to_variant_unit_name? - variants.each(&:update_units) + variants.each do |v| + if v.persisted? + v.update_units + else + v.assign_units + end + end end - def touch_distributors - Enterprise.distributing_products(id).each(&:touch) + def touch_supplier + return if variants.empty? + + # Assume the product supplier is the supplier of the first variant + # Will breack if product has mutiple variants with different supplier + first_variant = variants.first + + # The variant is invalid if no supplier is present, but this method can be triggered when + # importing product. In this scenario the variant has not been updated with the supplier yet + # hence the check. + first_variant.supplier.touch if first_variant.supplier.present? end def validate_image diff --git a/app/models/spree/property.rb b/app/models/spree/property.rb index 9f45270a78..0aae0d04dd 100644 --- a/app/models/spree/property.rb +++ b/app/models/spree/property.rb @@ -6,6 +6,8 @@ module Spree has_many :products, through: :product_properties has_many :producer_properties, dependent: :destroy + after_touch :touch_producer_properties + validates :name, :presentation, presence: true scope :sorted, -> { order(:name) } @@ -13,5 +15,11 @@ module Spree def property self end + + private + + def touch_producer_properties + producer_properties.each(&:touch) + end end end diff --git a/app/models/spree/taxon.rb b/app/models/spree/taxon.rb index 0ccb9e6499..bc0b85371a 100644 --- a/app/models/spree/taxon.rb +++ b/app/models/spree/taxon.rb @@ -57,7 +57,7 @@ module Spree taxons = {} Spree::Taxon. - joins(products: :supplier). + joins(variants: :supplier). select('spree_taxons.*, enterprises.id AS enterprise_id'). each do |t| taxons[t.enterprise_id.to_i] ||= Set.new diff --git a/app/models/spree/variant.rb b/app/models/spree/variant.rb index 32f04ac651..00340f49d7 100644 --- a/app/models/spree/variant.rb +++ b/app/models/spree/variant.rb @@ -13,8 +13,8 @@ module Spree acts_as_paranoid - searchable_attributes :sku, :display_as, :display_name, :primary_taxon_id - searchable_associations :product, :default_price, :primary_taxon + searchable_attributes :sku, :display_as, :display_name, :primary_taxon_id, :supplier_id + searchable_associations :product, :default_price, :primary_taxon, :supplier searchable_scopes :active, :deleted NAME_FIELDS = ["display_name", "display_as", "weight", "unit_value", "unit_description"].freeze @@ -23,12 +23,15 @@ module Spree meta_keywords variants_display_as variants_display_name - supplier_name).join('_or_')}_cont".freeze + variants_supplier_name).join('_or_')}_cont".freeze - belongs_to :product, -> { with_deleted }, touch: true, class_name: 'Spree::Product' + belongs_to :product, -> { + with_deleted + }, touch: true, class_name: 'Spree::Product', optional: false 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 + belongs_to :supplier, class_name: 'Enterprise', optional: false, touch: true delegate :name, :name=, :description, :description=, :meta_keywords, to: :product @@ -58,6 +61,7 @@ module Spree has_many :variant_overrides, dependent: :destroy has_many :inventory_items, dependent: :destroy has_many :semantic_links, dependent: :delete_all + has_many :supplier_properties, through: :supplier, source: :properties localize_number :price, :weight @@ -94,7 +98,7 @@ module Spree after_save :save_default_price # default variant scope only lists non-deleted variants - scope :deleted, lambda { where.not(deleted_at: nil) } + scope :deleted, -> { where.not(deleted_at: nil) } scope :with_order_cycles_inner, -> { joins(exchanges: :order_cycle) } @@ -139,11 +143,9 @@ module Spree .where("o_inventory_items.id IS NULL OR o_inventory_items.visible = (?)", true) } - scope :stockable_by, lambda { |enterprise| - return where("1=0") if enterprise.blank? - - joins(:product). - where(spree_products: { id: Spree::Product.stockable_by(enterprise).select(:id) }) + scope :with_properties, lambda { |property_ids| + left_outer_joins(:supplier_properties). + where(producer_properties: { property_id: property_ids }) } # Define sope as class method to allow chaining with other scopes filtering id. @@ -250,8 +252,16 @@ module Spree end def destruction - exchange_variants.reload.destroy_all - yield + transaction do + # Even tough Enterprise will touch associated variant distributors when touched, + # the variant will be removed from the exchange by the time it's triggered, + # so it won't be able to find the deleted variant's distributors. + # This why we do it here + touch_distributors + + exchange_variants.reload.destroy_all + yield + end end def ensure_unit_value @@ -268,5 +278,9 @@ module Spree def convert_variant_weight_to_decimal self.weight = weight.to_d end + + def touch_distributors + Enterprise.distributing_variants(id).each(&:touch) + end end end diff --git a/app/queries/product_scope_query.rb b/app/queries/product_scope_query.rb index 00f4a401e8..1a89711c04 100644 --- a/app/queries/product_scope_query.rb +++ b/app/queries/product_scope_query.rb @@ -31,16 +31,17 @@ class ProductScopeQuery product_scope.find(@params[:product_id]) end - def paged_products_for_producers + def products_for_producers producer_ids = OpenFoodNetwork::Permissions.new(@user). variant_override_producers.by_name.select('enterprises.id') + # Use `order("enterprises.name")` instead of `by_producer scope`, the scope adds a join + # on variants which messes our query Spree::Product.where(nil). merge(product_scope). - includes(variants: [:product, :default_price, :stock_items]). - where(supplier_id: producer_ids). - by_producer.by_name. - ransack(@params[:q]).result + includes(variants: [:product, :default_price, :stock_items, :supplier]). + where(variants: { supplier_id: producer_ids }). + ransack(@params[:q]).result(distinct: true) end def product_scope diff --git a/app/reflexes/products_reflex.rb b/app/reflexes/products_reflex.rb index a2feef4d28..c96f49050e 100644 --- a/app/reflexes/products_reflex.rb +++ b/app/reflexes/products_reflex.rb @@ -108,7 +108,7 @@ class ProductsReflex < ApplicationReflex def ransack_query query = {} - query.merge!(supplier_id_in: @producer_id) if @producer_id.present? + query.merge!(variants_supplier_id_in: @producer_id) if @producer_id.present? if @search_term.present? query.merge!(Spree::Variant::SEARCH_KEY => @search_term) end diff --git a/app/serializers/api/admin/for_order_cycle/supplied_product_serializer.rb b/app/serializers/api/admin/for_order_cycle/supplied_product_serializer.rb index f135d31dab..7e22751b72 100644 --- a/app/serializers/api/admin/for_order_cycle/supplied_product_serializer.rb +++ b/app/serializers/api/admin/for_order_cycle/supplied_product_serializer.rb @@ -7,7 +7,7 @@ module Api attributes :name, :supplier_name, :image_url, :variants def supplier_name - object.supplier&.name + object.variants.first.supplier&.name end def image_url diff --git a/app/serializers/api/admin/line_item_serializer.rb b/app/serializers/api/admin/line_item_serializer.rb index b264e32056..acdb9053ff 100644 --- a/app/serializers/api/admin/line_item_serializer.rb +++ b/app/serializers/api/admin/line_item_serializer.rb @@ -9,7 +9,7 @@ module Api has_one :order, serializer: Api::Admin::IdSerializer def supplier - { id: object.product.supplier_id } + { id: object.supplier.id } end def units_product diff --git a/app/serializers/api/admin/product_serializer.rb b/app/serializers/api/admin/product_serializer.rb index 356c0debdf..836b78bdaa 100644 --- a/app/serializers/api/admin/product_serializer.rb +++ b/app/serializers/api/admin/product_serializer.rb @@ -7,8 +7,6 @@ module Api :inherits_properties, :on_hand, :price, :import_date, :image_url, :thumb_url, :variants - has_one :supplier, key: :producer_id, embed: :id - def variants ActiveModel::ArraySerializer.new( object.variants, diff --git a/app/serializers/api/admin/product_simple_serializer.rb b/app/serializers/api/admin/product_simple_serializer.rb index 6b968ab8d3..07f66294b6 100644 --- a/app/serializers/api/admin/product_simple_serializer.rb +++ b/app/serializers/api/admin/product_simple_serializer.rb @@ -3,13 +3,9 @@ module Api module Admin class ProductSimpleSerializer < ActiveModel::Serializer - attributes :id, :name, :producer_id + attributes :id, :name has_many :variants, key: :variants, serializer: Api::Admin::VariantSimpleSerializer - - def producer_id - object.supplier_id - end end end end diff --git a/app/serializers/api/admin/variant_serializer.rb b/app/serializers/api/admin/variant_serializer.rb index 06732aa7b5..5ccb8347c7 100644 --- a/app/serializers/api/admin/variant_serializer.rb +++ b/app/serializers/api/admin/variant_serializer.rb @@ -9,6 +9,7 @@ module Api :price, :on_demand, :on_hand, :in_stock, :stock_location_id, :stock_location_name has_one :primary_taxon, key: :category_id, embed: :id + has_one :supplier, key: :producer_id, embed: :id def name if object.full_name.present? @@ -31,7 +32,7 @@ module Api end def producer_name - object.product.supplier.name + object.supplier.name end def image diff --git a/app/serializers/api/admin/variant_simple_serializer.rb b/app/serializers/api/admin/variant_simple_serializer.rb index d94421321f..3a2944aff0 100644 --- a/app/serializers/api/admin/variant_simple_serializer.rb +++ b/app/serializers/api/admin/variant_simple_serializer.rb @@ -6,7 +6,7 @@ module Api attributes :id, :name, :import_date, :options_text, :unit_value, :unit_description, :unit_to_display, :display_as, :display_name, :name_to_display, - :price, :on_demand, :on_hand + :price, :on_demand, :on_hand, :producer_id has_many :variant_overrides @@ -27,6 +27,10 @@ module Api def price object.price.nil? ? 0.to_f : object.price end + + def producer_id + object.supplier_id + end end end end diff --git a/app/serializers/api/product_serializer.rb b/app/serializers/api/product_serializer.rb index e25fdaf04a..1a34ece6b2 100644 --- a/app/serializers/api/product_serializer.rb +++ b/app/serializers/api/product_serializer.rb @@ -10,7 +10,6 @@ class Api::ProductSerializer < ActiveModel::Serializer has_many :variants, serializer: Api::VariantSerializer has_one :image, serializer: Api::ImageSerializer - has_one :supplier, serializer: Api::IdSerializer # return an unformatted descripton def description diff --git a/app/serializers/api/variant_serializer.rb b/app/serializers/api/variant_serializer.rb index 1d58eda967..660d45467f 100644 --- a/app/serializers/api/variant_serializer.rb +++ b/app/serializers/api/variant_serializer.rb @@ -9,6 +9,8 @@ class Api::VariantSerializer < ActiveModel::Serializer :tag_list, :thumb_url, :unit_price_price, :unit_price_unit + has_one :supplier, serializer: Api::IdSerializer + delegate :price, to: :object def fees diff --git a/app/serializers/invoice/product_serializer.rb b/app/serializers/invoice/product_serializer.rb index 61f5ecadd0..d5900200c6 100644 --- a/app/serializers/invoice/product_serializer.rb +++ b/app/serializers/invoice/product_serializer.rb @@ -3,6 +3,5 @@ class Invoice class ProductSerializer < ActiveModel::Serializer attributes :name - has_one :supplier, serializer: Invoice::EnterpriseSerializer end end diff --git a/app/serializers/invoice/variant_serializer.rb b/app/serializers/invoice/variant_serializer.rb index 3f0a547b96..ef00becdf4 100644 --- a/app/serializers/invoice/variant_serializer.rb +++ b/app/serializers/invoice/variant_serializer.rb @@ -4,5 +4,6 @@ class Invoice class VariantSerializer < ActiveModel::Serializer attributes :id, :display_name, :options_text has_one :product, serializer: Invoice::ProductSerializer + has_one :supplier, serializer: Invoice::EnterpriseSerializer end end diff --git a/app/services/exchange_products_renderer.rb b/app/services/exchange_products_renderer.rb index c6cfceab22..fc95368af0 100644 --- a/app/services/exchange_products_renderer.rb +++ b/app/services/exchange_products_renderer.rb @@ -30,7 +30,7 @@ class ExchangeProductsRenderer end def supplied_products(enterprises_query_matcher) - products_relation = Spree::Product.where(supplier_id: enterprises_query_matcher).order(:name) + products_relation = Spree::Product.in_supplier(enterprises_query_matcher).order(:name) filter_visible(products_relation) end @@ -95,7 +95,7 @@ class ExchangeProductsRenderer return enterprises if enterprises.empty? enterprises.includes( - supplied_products: [:supplier, :variants, :image] + supplied_products: [{ variants: :supplier }, :image] ) end end diff --git a/app/services/exchange_variant_deleter.rb b/app/services/exchange_variant_deleter.rb index 0db1abd884..beb005795b 100644 --- a/app/services/exchange_variant_deleter.rb +++ b/app/services/exchange_variant_deleter.rb @@ -1,9 +1,7 @@ # frozen_string_literal: true class ExchangeVariantDeleter - def delete(product) - ExchangeVariant. - where(variant_id: product.variants.select(:id)). - delete_all + def delete(variant) + ExchangeVariant.where(variant_id: variant.id).delete_all end end diff --git a/app/services/order_cycles/distributed_products_service.rb b/app/services/order_cycles/distributed_products_service.rb index 4da5039e26..5f349dae37 100644 --- a/app/services/order_cycles/distributed_products_service.rb +++ b/app/services/order_cycles/distributed_products_service.rb @@ -11,11 +11,8 @@ module OrderCycles @customer = customer end - def products_relation - 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 + # TODO refactor products_taxons_relation and products_supplier_relation + # 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 @@ -31,6 +28,28 @@ module OrderCycles group("spree_products.id, first_variant.primary_taxon_id") end + # Joins on the first product variant to allow us to filter product by supplier. This is so + # enterprise can display product sorted by supplier in a custom order on their shopfront. + # + # Caveat, the supplier sorting won't work properly if there are multiple variant with different + # supplier for a given product. + # + def products_supplier_relation + Spree::Product.where(id: stocked_products). + joins("LEFT JOIN (SELECT DISTINCT ON(product_id) id, product_id, supplier_id + FROM spree_variants WHERE deleted_at IS NULL) first_variant + ON spree_products.id = first_variant.product_id"). + select("spree_products.*, first_variant.supplier_id"). + group("spree_products.id, first_variant.supplier_id") + end + + def supplier_property_join(query) + query.joins(" + JOIN enterprises ON enterprises.id = first_variant.supplier_id + LEFT OUTER JOIN producer_properties ON producer_properties.producer_id = enterprises.id + ") + end + def variants_relation order_cycle. variants_distributed_by(distributor). diff --git a/app/services/permissions/order.rb b/app/services/permissions/order.rb index 842e858321..d4c2f4b0ea 100644 --- a/app/services/permissions/order.rb +++ b/app/services/permissions/order.rb @@ -83,7 +83,7 @@ module Permissions Spree::Order.with_line_items_variants_and_products_outer. where( distributor_id: granted_distributor_ids, - spree_products: { supplier_id: enterprises_with_associated_orders } + spree_variants: { supplier_id: enterprises_with_associated_orders } ). where_clause.__send__(:predicates). reduce(:and) diff --git a/app/services/permitted_attributes/product.rb b/app/services/permitted_attributes/product.rb index 1adc8ff7d9..f080f2b730 100644 --- a/app/services/permitted_attributes/product.rb +++ b/app/services/permitted_attributes/product.rb @@ -4,11 +4,11 @@ module PermittedAttributes class Product def self.attributes [ - :id, :name, :description, :supplier_id, :price, + :id, :name, :description, :price, :variant_unit, :variant_unit_scale, :variant_unit_with_scale, :unit_value, :unit_description, :variant_unit_name, :display_as, :sku, :group_buy, :group_buy_unit_size, - :taxon_ids, :primary_taxon_id, :tax_category_id, + :taxon_ids, :primary_taxon_id, :tax_category_id, :supplier_id, :meta_keywords, :notes, :inherits_properties, { product_properties_attributes: [:id, :property_name, :value], variants_attributes: [PermittedAttributes::Variant.attributes], diff --git a/app/services/permitted_attributes/variant.rb b/app/services/permitted_attributes/variant.rb index 21f0f997b8..4d4fdcea38 100644 --- a/app/services/permitted_attributes/variant.rb +++ b/app/services/permitted_attributes/variant.rb @@ -7,7 +7,8 @@ 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, :taxon_ids, :primary_taxon_id + :weight, :height, :width, :depth, :taxon_ids, :primary_taxon_id, + :supplier_id ] end end diff --git a/app/services/products_renderer.rb b/app/services/products_renderer.rb index 57608f849a..9db07fa975 100644 --- a/app/services/products_renderer.rb +++ b/app/services/products_renderer.rb @@ -2,7 +2,8 @@ require 'open_food_network/scope_product_to_hub' -class ProductsRenderer + +class ProductsRenderer # rubocop:disable Metrics/ClassLength include Pagy::Backend class NoProducts < RuntimeError; end @@ -34,12 +35,12 @@ class ProductsRenderer return unless order_cycle @products ||= begin - results = distributed_products. - products_taxons_relation. + results = products_relation. order(Arel.sql(products_order)) - filter_and_paginate(results). - each { |product| product_scoper.scope(product) } # Scope results with variant_overrides + results = filter(results) + # Scope results with variant_overrides + paginate(results).each { |product| product_scoper.scope(product) } end end @@ -51,10 +52,57 @@ class ProductsRenderer OpenFoodNetwork::EnterpriseFeeCalculator.new distributor, order_cycle end - def filter_and_paginate(query) - results = query.ransack(args[:q]).result + # TODO refactor this, distributed_products should be able to give use the relation based + # on the sorting method, same for ordering. It would prevent the SQL implementation from + # leaking here + def products_relation + if distributor.preferred_shopfront_product_sorting_method == "by_category" && + distributor.preferred_shopfront_taxon_order.present? + return distributed_products.products_taxons_relation + end - _pagy, paginated_results = pagy_arel( + distributed_products.products_supplier_relation + end + + # TODO: refactor to address CyclomaticComplexity + def filter(query) # rubocop:disable Metrics/CyclomaticComplexity + supplier_properties = args[:q]&.slice("with_variants_supplier_properties") + + ransack_results = query.ransack(args[:q]).result.to_a + + return ransack_results if supplier_properties.blank? + + with_properties = args[:q]&.dig("with_properties") + supplier_properties_results = [] + + if supplier_properties.present? + # We can't search on an association's scope with ransack, a work around is to define + # the a scope on the parent (Spree::Product) but because we are joining on "first_variant" + # to get the supplier it doesn't work, so we do the filtering manually here + # see: + # OrderCycleDistributedProducts#products_supplier_relation + # OrderCycleDistributedProducts#supplier_property_join + supplier_property_ids = supplier_properties["with_variants_supplier_properties"] + supplier_properties_results = distributed_products.supplier_property_join(query). + where(producer_properties: { property_id: supplier_property_ids }). + where(inherits_properties: true) + end + + if supplier_properties_results.present? && with_properties.present? + # apply "OR" between property search + return ransack_results | supplier_properties_results + end + + # Intersect the result to apply "AND" with other search criteria + return ransack_results.intersection(supplier_properties_results) \ + unless supplier_properties_results.empty? + + # We should get here but just in case we return the ransack results + ransack_results + end + + def paginate(results) + _pagy, paginated_results = pagy_array( results, page: args[:page] || 1, items: args[:per_page] || DEFAULT_PER_PAGE @@ -67,12 +115,13 @@ class ProductsRenderer OrderCycles::DistributedProductsService.new(distributor, order_cycle, customer) end + # TODO refactor, see above def products_order if distributor.preferred_shopfront_product_sorting_method == "by_producer" && distributor.preferred_shopfront_producer_order.present? order_by_producer = distributor .preferred_shopfront_producer_order - .split(",").map { |id| "spree_products.supplier_id=#{id} DESC" } + .split(",").map { |id| "first_variant.supplier_id=#{id} DESC" } .join(", ") "#{order_by_producer}, spree_products.name ASC, spree_products.id ASC" elsif distributor.preferred_shopfront_product_sorting_method == "by_category" && @@ -87,7 +136,6 @@ class ProductsRenderer end end - def variants_for_shop @variants_for_shop ||= begin scoper = OpenFoodNetwork::ScopeVariantToHub.new(distributor) diff --git a/app/services/sets/product_set.rb b/app/services/sets/product_set.rb index 04138eedbc..15ffe47e21 100644 --- a/app/services/sets/product_set.rb +++ b/app/services/sets/product_set.rb @@ -55,8 +55,6 @@ module Sets def update_product(product, attributes) return false unless update_product_only_attributes(product, attributes) - ExchangeVariantDeleter.new.delete(product) if product.saved_change_to_supplier_id? - update_product_variants(product, attributes) end @@ -107,6 +105,8 @@ module Sets if variant.present? variant.assign_attributes(variant_attributes.except(:id)) variant.save if variant.changed? + + ExchangeVariantDeleter.new.delete(variant) if variant.saved_change_to_supplier_id? else variant = create_variant(product, variant_attributes) end diff --git a/app/views/admin/products_v3/_product_row.html.haml b/app/views/admin/products_v3/_product_row.html.haml index 375e8ad47a..f5c4fd8b5d 100644 --- a/app/views/admin/products_v3/_product_row.html.haml +++ b/app/views/admin/products_v3/_product_row.html.haml @@ -25,13 +25,8 @@ -# empty %td.col-on_hand.align-right -# empty -%td.col-producer.naked_inputs - = render(SearchableDropdownComponent.new(form: f, - name: :supplier_id, - aria_label: t('.producer_field_name'), - options: producer_options, - selected_option: product.supplier_id, - placeholder_value: t('admin.products_v3.filters.search_for_producers'))) +%td.col-on_hand.align-right + -# empty %td.col-category.align-left -# empty %td.col-tax_category.align-left diff --git a/app/views/admin/products_v3/_product_variant_row.html.haml b/app/views/admin/products_v3/_product_variant_row.html.haml index b77f30b0f8..5c2765ab2d 100644 --- a/app/views/admin/products_v3/_product_variant_row.html.haml +++ b/app/views/admin/products_v3/_product_variant_row.html.haml @@ -4,17 +4,17 @@ action: 'rails-nested-form:add->bulk-form#registerElements rails-nested-form:remove->bulk-form#toggleFormChanged' }, class: (defined?(should_slide_in) && should_slide_in) ? 'slide-in' : '' } %tr - = render partial: 'product_row', locals: { f: product_form, product:, producer_options:, product_index: } + = render partial: 'product_row', locals: { f: product_form, product:, product_index: } - product.variants.each_with_index do |variant, variant_index| = form.fields_for("products][#{product_index}][variants_attributes][", variant, index: variant_index) do |variant_form| %tr.condensed{ id: dom_id(variant), 'data-controller': "variant", 'class': "nested-form-wrapper", 'data-new-record': variant.new_record? ? "true" : false } - = render partial: 'variant_row', locals: { variant:, f: variant_form, category_options:, tax_category_options: } + = render partial: 'variant_row', locals: { variant:, f: variant_form, category_options:, tax_category_options:, producer_options: } = form.fields_for("products][#{product_index}][variants_attributes][NEW_RECORD", product.variants.build) do |new_variant_form| %template{ 'data-nested-form-target': "template" } %tr.condensed{ 'data-controller': "variant", 'class': "nested-form-wrapper", 'data-new-record': "true" } - = render partial: 'variant_row', locals: { variant: new_variant_form.object, f: new_variant_form, category_options:, tax_category_options: } + = render partial: 'variant_row', locals: { variant: new_variant_form.object, f: new_variant_form, category_options:, tax_category_options:, producer_options: } %tr{ 'data-nested-form-target': "target" } %tr.condensed diff --git a/app/views/admin/products_v3/_variant_row.html.haml b/app/views/admin/products_v3/_variant_row.html.haml index 311b833e04..b137c25de0 100644 --- a/app/views/admin/products_v3/_variant_row.html.haml +++ b/app/views/admin/products_v3/_variant_row.html.haml @@ -39,8 +39,13 @@ = f.label :on_demand do = f.check_box :on_demand, 'data-action': 'change->toggle-control#disableIfPresent change->popout#closeIfChecked' = t(:on_demand) -%td.col-producer.align-left - -# empty producer name +%td.col-producer.naked_inputs + = render(SearchableDropdownComponent.new(form: f, + name: :supplier_id, + aria_label: t('.producer_field_name'), + options: producer_options, + selected_option: variant.supplier_id, + placeholder_value: t('admin.products_v3.filters.search_for_producers'))) %td.col-category.field.naked_inputs = render(SearchableDropdownComponent.new(form: f, name: :primary_taxon_id, diff --git a/app/views/admin/variant_overrides/_products_product.html.haml b/app/views/admin/variant_overrides/_products_product.html.haml index 188f332e26..30ca0ed176 100644 --- a/app/views/admin/variant_overrides/_products_product.html.haml +++ b/app/views/admin/variant_overrides/_products_product.html.haml @@ -1,5 +1,5 @@ %tr.product.even - %td.producer{ "ng-show": 'columns.producer.visible', "ng-bind-html": '::producersByID[product.producer_id].name' } + %td.producer{ "ng-show": 'columns.producer.visible' } %td.product{ "ng-show": 'columns.product.visible', "ng-bind": '::product.name' } %td.sku{ "ng-show": 'columns.sku.visible' } %td.price{ "ng-show": 'columns.price.visible' } diff --git a/app/views/admin/variant_overrides/_products_variants.html.haml b/app/views/admin/variant_overrides/_products_variants.html.haml index 43decfe33f..c924044c9b 100644 --- a/app/views/admin/variant_overrides/_products_variants.html.haml +++ b/app/views/admin/variant_overrides/_products_variants.html.haml @@ -1,5 +1,5 @@ %tr.variant{ id: "v_{{variant.id}}", "ng-repeat": 'variant in product.variants | inventoryVariants:hub_id:views' } - %td.producer{ "ng-show": 'columns.producer.visible' } + %td.producer{ "ng-show": 'columns.producer.visible', "ng-bind": '::producersByID[variant.producer_id].name' } %td.product{ "ng-show": 'columns.product.visible' } %span{ "ng-bind": '::variant.display_name || ""' } .variant-override-unit{ "ng-bind": '::variant.unit_to_display' } diff --git a/app/views/admin/variant_overrides/index.html.haml b/app/views/admin/variant_overrides/index.html.haml index adfcc8f71c..f80ad3ae04 100644 --- a/app/views/admin/variant_overrides/index.html.haml +++ b/app/views/admin/variant_overrides/index.html.haml @@ -7,6 +7,7 @@ = render 'admin/variant_overrides/loading_flash' = render 'admin/variant_overrides/controls' = render 'admin/variant_overrides/no_results' + // filteredProducts is defined in admin/variant_overrides/products %div{ "ng-cloak": true, "ng-show": 'hub_id && filteredProducts.length > 0' } = render 'admin/variant_overrides/new_products' = render 'admin/variant_overrides/hidden_products' diff --git a/app/views/producer_mailer/order_cycle_report.html.haml b/app/views/producer_mailer/order_cycle_report.html.haml index c44742a4b1..4b85b9ce44 100644 --- a/app/views/producer_mailer/order_cycle_report.html.haml +++ b/app/views/producer_mailer/order_cycle_report.html.haml @@ -40,7 +40,7 @@ = line_items.first.variant.sku - if @distributors_pickup_times.many? %td - = line_items.first.product.supplier.name + = line_items.first.variant.supplier.name %td = product_and_full_name %td.text-right diff --git a/app/views/producer_mailer/order_cycle_report.text.haml b/app/views/producer_mailer/order_cycle_report.text.haml index 2cfc55cd76..adf77e8eb8 100644 --- a/app/views/producer_mailer/order_cycle_report.text.haml +++ b/app/views/producer_mailer/order_cycle_report.text.haml @@ -15,7 +15,7 @@ Orders summary = t :producer_mail_order_text \ - @grouped_line_items.each_pair do |product_and_full_name, line_items| - #{line_items.first.variant.sku} - #{raw(line_items.first.product.supplier.name)} - #{raw(product_and_full_name)} (QTY: #{line_items.sum(&:quantity)}) @ #{line_items.first.single_money} = #{Spree::Money.new(line_items.sum(&:total), currency: line_items.first.currency)} + #{line_items.first.variant.sku} - #{raw(line_items.first.variant.supplier.name)} - #{raw(product_and_full_name)} (QTY: #{line_items.sum(&:quantity)}) @ #{line_items.first.single_money} = #{Spree::Money.new(line_items.sum(&:total), currency: line_items.first.currency)} \ \ #{t :total}: #{@total} diff --git a/app/views/shop/products/_applied_filters_feedback.haml b/app/views/shop/products/_applied_filters_feedback.haml index 2eb647b3b5..1d21075b38 100644 --- a/app/views/shop/products/_applied_filters_feedback.haml +++ b/app/views/shop/products/_applied_filters_feedback.haml @@ -1,10 +1,12 @@ = cache_with_locale do - %span{ "ng-show" => "query && ( appliedPropertiesList() || appliedTaxonsList() )" } + %span{ "ng-show" => "query && ( appliedPropertiesList() || appliedProducerPropertiesList() || appliedTaxonsList() )" } = t :products_filters_in - %span.applied-properties{'ng-bind-html' => 'appliedPropertiesList()'} + %span{ "ng-show" => "appliedPropertiesList() && appliedProducerPropertiesList()" } + = t :products_or + %span.applied-properties{'ng-bind-html' => 'appliedProducerPropertiesList()'} - %span{ "ng-show" => "appliedPropertiesList() && appliedTaxonsList()" } + %span{ "ng-show" => "(appliedPropertiesList() || appliedProducerPropertiesList()) && appliedTaxonsList()" } = t :products_and %span.applied-taxons{'ng-bind-html' => 'appliedTaxonsList()'} diff --git a/app/views/shop/products/_filters.html.haml b/app/views/shop/products/_filters.html.haml index 8a929af886..c9f5455e95 100644 --- a/app/views/shop/products/_filters.html.haml +++ b/app/views/shop/products/_filters.html.haml @@ -4,3 +4,7 @@ .filter-shopfront.property-selectors{ "ng-show": 'supplied_properties != null' } %filter-selector{ 'selector-set' => "propertySelectors", objects: "supplied_properties", "active-selectors" => "activeProperties"} + + .filter-shopfront.property-selectors{ng: {show: 'supplied_producer_properties != null'}} + %filter-selector{ 'selector-set' => "producerPropertySelectors", objects: "supplied_producer_properties", "active-selectors" => "activeProducerProperties"} + diff --git a/app/views/shop/products/_search_feedback.haml b/app/views/shop/products/_search_feedback.haml index eb71015376..d0511135d8 100644 --- a/app/views/shop/products/_search_feedback.haml +++ b/app/views/shop/products/_search_feedback.haml @@ -1,5 +1,5 @@ = cache_with_locale do - .row.animate-slide{ "ng-show" => "query || appliedPropertiesList() || appliedTaxonsList()" } + .row.animate-slide{ "ng-show" => "query || appliedPropertiesList() || appliedProducerPropertiesList() || appliedTaxonsList()" } .small-12.columns .alert-box.search-alert.ng-scope %div{"ng-show" => "Products.products.length > 0"} diff --git a/app/views/spree/admin/orders/_invoice_table.html.haml b/app/views/spree/admin/orders/_invoice_table.html.haml index 04ac30e81a..6a83e036b6 100644 --- a/app/views/spree/admin/orders/_invoice_table.html.haml +++ b/app/views/spree/admin/orders/_invoice_table.html.haml @@ -16,7 +16,7 @@ = render 'spree/shared/line_item_name', line_item: item %br %small - %em= item.variant.product.supplier.name + %em= item.variant.supplier.name %td{:align => "right"} = item.quantity %td{:align => "right"} diff --git a/app/views/spree/admin/orders/_invoice_table2.html.haml b/app/views/spree/admin/orders/_invoice_table2.html.haml index 789737bdad..863a301b41 100644 --- a/app/views/spree/admin/orders/_invoice_table2.html.haml +++ b/app/views/spree/admin/orders/_invoice_table2.html.haml @@ -19,7 +19,7 @@ = render 'spree/shared/line_item_name', line_item: item %br %small - %em= item.variant.product.supplier.name + %em= item.variant.supplier.name %td{:align => "right"} = item.quantity %td{:align => "right"} diff --git a/app/views/spree/admin/product_properties/index.html.haml b/app/views/spree/admin/product_properties/index.html.haml index 12660fc2cd..cd07017706 100644 --- a/app/views/spree/admin/product_properties/index.html.haml +++ b/app/views/spree/admin/product_properties/index.html.haml @@ -27,7 +27,7 @@ = render partial: 'product_property_fields', locals: { f: pp_form } = f.check_box :inherits_properties - = f.label :inherits_properties, t('.inherits_properties_checkbox_hint', supplier: @product.supplier.name) + = f.label :inherits_properties, t('.inherits_properties_checkbox_hint', supplier: @supplier.name) %br %br @@ -39,7 +39,7 @@ %th= t('admin.description') %th.actions %tbody#producer_properties - - @product.supplier.producer_properties.each do |producer_property| + - @supplier.producer_properties.each do |producer_property| %tr %td= producer_property.property.presentation %td= producer_property.value diff --git a/app/views/spree/admin/products/_form.html.haml b/app/views/spree/admin/products/_form.html.haml index 1f3492c123..546efeace4 100644 --- a/app/views/spree/admin/products/_form.html.haml +++ b/app/views/spree/admin/products/_form.html.haml @@ -27,12 +27,6 @@ = f.text_field :variant_unit_name, {placeholder: t('admin.products.unit_name_placeholder')} = f.error_message_on :variant_unit_name - = f.field_container :supplier do - = f.label :supplier, t(:spree_admin_supplier) - %br - = f.collection_select(:supplier_id, @producers, :id, :name, {:include_blank => true}, {:class => "select2"}) - = f.error_message_on :supplier - .clear .clear diff --git a/app/views/spree/admin/products/index/_products_product.html.haml b/app/views/spree/admin/products/index/_products_product.html.haml index e399d64aa3..b5a778a043 100644 --- a/app/views/spree/admin/products/index/_products_product.html.haml +++ b/app/views/spree/admin/products/index/_products_product.html.haml @@ -6,7 +6,6 @@ %a{class: 'image-modal'} %img{'ng-src' => '{{ product.thumb_url }}'} %td.producer{ 'ng-show' => 'columns.producer.visible' } - %select.fullwidth{ "data-controller": "tom-select", 'ng-model' => 'product.producer_id', :name => 'producer_id', 'ofn-track-product' => 'producer_id', 'ng-options' => 'producer.id as producer.name for producer in producers' } %td.sku{ 'ng-show' => 'columns.sku.visible' } %input{ 'ng-model' => "product.sku", :name => 'product_sku', 'ofn-track-product' => 'sku', :type => 'text' } %td.name{ 'ng-show' => 'columns.name.visible' } diff --git a/app/views/spree/admin/products/index/_products_variant.html.haml b/app/views/spree/admin/products/index/_products_variant.html.haml index 48c732c155..8a5c2c0311 100644 --- a/app/views/spree/admin/products/index/_products_variant.html.haml +++ b/app/views/spree/admin/products/index/_products_variant.html.haml @@ -4,6 +4,7 @@ %a{ :class => "add-variant icon-plus-sign", 'ng-click' => "addVariant(product)", 'ng-show' => "$last", 'ofn-with-tip' => t('.new_variant') } %td{ 'ng-show' => 'columns.image.visible' } %td{ 'ng-show' => 'columns.producer.visible' } + %select.fullwidth{ "data-controller": "tom-select", 'ng-model' => 'variant.producer_id', :name => 'producer_id', 'ofn-track-variant' => 'producer_id', 'ng-options' => 'producer.id as producer.name for producer in producers' } %td{ 'ng-show' => 'columns.sku.visible' } %input{ 'ng-model' => "variant.sku", :name => 'variant_sku', 'ofn-track-variant' => 'sku', :type => 'text' } %td{ 'ng-show' => 'columns.name.visible' } diff --git a/app/views/spree/admin/variants/_form.html.haml b/app/views/spree/admin/variants/_form.html.haml index f90f15c1f7..dcb4d68b6e 100644 --- a/app/views/spree/admin/variants/_form.html.haml +++ b/app/views/spree/admin/variants/_form.html.haml @@ -76,4 +76,8 @@ = 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" }) + .field + = f.label :supplier, t(:spree_admin_supplier) + = f.collection_select(:supplier_id, @producers, :id, :name, {:include_blank => true}, {:class => "select2 fullwidth"}) + .clear diff --git a/app/views/spree/order_mailer/_order_summary.html.haml b/app/views/spree/order_mailer/_order_summary.html.haml index 55e0e67cc3..1c3b4c7480 100644 --- a/app/views/spree/order_mailer/_order_summary.html.haml +++ b/app/views/spree/order_mailer/_order_summary.html.haml @@ -20,7 +20,7 @@ = render 'spree/shared/line_item_name', line_item: item %br %small - %em= item.variant.product.supplier.name + %em= item.variant.supplier.name %td - if item.variant.sku.blank? \- diff --git a/config/initializers/pagy.rb b/config/initializers/pagy.rb index cd18f12b32..eab7d3a437 100644 --- a/config/initializers/pagy.rb +++ b/config/initializers/pagy.rb @@ -1,15 +1,16 @@ # frozen_string_literal: true require 'pagy/extras/arel' +require 'pagy/extras/array' require 'pagy/extras/items' require 'pagy/extras/overflow' - # Pagy Variables # See https://ddnexus.github.io/pagy/api/pagy#variables -Pagy::DEFAULT[:items] = 100 +Pagy::DEFAULT[:items] = 100 -# Items extra: Allow the client to request a custom number of items per page with an optional selector UI +# 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 Pagy::DEFAULT[:items_param] = :per_page Pagy::DEFAULT[:max_items] = 100 diff --git a/config/locales/en.yml b/config/locales/en.yml index fafdc31f2b..d0764b7a91 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -69,7 +69,6 @@ en: name: "Product Name" price: "Price" primary_taxon: "Product Category" - supplier: "Supplier" shipping_category_id: "Shipping Category" variant_unit: "Variant Unit" variant_unit_name: "Variant Unit Name" @@ -77,6 +76,7 @@ en: spree/variant: primary_taxon: "Product Category" shipping_category_id: "Shipping Category" + supplier: "Supplier" spree/credit_card: base: "Credit Card" number: "Number" @@ -917,7 +917,6 @@ en: search_for_tax_categories: "Search for tax categories" category_field_name: "Category" tax_category_field_name: "Tax Category" - product_row: producer_field_name: "Producer" clone: success: Successfully cloned the product diff --git a/config/routes/api.rb b/config/routes/api.rb index a661c6f359..74f2ad3652 100644 --- a/config/routes/api.rb +++ b/config/routes/api.rb @@ -60,6 +60,7 @@ Openfoodnetwork::Application.routes.draw do get :products, on: :member get :taxons, on: :member get :properties, on: :member + get :producer_properties, on: :member end resources :exchanges, only: [:show], to: 'exchange_products#index' do diff --git a/db/migrate/20240221060301_add_supplier_to_variants.rb b/db/migrate/20240221060301_add_supplier_to_variants.rb new file mode 100644 index 0000000000..84eea57fd2 --- /dev/null +++ b/db/migrate/20240221060301_add_supplier_to_variants.rb @@ -0,0 +1,5 @@ +class AddSupplierToVariants < ActiveRecord::Migration[7.0] + def change + add_reference :spree_variants, :supplier, foreign_key: { to_table: :enterprises } + end +end diff --git a/db/migrate/20240422064305_migrate_product_supplier.rb b/db/migrate/20240422064305_migrate_product_supplier.rb new file mode 100644 index 0000000000..5ab52b4b32 --- /dev/null +++ b/db/migrate/20240422064305_migrate_product_supplier.rb @@ -0,0 +1,11 @@ +class MigrateProductSupplier < ActiveRecord::Migration[7.0] + def up + ActiveRecord::Base.connection.execute(<<-SQL + UPDATE spree_variants + SET supplier_id = spree_products.supplier_id + FROM spree_products + WHERE spree_variants.product_id = spree_products.id + SQL + ) + end +end diff --git a/db/schema.rb b/db/schema.rb index ba68c5c4b6..e8b49d884d 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -977,10 +977,12 @@ ActiveRecord::Schema[7.0].define(version: 2024_06_25_024328) do t.bigint "tax_category_id" t.bigint "shipping_category_id" t.bigint "primary_taxon_id" + t.bigint "supplier_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" + t.index ["supplier_id"], name: "index_spree_variants_on_supplier_id" t.index ["tax_category_id"], name: "index_spree_variants_on_tax_category_id" t.check_constraint "unit_value > 0::double precision", name: "positive_unit_value" end @@ -1221,6 +1223,7 @@ ActiveRecord::Schema[7.0].define(version: 2024_06_25_024328) do add_foreign_key "spree_taxons", "spree_taxons", column: "parent_id", name: "spree_taxons_parent_id_fk" add_foreign_key "spree_users", "spree_addresses", column: "bill_address_id", name: "spree_users_bill_address_id_fk" add_foreign_key "spree_users", "spree_addresses", column: "ship_address_id", name: "spree_users_ship_address_id_fk" + add_foreign_key "spree_variants", "enterprises", column: "supplier_id" 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" diff --git a/engines/catalog/app/services/catalog/product_import/products_reset_strategy.rb b/engines/catalog/app/services/catalog/product_import/products_reset_strategy.rb index ac7dac8de4..f4a8b9a2dd 100644 --- a/engines/catalog/app/services/catalog/product_import/products_reset_strategy.rb +++ b/engines/catalog/app/services/catalog/product_import/products_reset_strategy.rb @@ -20,12 +20,7 @@ module Catalog attr_reader :excluded_items_ids, :enterprise_ids def enterprise_variants_relation - relation = Spree::Variant - .joins(:product) - .where( - spree_products: { supplier_id: enterprise_ids }, - spree_variants: { deleted_at: nil } - ) + relation = Spree::Variant.where(supplier_id: enterprise_ids) return relation if excluded_items_ids.blank? diff --git a/engines/catalog/spec/services/catalog/product_import/products_reset_strategy_spec.rb b/engines/catalog/spec/services/catalog/product_import/products_reset_strategy_spec.rb index c6d43be45f..d407537e26 100644 --- a/engines/catalog/spec/services/catalog/product_import/products_reset_strategy_spec.rb +++ b/engines/catalog/spec/services/catalog/product_import/products_reset_strategy_spec.rb @@ -10,8 +10,8 @@ module Catalog describe '#reset' do let(:supplier_ids) { enterprise.id } let(:product) { create(:product) } - let(:enterprise) { product.supplier } let(:variant) { product.variants.first } + let(:enterprise) { variant.supplier } before { variant.on_hand = 2 } diff --git a/engines/dfc_provider/app/controllers/dfc_provider/supplied_products_controller.rb b/engines/dfc_provider/app/controllers/dfc_provider/supplied_products_controller.rb index 57d25c1929..3e5b26ec8d 100644 --- a/engines/dfc_provider/app/controllers/dfc_provider/supplied_products_controller.rb +++ b/engines/dfc_provider/app/controllers/dfc_provider/supplied_products_controller.rb @@ -20,8 +20,12 @@ module DfcProvider ) product = variant.product + if variant.new_record? + variant.supplier = current_enterprise + variant.save! + end + product.save! if product.new_record? - variant.save! if variant.new_record? supplied_product = SuppliedProductBuilder.supplied_product(variant) render json: DfcIo.export(supplied_product) diff --git a/engines/dfc_provider/app/services/dfc_builder.rb b/engines/dfc_provider/app/services/dfc_builder.rb index 5411d8ab28..4f55e301fb 100644 --- a/engines/dfc_provider/app/services/dfc_builder.rb +++ b/engines/dfc_provider/app/services/dfc_builder.rb @@ -3,7 +3,7 @@ class DfcBuilder def self.catalog_item(variant) id = urls.enterprise_catalog_item_url( - enterprise_id: variant.product.supplier_id, + enterprise_id: variant.supplier_id, id: variant.id, ) product = SuppliedProductBuilder.supplied_product(variant) diff --git a/engines/dfc_provider/app/services/offer_builder.rb b/engines/dfc_provider/app/services/offer_builder.rb index b499c1d945..0c3a8b276c 100644 --- a/engines/dfc_provider/app/services/offer_builder.rb +++ b/engines/dfc_provider/app/services/offer_builder.rb @@ -3,7 +3,7 @@ class OfferBuilder < DfcBuilder def self.build(variant) id = urls.enterprise_offer_url( - enterprise_id: variant.product.supplier_id, + enterprise_id: variant.supplier_id, id: variant.id, ) diff --git a/engines/dfc_provider/app/services/supplied_product_builder.rb b/engines/dfc_provider/app/services/supplied_product_builder.rb index bdf069bdfb..9c1e83e8f5 100644 --- a/engines/dfc_provider/app/services/supplied_product_builder.rb +++ b/engines/dfc_provider/app/services/supplied_product_builder.rb @@ -3,11 +3,11 @@ class SuppliedProductBuilder < DfcBuilder def self.supplied_product(variant) id = urls.enterprise_supplied_product_url( - enterprise_id: variant.product.supplier_id, + enterprise_id: variant.supplier_id, id: variant.id, ) product_uri = urls.enterprise_url( - variant.product.supplier_id, + variant.supplier_id, spree_product_id: variant.product_id ) @@ -29,14 +29,15 @@ class SuppliedProductBuilder < DfcBuilder if product Spree::Variant.new( product:, + supplier:, price: 0, ).tap do |variant| apply(supplied_product, variant) end else product = import_product(supplied_product) - product.supplier = supplier product.ensure_standard_variant + product.variants.first.supplier = supplier product.variants.first end.tap do |variant| link = supplied_product.semanticId diff --git a/engines/dfc_provider/spec/requests/catalog_items_spec.rb b/engines/dfc_provider/spec/requests/catalog_items_spec.rb index 1405a5b0a5..03e1ba468a 100644 --- a/engines/dfc_provider/spec/requests/catalog_items_spec.rb +++ b/engines/dfc_provider/spec/requests/catalog_items_spec.rb @@ -15,7 +15,7 @@ RSpec.describe "CatalogItems", type: :request, swagger_doc: "dfc.yaml", let(:product) { create( :base_product, - id: 90_000, supplier: enterprise, name: "Apple", description: "Red", + id: 90_000, name: "Apple", description: "Red", variants: [variant], primary_taxon: non_local_vegetable ) @@ -27,7 +27,7 @@ RSpec.describe "CatalogItems", type: :request, swagger_doc: "dfc.yaml", dfc_id: "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/productTypes.rdf#non-local-vegetable" ) } - let(:variant) { build(:base_variant, id: 10_001, unit_value: 1, sku: "AR") } + let(:variant) { build(:base_variant, id: 10_001, unit_value: 1, sku: "AR", supplier: enterprise) } before { login_as user } diff --git a/engines/dfc_provider/spec/requests/enterprises_spec.rb b/engines/dfc_provider/spec/requests/enterprises_spec.rb index 9785361aa6..13c9f249be 100644 --- a/engines/dfc_provider/spec/requests/enterprises_spec.rb +++ b/engines/dfc_provider/spec/requests/enterprises_spec.rb @@ -27,7 +27,7 @@ RSpec.describe "Enterprises", type: :request, swagger_doc: "dfc.yaml", rswag_aut let!(:product) { create( :product_with_image, - id: 90_000, supplier: enterprise, name: "Apple", description: "Round", + id: 90_000, name: "Apple", description: "Round", variants: [variant], primary_taxon: non_local_vegetable ) @@ -39,7 +39,9 @@ RSpec.describe "Enterprises", type: :request, swagger_doc: "dfc.yaml", rswag_aut dfc_id: "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/productTypes.rdf#non-local-vegetable" ) } - let(:variant) { build(:base_variant, id: 10_001, unit_value: 1, sku: "APP") } + let(:variant) { + build(:base_variant, id: 10_001, unit_value: 1, sku: "APP", supplier: enterprise) + } before { login_as user } diff --git a/engines/dfc_provider/spec/requests/offers_spec.rb b/engines/dfc_provider/spec/requests/offers_spec.rb index f309613c0c..dd5274e1b9 100644 --- a/engines/dfc_provider/spec/requests/offers_spec.rb +++ b/engines/dfc_provider/spec/requests/offers_spec.rb @@ -9,11 +9,11 @@ RSpec.describe "Offers", type: :request, swagger_doc: "dfc.yaml", rswag_autodoc: create( :product, id: 90_000, - supplier: enterprise, name: "Pesto", description: "Basil Pesto", + name: "Pesto", description: "Basil Pesto", variants: [variant], ) } - let(:variant) { build(:base_variant, id: 10_001, unit_value: 1) } + let(:variant) { build(:base_variant, id: 10_001, unit_value: 1, supplier: enterprise) } before { login_as user } diff --git a/engines/dfc_provider/spec/requests/supplied_products_spec.rb b/engines/dfc_provider/spec/requests/supplied_products_spec.rb index e72e13017f..cb707b3fdb 100644 --- a/engines/dfc_provider/spec/requests/supplied_products_spec.rb +++ b/engines/dfc_provider/spec/requests/supplied_products_spec.rb @@ -9,11 +9,13 @@ RSpec.describe "SuppliedProducts", type: :request, swagger_doc: "dfc.yaml", rswa create( :product_with_image, id: 90_000, - supplier: enterprise, name: "Pesto", description: "Basil Pesto", + name: "Pesto", description: "Basil Pesto", variants: [variant] ) } - let(:variant) { build(:base_variant, id: 10_001, unit_value: 1, primary_taxon: taxon) } + let(:variant) { + build(:base_variant, id: 10_001, unit_value: 1, primary_taxon: taxon, supplier: enterprise) + } let(:taxon) { build( :taxon, diff --git a/engines/dfc_provider/spec/services/catalog_item_builder_spec.rb b/engines/dfc_provider/spec/services/catalog_item_builder_spec.rb index 7c1b4d2c0f..6f7d5d42a6 100644 --- a/engines/dfc_provider/spec/services/catalog_item_builder_spec.rb +++ b/engines/dfc_provider/spec/services/catalog_item_builder_spec.rb @@ -8,7 +8,7 @@ RSpec.describe DfcBuilder do describe ".catalog_item" do it "assigns a semantic id" do variant.id = 5 - variant.product.supplier_id = 7 + variant.supplier_id = 7 item = DfcBuilder.catalog_item(variant) @@ -19,7 +19,7 @@ RSpec.describe DfcBuilder do it "refers to a supplied product" do variant.id = 5 - variant.product.supplier_id = 7 + variant.supplier_id = 7 item = DfcBuilder.catalog_item(variant) diff --git a/engines/dfc_provider/spec/services/enterprise_builder_spec.rb b/engines/dfc_provider/spec/services/enterprise_builder_spec.rb index cc26fe767b..05fa5ae17d 100644 --- a/engines/dfc_provider/spec/services/enterprise_builder_spec.rb +++ b/engines/dfc_provider/spec/services/enterprise_builder_spec.rb @@ -5,7 +5,7 @@ require_relative "../spec_helper" RSpec.describe EnterpriseBuilder do subject(:builder) { described_class } let(:enterprise) { - build( + create( :enterprise, id: 10_000, name: "Fabi's Farm", description: "The place where stuff grows", abn: "123 456 789 0", @@ -13,7 +13,7 @@ RSpec.describe EnterpriseBuilder do ) } let(:variant) { - create(:product, supplier: enterprise, name: "Apple").variants.first + create(:product, supplier_id: enterprise.id, name: "Apple").variants.first } describe ".enterprise" do diff --git a/engines/dfc_provider/spec/services/supplied_product_builder_spec.rb b/engines/dfc_provider/spec/services/supplied_product_builder_spec.rb index 3e582a7aa4..c39aab031e 100644 --- a/engines/dfc_provider/spec/services/supplied_product_builder_spec.rb +++ b/engines/dfc_provider/spec/services/supplied_product_builder_spec.rb @@ -7,13 +7,13 @@ RSpec.describe SuppliedProductBuilder do subject(:builder) { described_class } let(:variant) { - build(:variant, id: 5, product: spree_product, primary_taxon: taxon) + create(:variant, id: 5, product: spree_product, primary_taxon: taxon, supplier:) } let(:spree_product) { - create(:product, id: 6, supplier:) + create(:product, id: 6) } let(:supplier) { - build(:supplier_enterprise, id: 7) + create(:supplier_enterprise, id: 7) } let(:taxon) { build( @@ -43,7 +43,7 @@ RSpec.describe SuppliedProductBuilder do variant.product.name = "Apple" product = builder.supplied_product(variant) - expect(product.name).to eq "Apple" + expect(product.name).to match /Apple/ end it "assigns the variant name if present" do @@ -51,7 +51,7 @@ RSpec.describe SuppliedProductBuilder do variant.display_name = "Granny Smith" product = builder.supplied_product(variant) - expect(product.name).to eq "Apple - Granny Smith" + expect(product.name).to match /Apple - Granny Smith/ end context "product_type mapping" do @@ -244,7 +244,7 @@ RSpec.describe SuppliedProductBuilder do it "doesn't return a product of another enterprise" do variant.save! - create(:product, id: 8, supplier: create(:enterprise)) + create(:product, id: 8, supplier_id: create(:enterprise).id) supplied_product.spree_product_uri = "http://test.host/api/dfc/enterprises/7?spree_product_id=8" diff --git a/engines/order_management/app/services/order_management/subscriptions/variants_list.rb b/engines/order_management/app/services/order_management/subscriptions/variants_list.rb index 1807f5154f..fb06ed8829 100644 --- a/engines/order_management/app/services/order_management/subscriptions/variants_list.rb +++ b/engines/order_management/app/services/order_management/subscriptions/variants_list.rb @@ -8,15 +8,14 @@ module OrderManagement # - Variants of hub # - Variants that are in outgoing exchanges where the hub is receiver def self.eligible_variants(distributor) - variant_conditions = ["spree_products.supplier_id IN (?)", - permitted_producer_ids(distributor)] + query = Spree::Variant.where(supplier_id: permitted_producer_ids(distributor)) + exchange_variant_ids = outgoing_exchange_variant_ids(distributor) if exchange_variant_ids.present? - variant_conditions[0] << " OR spree_variants.id IN (?)" - variant_conditions << exchange_variant_ids + query = query.or(Spree::Variant.where(id: exchange_variant_ids)) end - Spree::Variant.joins(:product).where(*variant_conditions) + query end def self.in_open_and_upcoming_order_cycles?(distributor, schedule, variant) diff --git a/engines/order_management/spec/services/order_management/subscriptions/form_spec.rb b/engines/order_management/spec/services/order_management/subscriptions/form_spec.rb index c915ff0e99..1533f82df8 100644 --- a/engines/order_management/spec/services/order_management/subscriptions/form_spec.rb +++ b/engines/order_management/spec/services/order_management/subscriptions/form_spec.rb @@ -8,18 +8,18 @@ module OrderManagement describe "creating a new subscription" do let!(:shop) { create(:distributor_enterprise) } let!(:customer) { create(:customer, enterprise: shop) } - let!(:product1) { create(:product, supplier: shop) } - let!(:product2) { create(:product, supplier: shop) } - let!(:product3) { create(:product, supplier: shop) } + let!(:product1) { create(:product) } + let!(:product2) { create(:product) } + let!(:product3) { create(:product) } let!(:variant1) { - create(:variant, product: product1, unit_value: '100', price: 12.00) + create(:variant, product: product1, unit_value: '100', price: 12.00, supplier: shop) } let!(:variant2) { - create(:variant, product: product2, unit_value: '1000', price: 6.00) + create(:variant, product: product2, unit_value: '1000', price: 6.00, supplier: shop) } let!(:variant3) { create(:variant, product: product2, unit_value: '1000', - price: 2.50, on_hand: 1) + price: 2.50, on_hand: 1, supplier: shop) } let!(:enterprise_fee) { create(:enterprise_fee, amount: 1.75) } let!(:order_cycle1) { diff --git a/engines/order_management/spec/services/order_management/subscriptions/variants_list_spec.rb b/engines/order_management/spec/services/order_management/subscriptions/variants_list_spec.rb index bb95c0dc77..f932ad36aa 100644 --- a/engines/order_management/spec/services/order_management/subscriptions/variants_list_spec.rb +++ b/engines/order_management/spec/services/order_management/subscriptions/variants_list_spec.rb @@ -8,8 +8,7 @@ module OrderManagement describe "variant eligibility for subscription" do let!(:shop) { create(:distributor_enterprise) } let!(:producer) { create(:supplier_enterprise) } - let!(:product) { create(:product, supplier: producer) } - let!(:variant) { product.variants.first } + let!(:variant) { create(:variant, supplier: producer) } let!(:schedule) { create(:schedule, order_cycles: [order_cycle]) } let!(:subscription) { create(:subscription, shop:, schedule:) } @@ -45,7 +44,7 @@ module OrderManagement context "if the supplier is permitted for the shop" do let!(:enterprise_relationship) { create(:enterprise_relationship, child: shop, - parent: product.supplier, + parent: variant.supplier, permissions_list: [:add_to_order_cycle]) } @@ -60,7 +59,7 @@ module OrderManagement context "if it is an incoming exchange where the shop is the receiver" do let!(:incoming_exchange) { - order_cycle.exchanges.create(sender: product.supplier, + order_cycle.exchanges.create(sender: variant.supplier, receiver: shop, incoming: true, variants: [variant]) } @@ -72,7 +71,7 @@ module OrderManagement context "if it is an outgoing exchange where the shop is the receiver" do let!(:outgoing_exchange) { - order_cycle.exchanges.create(sender: product.supplier, + order_cycle.exchanges.create(sender: variant.supplier, receiver: shop, incoming: false, variants: [variant]) @@ -123,7 +122,7 @@ module OrderManagement context "if it is an incoming exchange where the shop is the receiver" do let!(:incoming_exchange) { - order_cycle.exchanges.create(sender: product.supplier, + order_cycle.exchanges.create(sender: variant.supplier, receiver: shop, incoming: true, variants: [variant]) @@ -138,7 +137,7 @@ module OrderManagement context "if it is an outgoing exchange where the shop is the receiver" do let!(:outgoing_exchange) { - order_cycle.exchanges.create(sender: product.supplier, + order_cycle.exchanges.create(sender: variant.supplier, receiver: shop, incoming: false, variants: [variant]) diff --git a/lib/open_food_network/order_cycle_permissions.rb b/lib/open_food_network/order_cycle_permissions.rb index c6c2508112..1ab7933938 100644 --- a/lib/open_food_network/order_cycle_permissions.rb +++ b/lib/open_food_network/order_cycle_permissions.rb @@ -151,7 +151,7 @@ module OpenFoodNetwork end def all_variants_supplied_by(producer) - Spree::Variant.joins(:product).where('spree_products.supplier_id = (?)', producer) + Spree::Variant.where(supplier: producer) end def no_variants @@ -163,9 +163,9 @@ module OpenFoodNetwork user_manages_coordinator_or(enterprise) end.map(&:id) - Spree::Variant.includes(product: :supplier). - select("spree_variants.id, spree_variants.product_id, spree_products.supplier_id"). - joins(:product).where(spree_products: { supplier_id: valid_suppliers }) + Spree::Variant.includes(:supplier). + select(:id, :product_id, :supplier_id). + where(supplier_id: valid_suppliers) end # Find the variants that a user is permitted see within outgoing exchanges @@ -185,13 +185,10 @@ module OpenFoodNetwork permitted_variants = variants_from_suppliers(producer_ids) # PLUS my incoming producers' variants that are already in an outgoing exchange of this hub, - # so things don't break. TODO: Remove this when all P-OC are sorted out - active_variants = Spree::Variant.joins(:exchanges, :product). - where("exchanges.receiver_id = (?) - AND spree_products.supplier_id IN (?) - AND incoming = 'f'", - hub.id, - managed_producer_ids) + # so things don't break. + # TODO: Remove this when all P-OC are sorted out + active_variants = Spree::Variant.joins(:exchanges). + where(exchanges: { receiver: hub, incoming: false }, supplier_id: managed_producer_ids) Spree::Variant.where(id: permitted_variants | active_variants) end @@ -238,7 +235,7 @@ module OpenFoodNetwork end def variants_from_suppliers(supplier_ids) - Spree::Variant.joins(:product).where(spree_products: { supplier_id: supplier_ids }) + Spree::Variant.where(supplier_id: supplier_ids) end def active_outgoing_variants(hub) diff --git a/lib/open_food_network/permissions.rb b/lib/open_food_network/permissions.rb index 27a0312a7f..87f43f052f 100644 --- a/lib/open_food_network/permissions.rb +++ b/lib/open_food_network/permissions.rb @@ -62,28 +62,26 @@ module OpenFoodNetwork def editable_products return Spree::Product.all if admin? - Spree::Product.where(supplier_id: @user.enterprises).or( - Spree::Product.where(supplier_id: related_enterprises_granting(:manage_products)) + product_with_variants.where(spree_variants: { supplier_id: @user.enterprises }).or( + product_with_variants.where( + spree_variants: { supplier_id: related_enterprises_granting(:manage_products) } + ) ) end def visible_products return Spree::Product.all if admin? - Spree::Product.where( - supplier_id: @user.enterprises - ).or( - Spree::Product.where( - supplier_id: related_enterprises_granting(:manage_products) | - related_enterprises_granting(:add_to_order_cycle) + product_with_variants.where(spree_variants: { supplier_id: @user.enterprises }).or( + product_with_variants.where( + spree_variants: { + supplier_id: related_enterprises_granting(:manage_products) | + related_enterprises_granting(:add_to_order_cycle) + } ) ) end - def product_ids_supplied_by(supplier_ids) - Spree::Product.where(supplier_id: supplier_ids).select(:id) - end - def managed_product_enterprises managed_and_related_enterprises_granting :manage_products end @@ -176,5 +174,9 @@ module OpenFoodNetwork def managed_enterprise_products Spree::Product.managed_by(@user) end + + def product_with_variants + Spree::Product.joins(:variants) + end end end diff --git a/lib/reporting/line_items.rb b/lib/reporting/line_items.rb index 233c965c2a..01cd726ed0 100644 --- a/lib/reporting/line_items.rb +++ b/lib/reporting/line_items.rb @@ -15,7 +15,7 @@ module Reporting @orders ||= search_orders end - def list(line_item_includes = [variant: [product: :supplier]]) + def list(line_item_includes = [variant: [:supplier, :product]]) line_items = order_permissions.visible_line_items.in_orders(orders.result) .order("supplier.name", "product.name", "variant.display_name") diff --git a/lib/reporting/queries/joins.rb b/lib/reporting/queries/joins.rb index e0097aa3a0..e4ad23e802 100644 --- a/lib/reporting/queries/joins.rb +++ b/lib/reporting/queries/joins.rb @@ -19,8 +19,8 @@ module Reporting reflect query.join(association(Spree::Variant, :product)) end - def joins_product_supplier - reflect query.join(association(Spree::Product, :supplier, supplier_alias)) + def joins_variant_supplier + reflect query.join(association(Spree::Variant, :supplier, supplier_alias)) end def joins_variant_shipping_category diff --git a/lib/reporting/reports/bulk_coop/base.rb b/lib/reporting/reports/bulk_coop/base.rb index 5bb079d923..ce4c154e35 100644 --- a/lib/reporting/reports/bulk_coop/base.rb +++ b/lib/reporting/reports/bulk_coop/base.rb @@ -22,7 +22,7 @@ module Reporting [ { order: [:bill_address], - variant: { product: :supplier } + variant: [:product, :supplier] } ] end diff --git a/lib/reporting/reports/enterprise_fee_summary/scope.rb b/lib/reporting/reports/enterprise_fee_summary/scope.rb index 7b6ed51425..5aa518f5c4 100644 --- a/lib/reporting/reports/enterprise_fee_summary/scope.rb +++ b/lib/reporting/reports/enterprise_fee_summary/scope.rb @@ -201,7 +201,6 @@ module Reporting # Includes: # * Line item # * Variant - # * Product # * Tax category of product, if enterprise fee tells to inherit def include_line_item_source_details join_scope( @@ -224,13 +223,6 @@ module Reporting JOIN_STRING ) - join_scope( - <<~JOIN_STRING - LEFT OUTER JOIN spree_products - ON (spree_products.id = spree_variants.product_id) - JOIN_STRING - ) - join_scope( <<~JOIN_STRING LEFT OUTER JOIN spree_tax_categories AS product_tax_categories @@ -324,7 +316,7 @@ module Reporting def filter_by_distribution(params) filter_scope(spree_orders: { distributor_id: params.distributor_ids }) \ if params.distributor_ids.present? - filter_scope(spree_products: { supplier_id: params.producer_ids }) \ + filter_scope(spree_variants: { supplier_id: params.producer_ids }) \ if params.producer_ids.present? filter_scope(spree_orders: { order_cycle_id: params.order_cycle_ids }) \ if params.order_cycle_ids.present? diff --git a/lib/reporting/reports/orders_and_fulfillment/base.rb b/lib/reporting/reports/orders_and_fulfillment/base.rb index 4d223be657..36b6ecf325 100644 --- a/lib/reporting/reports/orders_and_fulfillment/base.rb +++ b/lib/reporting/reports/orders_and_fulfillment/base.rb @@ -49,11 +49,11 @@ module Reporting end def supplier_name - proc { |line_items| line_items.first.variant.product.supplier.name } + proc { |line_items| line_items.first.variant.supplier.name } end def supplier_charges_sales_tax? - proc { |line_items| line_items.first.variant.product.supplier.charges_sales_tax } + proc { |line_items| line_items.first.variant.supplier.charges_sales_tax } end def product_name diff --git a/lib/reporting/reports/orders_and_fulfillment/order_cycle_customer_totals.rb b/lib/reporting/reports/orders_and_fulfillment/order_cycle_customer_totals.rb index 8a538dfe23..b5703dcbda 100644 --- a/lib/reporting/reports/orders_and_fulfillment/order_cycle_customer_totals.rb +++ b/lib/reporting/reports/orders_and_fulfillment/order_cycle_customer_totals.rb @@ -93,7 +93,7 @@ module Reporting end def line_item_includes - [{ variant: { product: :supplier }, + [{ variant: [:product, :supplier], order: [:bill_address, :ship_address, :order_cycle, :adjustments, :payments, :user, :distributor, :shipments] }] end diff --git a/lib/reporting/reports/orders_and_fulfillment/order_cycle_distributor_totals_by_supplier.rb b/lib/reporting/reports/orders_and_fulfillment/order_cycle_distributor_totals_by_supplier.rb index 9e2dcd4d43..f39cae4725 100644 --- a/lib/reporting/reports/orders_and_fulfillment/order_cycle_distributor_totals_by_supplier.rb +++ b/lib/reporting/reports/orders_and_fulfillment/order_cycle_distributor_totals_by_supplier.rb @@ -40,7 +40,7 @@ module Reporting :adjustments, { shipments: { shipping_rates: :shipping_method } } ], - variant: { product: :supplier } + variant: [:product, :supplier] }] end end diff --git a/lib/reporting/reports/orders_and_fulfillment/order_cycle_supplier_totals.rb b/lib/reporting/reports/orders_and_fulfillment/order_cycle_supplier_totals.rb index 87837d32a0..dedbc9e2d8 100644 --- a/lib/reporting/reports/orders_and_fulfillment/order_cycle_supplier_totals.rb +++ b/lib/reporting/reports/orders_and_fulfillment/order_cycle_supplier_totals.rb @@ -42,7 +42,7 @@ module Reporting end def line_item_includes - [{ variant: { product: :supplier } }] + [{ variant: [:supplier, :product] }] end def query_result diff --git a/lib/reporting/reports/orders_and_fulfillment/order_cycle_supplier_totals_by_distributor.rb b/lib/reporting/reports/orders_and_fulfillment/order_cycle_supplier_totals_by_distributor.rb index 3d7c211126..aae3478837 100644 --- a/lib/reporting/reports/orders_and_fulfillment/order_cycle_supplier_totals_by_distributor.rb +++ b/lib/reporting/reports/orders_and_fulfillment/order_cycle_supplier_totals_by_distributor.rb @@ -41,7 +41,7 @@ module Reporting def line_item_includes [{ order: :distributor, - variant: { product: :supplier } }] + variant: [:product, :supplier] }] end end end diff --git a/lib/reporting/reports/packing/base.rb b/lib/reporting/reports/packing/base.rb index 8fb00736d8..07ac39a59a 100644 --- a/lib/reporting/reports/packing/base.rb +++ b/lib/reporting/reports/packing/base.rb @@ -18,7 +18,7 @@ module Reporting joins_order_bill_address. joins_variant. joins_variant_product. - joins_product_supplier. + joins_variant_supplier. joins_variant_shipping_category. selecting(select_fields). ordered_by(ordering_fields) diff --git a/lib/reporting/reports/products_and_inventory/base.rb b/lib/reporting/reports/products_and_inventory/base.rb index 3b58daca76..06b1e9b3f0 100644 --- a/lib/reporting/reports/products_and_inventory/base.rb +++ b/lib/reporting/reports/products_and_inventory/base.rb @@ -13,8 +13,8 @@ module Reporting # rubocop:disable Metrics/AbcSize def columns { - supplier: proc { |variant| variant.product.supplier.name }, - producer_suburb: proc { |variant| variant.product.supplier.address.city }, + supplier: proc { |variant| variant.supplier.name }, + producer_suburb: proc { |variant| variant.supplier.address.city }, product: proc { |variant| variant.product.name }, product_properties: proc { |v| v.product.properties.map(&:name).join(", ") }, taxons: proc { |variant| variant.primary_taxon.name }, @@ -33,9 +33,10 @@ module Reporting def child_variants Spree::Variant. + select("spree_variants.*, spree_products.name"). joins(:product). merge(visible_products). - order('spree_products.name') + order('spree_products.name').distinct end private @@ -59,7 +60,7 @@ module Reporting def filter_to_supplier(variants) if params[:supplier_id].to_i > 0 - variants.where(spree_products: { supplier_id: params[:supplier_id] }) + variants.where(supplier: params[:supplier_id]) else variants end diff --git a/lib/reporting/reports/products_and_inventory/lettuce_share.rb b/lib/reporting/reports/products_and_inventory/lettuce_share.rb index 366332fd55..7d3fa4a807 100644 --- a/lib/reporting/reports/products_and_inventory/lettuce_share.rb +++ b/lib/reporting/reports/products_and_inventory/lettuce_share.rb @@ -65,7 +65,7 @@ module Reporting end def producer_name(variant) - variant.product.supplier.name + variant.supplier.name end def certification(variant) diff --git a/spec/controllers/admin/bulk_line_items_controller_spec.rb b/spec/controllers/admin/bulk_line_items_controller_spec.rb index 999c39c945..dcca09f313 100644 --- a/spec/controllers/admin/bulk_line_items_controller_spec.rb +++ b/spec/controllers/admin/bulk_line_items_controller_spec.rb @@ -95,29 +95,26 @@ RSpec.describe Admin::BulkLineItemsController, type: :controller do let(:coordinator) { create(:distributor_enterprise) } let(:order_cycle) { create(:simple_order_cycle, coordinator:) } let!(:order1) { - FactoryBot.create(:order, order_cycle:, state: 'complete', - completed_at: Time.zone.now, distributor: distributor1, - billing_address: FactoryBot.create(:address) ) + create(:order, order_cycle:, state: 'complete', + completed_at: Time.zone.now, distributor: distributor1, + billing_address: create(:address) ) } let!(:line_item1) { - FactoryBot.create(:line_item_with_shipment, order: order1, - product: FactoryBot.create(:product, - supplier:)) + create(:line_item_with_shipment, order: order1, + variant: create(:variant, supplier:)) } let!(:line_item2) { - FactoryBot.create(:line_item_with_shipment, order: order1, - product: FactoryBot.create(:product, - supplier:)) + create(:line_item_with_shipment, order: order1, + variant: create(:variant, supplier:)) } let!(:order2) { - FactoryBot.create(:order, order_cycle:, state: 'complete', - completed_at: Time.zone.now, distributor: distributor2, - billing_address: FactoryBot.create(:address) ) + create(:order, order_cycle:, state: 'complete', + completed_at: Time.zone.now, distributor: distributor2, + billing_address: create(:address) ) } let!(:line_item3) { - FactoryBot.create(:line_item_with_shipment, order: order2, - product: FactoryBot.create(:product, - supplier:)) + create(:line_item_with_shipment, order: order2, + variant: create(:variant, supplier:)) } context "producer enterprise" do @@ -189,15 +186,15 @@ RSpec.describe Admin::BulkLineItemsController, type: :controller do let(:coordinator) { create(:distributor_enterprise) } let(:order_cycle) { create(:simple_order_cycle, coordinator:) } let!(:order1) { - FactoryBot.create(:order, order_cycle:, state: 'complete', - completed_at: Time.zone.now, - distributor: distributor1, - billing_address: FactoryBot.create(:address) ) + create(:order, order_cycle:, state: 'complete', + completed_at: Time.zone.now, + distributor: distributor1, + billing_address: create(:address) ) } let!(:line_item1) { - line_item1 = FactoryBot.create(:line_item_with_shipment, - order: order1, - product: FactoryBot.create(:product, supplier:)) + line_item1 = create(:line_item_with_shipment, + order: order1, + variant: create(:variant, supplier:)) # make sure shipment is available through db reloads of this line_item line_item1.tap(&:save!) } @@ -298,14 +295,13 @@ RSpec.describe Admin::BulkLineItemsController, type: :controller do let(:coordinator) { create(:distributor_enterprise) } let(:order_cycle) { create(:simple_order_cycle, coordinator:) } let!(:order1) { - FactoryBot.create(:order, order_cycle:, state: 'complete', - completed_at: Time.zone.now, distributor: distributor1, - billing_address: FactoryBot.create(:address) ) + create(:order, order_cycle:, state: 'complete', + completed_at: Time.zone.now, distributor: distributor1, + billing_address: create(:address) ) } let!(:line_item1) { - FactoryBot.create(:line_item_with_shipment, order: order1, - product: FactoryBot.create(:product, - supplier:)) + create(:line_item_with_shipment, order: order1, + variant: create(:variant, supplier:)) } let(:params) { { id: line_item1.id, order_id: order1.number } } diff --git a/spec/controllers/admin/inventory_items_controller_spec.rb b/spec/controllers/admin/inventory_items_controller_spec.rb index 8375a89bbd..be33be4871 100644 --- a/spec/controllers/admin/inventory_items_controller_spec.rb +++ b/spec/controllers/admin/inventory_items_controller_spec.rb @@ -44,7 +44,7 @@ RSpec.describe Admin::InventoryItemsController, type: :controller do context "and the producer has granted VO permission" do before do - create(:enterprise_relationship, parent: variant.product.supplier, child: enterprise, + create(:enterprise_relationship, parent: variant.supplier, child: enterprise, permissions_list: [:create_variant_overrides]) end @@ -114,7 +114,7 @@ RSpec.describe Admin::InventoryItemsController, type: :controller do context "and the producer has granted VO permission" do before do - create(:enterprise_relationship, parent: variant.product.supplier, child: enterprise, + create(:enterprise_relationship, parent: variant.supplier, child: enterprise, permissions_list: [:create_variant_overrides]) end diff --git a/spec/controllers/admin/order_cycles_controller_spec.rb b/spec/controllers/admin/order_cycles_controller_spec.rb index aaeafbd8d7..2a859e7210 100644 --- a/spec/controllers/admin/order_cycles_controller_spec.rb +++ b/spec/controllers/admin/order_cycles_controller_spec.rb @@ -217,8 +217,7 @@ module Admin let(:coordinator) { order_cycle.coordinator } let(:producer) { create(:supplier_enterprise) } let!(:schedule) { create(:schedule, order_cycles: [order_cycle]) } - let!(:p) { create(:product) } - let!(:v) { p.variants.first } + let(:v) { create(:variant) } let!(:incoming_exchange) { create(:exchange, order_cycle:, sender: producer, receiver: coordinator, incoming: true, variants: [v]) diff --git a/spec/controllers/admin/reports_controller_spec.rb b/spec/controllers/admin/reports_controller_spec.rb index 5e80fe5f68..4501adf433 100644 --- a/spec/controllers/admin/reports_controller_spec.rb +++ b/spec/controllers/admin/reports_controller_spec.rb @@ -15,9 +15,9 @@ RSpec.describe Admin::ReportsController, type: :controller do let(:distributor1) { create(:distributor_enterprise) } let(:distributor2) { create(:distributor_enterprise) } let(:distributor3) { create(:distributor_enterprise) } - let(:product1) { create(:product, price: 12.34, supplier: supplier1) } - let(:product2) { create(:product, price: 23.45, supplier: supplier2) } - let(:product3) { create(:product, price: 34.56, supplier: supplier3) } + let(:product1) { create(:product, price: 12.34, supplier_id: supplier1.id) } + let(:product2) { create(:product, price: 23.45, supplier_id: supplier2.id) } + let(:product3) { create(:product, price: 34.56, supplier_id: supplier3.id) } # Given two order cycles with both distributors let(:ocA) { diff --git a/spec/controllers/admin/subscriptions_controller_spec.rb b/spec/controllers/admin/subscriptions_controller_spec.rb index 0960e2fc86..a59001731a 100644 --- a/spec/controllers/admin/subscriptions_controller_spec.rb +++ b/spec/controllers/admin/subscriptions_controller_spec.rb @@ -262,9 +262,9 @@ RSpec.describe Admin::SubscriptionsController, type: :controller do let!(:user) { create(:user) } let!(:shop) { create(:distributor_enterprise, owner: user) } let!(:customer) { create(:customer, enterprise: shop) } - let!(:product1) { create(:product, supplier: shop) } + let!(:product1) { create(:product) } let!(:variant1) { - create(:variant, product: product1, unit_value: '100', price: 12.00) + create(:variant, product: product1, unit_value: '100', price: 12.00, supplier: shop) } let!(:enterprise_fee) { create(:enterprise_fee, amount: 1.75) } let!(:order_cycle) { diff --git a/spec/controllers/admin/variant_overrides_controller_spec.rb b/spec/controllers/admin/variant_overrides_controller_spec.rb index 7e37e63d60..13848a22d1 100644 --- a/spec/controllers/admin/variant_overrides_controller_spec.rb +++ b/spec/controllers/admin/variant_overrides_controller_spec.rb @@ -52,15 +52,15 @@ RSpec.describe Admin::VariantOverridesController, type: :controller do context "and the producer has granted VO permission" do before do - create(:enterprise_relationship, parent: variant.product.supplier, child: hub, + create(:enterprise_relationship, parent: variant.supplier, child: hub, permissions_list: [:create_variant_overrides]) end it "loads data" do put :bulk_update, as: format, params: { variant_overrides: variant_override_params } expect(assigns[:hubs]).to eq [hub] - expect(assigns[:producers]).to eq [variant.product.supplier] - expect(assigns[:hub_permissions]).to eq Hash[hub.id, [variant.product.supplier.id]] + expect(assigns[:producers]).to eq [variant.supplier] + expect(assigns[:hub_permissions]).to eq Hash[hub.id, [variant.supplier.id]] expect(assigns[:inventory_items]).to eq [inventory_item] end @@ -113,9 +113,9 @@ RSpec.describe Admin::VariantOverridesController, type: :controller do let(:hub) { create(:distributor_enterprise) } let(:producer) { create(:supplier_enterprise) } - let(:product) { create(:product, supplier: producer) } - let(:variant1) { create(:variant, product:) } - let(:variant2) { create(:variant, product:) } + let(:product) { create(:product) } + let(:variant1) { create(:variant, product:, supplier: producer) } + let(:variant2) { create(:variant, product:, supplier: producer) } let!(:variant_override1) { create(:variant_override, hub:, variant: variant1, count_on_hand: 5, default_stock: 7, resettable: true) @@ -179,8 +179,8 @@ RSpec.describe Admin::VariantOverridesController, type: :controller do "to another hub I manage" do before { hub.owner.update_attribute(:enterprise_limit, 2) } let(:hub2) { create(:distributor_enterprise, owner: hub.owner) } - let(:product) { create(:product, supplier: producer) } - let(:variant3) { create(:variant, product:) } + let(:product) { create(:product) } + let(:variant3) { create(:variant, product:, supplier: producer) } let!(:variant_override3) { create(:variant_override, hub: hub2, variant: variant3, count_on_hand: 1, default_stock: 13, resettable: true) diff --git a/spec/controllers/api/v0/exchange_products_controller_spec.rb b/spec/controllers/api/v0/exchange_products_controller_spec.rb index ea226a80f1..f290186450 100644 --- a/spec/controllers/api/v0/exchange_products_controller_spec.rb +++ b/spec/controllers/api/v0/exchange_products_controller_spec.rb @@ -39,7 +39,7 @@ module Api api_get :index, exchange_id: exchange.id, order_cycle_id: 666, enterprise_id: 666, incoming: false expect(json_response["products"].first["supplier_name"]) - .to eq exchange.variants.first.product.supplier.name + .to eq exchange.variants.first.supplier.name end end @@ -48,7 +48,7 @@ module Api api_get :index, order_cycle_id: order_cycle.id, enterprise_id: exchange.sender_id, incoming: true expect(json_response["products"].first["supplier_name"]) - .to eq exchange.variants.first.product.supplier.name + .to eq exchange.variants.first.supplier.name end end end diff --git a/spec/controllers/api/v0/order_cycles_controller_spec.rb b/spec/controllers/api/v0/order_cycles_controller_spec.rb index bb77374986..225b6f22be 100644 --- a/spec/controllers/api/v0/order_cycles_controller_spec.rb +++ b/spec/controllers/api/v0/order_cycles_controller_spec.rb @@ -52,7 +52,7 @@ module Api it "returns products that were searched for" do ransack_param = "name_or_meta_keywords_or_variants_display_as_or_" \ - "variants_display_name_or_supplier_name_cont" + "variants_display_name_or_variants_supplier_name_cont" api_get :products, id: order_cycle.id, distributor: distributor.id, q: { ransack_param => "Kangaroo" } @@ -107,14 +107,15 @@ module Api let!(:supplier) { create(:supplier_enterprise, properties: [supplier_property]) } before do - product1.update!(supplier:) - product2.update!(supplier:) - product3.update!(supplier:, inherits_properties: false) + product1.variants.first.update!(supplier:) + product2.variants.first.update!(supplier:) + product3.update!(inherits_properties: false) + product3.variants.first.update!(supplier:) end it "filter out the product that don't inherits from supplier properties" do api_get :products, id: order_cycle.id, distributor: distributor.id, - q: { with_properties: [supplier_property.id] } + q: { with_variants_supplier_properties: [supplier_property.id] } expect(response.status).to eq 200 expect(product_ids).to match_array [product1.id, product2.id] @@ -238,38 +239,44 @@ module Api expect(json_response.length).to be 2 expect(properties).to include property1.presentation, property2.presentation end + end - context "with producer properties" do - let!(:property4) { create(:property) } - let!(:producer_property) { - create(:producer_property, producer_id: product1.supplier.id, property: property4) - } + describe "#producer_properties" do + let!(:property4) { create(:property) } + let!(:supplier) { create(:supplier_enterprise) } + let!(:producer_property) { + create(:producer_property, producer_id: supplier.id, property: property4) + } - it "loads producer properties for distributed products in the order cycle" do - api_get :properties, id: order_cycle.id, distributor: distributor.id + before { product1.variants.first.update(supplier: ) } - properties = json_response.pluck(:name) + it "loads producer properties for distributed products in the order cycle" do + api_get :producer_properties, id: order_cycle.id, distributor: distributor.id - expect(json_response.length).to be 3 - expect(properties).to include property1.presentation, property2.presentation, - producer_property.property.presentation - end + properties = json_response.pluck(:name) + + expect(json_response.length).to be 1 + expect(properties).to include producer_property.property.presentation end end context "with custom taxon ordering applied and duplicate product names in the order cycle" do let!(:supplier) { create(:supplier_enterprise) } let!(:product5) { - create(:product, name: "Duplicate name", primary_taxon: taxon3, supplier:) + create(:product, name: "Duplicate name", primary_taxon_id: taxon3.id, + supplier_id: supplier.id) } let!(:product6) { - create(:product, name: "Duplicate name", primary_taxon: taxon3, supplier:) + create(:product, name: "Duplicate name", primary_taxon_id: taxon3.id, + supplier_id: supplier.id) } let!(:product7) { - create(:product, name: "Duplicate name", primary_taxon: taxon2, supplier:) + create(:product, name: "Duplicate name", primary_taxon_id: taxon2.id, + supplier_id: supplier.id) } let!(:product8) { - create(:product, name: "Duplicate name", primary_taxon: taxon2, supplier:) + create(:product, name: "Duplicate name", primary_taxon_id: taxon2.id, + supplier_id: supplier.id) } before do diff --git a/spec/controllers/api/v0/orders_controller_spec.rb b/spec/controllers/api/v0/orders_controller_spec.rb index 6755ae0678..a6d15774fa 100644 --- a/spec/controllers/api/v0/orders_controller_spec.rb +++ b/spec/controllers/api/v0/orders_controller_spec.rb @@ -42,19 +42,19 @@ module Api let!(:order5) { create(:order, state: 'cart', completed_at: nil) } let!(:line_item1) do create(:line_item_with_shipment, order: order1, - product: create(:product, supplier:)) + product: create(:product, supplier_id: supplier.id)) end let!(:line_item2) do create(:line_item_with_shipment, order: order2, - product: create(:product, supplier:)) + product: create(:product, supplier_id: supplier.id)) end let!(:line_item3) do create(:line_item_with_shipment, order: order2, - product: create(:product, supplier:)) + product: create(:product, supplier_id: supplier.id)) end let!(:line_item4) do create(:line_item_with_shipment, order: order3, - product: create(:product, supplier:)) + product: create(:product, supplier_id: supplier.id)) end context 'as a regular user' do @@ -235,7 +235,7 @@ module Api it "returns unauthorized, as the order product's supplier owner" do allow(controller).to receive(:spree_current_user) { - order.line_items.first.variant.product.supplier.owner + order.line_items.first.variant.supplier.owner } get :show, params: { id: order.number } assert_unauthorized! diff --git a/spec/controllers/api/v0/products_controller_spec.rb b/spec/controllers/api/v0/products_controller_spec.rb index 3775ed1947..df999c4ab4 100644 --- a/spec/controllers/api/v0/products_controller_spec.rb +++ b/spec/controllers/api/v0/products_controller_spec.rb @@ -8,10 +8,10 @@ RSpec.describe Api::V0::ProductsController, type: :controller do let(:supplier) { create(:supplier_enterprise) } let(:supplier2) { create(:supplier_enterprise) } - let!(:product) { create(:product, supplier:) } - let!(:other_product) { create(:product) } - let(:product_other_supplier) { create(:product, supplier: supplier2) } - let(:product_with_image) { create(:product_with_image, supplier:) } + let!(:product) { create(:product, supplier_id: supplier.id) } + let!(:other_product) { create(:product, supplier_id: supplier.id) } + let(:product_other_supplier) { create(:product, supplier_id: supplier2.id) } + let(:product_with_image) { create(:product_with_image, supplier_id: supplier.id) } let(:all_attributes) { ["id", "name", "variants"] } let(:variants_attributes) { ["id", "options_text", "unit_value", "unit_description", "unit_to_display", "on_demand", @@ -36,7 +36,7 @@ RSpec.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, - primary_taxon: taxon) + primary_taxon: taxon, supplier:) product.variants.first.images.create!(attachment:) product.set_property("spree", "rocks") @@ -120,7 +120,7 @@ RSpec.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", "supplier", "variant_unit", "price"]) + expect(errors.keys).to match_array(["name", "variant_unit", "price"]) end it "can update a product" do @@ -228,9 +228,9 @@ RSpec.describe Api::V0::ProductsController, type: :controller do describe '#bulk_products' do context "as an enterprise user" do let!(:taxon) { create(:taxon) } - let!(:product2) { create(:product, supplier:, primary_taxon: taxon) } - let!(:product3) { create(:product, supplier: supplier2, primary_taxon: taxon) } - let!(:product4) { create(:product, supplier: supplier2) } + let!(:product2) { create(:product, supplier_id: supplier.id, primary_taxon: taxon) } + let!(:product3) { create(:product, supplier_id: supplier2.id, primary_taxon: taxon) } + let!(:product4) { create(:product, supplier_id: supplier2.id) } let(:current_api_user) { supplier_enterprise_user(supplier) } before { current_api_user.enterprise_roles.create(enterprise: supplier2) } @@ -262,7 +262,8 @@ RSpec.describe Api::V0::ProductsController, type: :controller do end it "filters results by supplier" do - api_get :bulk_products, { page: 1, per_page: 15, q: { supplier_id_eq: supplier.id } }, + api_get :bulk_products, + { page: 1, per_page: 15, q: { variants_supplier_id_eq: supplier.id } }, format: :json expect(returned_product_ids).to eq [product2.id, other_product.id, product.id] end diff --git a/spec/controllers/api/v0/reports/packing_report_spec.rb b/spec/controllers/api/v0/reports/packing_report_spec.rb index 1b35ddbe66..1dbf9f287d 100644 --- a/spec/controllers/api/v0/reports/packing_report_spec.rb +++ b/spec/controllers/api/v0/reports/packing_report_spec.rb @@ -34,7 +34,7 @@ RSpec.describe Api::V0::ReportsController, type: :controller do context "as an enterprise user with partial order permissions (supplier with P-OC)" do let!(:order) { create(:completed_order_with_totals) } - let(:supplier) { order.line_items.first.product.supplier } + let(:supplier) { order.line_items.first.variant.supplier } let(:current_user) { supplier.owner } let!(:perms) { create(:enterprise_relationship, parent: supplier, child: order.distributor, @@ -62,7 +62,7 @@ RSpec.describe Api::V0::ReportsController, type: :controller do { "hub" => line_item.order.distributor.name, "customer_code" => line_item.order.customer&.code, - "supplier" => line_item.product.supplier.name, + "supplier" => line_item.variant.supplier.name, "product" => line_item.product.name, "variant" => line_item.full_name, "quantity" => line_item.quantity, @@ -80,7 +80,7 @@ RSpec.describe Api::V0::ReportsController, type: :controller do 'first_name' => '< Hidden >', 'last_name' => '< Hidden >', 'phone' => '< Hidden >', - "supplier" => line_item.product.supplier.name, + "supplier" => line_item.variant.supplier.name, "product" => line_item.product.name, "variant" => line_item.full_name, "quantity" => line_item.quantity, diff --git a/spec/controllers/api/v0/shipments_controller_spec.rb b/spec/controllers/api/v0/shipments_controller_spec.rb index f7c9584e01..a4400e7cad 100644 --- a/spec/controllers/api/v0/shipments_controller_spec.rb +++ b/spec/controllers/api/v0/shipments_controller_spec.rb @@ -44,8 +44,8 @@ RSpec.describe Api::V0::ShipmentsController, type: :controller do before do order.update_attribute :ship_address_id, order_ship_address.id - order.update_attribute :distributor, variant.product.supplier - shipment.shipping_method.distributors << variant.product.supplier + order.update_attribute :distributor, variant.supplier + shipment.shipping_method.distributors << variant.supplier end context '#create' do @@ -364,7 +364,7 @@ RSpec.describe Api::V0::ShipmentsController, type: :controller do context "when line items have fees" do let(:fee_order) { - instance_double(Spree::Order, number: "123", distributor: variant.product.supplier) + instance_double(Spree::Order, number: "123", distributor: variant.supplier) } let(:contents) { instance_double(Spree::OrderContents) } let(:fee_order_shipment) { diff --git a/spec/controllers/api/v0/shops_controller_spec.rb b/spec/controllers/api/v0/shops_controller_spec.rb index 2cd251b762..c00be92165 100644 --- a/spec/controllers/api/v0/shops_controller_spec.rb +++ b/spec/controllers/api/v0/shops_controller_spec.rb @@ -12,7 +12,7 @@ RSpec.describe Api::V0::ShopsController, type: :controller do } let!(:producer) { create(:supplier_enterprise, name: 'Shopfront Test Producer') } let!(:category) { create(:taxon, name: 'Fruit') } - let!(:product) { create(:product, supplier: producer, primary_taxon: category ) } + let!(:product) { create(:product, supplier_id: producer.id, primary_taxon: category ) } let!(:relationship) { create(:enterprise_relationship, parent: hub, child: producer) } let!(:closed_hub1) { create(:distributor_enterprise) } let!(:closed_hub2) { create(:distributor_enterprise) } diff --git a/spec/controllers/api/v0/variants_controller_spec.rb b/spec/controllers/api/v0/variants_controller_spec.rb index 496af4d6b4..ce9f486512 100644 --- a/spec/controllers/api/v0/variants_controller_spec.rb +++ b/spec/controllers/api/v0/variants_controller_spec.rb @@ -88,9 +88,9 @@ RSpec.describe Api::V0::VariantsController, type: :controller do context "as an enterprise user" do let(:current_api_user) { create(:user, enterprises: [supplier]) } let(:supplier_other) { create(:supplier_enterprise) } - let!(:product) { create(:product, supplier:) } + let!(:product) { create(:product, supplier_id: supplier.id) } let(:variant) { product.variants.first } - let(:product_other) { create(:product, supplier: supplier_other) } + let(:product_other) { create(:product, supplier_id: supplier_other.id) } let(:variant_other) { product_other.variants.first } context "with a single remaining variant" do @@ -102,7 +102,7 @@ RSpec.describe Api::V0::VariantsController, type: :controller do end context "with more than one variants" do - let(:variant_to_delete) { create(:variant, product:) } + let(:variant_to_delete) { create(:variant, product:, supplier:) } it "deletes a variant" do api_delete :destroy, id: variant_to_delete.id @@ -125,7 +125,7 @@ RSpec.describe Api::V0::VariantsController, type: :controller do context "as an administrator" do let(:current_api_user) { create(:admin_user) } - let(:product) { create(:product) } + let(:product) { create(:product, supplier_id: create(:supplier_enterprise).id) } let(:variant) { product.variants.first } let(:taxon) { create(:taxon) } let!(:variant2) { create(:variant, product:) } @@ -144,8 +144,9 @@ RSpec.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", primary_taxon_id: taxon.id }, + api_post :create, variant: { sku: "12345", unit_value: "1", unit_description: "L", + price: "1", primary_taxon_id: taxon.id, + supplier_id: variant.supplier.id }, product_id: variant.product.id expect(attributes.all?{ |attr| json_response.include? attr.to_s }).to eq(true) diff --git a/spec/controllers/line_items_controller_spec.rb b/spec/controllers/line_items_controller_spec.rb index a825e77b35..42949bdcc4 100644 --- a/spec/controllers/line_items_controller_spec.rb +++ b/spec/controllers/line_items_controller_spec.rb @@ -168,7 +168,7 @@ RSpec.describe LineItemsController, type: :controller do } let(:enterprise_fee) { create(:enterprise_fee, calculator:) } let!(:exchange) { - create(:exchange, incoming: true, sender: variant1.product.supplier, + create(:exchange, incoming: true, sender: variant1.supplier, receiver: order_cycle.coordinator, variants: [variant1, variant2], enterprise_fees: [enterprise_fee]) } diff --git a/spec/controllers/shops_controller_spec.rb b/spec/controllers/shops_controller_spec.rb index a12a964038..84d639eb13 100644 --- a/spec/controllers/shops_controller_spec.rb +++ b/spec/controllers/shops_controller_spec.rb @@ -30,7 +30,7 @@ RSpec.describe ShopsController, type: :controller do it 'renders distributed producer properties' do producer_property = create(:property, presentation: 'certified') producer = create(:supplier_enterprise, properties: [producer_property]) - product = create(:product) + product = create(:product, supplier_id: producer.id) create( :simple_order_cycle, @@ -53,7 +53,8 @@ RSpec.describe ShopsController, type: :controller do property = create(:property, presentation: 'dairy') product = create(:product, properties: [property]) - producer.supplied_products << product + + producer.supplied_variants << product.variants.first create( :simple_order_cycle, diff --git a/spec/controllers/spree/admin/orders_controller_spec.rb b/spec/controllers/spree/admin/orders_controller_spec.rb index d4dab4e0b2..6011c818db 100644 --- a/spec/controllers/spree/admin/orders_controller_spec.rb +++ b/spec/controllers/spree/admin/orders_controller_spec.rb @@ -75,7 +75,7 @@ RSpec.describe Spree::Admin::OrdersController, type: :controller do let(:order_cycle) { create(:simple_order_cycle, distributors: [distributor]) } let(:enterprise_fee) { create(:enterprise_fee, calculator: build(:calculator_per_item) ) } let!(:exchange) { - create(:exchange, incoming: true, sender: variant1.product.supplier, + create(:exchange, incoming: true, sender: variant1.supplier, receiver: order_cycle.coordinator, variants: [variant1, variant2], enterprise_fees: [enterprise_fee]) } @@ -234,7 +234,7 @@ RSpec.describe Spree::Admin::OrdersController, type: :controller do } before do - line_item.product.supplier = distributor + line_item.variant.supplier = distributor order.shipments << shipment order.line_items << line_item distributor.shipping_methods << shipment.shipping_method diff --git a/spec/controllers/spree/admin/products_controller_spec.rb b/spec/controllers/spree/admin/products_controller_spec.rb index 2e33fc3926..f41c4adfcd 100644 --- a/spec/controllers/spree/admin/products_controller_spec.rb +++ b/spec/controllers/spree/admin/products_controller_spec.rb @@ -8,7 +8,7 @@ RSpec.describe Spree::Admin::ProductsController, type: :controller do let(:s_managed) { create(:enterprise) } let(:s_unmanaged) { create(:enterprise) } let(:product) do - create(:simple_product, supplier: s_unmanaged, name: 'Peas') + create(:simple_product, supplier_id: s_unmanaged.id, name: 'Peas') end before do @@ -31,7 +31,7 @@ RSpec.describe Spree::Admin::ProductsController, type: :controller do let!(:product) do create( :simple_product, - supplier: producer, + supplier_id: producer.id, variant_unit: 'items', variant_unit_scale: nil, variant_unit_name: 'bunches', @@ -76,7 +76,7 @@ RSpec.describe Spree::Admin::ProductsController, type: :controller do let!(:product) do create( :simple_product, - supplier: producer, + supplier_id: producer.id, variant_unit: 'items', variant_unit_scale: nil, variant_unit_name: 'bunches', @@ -87,7 +87,7 @@ RSpec.describe Spree::Admin::ProductsController, type: :controller do let!(:another_product) do create( :simple_product, - supplier: producer, + supplier_id: producer.id, variant_unit: 'weight', variant_unit_scale: 1000, variant_unit_name: nil @@ -113,7 +113,8 @@ RSpec.describe Spree::Admin::ProductsController, type: :controller do "unit_value" => 4, "unit_description" => "", "display_name" => "name", - "primary_taxon_id" => taxon.id + "primary_taxon_id" => taxon.id, + "supplier_id" => producer.id } ] } @@ -174,28 +175,12 @@ RSpec.describe Spree::Admin::ProductsController, type: :controller do describe "updating a product" do let(:producer) { create(:enterprise) } - let!(:product) { create(:simple_product, supplier: producer) } + let!(:product) { create(:simple_product, supplier_id: producer.id) } before do controller_login_as_enterprise_user [producer] end - describe "change product supplier" do - let(:distributor) { create(:distributor_enterprise) } - let!(:order_cycle) { - create(:simple_order_cycle, variants: [product.variants.first], coordinator: distributor, - distributors: [distributor]) - } - - it "should remove product from existing Order Cycles" do - new_producer = create(:enterprise) - spree_put :update, id: product, product: { supplier_id: new_producer.id } - - expect(product.reload.supplier.id).to eq new_producer.id - expect(order_cycle.reload.distributed_variants).not_to include product.variants.first - end - end - describe "product stock setting with errors" do it "notifies bugsnag and still raise error" do # forces an error in the variant diff --git a/spec/controllers/spree/admin/variants_controller_spec.rb b/spec/controllers/spree/admin/variants_controller_spec.rb index 8585415d0e..ba6e1542e5 100644 --- a/spec/controllers/spree/admin/variants_controller_spec.rb +++ b/spec/controllers/spree/admin/variants_controller_spec.rb @@ -12,7 +12,8 @@ module Spree let(:product) { create(:product, name: 'Product A') } let(:deleted_variant) do deleted_variant = product.variants.create( - unit_value: "2", price: 1, primary_taxon: create(:taxon) + unit_value: "2", price: 1, primary_taxon: create(:taxon), + supplier: create(:supplier_enterprise) ) deleted_variant.delete deleted_variant @@ -30,9 +31,63 @@ module Spree end end + describe "#update" do + let!(:variant) { create(:variant, display_name: "Tomatoes", sku: 123, supplier: producer) } + let(:producer) { create(:enterprise) } + + it "updates the variant" do + expect { + spree_put( + :update, + id: variant.id, + product_id: variant.product.id, + variant: { display_name: "Better tomatoes", sku: 456 } + ) + variant.reload + }.to change { variant.display_name }.to("Better tomatoes") + .and change { variant.sku }.to(456.to_s) + end + + context "when updating supplier" do + let(:new_producer) { create(:enterprise) } + + it "updates the supplier" do + expect { + spree_put( + :update, + id: variant.id, + product_id: variant.product.id, + variant: { supplier_id: new_producer.id } + ) + variant.reload + }.to change { variant.supplier_id }.to(new_producer.id) + end + + it "removes associated product from existing Order Cycles" do + distributor = create(:distributor_enterprise) + order_cycle = create( + :simple_order_cycle, + variants: [variant], + coordinator: distributor, + distributors: [distributor] + ) + + spree_put( + :update, + id: variant.id, + product_id: variant.product.id, + variant: { supplier_id: new_producer.id } + ) + + expect(order_cycle.reload.distributed_variants).not_to include variant + end + end + end + describe "#search" do - let!(:p1) { create(:simple_product, name: 'Product 1') } - let!(:p2) { create(:simple_product, name: 'Product 2') } + let(:supplier) { create(:supplier_enterprise) } + let!(:p1) { create(:simple_product, name: 'Product 1', supplier_id: supplier.id) } + let!(:p2) { create(:simple_product, name: 'Product 2', supplier_id: supplier.id) } let!(:v1) { p1.variants.first } let!(:v2) { p2.variants.first } let!(:vo) { create(:variant_override, variant: v1, hub: d, count_on_hand: 44) } diff --git a/spec/controllers/spree/orders_controller_spec.rb b/spec/controllers/spree/orders_controller_spec.rb index f1f47762c2..e4eeb84b8c 100644 --- a/spec/controllers/spree/orders_controller_spec.rb +++ b/spec/controllers/spree/orders_controller_spec.rb @@ -283,7 +283,7 @@ RSpec.describe Spree::OrdersController, type: :controller do let(:order_cycle) { create(:simple_order_cycle, distributors: [distributor]) } let(:enterprise_fee) { create(:enterprise_fee, calculator: build(:calculator_per_item) ) } let!(:exchange) { - create(:exchange, incoming: true, sender: variant1.product.supplier, + create(:exchange, incoming: true, sender: variant1.supplier, receiver: order_cycle.coordinator, variants: [variant1, variant2], enterprise_fees: [enterprise_fee]) } diff --git a/spec/factories/order_cycle_factory.rb b/spec/factories/order_cycle_factory.rb index 18c1fd7181..6693ece689 100644 --- a/spec/factories/order_cycle_factory.rb +++ b/spec/factories/order_cycle_factory.rb @@ -30,7 +30,11 @@ FactoryBot.define do # Products with images proxy.exchanges.incoming.each do |exchange| - product = create(:product, supplier: exchange.sender) + product = create(:product).tap do |p| + variant = p.variants.first + variant.supplier = exchange.sender + variant.save! + end Spree::Image.create( viewable_id: product.id, viewable_type: 'Spree::Product', diff --git a/spec/factories/product_factory.rb b/spec/factories/product_factory.rb index 8e1d5e64c7..1095091cf0 100644 --- a/spec/factories/product_factory.rb +++ b/spec/factories/product_factory.rb @@ -4,6 +4,10 @@ FactoryBot.define do factory :base_product, class: Spree::Product do sequence(:name) { |n| "Product ##{n} - #{Kernel.rand(9999)}" } + supplier_id do + Enterprise.is_primary_producer.first&.id || FactoryBot.create(:supplier_enterprise).id + end + transient do primary_taxon { nil } end @@ -14,8 +18,6 @@ FactoryBot.define do sku { 'ABC' } deleted_at { nil } - supplier { Enterprise.is_primary_producer.first || FactoryBot.create(:supplier_enterprise) } - unit_value { 1 } unit_description { '' } diff --git a/spec/factories/variant_factory.rb b/spec/factories/variant_factory.rb index 3cb44c6344..dc729a29e6 100644 --- a/spec/factories/variant_factory.rb +++ b/spec/factories/variant_factory.rb @@ -12,7 +12,15 @@ FactoryBot.define do depth { generate(:random_float) } primary_taxon { Spree::Taxon.first || FactoryBot.create(:taxon) } - product { |p| p.association(:product) } + supplier { Enterprise.is_primary_producer.first || FactoryBot.create(:supplier_enterprise) } + + # createing a product here will end up creating an extra variant, as creating product will + # create a "standard variant" by default. We could try to pass the variant instance we + # are creating but it fails because then the variant instance gets saved and it fails because + # the product isn't associated yet. It's a chicken and egg problem. + # It will be fixed once we finish the product refactor, and we don't need the product to + # create a "standard variant" + product { association :base_product } # ensure stock item will be created for this variant before(:create) { create(:stock_location) if Spree::StockLocation.count.zero? } @@ -35,7 +43,7 @@ FactoryBot.define do trait :with_order_cycle do transient do order_cycle { create(:order_cycle) } - producer { product.supplier } + producer { supplier } coordinator { create(:distributor_enterprise) } distributor { create(:distributor_enterprise) } incoming_exchange_fees { [] } diff --git a/spec/helpers/admin/orders_helper_spec.rb b/spec/helpers/admin/orders_helper_spec.rb index c24f92c471..a9b301af5e 100644 --- a/spec/helpers/admin/orders_helper_spec.rb +++ b/spec/helpers/admin/orders_helper_spec.rb @@ -70,7 +70,8 @@ RSpec.describe Admin::OrdersHelper, type: :helper do } let!(:ship_address){ create(:ship_address) } let!(:product) { - create(:simple_product, supplier: enterprise, price: 10, tax_category_id: tax_category.id) + create(:simple_product, supplier_id: enterprise.id, price: 10, + tax_category_id: tax_category.id) } let!(:variant){ create(:variant, :with_order_cycle, product:, distributor: enterprise, order_cycle:, diff --git a/spec/javascripts/unit/admin/bulk_product_update_spec.js.coffee b/spec/javascripts/unit/admin/bulk_product_update_spec.js.coffee index 57f6225110..77802a3df2 100644 --- a/spec/javascripts/unit/admin/bulk_product_update_spec.js.coffee +++ b/spec/javascripts/unit/admin/bulk_product_update_spec.js.coffee @@ -192,7 +192,6 @@ describe "filtering products for submission to database", -> updated_at: null on_hand: 0 on_demand: false - producer_id: 5 group_buy: null group_buy_unit_size: null variants: [ @@ -204,6 +203,7 @@ describe "filtering products for submission to database", -> unit_description: "(bottle)" display_as: "bottle" display_name: "nothing" + producer_id: 5 ] variant_unit: 'volume' variant_unit_scale: 1 @@ -213,7 +213,6 @@ describe "filtering products for submission to database", -> expect(filterSubmitProducts([testProduct])).toEqual [ id: 1 name: "TestProduct" - supplier_id: 5 variant_unit: 'volume' variant_unit_scale: 1 variant_unit_name: 'loaf' @@ -226,6 +225,7 @@ describe "filtering products for submission to database", -> unit_description: "(bottle)" display_as: "bottle" display_name: "nothing" + supplier_id: 5 ] ] diff --git a/spec/javascripts/unit/admin/services/variant_overrides_spec.js.coffee b/spec/javascripts/unit/admin/services/variant_overrides_spec.js.coffee index 26f6d1ce93..b2a4bcd728 100644 --- a/spec/javascripts/unit/admin/services/variant_overrides_spec.js.coffee +++ b/spec/javascripts/unit/admin/services/variant_overrides_spec.js.coffee @@ -1,9 +1,9 @@ describe "VariantOverrides service", -> VariantOverrides = $httpBackend = null variantOverrides = [ - {id: 1, hub_id: 10, variant_id: 100, sku: "V100", price: 1, count_on_hand: 1, on_demand: null, default_stock: null, resettable: false, tag_list : '', tags: [] } - {id: 2, hub_id: 10, variant_id: 200, sku: "V200", price: 2, count_on_hand: 2, on_demand: null, default_stock: null, resettable: false, tag_list : '', tags: [] } - {id: 3, hub_id: 20, variant_id: 300, sku: "V300", price: 3, count_on_hand: 3, on_demand: null, default_stock: null, resettable: false, tag_list : '', tags: [] } + {id: 1, hub_id: 10, variant_id: 100, producer_id: 500, sku: "V100", price: 1, count_on_hand: 1, on_demand: null, default_stock: null, resettable: false, tag_list : '', tags: [] } + {id: 2, hub_id: 10, variant_id: 200, producer_id: 500, sku: "V200", price: 2, count_on_hand: 2, on_demand: null, default_stock: null, resettable: false, tag_list : '', tags: [] } + {id: 3, hub_id: 20, variant_id: 300, producer_id: 500, sku: "V300", price: 3, count_on_hand: 3, on_demand: null, default_stock: null, resettable: false, tag_list : '', tags: [] } ] beforeEach -> @@ -19,38 +19,44 @@ describe "VariantOverrides service", -> it "indexes variant overrides by hub_id -> variant_id", -> expect(VariantOverrides.variantOverrides).toEqual 10: - 100: {id: 1, hub_id: 10, variant_id: 100, sku: "V100", price: 1, count_on_hand: 1, on_demand: null, default_stock: null, resettable: false, tag_list : '', tags: [] } - 200: {id: 2, hub_id: 10, variant_id: 200, sku: "V200", price: 2, count_on_hand: 2, on_demand: null, default_stock: null, resettable: false, tag_list : '', tags: [] } + 100: {id: 1, hub_id: 10, variant_id: 100, producer_id: 500, sku: "V100", price: 1, count_on_hand: 1, on_demand: null, default_stock: null, resettable: false, tag_list : '', tags: [] } + 200: {id: 2, hub_id: 10, variant_id: 200, producer_id: 500, sku: "V200", price: 2, count_on_hand: 2, on_demand: null, default_stock: null, resettable: false, tag_list : '', tags: [] } 20: - 300: {id: 3, hub_id: 20, variant_id: 300, sku: "V300", price: 3, count_on_hand: 3, on_demand: null, default_stock: null, resettable: false, tag_list : '', tags: [] } + 300: {id: 3, hub_id: 20, variant_id: 300, producer_id: 500, sku: "V300", price: 3, count_on_hand: 3, on_demand: null, default_stock: null, resettable: false, tag_list : '', tags: [] } it "ensures blank data available for some products", -> hubs = [{id: 10}, {id: 20}, {id: 30}] products = [ { id: 1 - variants: [{id: 100}, {id: 200}, {id: 300}, {id: 400}, {id: 500}] + variants: [ + {id: 100, producer_id: 1000}, + {id: 200, producer_id: 1000}, + {id: 300, producer_id: 1000}, + {id: 400, producer_id: 1000}, + {id: 500, producer_id: 1001} + ] } ] VariantOverrides.ensureDataFor hubs, products expect(VariantOverrides.variantOverrides[10]).toEqual - 100: { id: 1, hub_id: 10, variant_id: 100, sku: "V100", price: 1, count_on_hand: 1, on_demand: null, default_stock: null, resettable: false, tag_list : '', tags: [] } - 200: { id: 2, hub_id: 10, variant_id: 200, sku: "V200", price: 2, count_on_hand: 2, on_demand: null, default_stock: null, resettable: false, tag_list : '', tags: [] } - 300: { hub_id: 10, variant_id: 300, sku: null, price: null, count_on_hand: null, on_demand: null, default_stock: null, resettable: false, tag_list : '', tags: [] } - 400: { hub_id: 10, variant_id: 400, sku: null, price: null, count_on_hand: null, on_demand: null, default_stock: null, resettable: false, tag_list : '', tags: [] } - 500: { hub_id: 10, variant_id: 500, sku: null, price: null, count_on_hand: null, on_demand: null, default_stock: null, resettable: false, tag_list : '', tags: [] } + 100: { id: 1, hub_id: 10, variant_id: 100, producer_id: 500, sku: "V100", price: 1, count_on_hand: 1, on_demand: null, default_stock: null, resettable: false, tag_list : '', tags: [] } + 200: { id: 2, hub_id: 10, variant_id: 200, producer_id: 500, sku: "V200", price: 2, count_on_hand: 2, on_demand: null, default_stock: null, resettable: false, tag_list : '', tags: [] } + 300: { hub_id: 10, variant_id: 300, producer_id: 1000, sku: null, price: null, count_on_hand: null, on_demand: null, default_stock: null, resettable: false, tag_list : '', tags: [] } + 400: { hub_id: 10, variant_id: 400, producer_id: 1000, sku: null, price: null, count_on_hand: null, on_demand: null, default_stock: null, resettable: false, tag_list : '', tags: [] } + 500: { hub_id: 10, variant_id: 500, producer_id: 1001, sku: null, price: null, count_on_hand: null, on_demand: null, default_stock: null, resettable: false, tag_list : '', tags: [] } expect(VariantOverrides.variantOverrides[20]).toEqual - 100: { hub_id: 20, variant_id: 100, sku: null, price: null, count_on_hand: null, on_demand: null, default_stock: null, resettable: false, tag_list : '', tags: [] } - 200: { hub_id: 20, variant_id: 200, sku: null, price: null, count_on_hand: null, on_demand: null, default_stock: null, resettable: false, tag_list : '', tags: [] } - 300: { id: 3, hub_id: 20, variant_id: 300, sku: "V300", price: 3, count_on_hand: 3, on_demand: null, default_stock: null, resettable: false, tag_list : '', tags: [] } - 400: { hub_id: 20, variant_id: 400, sku: null, price: null, count_on_hand: null, on_demand: null, default_stock: null, resettable: false, tag_list : '', tags: []} - 500: { hub_id: 20, variant_id: 500, sku: null, price: null, count_on_hand: null, on_demand: null, default_stock: null, resettable: false, tag_list : '', tags: [] } + 100: { hub_id: 20, variant_id: 100, producer_id: 1000, sku: null, price: null, count_on_hand: null, on_demand: null, default_stock: null, resettable: false, tag_list : '', tags: [] } + 200: { hub_id: 20, variant_id: 200, producer_id: 1000, sku: null, price: null, count_on_hand: null, on_demand: null, default_stock: null, resettable: false, tag_list : '', tags: [] } + 300: { id: 3, hub_id: 20, variant_id: 300, producer_id: 500, sku: "V300", price: 3, count_on_hand: 3, on_demand: null, default_stock: null, resettable: false, tag_list : '', tags: [] } + 400: { hub_id: 20, variant_id: 400, producer_id: 1000, sku: null, price: null, count_on_hand: null, on_demand: null, default_stock: null, resettable: false, tag_list : '', tags: []} + 500: { hub_id: 20, variant_id: 500, producer_id: 1001, sku: null, price: null, count_on_hand: null, on_demand: null, default_stock: null, resettable: false, tag_list : '', tags: [] } expect(VariantOverrides.variantOverrides[30]).toEqual - 100: { hub_id: 30, variant_id: 100, sku: null, price: null, count_on_hand: null, on_demand: null, default_stock: null, resettable: false, tag_list : '', tags: [] } - 200: { hub_id: 30, variant_id: 200, sku: null, price: null, count_on_hand: null, on_demand: null, default_stock: null, resettable: false, tag_list : '', tags: [] } - 300: { hub_id: 30, variant_id: 300, sku: null, price: null, count_on_hand: null, on_demand: null, default_stock: null, resettable: false, tag_list : '', tags: []} - 400: { hub_id: 30, variant_id: 400, sku: null, price: null, count_on_hand: null, on_demand: null, default_stock: null, resettable: false, tag_list : '', tags: [] } - 500: { hub_id: 30, variant_id: 500, sku: null, price: null, count_on_hand: null, on_demand: null, default_stock: null, resettable: false, tag_list : '', tags: [] } + 100: { hub_id: 30, variant_id: 100, producer_id: 1000, sku: null, price: null, count_on_hand: null, on_demand: null, default_stock: null, resettable: false, tag_list : '', tags: [] } + 200: { hub_id: 30, variant_id: 200, producer_id: 1000, sku: null, price: null, count_on_hand: null, on_demand: null, default_stock: null, resettable: false, tag_list : '', tags: [] } + 300: { hub_id: 30, variant_id: 300, producer_id: 1000, sku: null, price: null, count_on_hand: null, on_demand: null, default_stock: null, resettable: false, tag_list : '', tags: []} + 400: { hub_id: 30, variant_id: 400, producer_id: 1000, sku: null, price: null, count_on_hand: null, on_demand: null, default_stock: null, resettable: false, tag_list : '', tags: [] } + 500: { hub_id: 30, variant_id: 500, producer_id: 1001, sku: null, price: null, count_on_hand: null, on_demand: null, default_stock: null, resettable: false, tag_list : '', tags: [] } it "updates the IDs of variant overrides", -> VariantOverrides.variantOverrides[2] = {} diff --git a/spec/javascripts/unit/darkswarm/services/products_spec.js.coffee b/spec/javascripts/unit/darkswarm/services/products_spec.js.coffee index baea195cf2..7f9ea624a3 100644 --- a/spec/javascripts/unit/darkswarm/services/products_spec.js.coffee +++ b/spec/javascripts/unit/darkswarm/services/products_spec.js.coffee @@ -19,16 +19,16 @@ describe 'Products service', -> beforeEach -> product = test: "cats" - supplier: - id: 9 price: 11 - master: {} - variants: [] + variants: [ + id: 1000, price: 11, supplier: {id: 9} + ] productWithImage = supplier: id: 9 - master: {} - variants: [] + variants: [ + id: 1000, price: 20, supplier: {id: 9} + ] image: { large_url: 'foo.png' } @@ -79,7 +79,7 @@ describe 'Products service', -> it "dereferences suppliers", -> Shopfront.producers_by_id = {id: 9, name: "test"} - $httpBackend.expectGET(endpoint).respond([{supplier : {id: 9}, master: {}}]) + $httpBackend.expectGET(endpoint).respond([product]) $httpBackend.flush() expect(Products.products[0].supplier).toBe Shopfront.producers_by_id["9"] @@ -96,13 +96,16 @@ describe 'Products service', -> expect(Products.products[0].properties[1]).toBe properties[0] it "registers variants with Variants service", -> - product.variants = [{id: 1}] + product.variants = [{id: 1, supplier: {id: 9}}] $httpBackend.expectGET(endpoint).respond([product]) $httpBackend.flush() expect(Products.products[0].variants[0]).toBe Variants.variants[1] it "stores variant names", -> - product.variants = [{id: 1, name_to_display: "one"}, {id: 2, name_to_display: "two"}] + product.variants = [ + {id: 1, name_to_display: "one", supplier: {id: 9}}, + {id: 2, name_to_display: "two", supplier: {id: 9}} + ] $httpBackend.expectGET(endpoint).respond([product]) $httpBackend.flush() expect(Products.products[0].variant_names).toEqual "one two " @@ -125,7 +128,9 @@ describe 'Products service', -> expect(Products.products[0].price).toEqual 11.00 it "displays the minimum variant price when the product has variants", -> - product.variants = [{price: 22}, {price: 33}] + product.variants = [ + {price: 22, supplier: {id: 9} }, {price: 33, supplier: {id: 9}} + ] $httpBackend.expectGET(endpoint).respond([product]) $httpBackend.flush() expect(Products.products[0].price).toEqual 22 diff --git a/spec/lib/open_food_network/enterprise_fee_calculator_spec.rb b/spec/lib/open_food_network/enterprise_fee_calculator_spec.rb index 29f96c231a..5478e99934 100644 --- a/spec/lib/open_food_network/enterprise_fee_calculator_spec.rb +++ b/spec/lib/open_food_network/enterprise_fee_calculator_spec.rb @@ -11,8 +11,8 @@ module OpenFoodNetwork let(:coordinator) { create(:distributor_enterprise) } let(:distributor) { create(:distributor_enterprise) } let(:order_cycle) { create(:simple_order_cycle) } - let(:product1) { create(:simple_product, supplier: supplier1, price: 10.00) } - let(:product2) { create(:simple_product, supplier: supplier2, price: 20.00) } + let(:product1) { create(:simple_product, supplier_id: supplier1.id, price: 10.00) } + let(:product2) { create(:simple_product, supplier_id: supplier2.id, price: 20.00) } describe "calculating fees for a variant" do describe "summing all the per-item fees for variant in the specified hub + order cycle" do diff --git a/spec/lib/open_food_network/order_cycle_permissions_spec.rb b/spec/lib/open_food_network/order_cycle_permissions_spec.rb index 72a9ada9e5..11d13debfc 100644 --- a/spec/lib/open_food_network/order_cycle_permissions_spec.rb +++ b/spec/lib/open_food_network/order_cycle_permissions_spec.rb @@ -207,7 +207,7 @@ module OpenFoodNetwork context "and distributes variants distributed by an unmanaged & unpermitted producer" do before { - ex.variants << create(:variant, product: create(:product, supplier: producer)) + ex.variants << create(:variant, supplier: producer) } # TODO: update this when we are confident about P-OCs @@ -338,8 +338,7 @@ module OpenFoodNetwork incoming: false) } before { - ex_outgoing.variants << create(:variant, - product: create(:product, supplier: producer)) + ex_outgoing.variants << create(:variant, supplier: producer) } # TODO: update this when we are confident about P-OCs @@ -423,8 +422,7 @@ module OpenFoodNetwork describe "legacy compatability" do context "where my hub's outgoing exchange contains variants of a producer " \ "I don't manage and has not given my hub P-OC" do - let!(:product) { create(:product, supplier: producer) } - let!(:variant) { create(:variant, product:) } + let!(:variant) { create(:variant, supplier: producer) } let!(:ex_out) { create(:exchange, order_cycle: oc, sender: coordinator, receiver: hub, incoming: true) } @@ -484,8 +482,8 @@ module OpenFoodNetwork # remove when behaviour no longer required describe "legacy compatability" do context "where an outgoing exchange contains variants of a producer I manage" do - let!(:product) { create(:product, supplier: producer) } - let!(:variant) { create(:variant, product:) } + let!(:variant) { create(:variant, supplier: producer) } + before { ex_out.variants << variant } context "where my producer supplies to the order cycle" do @@ -513,8 +511,8 @@ module OpenFoodNetwork "between two enterprises which are visible to a user" do let!(:producer1) { create(:supplier_enterprise) } let!(:producer2) { create(:supplier_enterprise) } - let!(:v1) { create(:variant, product: create(:simple_product, supplier: producer1)) } - let!(:v2) { create(:variant, product: create(:simple_product, supplier: producer2)) } + let!(:v1) { create(:variant, supplier: producer1) } + let!(:v2) { create(:variant, supplier: producer2) } describe "incoming exchanges" do context "as a manager of the coordinator" do @@ -593,7 +591,7 @@ module OpenFoodNetwork end context "where the coordinator produces products" do - let!(:v3) { create(:variant, product: create(:simple_product, supplier: coordinator)) } + let!(:v3) { create(:variant, supplier: coordinator) } it "returns any variants produced by the coordinator itself for exchanges w/ 'self'" do visible = permissions.visible_variants_for_outgoing_exchanges_to(coordinator) @@ -642,7 +640,7 @@ module OpenFoodNetwork context "where the hub produces products" do # NOTE: No relationship to self required - let!(:v3) { create(:variant, product: create(:simple_product, supplier: hub)) } + let!(:v3) { create(:variant, supplier: hub) } it "returns any variants produced by the hub" do visible = permissions.visible_variants_for_outgoing_exchanges_to(hub) @@ -720,7 +718,7 @@ module OpenFoodNetwork incoming: false) } # This one won't be in the exchange, and so shouldn't be visible - let!(:v3) { create(:variant, product: create(:simple_product, supplier: producer2)) } + let!(:v3) { create(:variant, supplier: producer2) } before { ex.variants << v2 } @@ -738,8 +736,8 @@ module OpenFoodNetwork "between two enterprises which are editable by a user" do let!(:producer1) { create(:supplier_enterprise) } let!(:producer2) { create(:supplier_enterprise) } - let!(:v1) { create(:variant, product: create(:simple_product, supplier: producer1)) } - let!(:v2) { create(:variant, product: create(:simple_product, supplier: producer2)) } + let!(:v1) { create(:variant, supplier: producer1) } + let!(:v2) { create(:variant, supplier: producer2) } describe "incoming exchanges" do context "as a manager of the coordinator" do @@ -801,7 +799,7 @@ module OpenFoodNetwork end context "where the coordinator produces products" do - let!(:v3) { create(:variant, product: create(:simple_product, supplier: coordinator)) } + let!(:v3) { create(:variant, supplier: coordinator) } it "returns any variants produced by the coordinator itself for exchanges w/ 'self'" do visible = permissions.editable_variants_for_outgoing_exchanges_to(coordinator) @@ -849,7 +847,7 @@ module OpenFoodNetwork context "where the hub produces products" do # NOTE: No relationship to self required - let!(:v3) { create(:variant, product: create(:simple_product, supplier: hub)) } + let!(:v3) { create(:variant, supplier: hub) } it "returns any variants produced by the hub" do visible = permissions.visible_variants_for_outgoing_exchanges_to(hub) @@ -943,7 +941,7 @@ module OpenFoodNetwork incoming: false) } # This one won't be in the exchange, and so shouldn't be visible - let!(:v3) { create(:variant, product: create(:simple_product, supplier: producer2)) } + let!(:v3) { create(:variant, supplier: producer2) } before { ex.variants << v2 } diff --git a/spec/lib/open_food_network/permissions_spec.rb b/spec/lib/open_food_network/permissions_spec.rb index 457b1fd98f..d0c678a874 100644 --- a/spec/lib/open_food_network/permissions_spec.rb +++ b/spec/lib/open_food_network/permissions_spec.rb @@ -190,8 +190,8 @@ module OpenFoodNetwork end describe "#editable_products" do - let!(:p1) { create(:simple_product, supplier: create(:supplier_enterprise) ) } - let!(:p2) { create(:simple_product, supplier: create(:supplier_enterprise) ) } + let!(:p1) { create(:simple_product, supplier_id: create(:supplier_enterprise).id ) } + let!(:p2) { create(:simple_product, supplier_id: create(:supplier_enterprise).id ) } before do allow(permissions).to receive(:managed_enterprise_products) { Spree::Product.where('1=0') } @@ -202,7 +202,7 @@ module OpenFoodNetwork it "returns products produced by managed enterprises" do allow(user).to receive(:admin?) { false } - allow(user).to receive(:enterprises) { [p1.supplier] } + allow(user).to receive(:enterprises) { [p1.variants.first.supplier] } expect(permissions.editable_products).to eq([p1]) end @@ -211,7 +211,7 @@ module OpenFoodNetwork allow(user).to receive(:admin?) { false } allow(user).to receive(:enterprises) { [] } allow(permissions).to receive(:related_enterprises_granting). - with(:manage_products) { Enterprise.where(id: p2.supplier) } + with(:manage_products) { Enterprise.where(id: p2.variants.first.supplier) } expect(permissions.editable_products).to eq([p2]) end @@ -226,9 +226,9 @@ module OpenFoodNetwork end describe "finding visible products" do - let!(:p1) { create(:simple_product, supplier: create(:supplier_enterprise) ) } - let!(:p2) { create(:simple_product, supplier: create(:supplier_enterprise) ) } - let!(:p3) { create(:simple_product, supplier: create(:supplier_enterprise) ) } + let!(:p1) { create(:simple_product, supplier_id: create(:supplier_enterprise).id ) } + let!(:p2) { create(:simple_product, supplier_id: create(:supplier_enterprise).id ) } + let!(:p3) { create(:simple_product, supplier_id: create(:supplier_enterprise).id ) } before do allow(permissions).to receive(:managed_enterprise_products) { Spree::Product.where("1=0") } @@ -242,7 +242,7 @@ module OpenFoodNetwork it "returns products produced by managed enterprises" do allow(user).to receive(:admin?) { false } - allow(user).to receive(:enterprises) { Enterprise.where(id: p1.supplier_id) } + allow(user).to receive(:enterprises) { Enterprise.where(id: p1.variants.first.supplier_id) } expect(permissions.visible_products).to eq([p1]) end @@ -251,7 +251,7 @@ module OpenFoodNetwork allow(user).to receive(:admin?) { false } allow(user).to receive(:enterprises) { [] } allow(permissions).to receive(:related_enterprises_granting). - with(:manage_products) { Enterprise.where(id: p2.supplier) } + with(:manage_products) { Enterprise.where(id: p2.variants.first.supplier) } expect(permissions.visible_products).to eq([p2]) end @@ -260,7 +260,7 @@ module OpenFoodNetwork allow(user).to receive(:admin?) { false } allow(user).to receive(:enterprises) { [] } allow(permissions).to receive(:related_enterprises_granting). - with(:add_to_order_cycle) { Enterprise.where(id: p3.supplier).select(:id) } + with(:add_to_order_cycle) { Enterprise.where(id: p3.variants.first.supplier).select(:id) } expect(permissions.visible_products).to eq([p3]) end diff --git a/spec/lib/reports/bulk_coop_report_spec.rb b/spec/lib/reports/bulk_coop_report_spec.rb index 775350d18d..fb6b64a7aa 100644 --- a/spec/lib/reports/bulk_coop_report_spec.rb +++ b/spec/lib/reports/bulk_coop_report_spec.rb @@ -122,7 +122,7 @@ module Reporting ship_address: create(:address)) end let(:li2) do - build(:line_item_with_shipment, product: create(:simple_product, supplier: s1)) + build(:line_item_with_shipment, variant: create(:variant, supplier: s1)) end before do @@ -144,7 +144,7 @@ module Reporting ship_address: create(:address)) end let(:li2) do - build(:line_item_with_shipment, product: create(:simple_product, supplier: s1)) + build(:line_item_with_shipment, variant: create(:variant, supplier: s1)) end before do diff --git a/spec/lib/reports/customers_report_spec.rb b/spec/lib/reports/customers_report_spec.rb index bdd18d57b6..19f7283a78 100644 --- a/spec/lib/reports/customers_report_spec.rb +++ b/spec/lib/reports/customers_report_spec.rb @@ -209,7 +209,7 @@ module Reporting describe "fetching orders" do let(:supplier) { create(:supplier_enterprise) } - let(:product) { create(:simple_product, supplier:) } + let(:product) { create(:simple_product, supplier_id: supplier.id) } let(:order) { create(:order, completed_at: 1.day.ago) } it "only shows orders managed by the current user" do diff --git a/spec/lib/reports/enterprise_fee_summary/enterprise_fee_summary_report_spec.rb b/spec/lib/reports/enterprise_fee_summary/enterprise_fee_summary_report_spec.rb index a11f40a7ac..ceeb312b5b 100644 --- a/spec/lib/reports/enterprise_fee_summary/enterprise_fee_summary_report_spec.rb +++ b/spec/lib/reports/enterprise_fee_summary/enterprise_fee_summary_report_spec.rb @@ -523,21 +523,21 @@ RSpec.describe Reporting::Reports::EnterpriseFeeSummary::FeeSummary do let!(:producer_c) { create(:supplier_enterprise, name: "Producer C") } let!(:fee_a) { create(:enterprise_fee, name: "Fee A", enterprise: producer_a, amount: 1) } - let!(:fee_b) { create(:enterprise_fee, name: "Fee B", enterprise: producer_b, amount: 1) } - let!(:fee_c) { create(:enterprise_fee, name: "Fee C", enterprise: producer_c, amount: 1) } + let!(:fee_b) { create(:enterprise_fee, name: "Fee B", enterprise: producer_b, amount: 2) } + let!(:fee_c) { create(:enterprise_fee, name: "Fee C", enterprise: producer_c, amount: 3) } - let!(:product_a) { create(:product, supplier: producer_a) } - let!(:product_b) { create(:product, supplier: producer_b) } - let!(:product_c) { create(:product, supplier: producer_c) } + let!(:product_a) { create(:product, supplier_id: producer_a.id) } + let!(:product_b) { create(:product, supplier_id: producer_b.id) } + let!(:product_c) { create(:product, supplier_id: producer_c.id) } let!(:variant_a) do - prepare_variant(product: product_a, producer: producer_a, incoming_exchange_fees: [fee_a]) + prepare_variant(product: product_a, supplier: producer_a, incoming_exchange_fees: [fee_a]) end let!(:variant_b) do - prepare_variant(product: product_b, producer: producer_b, incoming_exchange_fees: [fee_b]) + prepare_variant(product: product_b, supplier: producer_b, incoming_exchange_fees: [fee_b]) end let!(:variant_c) do - prepare_variant(product: product_c, producer: producer_c, incoming_exchange_fees: [fee_c]) + prepare_variant(product: product_c, supplier: producer_c, incoming_exchange_fees: [fee_c]) end let!(:order_a) { prepare_order(variant: variant_a) } @@ -697,8 +697,7 @@ RSpec.describe Reporting::Reports::EnterpriseFeeSummary::FeeSummary do end def default_order_options - { customer:, distributor:, order_cycle:, - shipping_method:, variant: } + { customer:, distributor:, order_cycle:, shipping_method:, variant: } end def prepare_incomplete_order(options = {}) @@ -713,8 +712,7 @@ RSpec.describe Reporting::Reports::EnterpriseFeeSummary::FeeSummary do end def default_variant_options - { product:, producer:, coordinator:, - distributor:, order_cycle: } + { product:, coordinator:, distributor:, order_cycle: } end def prepare_variant(options = {}) diff --git a/spec/lib/reports/enterprise_fee_summary/enterprise_fees_with_tax_report_by_producer_spec.rb b/spec/lib/reports/enterprise_fee_summary/enterprise_fees_with_tax_report_by_producer_spec.rb index a993becede..ae58f2e211 100644 --- a/spec/lib/reports/enterprise_fee_summary/enterprise_fees_with_tax_report_by_producer_spec.rb +++ b/spec/lib/reports/enterprise_fee_summary/enterprise_fees_with_tax_report_by_producer_spec.rb @@ -26,7 +26,7 @@ RSpec.describe Reporting::Reports::EnterpriseFeeSummary::EnterpriseFeesWithTaxRe outgoing.exchange_variants.create(variant:) end } - let(:variant) { create(:product, supplier: enterprise).variants.first } + let(:variant) { create(:variant, supplier: enterprise) } let(:order) { create( :order, :with_line_item, diff --git a/spec/lib/reports/order_cycle_management_report_spec.rb b/spec/lib/reports/order_cycle_management_report_spec.rb index 65c3a2d86b..17136d5942 100644 --- a/spec/lib/reports/order_cycle_management_report_spec.rb +++ b/spec/lib/reports/order_cycle_management_report_spec.rb @@ -66,7 +66,7 @@ module Reporting describe "fetching orders" do let(:supplier) { create(:supplier_enterprise) } - let(:product) { create(:simple_product, supplier:) } + let(:product) { create(:simple_product, supplier_id: supplier.id) } let(:order) { create(:order, completed_at: 1.day.ago) } it "only shows orders managed by the current user" do diff --git a/spec/lib/reports/orders_and_fulfillment/order_cycle_customer_totals_report_spec.rb b/spec/lib/reports/orders_and_fulfillment/order_cycle_customer_totals_report_spec.rb index 16511bca45..8ca2d09130 100644 --- a/spec/lib/reports/orders_and_fulfillment/order_cycle_customer_totals_report_spec.rb +++ b/spec/lib/reports/orders_and_fulfillment/order_cycle_customer_totals_report_spec.rb @@ -29,7 +29,7 @@ RSpec.describe Reporting::Reports::OrdersAndFulfillment::OrderCycleCustomerTotal distributor:, completed_at: order_date, ).tap do |order| - order.line_items[0].product.supplier.update(name: "Apple Farmer") + order.line_items[0].variant.supplier.update(name: "Apple Farmer") order.line_items[0].product.update(name: "Apples") order.line_items[0].variant.update(sku: "APP") end diff --git a/spec/lib/reports/orders_and_fulfillment/order_cycle_distributor_totals_by_supplier_report_spec.rb b/spec/lib/reports/orders_and_fulfillment/order_cycle_distributor_totals_by_supplier_report_spec.rb index 9bedefa186..094e9a5c72 100644 --- a/spec/lib/reports/orders_and_fulfillment/order_cycle_distributor_totals_by_supplier_report_spec.rb +++ b/spec/lib/reports/orders_and_fulfillment/order_cycle_distributor_totals_by_supplier_report_spec.rb @@ -30,7 +30,7 @@ module Reporting distributor_name_field = report_table.first[0] expect(distributor_name_field).to eq distributor.name - supplier = order.line_items.first.variant.product.supplier + supplier = order.line_items.first.variant.supplier supplier_name_field = report_table.first[1] expect(supplier_name_field).to eq supplier.name end diff --git a/spec/lib/reports/orders_and_fulfillment/order_cycle_supplier_totals_by_distributor_report_spec.rb b/spec/lib/reports/orders_and_fulfillment/order_cycle_supplier_totals_by_distributor_report_spec.rb index c6cbb733d0..4681387af4 100644 --- a/spec/lib/reports/orders_and_fulfillment/order_cycle_supplier_totals_by_distributor_report_spec.rb +++ b/spec/lib/reports/orders_and_fulfillment/order_cycle_supplier_totals_by_distributor_report_spec.rb @@ -27,7 +27,7 @@ module Reporting end it "has a variant row under the distributor" do - supplier = order.line_items.first.variant.product.supplier + supplier = order.line_items.first.variant.supplier expect(report.rows.first.producer).to eq supplier.name expect(report.rows.first.hub).to eq distributor.name end diff --git a/spec/lib/reports/orders_and_fulfillment/orders_cycle_supplier_totals_report_spec.rb b/spec/lib/reports/orders_and_fulfillment/orders_cycle_supplier_totals_report_spec.rb index c40dcc90db..e81596cbbd 100644 --- a/spec/lib/reports/orders_and_fulfillment/orders_cycle_supplier_totals_report_spec.rb +++ b/spec/lib/reports/orders_and_fulfillment/orders_cycle_supplier_totals_report_spec.rb @@ -9,7 +9,7 @@ RSpec.describe Reporting::Reports::OrdersAndFulfillment::OrderCycleSupplierTotal create(:completed_order_with_totals, line_items_count: 1, distributor:) end let!(:supplier) do - order.line_items.first.variant.product.supplier + order.line_items.first.variant.supplier end let(:current_user) { distributor.owner } let(:params) { { display_summary_row: false, fields_to_hide: [] } } diff --git a/spec/lib/reports/packing/packing_report_spec.rb b/spec/lib/reports/packing/packing_report_spec.rb index 624a5dab5a..47553fe65b 100644 --- a/spec/lib/reports/packing/packing_report_spec.rb +++ b/spec/lib/reports/packing/packing_report_spec.rb @@ -3,8 +3,6 @@ require 'spec_helper' RSpec.describe "Packing Reports" do - include AuthenticationHelper - describe "fetching orders" do let(:distributor) { create(:distributor_enterprise) } let(:order_cycle) { create(:simple_order_cycle) } @@ -56,12 +54,20 @@ RSpec.describe "Packing Reports" do ship_address: create(:address)) } let(:line_item2) { - build(:line_item_with_shipment, - product: create(:simple_product, name: "visible", supplier: supplier1)) + build( + :line_item_with_shipment, + variant: create( + :variant, supplier: supplier1, product: create(:simple_product, name: "visible") + ) + ) } let(:line_item3) { - build(:line_item_with_shipment, - product: create(:simple_product, name: "not visible", supplier: supplier2)) + build( + :line_item_with_shipment, + variant: create( + :variant, supplier: supplier2, product: create(:simple_product, name: "not visible") + ) + ) } before do @@ -140,7 +146,7 @@ RSpec.describe "Packing Reports" do before do order4.line_items << line_item4 order4.finalize! - line_item4.variant.product.update(supplier: create(:supplier_enterprise)) + line_item4.variant.update(supplier: create(:supplier_enterprise)) end context "filtering by order cycle" do diff --git a/spec/lib/reports/products_and_inventory_report_spec.rb b/spec/lib/reports/products_and_inventory_report_spec.rb index 79b8c6d21c..19a7024bb1 100644 --- a/spec/lib/reports/products_and_inventory_report_spec.rb +++ b/spec/lib/reports/products_and_inventory_report_spec.rb @@ -36,10 +36,8 @@ module Reporting full_name: "Variant Name", count_on_hand: 10, price: 100) - allow(variant).to receive_message_chain(:product, :supplier, - :name).and_return("Supplier") - allow(variant).to receive_message_chain(:product, :supplier, :address, - :city).and_return("A city") + allow(variant).to receive_message_chain(:supplier, :name).and_return("Supplier") + allow(variant).to receive_message_chain(:supplier, :address, :city).and_return("A city") 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")] @@ -85,62 +83,62 @@ module Reporting describe "fetching child variants" do it "returns some variants" do - product1 = create(:simple_product, supplier:) - variant1 = product1.variants.first - variant2 = create(:variant, product: product1) + product1 = create(:simple_product) + variant1 = create(:variant, product: product1, supplier:) + variant2 = create(:variant, product: product1, supplier:) expect(subject.child_variants).to match_array [variant1, variant2] end it "should only return variants managed by the user" do - product1 = create(:simple_product, supplier: create(:supplier_enterprise)) - product2 = create(:simple_product, supplier:) - variant1 = product1.variants.first - variant2 = product2.variants.first + variant1 = create(:variant, supplier: create(:supplier_enterprise)) + variant2 = create(:variant, supplier:) - expect(subject.child_variants).to eq([variant2]) + expect(subject.child_variants).to match_array([variant2]) end end describe "Filtering variants" do - let(:variants) { Spree::Variant.where(nil).joins(:product) } + let(:variants) { Spree::Variant.joins(:product) } describe "based on report type" do it "returns only variants on hand" do - product1 = create(:simple_product, supplier:, on_hand: 99) - product2 = create(:simple_product, supplier:, on_hand: 0) + product1 = create(:simple_product, supplier_id: supplier.id, on_hand: 99) + product2 = create(:simple_product, supplier_id: supplier.id, on_hand: 0) subject = Inventory.new enterprise_user expect(subject.filter(variants)).to eq([product1.variants.first]) end end + it "filters to a specific supplier" do supplier2 = create(:supplier_enterprise) - product1 = create(:simple_product, supplier:) - product2 = create(:simple_product, supplier: supplier2) + variant1 = create(:variant, supplier: ) + variant2 = create(:variant, supplier: supplier2) allow(subject).to receive(:params).and_return(supplier_id: supplier.id) - expect(subject.filter(variants)).to eq([product1.variants.first]) + expect(subject.filter(variants)).to eq([variant1]) end + it "filters to a specific distributor" do distributor = create(:distributor_enterprise) - product1 = create(:simple_product, supplier:) - product2 = create(:simple_product, supplier:) + variant1 = create(:variant, supplier:) + variant2 = create(:variant, supplier:) order_cycle = create(:simple_order_cycle, suppliers: [supplier], distributors: [distributor], - variants: [product2.variants.first]) + variants: [variant2]) allow(subject).to receive(:params).and_return(distributor_id: distributor.id) - expect(subject.filter(variants)).to eq([product2.variants.first]) + expect(subject.filter(variants)).to eq([variant2]) end it "ignores variant overrides without filter" do distributor = create(:distributor_enterprise) - product = create(:simple_product, supplier:, price: 5) + product = create(:simple_product, supplier_id: supplier.id, price: 5) variant = product.variants.first order_cycle = create(:simple_order_cycle, suppliers: [supplier], distributors: [distributor], - variants: [product.variants.first]) + variants: [variant]) create(:variant_override, hub: distributor, variant:, price: 2) result = subject.filter(variants) @@ -150,11 +148,11 @@ module Reporting it "considers variant overrides with distributor" do distributor = create(:distributor_enterprise) - product = create(:simple_product, supplier:, price: 5) + product = create(:simple_product, supplier_id: supplier.id, price: 5) variant = product.variants.first order_cycle = create(:simple_order_cycle, suppliers: [supplier], distributors: [distributor], - variants: [product.variants.first]) + variants: [variant]) create(:variant_override, hub: distributor, variant:, price: 2) allow(subject).to receive(:params).and_return(distributor_id: distributor.id) @@ -165,14 +163,14 @@ module Reporting it "filters to a specific order cycle" do distributor = create(:distributor_enterprise) - product1 = create(:simple_product, supplier:) - product2 = create(:simple_product, supplier:) + variant1 = create(:variant, supplier:) + variant2 = create(:variant, supplier:) order_cycle = create(:simple_order_cycle, suppliers: [supplier], distributors: [distributor], - variants: [product1.variants.first]) + variants: [variant1]) allow(subject).to receive(:params).and_return(order_cycle_id: order_cycle.id) - expect(subject.filter(variants)).to eq([product1.variants.first]) + expect(subject.filter(variants)).to eq([variant1]) end it "should do all the filters at once" do @@ -181,15 +179,11 @@ module Reporting distributor = create(:distributor_enterprise) other_distributor = create(:distributor_enterprise) other_supplier = create(:supplier_enterprise) - not_filtered_variant = create(:simple_product, supplier:).variants.first - variant_filtered_by_order_cycle = create(:simple_product, - supplier:).variants.first - variant_filtered_by_distributor = create(:simple_product, - supplier:).variants.first - variant_filtered_by_supplier = create(:simple_product, - supplier: other_supplier).variants.first - variant_filtered_by_stock = create(:simple_product, supplier:, - on_hand: 0).variants.first + not_filtered_variant = create(:variant, supplier:) + variant_filtered_by_order_cycle = create(:variant, supplier:) + variant_filtered_by_distributor = create(:variant, supplier:) + variant_filtered_by_supplier = create(:variant, supplier: other_supplier) + variant_filtered_by_stock = create(:variant, supplier:, on_hand: 0) # This OC contains all products except the one that should be filtered # by order cycle. We create a separate OC further down to proof that @@ -273,8 +267,10 @@ module Reporting let(:report) do AllProducts.new user, { fields_to_hide: [] } end + let(:variant) { create(:variant, supplier:) } + let(:supplier) { create(:supplier_enterprise) } - it "Should return headers" do + it "returns headers" do expect(report.table_headers).to eq([ "Supplier", "Producer Suburb", @@ -291,31 +287,27 @@ module Reporting ]) end - it "Should render 'On demand' when the product is available on demand" do - product = create(:product) - variant = product.variants.first + it "renders 'On demand' when the product is available on demand" do variant.on_demand = true variant.on_hand = 15 variant.save! - first_row = report.table_rows.first - on_demand_column = first_row[-2] - on_hand_column = first_row[-1] + last_row = report.table_rows.last + on_demand_column = last_row[-2] + on_hand_column = last_row[-1] expect(on_demand_column).to eq("Yes") expect(on_hand_column).to eq("On demand") end - it "Should render the on hand count when the product is not available on demand" do - product = create(:product) - variant = product.variants.first + it "renders the on hand count when the product is not available on demand" do variant.on_demand = false variant.on_hand = 22 variant.save! - first_row = report.table_rows.first - on_demand_column = first_row[-2] - on_hand_column = first_row[-1] + last_row = report.table_rows.last + on_demand_column = last_row[-2] + on_hand_column = last_row[-1] expect(on_demand_column).to eq("No") expect(on_hand_column).to eq(22) diff --git a/spec/lib/reports/sales_tax_totals_by_order_spec.rb b/spec/lib/reports/sales_tax_totals_by_order_spec.rb index 641bc8030e..7987ddad0b 100644 --- a/spec/lib/reports/sales_tax_totals_by_order_spec.rb +++ b/spec/lib/reports/sales_tax_totals_by_order_spec.rb @@ -55,7 +55,7 @@ RSpec.describe "Reporting::Reports::SalesTax::SalesTaxTotalsByOrder" do end before do - product.update!(supplier_id: supplier.id) + variant.update!(supplier: ) order.update!( number: 'ORDER_NUMBER_1', diff --git a/spec/mailers/producer_mailer_spec.rb b/spec/mailers/producer_mailer_spec.rb index b4e1be284a..fe1bbe2d8b 100644 --- a/spec/mailers/producer_mailer_spec.rb +++ b/spec/mailers/producer_mailer_spec.rb @@ -16,13 +16,14 @@ RSpec.describe ProducerMailer, type: :mailer do let(:d1) { create(:distributor_enterprise, charges_sales_tax: true) } let(:d2) { create(:distributor_enterprise) } let(:p1) { - create(:product, name: "Zebra", price: 12.34, supplier: s1, tax_category_id: tax_category.id) + create(:product, name: "Zebra", price: 12.34, supplier_id: s1.id, + tax_category_id: tax_category.id) } - let(:p2) { create(:product, name: "Aardvark", price: 23.45, supplier: s2) } - let(:p3) { create(:product, name: "Banana", price: 34.56, supplier: s1) } - let(:p4) { create(:product, name: "coffee", price: 45.67, supplier: s1) } - let(:p5) { create(:product, name: "Daffodil", price: 56.78, supplier: s1) } - let(:p6) { create(:product, name: "Eggs", price: 67.89, supplier: s1) } + let(:p2) { create(:product, name: "Aardvark", price: 23.45, supplier_id: s2.id) } + let(:p3) { create(:product, name: "Banana", price: 34.56, supplier_id: s1.id) } + let(:p4) { create(:product, name: "coffee", price: 45.67, supplier_id: s1.id) } + let(:p5) { create(:product, name: "Daffodil", price: 56.78, supplier_id: s1.id) } + let(:p6) { create(:product, name: "Eggs", price: 67.89, supplier_id: s1.id) } let(:order_cycle) { create(:simple_order_cycle) } let!(:incoming_exchange) { order_cycle.exchanges.create! sender: s1, receiver: d1, incoming: true, diff --git a/spec/models/enterprise_caching_spec.rb b/spec/models/enterprise_caching_spec.rb index 5f10a52398..d2c8321690 100644 --- a/spec/models/enterprise_caching_spec.rb +++ b/spec/models/enterprise_caching_spec.rb @@ -11,10 +11,12 @@ RSpec.describe Enterprise do let(:supplier2) { create(:supplier_enterprise) } describe "with a supplied product" do - let(:product) { create(:simple_product, supplier: enterprise, primary_taxon_id: taxon.id) } - let(:variant) { product.variants.first } + let(:product) { + create(:simple_product, primary_taxon_id: taxon.id, supplier_id: enterprise.id) + } let(:property) { product.product_properties.last } let(:producer_property) { enterprise.producer_properties.last } + let(:variant) { product.variants.first } before do product.set_property 'Organic', 'NASAA 12345' @@ -41,7 +43,7 @@ RSpec.describe Enterprise do it "touches enterprise when the supplier of a product changes" do expect { - later { product.update!(supplier: supplier2) } + later { variant.update!(supplier: supplier2) } }.to change { enterprise.reload.updated_at } end end @@ -53,11 +55,15 @@ RSpec.describe Enterprise do create(:simple_order_cycle, distributors: [enterprise], variants: [product.variants.first]) } - let(:supplier) { product.supplier } + let(:supplier) { variant.supplier } let(:property) { product.product_properties.last } let(:producer_property) { supplier.producer_properties.last } + let(:variant) { create(:variant, product:, supplier: enterprise) } before do + product.variants = [] + product.variants << variant + product.set_property 'Organic', 'NASAA 12345' supplier.set_producer_property 'Biodynamic', 'ASDF 4321' end @@ -83,9 +89,9 @@ RSpec.describe Enterprise do }.to change { enterprise.reload.updated_at } end - it "touches enterprise when the supplier of a product changes" do + it "touches enterprise when the supplier of a variant changes" do expect { - later { product.update!(supplier: supplier2) } + later { variant.update!(supplier: supplier2) } }.to change { enterprise.reload.updated_at } end diff --git a/spec/models/enterprise_relationship_spec.rb b/spec/models/enterprise_relationship_spec.rb index dfdc2df022..d4d9dbd2c7 100644 --- a/spec/models/enterprise_relationship_spec.rb +++ b/spec/models/enterprise_relationship_spec.rb @@ -164,36 +164,31 @@ RSpec.describe EnterpriseRelationship do :create_variant_overrides] ) } let!(:vo1) { - create(:variant_override, hub:, - variant: create( - :variant, - product: create( - :product, supplier: producer - ) - )) + create( + :variant_override, + hub:, + variant: create(:variant, product: create(:product), supplier: producer) + ) } let!(:vo2) { - create(:variant_override, hub:, - variant: create( - :variant, - product: create( - :product, supplier: producer - ) - )) + create( + :variant_override, + hub:, + variant: create(:variant, product: create(:product), supplier: producer) + ) } let!(:vo3) { - create(:variant_override, hub:, - variant: create( - :variant, - product: create( - :product, supplier: some_other_producer - ) - )) + create( + :variant_override, + hub:, + variant: create(:variant, product: create(:product), supplier: some_other_producer) + ) } context "revoking variant override permissions" do context "when the enterprise relationship is destroyed" do before { er.destroy } + it "should set permission_revoked_at to the current time " \ "for all variant overrides of the relationship" do expect(vo1.reload.permission_revoked_at).not_to be_nil @@ -205,6 +200,7 @@ RSpec.describe EnterpriseRelationship do context "and is then removed" do before { er.permissions_list = [:add_to_order_cycles]; er.save! } + it "should set permission_revoked_at to the current time " \ "for all relevant variant overrides" do expect(vo1.reload.permission_revoked_at).not_to be_nil @@ -237,34 +233,28 @@ RSpec.describe EnterpriseRelationship do permissions_list: [:add_to_order_cycles] ) } let!(:vo1) { - create(:variant_override, hub:, - variant: create( - :variant, - product: create( - :product, supplier: producer - ) - ), - permission_revoked_at: Time.now.in_time_zone) + create( + :variant_override, + hub:, + variant: create(:variant, product: create(:product), supplier: producer), + permission_revoked_at: Time.now.in_time_zone + ) } let!(:vo2) { - create(:variant_override, hub:, - variant: create( - :variant, - product: create( - :product, supplier: producer - ) - ), - permission_revoked_at: Time.now.in_time_zone) + create( + :variant_override, + hub:, + variant: create(:variant, product: create(:product), supplier: producer), + permission_revoked_at: Time.now.in_time_zone + ) } let!(:vo3) { - create(:variant_override, hub:, - variant: create( - :variant, - product: create( - :product, supplier: some_other_producer - ) - ), - permission_revoked_at: Time.now.in_time_zone) + create( + :variant_override, + hub:, + variant: create(:variant, product: create(:product), supplier: some_other_producer), + permission_revoked_at: Time.now.in_time_zone + ) } context "and is then added" do @@ -292,6 +282,7 @@ RSpec.describe EnterpriseRelationship do end end end + describe "updating order cycles" do let(:hub) { create(:distributor_enterprise) } let(:producer) { create(:supplier_enterprise) } diff --git a/spec/models/enterprise_spec.rb b/spec/models/enterprise_spec.rb index 6b1fd9059c..f69c6f96d2 100644 --- a/spec/models/enterprise_spec.rb +++ b/spec/models/enterprise_spec.rb @@ -37,13 +37,14 @@ RSpec.describe Enterprise do expect(EnterpriseRole.where(id: role.id)).to be_empty end - xit "destroys supplied products upon destroy" do - s = create(:supplier_enterprise) - p = create(:simple_product, supplier: s) + it "destroys supplied variants upon destroy" do + pending "Variant are soft deletable, see: https://github.com/openfoodfoundation/openfoodnetwork/issues/2971" + supplier = create(:supplier_enterprise) + variant = create(:variant, supplier:) - s.destroy + supplier.destroy - expect(Spree::Product.where(id: p.id)).to be_empty + expect(Spree::Variant.where(id: variant.id)).to be_empty end it "destroys relationships upon destroy" do @@ -411,12 +412,33 @@ RSpec.describe Enterprise do end describe "callbacks" do - it "restores permalink to original value when it is changed and invalid" do - e1 = create(:enterprise, permalink: "taken") - e2 = create(:enterprise, permalink: "not_taken") - e2.permalink = "taken" - e2.save - expect(e2.reload.permalink).to eq "not_taken" + describe "restore_permalink" do + it "restores permalink to original value when it is changed and invalid" do + e1 = create(:enterprise, permalink: "taken") + e2 = create(:enterprise, permalink: "not_taken") + e2.permalink = "taken" + e2.save + expect(e2.reload.permalink).to eq "not_taken" + end + end + + describe "touch_distributors" do + it "touches supplied variant distributors" do + enterprise = create(:enterprise) + variant = create(:variant) + enterprise.supplied_variants << variant + + updated_at = 1.hour.ago + distributor1 = create(:distributor_enterprise, updated_at:) + distributor2 = create(:distributor_enterprise, updated_at:) + + create(:simple_order_cycle, distributors: [distributor1], variants: [variant]) + create(:simple_order_cycle, distributors: [distributor2], variants: [variant]) + + expect { enterprise.touch } + .to change { distributor1.reload.updated_at } + .and change { distributor2.reload.updated_at } + end end end @@ -567,51 +589,49 @@ RSpec.describe Enterprise do end end - describe "supplying_variant_in" do + describe ".supplying_variant_in" do it "finds producers by supply of variant" do - s = create(:supplier_enterprise) - p = create(:simple_product, supplier: s) - v = create(:variant, product: p) + supplier = create(:supplier_enterprise) + variant = create(:variant, supplier:) - expect(Enterprise.supplying_variant_in([v])).to eq([s]) + expect(Enterprise.supplying_variant_in([variant])).to eq([supplier]) end it "returns multiple enterprises when given multiple variants" do - s1 = create(:supplier_enterprise) - s2 = create(:supplier_enterprise) - p1 = create(:simple_product, supplier: s1) - p2 = create(:simple_product, supplier: s2) + supplier1 = create(:supplier_enterprise) + supplier2 = create(:supplier_enterprise) + variant1 = create(:variant, supplier: supplier1) + variant2 = create(:variant, supplier: supplier2) - expect(Enterprise.supplying_variant_in([p1.variants.first, - p2.variants.first])).to match_array [s1, s2] + expect(Enterprise.supplying_variant_in([variant1, variant2])) + .to match_array([supplier1, supplier2]) end it "does not return duplicates" do - s = create(:supplier_enterprise) - p1 = create(:simple_product, supplier: s) - p2 = create(:simple_product, supplier: s) + supplier = create(:supplier_enterprise) + variant1 = create(:variant, supplier:) + variant2 = create(:variant, supplier:) - expect(Enterprise.supplying_variant_in([p1.variants.first, p2.variants.first])).to eq([s]) + expect(Enterprise.supplying_variant_in([variant1, variant2])).to eq([supplier]) end end - describe "distributing_products" do + describe "distributing_variants" do let(:distributor) { create(:distributor_enterprise) } - let(:product) { create(:product) } + let(:variant) { create(:variant) } it "returns enterprises distributing via an order cycle" do - order_cycle = create(:simple_order_cycle, distributors: [distributor], - variants: [product.variants.first]) - expect(Enterprise.distributing_products(product.id)).to eq([distributor]) + order_cycle = create(:simple_order_cycle, distributors: [distributor], variants: [variant]) + expect(Enterprise.distributing_variants(variant.id)).to eq([distributor]) end it "does not return duplicate enterprises" do - another_product = create(:product) + another_variant = create(:variant) order_cycle = create(:simple_order_cycle, distributors: [distributor], - variants: [product.variants.first, - another_product.variants.first]) - expect(Enterprise.distributing_products([product.id, - another_product.id])).to eq([distributor]) + variants: [variant, another_variant]) + expect(Enterprise.distributing_variants( + [variant.id, another_variant.id] + )).to eq([distributor]) end end diff --git a/spec/models/order_cycle_spec.rb b/spec/models/order_cycle_spec.rb index b673da05e9..5c903f4985 100644 --- a/spec/models/order_cycle_spec.rb +++ b/spec/models/order_cycle_spec.rb @@ -151,12 +151,12 @@ RSpec.describe OrderCycle do end it "checks for variants" do - p1 = create(:simple_product) - p2 = create(:simple_product) - oc = create(:simple_order_cycle, suppliers: [p1.supplier], variants: [p1.variants.first]) + variant1 = create(:variant) + variant2 = create(:variant) + order_cycle = create(:simple_order_cycle, suppliers: [variant1.supplier], variants: [variant1]) - expect(oc).to have_variant(p1.variants.first) - expect(oc).not_to have_variant(p2.variants.first) + expect(order_cycle).to have_variant(variant1) + expect(order_cycle).not_to have_variant(variant2) end describe "product exchanges" do diff --git a/spec/models/product_import/entry_processor_spec.rb b/spec/models/product_import/entry_processor_spec.rb index 1306080ef8..13022d2207 100644 --- a/spec/models/product_import/entry_processor_spec.rb +++ b/spec/models/product_import/entry_processor_spec.rb @@ -150,4 +150,50 @@ RSpec.describe ProductImport::EntryProcessor do end end end + + describe "#count_existing_items" do + let(:settings) { instance_double(ProductImport::Settings, importing_into_inventory?: false) } + let(:editable_enterprises) do + { + "#{supplier1.name}": supplier1.id, + "#{supplier2.name}": supplier2.id + } + end + let(:supplier1) { create(:supplier_enterprise) } + let(:supplier2) { create(:supplier_enterprise) } + let!(:products) { create_list(:simple_product, 3, supplier_id: supplier1.id) } + + before do + allow(ProductImport::Settings).to receive(:new) { settings } + + enterprises = { + "#{supplier1.name}": { id: supplier1.id }, + "#{supplier2.name}": { id: supplier2.id } + } + allow(spreadsheet_data).to receive(:enterprises_index).and_return(enterprises) + + create_list(:simple_product, 2, supplier_id: supplier2.id) + end + + it "returns the total of existing variants for the given enterprises" do + entry_processor.count_existing_items + + expect(entry_processor.total_enterprise_products).to eq(5) + end + + context "when importing into inventory" do + let(:settings) { instance_double(ProductImport::Settings, importing_into_inventory?: true) } + + it "returns the total of existing variant override for the given enterprises" do + products.each do |p| + variant = p.variants.first + create(:variant_override, variant:, hub: variant.supplier) + end + + entry_processor.count_existing_items + + expect(entry_processor.total_enterprise_products).to eq(3) + end + end + end end diff --git a/spec/models/product_import/entry_validator_spec.rb b/spec/models/product_import/entry_validator_spec.rb index 3f9ef97209..47512e5a78 100644 --- a/spec/models/product_import/entry_validator_spec.rb +++ b/spec/models/product_import/entry_validator_spec.rb @@ -63,12 +63,12 @@ RSpec.describe ProductImport::EntryValidator do let(:potatoes) { create( :simple_product, - supplier: enterprise, on_hand: '100', name: 'Potatoes', unit_value: 1000, variant_unit_scale: 1000, - variant_unit: 'weight' + variant_unit: 'weight', + variants: [create(:variant, supplier: enterprise)] ) } @@ -119,24 +119,24 @@ RSpec.describe ProductImport::EntryValidator do let!(:product_g) { create( :simple_product, - supplier: enterprise, on_hand: '100', name: 'Tomato', unit_value: 500, variant_unit_scale: 1, - variant_unit: 'weight' + variant_unit: 'weight', + variants: [create(:variant, supplier: enterprise, unit_value: 500)] ) } let!(:product_kg) { create( :simple_product, - supplier: enterprise, on_hand: '100', name: 'Potatoes', unit_value: 1000, variant_unit_scale: 1000, - variant_unit: 'weight' + variant_unit: 'weight', + variants: [create(:variant, supplier: enterprise, unit_value: 1000)] ) } diff --git a/spec/models/product_import/inventory_reset_strategy_spec.rb b/spec/models/product_import/inventory_reset_strategy_spec.rb index 86a0b67de4..d7e6a7abc7 100644 --- a/spec/models/product_import/inventory_reset_strategy_spec.rb +++ b/spec/models/product_import/inventory_reset_strategy_spec.rb @@ -7,7 +7,7 @@ RSpec.describe ProductImport::InventoryResetStrategy do describe '#reset' do context 'when there are excluded_items_ids' do - let(:enterprise) { variant.product.supplier } + let(:enterprise) { variant.supplier } let(:variant) { build_stubbed(:variant) } let!(:variant_override) do build_stubbed( @@ -131,7 +131,7 @@ RSpec.describe ProductImport::InventoryResetStrategy do context 'and supplier_ids is set' do let(:supplier_ids) { enterprise.id } - let(:enterprise) { variant.product.supplier } + let(:enterprise) { variant.supplier } let(:variant) { create(:variant) } context "and variant overrides with count on hand" do @@ -202,7 +202,7 @@ RSpec.describe ProductImport::InventoryResetStrategy do context 'and supplier_ids is set' do let(:supplier_ids) { enterprise.id } - let(:enterprise) { variant.product.supplier } + let(:enterprise) { variant.supplier } let(:variant) { create(:variant) } context "and variant overrides with count on hand" do diff --git a/spec/models/product_importer_spec.rb b/spec/models/product_importer_spec.rb index 39b6508575..4dc3df2199 100644 --- a/spec/models/product_importer_spec.rb +++ b/spec/models/product_importer_spec.rb @@ -36,67 +36,70 @@ RSpec.describe ProductImport::ProductImporter do let!(:shipping_category) { create(:shipping_category) } let!(:product) { - create(:simple_product, supplier: enterprise2, name: 'Hypothetical Cake', description: nil, - primary_taxon_id: category2.id) + create(:simple_product, name: 'Hypothetical Cake', description: nil, + primary_taxon_id: category2.id, supplier_id: enterprise2.id, + variants: []) } let!(:variant) { create(:variant, product_id: product.id, price: '8.50', on_hand: '100', unit_value: '500', - display_name: 'Preexisting Banana') + display_name: 'Preexisting Banana', supplier: enterprise2) } let!(:variant_with_empty_display_name) { create(:variant, product_id: product.id, price: '8.50', on_hand: '100', unit_value: '500', - display_name: '') + display_name: '', supplier: enterprise2) } let!(:product2) { - create(:simple_product, supplier: enterprise, on_hand: '100', name: 'Beans', unit_value: '500', - primary_taxon_id: category.id, description: nil) + create(:simple_product, on_hand: '100', name: 'Beans', unit_value: '500', + primary_taxon_id: category.id, description: nil, variants: [] ) } let!(:product3) { - create(:simple_product, supplier: enterprise, on_hand: '100', name: 'Sprouts', - unit_value: '500', primary_taxon_id: category.id) + create(:simple_product, on_hand: '100', name: 'Sprouts', unit_value: '500', + primary_taxon_id: category.id, supplier_id: enterprise.id) } let!(:product4) { - create(:simple_product, supplier: enterprise, on_hand: '100', name: 'Cabbage', unit_value: '1', + create(:simple_product, on_hand: '100', name: 'Cabbage', unit_value: '1', variant_unit_scale: nil, variant_unit: "items", - variant_unit_name: "Whole", primary_taxon_id: category.id) + variant_unit_name: "Whole", primary_taxon_id: category.id, + supplier_id: enterprise.id) } let!(:product5) { - create(:simple_product, supplier: enterprise2, on_hand: '100', name: 'Lettuce', - unit_value: '500', primary_taxon_id: category.id) + create(:simple_product, on_hand: '100', name: 'Lettuce', unit_value: '500', + primary_taxon_id: category.id, supplier_id: enterprise2.id) } let!(:product6) { - create(:simple_product, supplier: enterprise3, on_hand: '100', name: 'Beetroot', + create(:simple_product, on_hand: '100', name: 'Beetroot', unit_value: '500', on_demand: true, variant_unit_scale: 1, - variant_unit: 'weight', primary_taxon_id: category.id, description: nil) + variant_unit: 'weight', primary_taxon_id: category.id, description: nil, + supplier_id: enterprise3.id) } let!(:product7) { - create(:simple_product, supplier: enterprise3, on_hand: '100', name: 'Tomato', - unit_value: '500', variant_unit_scale: 1, - variant_unit: 'weight', primary_taxon_id: category.id, - description: nil) + create(:simple_product, on_hand: '100', name: 'Tomato', unit_value: '500', + variant_unit_scale: 1, variant_unit: 'weight', + primary_taxon_id: category.id, description: nil, + supplier_id: enterprise3.id) } let!(:product8) { - create(:simple_product, supplier: enterprise, on_hand: '100', name: 'Oats', description: "", + create(:simple_product, on_hand: '100', name: 'Oats', description: "", unit_value: '500', variant_unit_scale: 1, variant_unit: 'weight', primary_taxon_id: category4.id) } let!(:product9) { - create(:simple_product, supplier: enterprise, on_hand: '100', name: 'Oats', description: "", + create(:simple_product, on_hand: '100', name: 'Oats', description: "", unit_value: '500', variant_unit_scale: 1, variant_unit: 'weight', primary_taxon_id: category4.id) } let!(:variant2) { create(:variant, product_id: product8.id, price: '4.50', on_hand: '100', unit_value: '500', - display_name: 'Porridge Oats') + display_name: 'Porridge Oats', supplier: enterprise) } let!(:variant3) { create(:variant, product_id: product8.id, price: '5.50', on_hand: '100', unit_value: '500', - display_name: 'Rolled Oats') + display_name: 'Rolled Oats', supplier: enterprise) } let!(:variant4) { create(:variant, product_id: product9.id, price: '6.50', on_hand: '100', unit_value: '500', - display_name: 'Flaked Oats') + display_name: 'Flaked Oats', supplier: enterprise) } let!(:variant_override) { @@ -155,54 +158,64 @@ RSpec.describe ProductImport::ProductImporter do expect(importer.updated_ids.count).to eq 5 carrots = Spree::Product.find_by(name: 'Carrots') - expect(carrots.supplier).to eq enterprise + carrots_variant = carrots.variants.first expect(carrots.on_hand).to eq 5 - expect(carrots.variants.first.price).to eq 3.20 - expect(carrots.variants.first.unit_value).to eq 500 expect(carrots.variant_unit).to eq 'weight' expect(carrots.variant_unit_scale).to eq 1 - expect(carrots.variants.first.on_demand).not_to eq true - expect(carrots.variants.first.import_date).to be_within(1.minute).of Time.zone.now + + expect(carrots_variant.supplier).to eq enterprise + expect(carrots_variant.price).to eq 3.20 + expect(carrots_variant.unit_value).to eq 500 + expect(carrots_variant.on_demand).not_to eq true + expect(carrots_variant.import_date).to be_within(1.minute).of Time.zone.now potatoes = Spree::Product.find_by(name: 'Potatoes') - expect(potatoes.supplier).to eq enterprise + potatoes_variant = potatoes.variants.first expect(potatoes.on_hand).to eq 6 - expect(potatoes.variants.first.price).to eq 6.50 - expect(potatoes.variants.first.unit_value).to eq 2000 expect(potatoes.variant_unit).to eq 'weight' expect(potatoes.variant_unit_scale).to eq 1000 - expect(potatoes.variants.first.on_demand).not_to eq true - expect(potatoes.variants.first.import_date).to be_within(1.minute).of Time.zone.now + + expect(potatoes_variant.supplier).to eq enterprise + expect(potatoes_variant.price).to eq 6.50 + expect(potatoes_variant.unit_value).to eq 2000 + expect(potatoes_variant.on_demand).not_to eq true + expect(potatoes_variant.import_date).to be_within(1.minute).of Time.zone.now pea_soup = Spree::Product.find_by(name: 'Pea Soup') - expect(pea_soup.supplier).to eq enterprise + pea_soup_variant = pea_soup.variants.first expect(pea_soup.on_hand).to eq 8 - expect(pea_soup.variants.first.price).to eq 5.50 - expect(pea_soup.variants.first.unit_value).to eq 0.75 expect(pea_soup.variant_unit).to eq 'volume' expect(pea_soup.variant_unit_scale).to eq 0.001 - expect(pea_soup.variants.first.on_demand).not_to eq true - expect(pea_soup.variants.first.import_date).to be_within(1.minute).of Time.zone.now + + expect(pea_soup_variant.supplier).to eq enterprise + expect(pea_soup_variant.price).to eq 5.50 + expect(pea_soup_variant.unit_value).to eq 0.75 + expect(pea_soup_variant.on_demand).not_to eq true + expect(pea_soup_variant.import_date).to be_within(1.minute).of Time.zone.now salad = Spree::Product.find_by(name: 'Salad') - expect(salad.supplier).to eq enterprise + salad_variant = salad.variants.first expect(salad.on_hand).to eq 7 - expect(salad.variants.first.price).to eq 4.50 - expect(salad.variants.first.unit_value).to eq 1 expect(salad.variant_unit).to eq 'items' expect(salad.variant_unit_scale).to eq nil - expect(salad.variants.first.on_demand).not_to eq true - expect(salad.variants.first.import_date).to be_within(1.minute).of Time.zone.now + + expect(salad_variant.supplier).to eq enterprise + expect(salad_variant.price).to eq 4.50 + expect(salad_variant.unit_value).to eq 1 + expect(salad_variant.on_demand).not_to eq true + expect(salad_variant.import_date).to be_within(1.minute).of Time.zone.now buns = Spree::Product.find_by(name: 'Hot Cross Buns') - expect(buns.supplier).to eq enterprise + buns_variant = buns.variants.first expect(buns.on_hand).to eq 7 - expect(buns.variants.first.price).to eq 3.50 - expect(buns.variants.first.unit_value).to eq 1 expect(buns.variant_unit).to eq 'items' expect(buns.variant_unit_scale).to eq nil - expect(buns.variants.first.on_demand).to eq true - expect(buns.variants.first.import_date).to be_within(1.minute).of Time.zone.now + + expect(buns_variant.supplier).to eq enterprise + expect(buns_variant.price).to eq 3.50 + expect(buns_variant.unit_value).to eq 1 + expect(buns_variant.on_demand).to eq true + expect(buns_variant.import_date).to be_within(1.minute).of Time.zone.now end end @@ -236,10 +249,11 @@ RSpec.describe ProductImport::ProductImporter do expect(importer.updated_ids.count).to eq 1 carrots = Spree::Product.find_by(name: 'Good Carrots') - expect(carrots.supplier).to eq enterprise + carrots_variant = carrots.variants.first expect(carrots.on_hand).to eq 5 - expect(carrots.variants.first.price).to eq 3.20 - expect(carrots.variants.first.import_date).to be_within(1.minute).of Time.zone.now + expect(carrots_variant.supplier).to eq enterprise + expect(carrots_variant.price).to eq 3.20 + expect(carrots_variant.import_date).to be_within(1.minute).of Time.zone.now expect(Spree::Product.find_by(name: 'Bad Potatoes')).to eq nil end @@ -282,12 +296,15 @@ RSpec.describe ProductImport::ProductImporter do expect(importer.products_created_count).to eq 1 carrots = Spree::Product.find_by(name: 'Good Carrots') + carrots_variant = carrots.variants.first + expect(carrots.on_hand).to eq 5 - expect(carrots.variants.first.price).to eq 3.20 - 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" + + expect(carrots_variant.primary_taxon.name).to eq "Vegetables" + expect(carrots_variant.supplier).to eq enterprise + expect(carrots_variant.price).to eq 3.20 + expect(carrots_variant.shipping_category).to eq shipping_category + expect(carrots_variant.unit_presentation).to eq "500g" end end @@ -446,7 +463,7 @@ RSpec.describe ProductImport::ProductImporter do CSV.generate do |csv| csv << ["name", "producer", "category", "on_hand", "price", "units", "unit_type", "display_name", "shipping_category"] - csv << ["Hypothetical Cake", enterprise2.name, "Cake", "5", "5.50", "1", "g", + csv << ["Hypothetical Cake", enterprise2.name, "Cake", "5", "5.50", "500", "g", "", shipping_category.name] end } diff --git a/spec/models/spree/ability_spec.rb b/spec/models/spree/ability_spec.rb index 9ff691368e..aa31506e30 100644 --- a/spec/models/spree/ability_spec.rb +++ b/spec/models/spree/ability_spec.rb @@ -304,9 +304,9 @@ RSpec.describe Spree::Ability do let(:d1) { create(:distributor_enterprise) } let(:d2) { create(:distributor_enterprise) } - let(:p1) { create(:product, supplier: s1) } - let(:p2) { create(:product, supplier: s2) } - let(:p_related) { create(:product, supplier: s_related) } + let(:p1) { create(:product, supplier_id: s1.id) } + let(:p2) { create(:product, supplier_id: s2.id) } + let(:p_related) { create(:product, supplier_id: s_related.id) } let(:er1) { create(:enterprise_relationship, parent: s1, child: d1) } let(:er2) { create(:enterprise_relationship, parent: d1, child: s1) } @@ -796,8 +796,7 @@ RSpec.describe Spree::Ability do describe "permissions for variant overrides" do let!(:distributor) { create(:distributor_enterprise) } let!(:producer) { create(:supplier_enterprise) } - let!(:product) { create(:product, supplier: producer) } - let!(:variant) { create(:variant, product:) } + let!(:variant) { create(:variant, supplier: producer) } let!(:variant_override) { create(:variant_override, hub: distributor, variant:) } subject { user } diff --git a/spec/models/spree/line_item_spec.rb b/spec/models/spree/line_item_spec.rb index f68e2555d1..d4a757cf99 100644 --- a/spec/models/spree/line_item_spec.rb +++ b/spec/models/spree/line_item_spec.rb @@ -7,6 +7,16 @@ module Spree let(:order) { create :order_with_line_items, line_items_count: 1 } let(:line_item) { order.line_items.first } + describe "associations" do + it { is_expected.to belong_to(:order).required } + it { is_expected.to have_one(:order_cycle).through(:order) } + it { is_expected.to belong_to(:variant).required } + it { is_expected.to have_one(:product).through(:variant) } + it { is_expected.to have_one(:supplier).through(:variant) } + it { is_expected.to belong_to(:tax_category).optional } + it { is_expected.to have_many(:adjustments) } + end + context '#save' do it 'should update inventory, totals, and tax' do # Regression check for Spree #1481 @@ -144,11 +154,11 @@ module Spree let(:s1) { create(:supplier_enterprise) } let(:s2) { create(:supplier_enterprise) } - let(:p1) { create(:simple_product, supplier: s1) } - let(:p2) { create(:simple_product, supplier: s2) } + let(:variant1) { create(:variant, supplier: s1) } + let(:variant2) { create(:variant, supplier: s2) } - let(:li1) { create(:line_item, order: o, product: p1) } - let(:li2) { create(:line_item, order: o, product: p2) } + let(:li1) { create(:line_item, order: o, variant: variant1) } + let(:li2) { create(:line_item, order: o, variant: variant2) } let(:p3) { create(:product, name: 'Clear Honey') } let(:p4) { create(:product, name: 'Apricots') } @@ -806,10 +816,12 @@ module Spree RSpec.describe "searching with ransack" do let(:order_cycle1) { create(:order_cycle) } let(:order_cycle2) { create(:order_cycle) } - let(:product1) { create(:product, supplier: create(:supplier_enterprise)) } - let(:product2) { create(:product, supplier: create(:supplier_enterprise)) } - let!(:line_item1) { create(:line_item, variant: product1.variants.first) } - let!(:line_item2) { create(:line_item, variant: product2.variants.first) } + let(:variant1) { create(:variant, supplier: supplier1) } + let(:variant2) { create(:variant, supplier: supplier2) } + let(:supplier1) { create(:supplier_enterprise) } + let(:supplier2) { create(:supplier_enterprise) } + let!(:line_item1) { create(:line_item, variant: variant1) } + let!(:line_item2) { create(:line_item, variant: variant2) } let(:search_result) { Spree::LineItem.ransack(query).result } @@ -819,7 +831,7 @@ module Spree end context "searching by supplier" do - let(:query) { { supplier_id_eq: line_item1.variant.product.supplier_id } } + let(:query) { { supplier_id_eq: line_item1.variant.supplier_id } } it "filters results" do expect(search_result.to_a).to eq [line_item1] diff --git a/spec/models/spree/order_spec.rb b/spec/models/spree/order_spec.rb index ec49b695cd..50ef24dc72 100644 --- a/spec/models/spree/order_spec.rb +++ b/spec/models/spree/order_spec.rb @@ -1459,10 +1459,10 @@ RSpec.describe Spree::Order do let(:aaron) { create(:supplier_enterprise, name: "Aaron the farmer") } let(:zed) { create(:supplier_enterprise, name: "Zed the farmer") } - let(:aaron_apple) { create(:product, name: "Apple", supplier: aaron) } - let(:aaron_banana) { create(:product, name: "Banana", supplier: aaron) } - let(:zed_apple) { create(:product, name: "Apple", supplier: zed) } - let(:zed_banana) { create(:product, name: "Banana", supplier: zed) } + let(:aaron_apple) { create(:product, name: "Apple", supplier_id: aaron.id) } + let(:aaron_banana) { create(:product, name: "Banana", supplier_id: aaron.id) } + let(:zed_apple) { create(:product, name: "Apple", supplier_id: zed.id) } + let(:zed_banana) { create(:product, name: "Banana", supplier_id: zed.id) } let(:distributor) { create(:distributor_enterprise) } let(:order) do diff --git a/spec/models/spree/product_spec.rb b/spec/models/spree/product_spec.rb index 26733c8759..385588538d 100644 --- a/spec/models/spree/product_spec.rb +++ b/spec/models/spree/product_spec.rb @@ -84,65 +84,6 @@ module Spree end end - describe "supplier properties" do - subject { create(:product) } - - it "has no supplier properties to start with" do - expect(subject.supplier_properties).to eq [] - end - - it "doesn't include product properties" do - subject.set_property("certified", "organic") - expect(subject.supplier_properties).to eq [] - end - - it "includes the supplier's properties" do - subject.supplier.set_producer_property("certified", "yes") - expect(subject.supplier_properties.map(&:presentation)).to eq ["certified"] - end - end - - describe ".with_properties scope" do - let!(:product_without_wanted_property_on_supplier) { - create(:product, supplier: supplier_without_wanted_property) - } - let!(:product_with_wanted_property_on_supplier) { - create(:product, supplier: supplier_with_wanted_property) - } - let!(:product_with_wanted_property) { create(:product, properties: [wanted_property]) } - let!(:product_without_wanted_property_property) { - create(:product, properties: [unwanted_property]) - } - let!(:product_with_wanted_property_and_on_supplier) { - create(:product, properties: [wanted_property], supplier: supplier_with_wanted_property) - } - let!(:product_ignoring_property) { - create(:product, supplier: supplier_with_wanted_property, inherits_properties: false) - } - let(:supplier_with_wanted_property) { - create(:supplier_enterprise, properties: [wanted_property]) - } - let(:supplier_without_wanted_property) { - create(:supplier_enterprise, properties: [unwanted_property]) - } - let(:wanted_property) { create(:property, presentation: 'Certified Organic') } - let(:unwanted_property) { create(:property, presentation: 'Latest Hype') } - - it "returns no products without a property id" do - expect(Spree::Product.with_properties([])).to eq [] - end - - it "returns only products with the wanted property set both on supplier & product itself" do - expect( - Spree::Product.with_properties([wanted_property.id]) - ).to match_array [ - product_with_wanted_property_on_supplier, - product_with_wanted_property, - product_with_wanted_property_and_on_supplier - ] - end - end - describe '#total_on_hand' do it 'returns sum of stock items count_on_hand' do product = build(:product) @@ -163,7 +104,14 @@ module Spree end describe "associations" do - it { is_expected.to belong_to(:supplier).required } + it { is_expected.to have_one(:image) } + + it { is_expected.to have_many(:product_properties) } + it { is_expected.to have_many(:properties).through(:product_properties) } + it { is_expected.to have_many(:variants) } + it { is_expected.to have_many(:prices).through(:variants) } + it { is_expected.to have_many(:stock_items).through(:variants) } + it { is_expected.to have_many(:variant_images).through(:variants) } end describe "validations and defaults" do @@ -196,10 +144,6 @@ module Spree end end - it "requires a supplier" do - expect(build(:simple_product, supplier: nil)).not_to be_valid - end - context "when the product has variants" do let(:product) do product = create(:simple_product) @@ -207,6 +151,8 @@ module Spree product.reload end + it { is_expected.to validate_numericality_of(:price).is_greater_than_or_equal_to(0) } + it "requires a unit" do product.variant_unit = nil expect(product).not_to be_valid @@ -234,26 +180,29 @@ module Spree let!(:product){ Spree::Product.new } let!(:shipping_category){ create(:shipping_category) } let!(:taxon){ create(:taxon) } + let(:supplier){ create(:enterprise) } before do create(:stock_location) product.primary_taxon_id = taxon.id - product.supplier = create(:supplier_enterprise) product.name = "Product1" product.variant_unit = "weight" product.variant_unit_scale = 1000 product.unit_value = 1 product.price = 4.27 product.shipping_category_id = shipping_category.id + product.supplier_id = supplier.id product.save! end it "copies properties to the first standard variant" do expect(product.variants.reload.length).to eq 1 standard_variant = product.variants.reload.first + expect(standard_variant).to be_valid 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 + expect(standard_variant.supplier).to eq supplier end end @@ -329,28 +278,57 @@ module Spree describe "callbacks" do let(:product) { create(:simple_product) } - describe "touching affected enterprises when the product is deleted" do - let(:product) { create(:simple_product, supplier: distributor) } - let(:supplier) { product.supplier } + describe "destroy product" do + let(:product) { create(:simple_product, supplier_id: distributor.id) } let(:distributor) { create(:distributor_enterprise) } let!(:oc) { create(:simple_order_cycle, distributors: [distributor], variants: [product.variants.first]) } - it "touches the supplier" do - expect { product.destroy }.to change { supplier.reload.updated_at } - end - - it "touches all distributors" do - expect { product.destroy }.to change { distributor.reload.updated_at } - end - it "removes variants from order cycles" do expect { product.destroy }.to change { ExchangeVariant.count } end end + describe "after updating primary taxon" do + let(:product) { create(:simple_product, supplier_id: supplier.id) } + let(:supplier) { create(:supplier_enterprise) } + let(:new_taxon) { create(:taxon) } + + it "touches the supplier" do + expect { product.update(primary_taxon_id: new_taxon.id) } + .to change { supplier.reload.updated_at } + end + + context "when product has no variant" do + it "doesn't blow up" do + product.variants = [] + product.save! + + expect { product.update(primary_taxon_id: new_taxon.id) }.not_to raise_error + end + end + end + + describe "after touching the product" do + let(:product) { create(:simple_product, supplier_id: supplier.id) } + let(:supplier) { create(:supplier_enterprise) } + + it "touches the supplier" do + expect { product.touch } + .to change { supplier.reload.updated_at } + end + + context "when the first variant is missing supplier" do + it "doesn't blow up" do + product.variants.first.update_attribute(:supplier_id, nil) + + expect { product.touch }.not_to raise_error + end + end + end + it "updates units when saved change to variant unit" do product.variant_unit = 'items' product.variant_unit_scale = nil @@ -377,12 +355,39 @@ module Spree end describe "scopes" do + describe ".with_properties" do + let!(:product_with_wanted_property) { create(:product, properties: [wanted_property]) } + let!(:product_without_wanted_property_property) { + create(:product, properties: [unwanted_property]) + } + let!(:product_ignoring_property) { + create(:product, inherits_properties: false) + } + let(:wanted_property) { create(:property, presentation: 'Certified Organic') } + let(:unwanted_property) { create(:property, presentation: 'Latest Hype') } + + it "returns no products without a property id" do + expect(Spree::Product.with_properties([])).to eq [] + end + + it "returns only products with the wanted property set both on supplier & product itself" do + expect( + Spree::Product.with_properties([wanted_property.id, 99_999]) + ).to match_array [product_with_wanted_property] + end + end + describe "in_supplier" do it "shows products in supplier" do s1 = create(:supplier_enterprise) + p1 = create(:product, supplier_id: s1.id) + # We create two variants to let us test we don't get duplicated product + create(:variant, product: p1, supplier: s1) + create(:variant, product: p1, supplier: s1) s2 = create(:supplier_enterprise) - p1 = create(:product, supplier: s1) - p2 = create(:product, supplier: s2) + p2 = create(:product, supplier_id: s2.id) + create(:variant, product: p2, supplier: s2) + expect(Product.in_supplier(s1)).to eq([p1]) end end @@ -458,38 +463,6 @@ module Spree end end - describe "in_supplier_or_distributor" do - it "shows products in supplier" do - s1 = create(:supplier_enterprise) - s2 = create(:supplier_enterprise) - p1 = create(:product, supplier: s1) - p2 = create(:product, supplier: s2) - expect(Product.in_supplier_or_distributor(s1)).to eq([p1]) - end - - it "shows products in order cycle distribution" do - s = create(:supplier_enterprise) - d1 = create(:distributor_enterprise) - d2 = create(:distributor_enterprise) - p1 = create(:product) - p2 = create(:product) - create(:simple_order_cycle, suppliers: [s], distributors: [d1], - variants: [p1.variants.first]) - create(:simple_order_cycle, suppliers: [s], distributors: [d2], - variants: [p2.variants.first]) - expect(Product.in_supplier_or_distributor(d1)).to eq([p1]) - end - - it "shows products in all three without duplicates" do - s = create(:supplier_enterprise) - d = create(:distributor_enterprise) - p = create(:product, supplier: s) - create(:simple_order_cycle, suppliers: [s], distributors: [d], - variants: [p.variants.first]) - [s, d].each { |e| expect(Product.in_supplier_or_distributor(e)).to eq([p]) } - end - end - describe "in_order_cycle" do it "shows products in order cycle distribution" do s = create(:supplier_enterprise) @@ -523,31 +496,50 @@ module Spree end end - describe "access roles" do + describe "by_producer" do + it "orders by producer name" do + producer_z = create(:enterprise, name: "z_cooperative") + producer_a = create(:enterprise, name: "a_cooperative") + producer_g = create(:enterprise, name: "g_cooperative") + + product1 = create(:product, supplier_id: producer_z.id) + product2 = create(:product, supplier_id: producer_a.id) + product3 = create(:product, supplier_id: producer_g.id) + + expect(Product.by_producer).to eq([product2, product3, product1]) + end + end + + describe "managed_by" do + let!(:e1) { create(:enterprise) } + let!(:e2) { create(:enterprise) } + let!(:p1) { create(:product) } + let!(:p2) { create(:product) } + before(:each) do - @e1 = create(:enterprise) - @e2 = create(:enterprise) - @p1 = create(:product, supplier: @e1) - @p2 = create(:product, supplier: @e2) + create(:variant, product: p1, supplier: e1) + create(:variant, product: p1, supplier: e2) end it "shows only products for given user" do user = create(:user) user.spree_roles = [] - @e1.enterprise_roles.build(user:).save + e1.enterprise_roles.build(user:).save product = Product.managed_by user + expect(product.count).to eq(1) - expect(product).to include @p1 + expect(product).to include p1 end it "shows all products for admin user" do user = create(:admin_user) product = Product.managed_by user + expect(product.count).to eq(2) - expect(product).to include @p1 - expect(product).to include @p2 + expect(product).to include p1 + expect(product).to include p2 end end @@ -579,28 +571,6 @@ module Spree end end - describe 'stockable_by' do - let(:shop) { create(:distributor_enterprise) } - let(:add_to_oc_producer) { create(:supplier_enterprise) } - let(:other_producer) { create(:supplier_enterprise) } - let!(:p1) { create(:simple_product, supplier: shop ) } - let!(:p2) { create(:simple_product, supplier: add_to_oc_producer ) } - let!(:p3) { create(:simple_product, supplier: other_producer ) } - - before do - create(:enterprise_relationship, parent: add_to_oc_producer, child: shop, - permissions_list: [:add_to_order_cycle]) - create(:enterprise_relationship, parent: other_producer, child: shop, - permissions_list: [:manage_products]) - end - - it 'shows products produced by the enterprise and any producers granting P-OC' do - stockable_products = Spree::Product.stockable_by(shop) - expect(stockable_products).to include p1, p2 - expect(stockable_products).not_to include p3 - end - end - describe "imported_on" do let!(:v1) { create(:variant, import_date: 1.day.ago) } let!(:v2) { create(:variant, import_date: 2.days.ago) } @@ -613,59 +583,54 @@ module Spree end end - describe "properties" do + describe "#properties_including_inherited" do + let(:product) { create(:simple_product) } + let(:supplier) { create(:supplier_enterprise) } + + before do + product.variants = [] + product.variants << create(:variant, product:, supplier:) + end + it "returns product properties as a hash" do - product = create(:simple_product) product.set_property 'Organic Certified', 'NASAA 12345' property = product.properties.last expect(product.properties_including_inherited) - .to eq([{ id: property.id, name: "Organic Certified", - value: 'NASAA 12345' }]) + .to eq([{ id: property.id, name: "Organic Certified", value: 'NASAA 12345' }]) end it "returns producer properties as a hash" do - supplier = create(:supplier_enterprise) - product = create(:simple_product, supplier:) - supplier.set_producer_property 'Organic Certified', 'NASAA 54321' property = supplier.properties.last expect(product.properties_including_inherited) - .to eq([{ id: property.id, name: "Organic Certified", - value: 'NASAA 54321' }]) + .to eq([{ id: property.id, name: "Organic Certified", value: 'NASAA 54321' }]) end it "overrides producer properties with product properties" do - supplier = create(:supplier_enterprise) - product = create(:simple_product, supplier:) - product.set_property 'Organic Certified', 'NASAA 12345' supplier.set_producer_property 'Organic Certified', 'NASAA 54321' property = product.properties.last expect(product.properties_including_inherited) - .to eq([{ id: property.id, name: "Organic Certified", - value: 'NASAA 12345' }]) + .to eq([{ id: property.id, name: "Organic Certified", value: 'NASAA 12345' }]) end context "when product has an inherit_properties value set to true" do - let(:supplier) { create(:supplier_enterprise) } - let(:product) { create(:simple_product, supplier:, inherits_properties: true) } + let(:product) { create(:simple_product, inherits_properties: true) } it "inherits producer properties" do supplier.set_producer_property 'Organic Certified', 'NASAA 54321' property = supplier.properties.last expect(product.properties_including_inherited) - .to eq([{ id: property.id, name: "Organic Certified", - value: 'NASAA 54321' }]) + .to eq([{ id: property.id, name: "Organic Certified", value: 'NASAA 54321' }]) end end context "when product has an inherit_properties value set to false" do - let(:supplier) { create(:supplier_enterprise) } - let(:product) { create(:simple_product, supplier:, inherits_properties: false) } + let(:product) { create(:simple_product, inherits_properties: false) } it "does not inherit producer properties" do supplier.set_producer_property 'Organic Certified', 'NASAA 54321' @@ -675,9 +640,6 @@ module Spree end it "sorts by position" do - supplier = create(:supplier_enterprise) - product = create(:simple_product, supplier:) - pa = Spree::Property.create! name: 'A', presentation: 'A' pb = Spree::Property.create! name: 'B', presentation: 'B' pc = Spree::Property.create! name: 'C', presentation: 'C' @@ -743,18 +705,25 @@ module Spree end describe "deletion" do - let(:p) { create(:simple_product) } - let(:v) { create(:variant, product: p) } - let(:oc) { create(:simple_order_cycle) } - let(:s) { create(:supplier_enterprise) } - let(:e) { - create(:exchange, order_cycle: oc, incoming: true, sender: s, receiver: oc.coordinator) + let(:product) { create(:simple_product) } + let(:variant) { create(:variant, product:) } + let(:order_cycle) { create(:simple_order_cycle) } + let(:supplier) { create(:supplier_enterprise) } + let(:exchange) { + create( + :exchange, + order_cycle:, + incoming: true, + sender: supplier, + receiver: order_cycle.coordinator + ) } it "removes all variants from order cycles" do - e.variants << v - p.destroy - expect(e.variants.reload).to be_empty + exchange.variants << variant + + product.destroy + expect(exchange.variants.reload).to be_empty end end diff --git a/spec/models/spree/shipment_spec.rb b/spec/models/spree/shipment_spec.rb index 4b4f70318c..a559d8b855 100644 --- a/spec/models/spree/shipment_spec.rb +++ b/spec/models/spree/shipment_spec.rb @@ -65,8 +65,8 @@ RSpec.describe Spree::Shipment do end describe "with soft-deleted products or variants" do - let!(:product) { create(:product) } - let!(:order) { create(:order, distributor: product.supplier) } + let(:variant) { create(:variant) } + let(:order) { create(:order, distributor: variant.supplier) } context "when the variant is soft-deleted" do it "can still access the variant" do @@ -79,7 +79,7 @@ RSpec.describe Spree::Shipment do context "when the product is soft-deleted" do it "can still access the variant" do - order.line_items.first.variant.delete + order.line_items.first.product.delete variants = shipment.reload.manifest.map(&:variant) expect(variants).to eq [order.line_items.first.variant] diff --git a/spec/models/spree/taxon_spec.rb b/spec/models/spree/taxon_spec.rb index 34bdda4395..49797f69f4 100644 --- a/spec/models/spree/taxon_spec.rb +++ b/spec/models/spree/taxon_spec.rb @@ -11,7 +11,9 @@ module Spree let(:t2) { create(:taxon) } describe "finding all supplied taxons" do - let!(:p1) { create(:simple_product, supplier: e, primary_taxon_id: t1.id) } + let!(:p1) { + create(:simple_product, primary_taxon_id: t1.id, supplier_id: e.id) + } it "finds taxons" do expect(Taxon.supplied_taxons).to eq(e.id => Set.new([t1.id])) @@ -40,7 +42,7 @@ module Spree describe "touches" do let!(:taxon1) { create(:taxon) } let!(:taxon2) { create(:taxon) } - let!(:product) { create(:simple_product, primary_taxon: taxon1) } + let!(:product) { create(:simple_product, primary_taxon_id: taxon1.id) } let(:variant) { product.variants.first } it "is touched when assignment of primary_taxon on a variant changes" do diff --git a/spec/models/spree/variant_spec.rb b/spec/models/spree/variant_spec.rb index 3cea5ba96f..ef1f22fcdf 100644 --- a/spec/models/spree/variant_spec.rb +++ b/spec/models/spree/variant_spec.rb @@ -7,7 +7,44 @@ RSpec.describe Spree::Variant do subject(:variant) { build(:variant) } it { is_expected.to have_many :semantic_links } + it { is_expected.to belong_to(:product).required } + it { is_expected.to belong_to(:supplier).required } + it { is_expected.to have_many(:inventory_units) } + it { is_expected.to have_many(:line_items) } + it { is_expected.to have_many(:stock_items) } + it { is_expected.to have_many(:stock_locations).through(:stock_items) } + it { is_expected.to have_many(:images) } + it { is_expected.to have_one(:default_price) } + it { is_expected.to have_many(:prices) } + it { is_expected.to have_many(:exchange_variants) } + it { is_expected.to have_many(:exchanges).through(:exchange_variants) } + it { is_expected.to have_many(:variant_overrides) } + it { is_expected.to have_many(:inventory_items) } + it { is_expected.to have_many(:supplier_properties).through(:supplier) } + describe "shipping category" do + it "sets a shipping category if none provided" do + variant = build(:variant, shipping_category: nil) + + expect(variant).to be_valid + expect(variant.shipping_category).not_to be_nil + end + end + + describe "supplier properties" do + subject { create(:variant) } + + it "has no supplier properties to start with" do + expect(subject.supplier_properties).to eq [] + end + + it "includes the supplier's properties" do + subject.supplier.set_producer_property("certified", "yes") + expect(subject.supplier_properties.map(&:presentation)).to eq ["certified"] + end + end + + # add test for the other validation context "validations" do it "should validate price is greater than 0" do variant.price = -1 @@ -266,6 +303,7 @@ RSpec.describe Spree::Variant do end describe "scopes" do + # TODO rename describer below with scope names describe "finding variants in a distributor" do let!(:d1) { create(:distributor_enterprise) } let!(:d2) { create(:distributor_enterprise) } @@ -410,27 +448,30 @@ RSpec.describe Spree::Variant do end end - describe 'stockable_by' do - let(:shop) { create(:distributor_enterprise) } - let(:add_to_oc_producer) { create(:supplier_enterprise) } - let(:other_producer) { create(:supplier_enterprise) } - let!(:v1) { create(:variant, product: create(:simple_product, supplier: shop ) ) } - let!(:v2) { - create(:variant, product: create(:simple_product, supplier: add_to_oc_producer ) ) + describe ".with_properties" do + let!(:variant_without_wanted_property_on_supplier) { + create(:variant, supplier: supplier_without_wanted_property) } - let!(:v3) { create(:variant, product: create(:simple_product, supplier: other_producer ) ) } + let!(:variant_with_wanted_property_on_supplier) { + create(:variant, supplier: supplier_with_wanted_property) + } + let(:supplier_with_wanted_property) { + create(:supplier_enterprise, properties: [wanted_property]) + } + let(:supplier_without_wanted_property) { + create(:supplier_enterprise, properties: [unwanted_property]) + } + let(:wanted_property) { create(:property, presentation: 'Certified Organic') } + let(:unwanted_property) { create(:property, presentation: 'Latest Hype') } - before do - create(:enterprise_relationship, parent: add_to_oc_producer, child: shop, - permissions_list: [:add_to_order_cycle]) - create(:enterprise_relationship, parent: other_producer, child: shop, - permissions_list: [:manage_products]) + it "returns no products without a property id" do + expect(Spree::Variant.with_properties([])).to eq [] end - it 'shows variants produced by the enterprise and any producers granting P-OC' do - stockable_variants = Spree::Variant.stockable_by(shop) - expect(stockable_variants).to include v1, v2 - expect(stockable_variants).not_to include v3 + it "returns only variants with the wanted property set on supplier" do + expect( + Spree::Variant.with_properties([wanted_property.id]) + ).to match_array [variant_with_wanted_property_on_supplier] end end end @@ -771,8 +812,8 @@ RSpec.describe Spree::Variant do let(:variant1) { create(:variant) } let(:variant2) { create(:variant) } let!(:order_cycle) do - enterprise1.supplied_products << variant1.product - enterprise2.supplied_products << variant2.product + enterprise1.supplied_variants << variant1 + enterprise2.supplied_variants << variant2 create( :simple_order_cycle, coordinator: enterprise1, @@ -789,11 +830,32 @@ RSpec.describe Spree::Variant do describe "destruction" do it "destroys exchange variants" do - v = create(:variant) - e = create(:exchange, variants: [v]) + variant = create(:variant) + exchange = create(:exchange, variants: [variant]) - v.destroy - expect(e.reload.variant_ids).to be_empty + variant.destroy + expect(exchange.reload.variant_ids).to be_empty + end + + it "touches the supplier" do + supplier = create(:supplier_enterprise, updated_at: 1.hour.ago) + variant = create(:variant, supplier:) + + expect { variant.destroy }.to change { supplier.reload.updated_at } + end + + it "touches distributors" do + variant = create(:variant) + updated_at = 1.hour.ago + distributor1 = create(:distributor_enterprise, updated_at:) + distributor2 = create(:distributor_enterprise, updated_at:) + + create(:simple_order_cycle, distributors: [distributor1], variants: [variant]) + create(:simple_order_cycle, distributors: [distributor2], variants: [variant]) + + expect { variant.destroy } + .to change { distributor1.reload.updated_at } + .and change { distributor2.reload.updated_at } end end diff --git a/spec/queries/product_scope_query_spec.rb b/spec/queries/product_scope_query_spec.rb index 197ec23aad..fb72a044a3 100755 --- a/spec/queries/product_scope_query_spec.rb +++ b/spec/queries/product_scope_query_spec.rb @@ -6,14 +6,14 @@ RSpec.describe ProductScopeQuery do let!(:taxon) { create(:taxon) } let(:supplier) { create(:supplier_enterprise) } let(:supplier2) { create(:supplier_enterprise) } - let!(:product) { create(:product, supplier:, primary_taxon: taxon) } - let!(:product2) { create(:product, supplier: supplier2, primary_taxon: taxon) } + let!(:product) { create(:product, supplier_id: supplier.id, primary_taxon: taxon) } + let!(:product2) { create(:product, supplier_id: supplier2.id, primary_taxon: taxon) } let!(:current_api_user) { supplier_enterprise_user(supplier) } before { current_api_user.enterprise_roles.create(enterprise: supplier2) } describe '#bulk_products' do - let!(:product3) { create(:product, supplier: supplier2) } + let!(:product3) { create(:product, supplier_id: supplier2.id) } it "returns a list of products" do expect(ProductScopeQuery.new(current_api_user, {}).bulk_products) @@ -22,7 +22,7 @@ RSpec.describe ProductScopeQuery do it "filters results by supplier" do subject = ProductScopeQuery - .new(current_api_user, { q: { supplier_id_eq: supplier.id } }).bulk_products + .new(current_api_user, { q: { variants_supplier_id_eq: supplier.id } }).bulk_products expect(subject).to include(product) expect(subject).not_to include(product2, product3) @@ -82,6 +82,32 @@ RSpec.describe ProductScopeQuery do end end + describe "#products_for_producers" do + subject(:query) { ProductScopeQuery.new(current_api_user, { page: 1, per_page: 20 }) } + + let(:hub) { create(:distributor_enterprise) } + let(:producer) { create(:supplier_enterprise) } + let!(:product3) { create(:product, supplier_id: producer.id) } + let!(:product4) { create(:product, supplier_id: producer.id) } + let!(:product5) { create(:product, supplier_id: supplier.id) } + let!(:er) { + create(:enterprise_relationship, parent: producer, child: hub, + permissions_list: [:create_variant_overrides]) + } + + before do + current_api_user.enterprise_roles.create(enterprise: hub) + end + + it "finds products by producer" do + # Add variants so we can check if we are not returning duplicate products + create(:variant, product: product3, supplier: producer) + create(:variant, product: product3, supplier: producer) + + expect(query.products_for_producers).to eq([product3, product4]) + end + end + private def supplier_enterprise_user(enterprise) diff --git a/spec/serializers/api/admin/product_serializer_spec.rb b/spec/serializers/api/admin/product_serializer_spec.rb index d2c0f25577..8cbb0a63ec 100644 --- a/spec/serializers/api/admin/product_serializer_spec.rb +++ b/spec/serializers/api/admin/product_serializer_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' RSpec.describe Api::Admin::ProductSerializer do - let(:product) { create(:simple_product) } + let(:product) { create(:simple_product, supplier_id: create(:supplier_enterprise).id) } let(:serializer) { described_class.new(product) } it "serializes a product" do diff --git a/spec/serializers/api/cached_enterprise_serializer_spec.rb b/spec/serializers/api/cached_enterprise_serializer_spec.rb index 584d365e73..c358870149 100644 --- a/spec/serializers/api/cached_enterprise_serializer_spec.rb +++ b/spec/serializers/api/cached_enterprise_serializer_spec.rb @@ -13,7 +13,7 @@ RSpec.describe Api::CachedEnterpriseSerializer do before do product = create(:product, properties: [property]) - enterprise.supplied_products << product + enterprise.supplied_variants << product.variants.first end context "when the enterprise is a producer" do @@ -56,7 +56,7 @@ RSpec.describe Api::CachedEnterpriseSerializer do before do product = create(:product, properties: [property]) - producer.supplied_products << product + producer.supplied_variants << product.variants.first create( :simple_order_cycle, diff --git a/spec/serializers/api/enterprise_shopfront_serializer_spec.rb b/spec/serializers/api/enterprise_shopfront_serializer_spec.rb index 766995c793..3f0dee41da 100644 --- a/spec/serializers/api/enterprise_shopfront_serializer_spec.rb +++ b/spec/serializers/api/enterprise_shopfront_serializer_spec.rb @@ -12,10 +12,10 @@ RSpec.describe Api::EnterpriseShopfrontSerializer do let!(:taxon1) { create(:taxon, name: 'Meat') } let!(:taxon2) { create(:taxon, name: 'Veg') } let!(:product) { - create(:product, supplier: producer, primary_taxon: taxon1 ) + create(:product, supplier_id: producer.id, primary_taxon: taxon1 ) } let!(:product2) { - create(:product, supplier: producer_hidden, primary_taxon: taxon2 ) + create(:product, supplier_id: producer_hidden.id, primary_taxon: taxon2 ) } let(:close_time) { 2.days.from_now } diff --git a/spec/serializers/api/product_serializer_spec.rb b/spec/serializers/api/product_serializer_spec.rb index af0b1f3bf4..73a6e952f9 100644 --- a/spec/serializers/api/product_serializer_spec.rb +++ b/spec/serializers/api/product_serializer_spec.rb @@ -28,7 +28,7 @@ RSpec.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, :image, :supplier + :properties_with_values, :variants, :image ] end diff --git a/spec/services/exchange_products_renderer_spec.rb b/spec/services/exchange_products_renderer_spec.rb index df0e482edc..dd64f43df2 100644 --- a/spec/services/exchange_products_renderer_spec.rb +++ b/spec/services/exchange_products_renderer_spec.rb @@ -14,7 +14,9 @@ RSpec.describe ExchangeProductsRenderer do it "loads products" do products = renderer.exchange_products(true, exchange.sender) - expect(products.first.supplier.name).to eq exchange.variants.first.product.supplier.name + expect(products.first.variants.first.supplier.name).to eq( + exchange.variants.first.supplier.name + ) end it "loads products in order" do @@ -31,10 +33,10 @@ RSpec.describe ExchangeProductsRenderer do it "loads products" do products = renderer.exchange_products(false, exchange.receiver) - suppliers = [exchange.variants[0].product.supplier.name, - exchange.variants[1].product.supplier.name] - expect(suppliers).to include products.first.supplier.name - expect(suppliers).to include products.second.supplier.name + suppliers = [exchange.variants[0].supplier.name, + exchange.variants[1].supplier.name] + expect(suppliers).to include products.first.variants.first.supplier.name + expect(suppliers).to include products.second.variants.first.supplier.name end it "loads products in order" do @@ -74,8 +76,8 @@ RSpec.describe ExchangeProductsRenderer do exchange = order_cycle.exchanges.incoming.first variants = renderer.exchange_variants(true, exchange.sender) - expect(variants.first.product.supplier.name) - .to eq exchange.variants.first.product.supplier.name + expect(variants.first.supplier.name) + .to eq exchange.variants.first.supplier.name end describe "when OC is showing only the coordinators inventory" do diff --git a/spec/services/order_cycles/distributed_products_service_spec.rb b/spec/services/order_cycles/distributed_products_service_spec.rb index 4427c48494..259955d41a 100644 --- a/spec/services/order_cycles/distributed_products_service_spec.rb +++ b/spec/services/order_cycles/distributed_products_service_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' RSpec.describe OrderCycles::DistributedProductsService do - describe "#products_relation" do + describe "#products_supplier_relation" do let(:distributor) { create(:distributor_enterprise) } let(:product) { create(:product) } let(:variant) { product.variants.first } @@ -12,10 +12,20 @@ RSpec.describe OrderCycles::DistributedProductsService do create(:simple_order_cycle, distributors: [distributor], variants: [variant]) end + it "returns de duplicated result" do + supplier = create(:supplier_enterprise) + variant.update(supplier: ) + create(:variant, product:, supplier: ) + expect( + described_class.new(distributor, order_cycle, customer).products_supplier_relation + ).to eq([product]) + end + describe "product distributed by distributor in the OC" do it "returns products" do - expect(described_class.new(distributor, order_cycle, - customer).products_relation).to eq([product]) + expect( + described_class.new(distributor, order_cycle, customer).products_supplier_relation + ).to eq([product]) end end @@ -29,8 +39,9 @@ RSpec.describe OrderCycles::DistributedProductsService do end it "does not return product" do - expect(described_class.new(distributor, order_cycle, - customer).products_relation).not_to include product + expect( + described_class.new(distributor, order_cycle, customer).products_supplier_relation + ).not_to include product end end @@ -41,22 +52,25 @@ RSpec.describe OrderCycles::DistributedProductsService do end it "does not return product" do - expect(described_class.new(distributor, order_cycle, - customer).products_relation).not_to include product + expect( + described_class.new(distributor, order_cycle, customer).products_supplier_relation + ).not_to include product end end describe "filtering products that are out of stock" do context "with regular variants" do it "returns product when variant is in stock" do - expect(described_class.new(distributor, order_cycle, - customer).products_relation).to include product + expect( + described_class.new(distributor, order_cycle, customer).products_supplier_relation + ).to include product end it "does not return product when variant is out of stock" do variant.update_attribute(:on_hand, 0) - expect(described_class.new(distributor, order_cycle, - customer).products_relation).not_to include product + expect( + described_class.new(distributor, order_cycle, customer).products_supplier_relation + ).not_to include product end end @@ -66,15 +80,17 @@ RSpec.describe OrderCycles::DistributedProductsService do } it "does not return product when an override is out of stock" do - expect(described_class.new(distributor, order_cycle, - customer).products_relation).not_to include product + expect( + described_class.new(distributor, order_cycle, customer).products_supplier_relation + ).not_to include product end it "returns product when an override is in stock" do variant.update_attribute(:on_hand, 0) override.update_attribute(:count_on_hand, 10) - expect(described_class.new(distributor, order_cycle, - customer).products_relation).to include product + expect( + described_class.new(distributor, order_cycle, customer).products_supplier_relation + ).to include product end end end diff --git a/spec/services/order_cycles/form_service_spec.rb b/spec/services/order_cycles/form_service_spec.rb index ca8c5e1b18..b0efb4fcee 100644 --- a/spec/services/order_cycles/form_service_spec.rb +++ b/spec/services/order_cycles/form_service_spec.rb @@ -138,7 +138,7 @@ RSpec.describe OrderCycles::FormService do let(:shipping_method) { create(:shipping_method, distributors: [distributor]) } let(:distributor_payment_method) { payment_method.distributor_payment_methods.first } let(:distributor_shipping_method) { shipping_method.distributor_shipping_methods.first } - let(:variant) { create(:variant, product: create(:product, supplier:)) } + let(:variant) { create(:variant, supplier:) } let(:params) { { name: 'Some new name' } } let(:form) { OrderCycles::FormService.new(order_cycle, params, user) } let(:outgoing_exchange_params) do diff --git a/spec/services/orders/compare_invoice_service_spec.rb b/spec/services/orders/compare_invoice_service_spec.rb index 19ad37422a..dd4757bd14 100644 --- a/spec/services/orders/compare_invoice_service_spec.rb +++ b/spec/services/orders/compare_invoice_service_spec.rb @@ -136,7 +136,9 @@ end RSpec.shared_examples "associated attribute changes - line items" do |boolean, type| context "line item changes" do - let(:line_item){ order.line_items.first } + let(:line_item) { order.line_items.first } + let(:variant) { create(:variant) } + context "on quantitity" do before { line_item.update!(quantity: line_item.quantity + 1) } it "returns #{boolean} if a #{type} attribute changes" do @@ -146,7 +148,7 @@ RSpec.shared_examples "associated attribute changes - line items" do |boolean, t end context "on variant id" do - before { line_item.update!(variant_id: Spree::Variant.first.id) } + before { line_item.update!(variant_id: variant.id) } it "returns #{boolean} if a #{type} attribute changes" do order.reload expect(subject).to be boolean diff --git a/spec/services/permissions/order_spec.rb b/spec/services/permissions/order_spec.rb index 385b4a336a..9460445f7d 100644 --- a/spec/services/permissions/order_spec.rb +++ b/spec/services/permissions/order_spec.rb @@ -88,8 +88,8 @@ module Permissions context "which contains my products" do before do - line_item.product.supplier = producer - line_item.product.save + line_item.variant.supplier = producer + line_item.variant.save end it "should let me see the order" do @@ -165,8 +165,8 @@ module Permissions create(:enterprise_relationship, parent: producer, child: distributor, permissions_list: [:add_to_order_cycle]) - line_item1.product.supplier = producer - line_item1.product.save + line_item1.variant.supplier = producer + line_item1.variant.save end it "should let me see the line_items pertaining to variants I produce" do diff --git a/spec/services/product_tag_rules_filterer_spec.rb b/spec/services/product_tag_rules_filterer_spec.rb index eafd6d6d38..5fd88f7dfa 100644 --- a/spec/services/product_tag_rules_filterer_spec.rb +++ b/spec/services/product_tag_rules_filterer_spec.rb @@ -5,11 +5,11 @@ require "spec_helper" RSpec.describe ProductTagRulesFilterer do describe "filtering by tag rules" do let!(:distributor) { create(:distributor_enterprise) } - let(:product) { create(:product, supplier: distributor) } - let(:v1) { create(:variant, product:) } - let(:v2) { create(:variant, product:) } - let(:v3) { create(:variant, product:) } - let(:v4) { create(:variant, product:) } + let(:product) { create(:product, ) } + let(:v1) { create(:variant, product:, supplier: distributor) } + let(:v2) { create(:variant, product:, supplier: distributor) } + let(:v3) { create(:variant, product:, supplier: distributor) } + let(:v4) { create(:variant, product:, supplier: distributor) } let(:variant_hidden_by_default) { create(:variant_override, variant: v1, hub: distributor) } let(:variant_hidden_by_rule) { create(:variant_override, variant: v2, hub: distributor) } let(:variant_shown_by_rule) { create(:variant_override, variant: v3, hub: distributor) } @@ -18,7 +18,7 @@ RSpec.describe ProductTagRulesFilterer do } let(:customer) { create(:customer, enterprise: distributor) } let(:variants_relation) { - Spree::Variant.joins(:product).where(spree_products: { supplier_id: distributor.id }) + Spree::Variant.where(supplier: distributor.id) } let(:default_hide_rule) { create(:filter_products_tag_rule, diff --git a/spec/services/products_renderer_spec.rb b/spec/services/products_renderer_spec.rb index 0913c61ee9..4044b354e6 100644 --- a/spec/services/products_renderer_spec.rb +++ b/spec/services/products_renderer_spec.rb @@ -16,19 +16,19 @@ RSpec.describe ProductsRenderer do let(:cakes_supplier) { create(:supplier_enterprise) } let!(:product_apples) { create(:product, name: "apples", primary_taxon_id: fruits.id, - supplier_id: fruits_supplier.id) + supplier_id: fruits_supplier.id, inherits_properties: true) } let!(:product_banana_bread) { create(:product, name: "banana bread", primary_taxon_id: cakes.id, - supplier_id: cakes_supplier.id) + supplier_id: cakes_supplier.id, inherits_properties: true) } let!(:product_cherries) { create(:product, name: "cherries", primary_taxon_id: fruits.id, - supplier_id: fruits_supplier.id) + supplier_id: fruits_supplier.id, inherits_properties: true) } let!(:product_doughnuts) { create(:product, name: "doughnuts", primary_taxon_id: cakes.id, - supplier_id: cakes_supplier.id) + supplier_id: cakes_supplier.id, inherits_properties: true) } before do @@ -68,12 +68,17 @@ RSpec.describe ProductsRenderer do context "filtering" do it "filters products by name_or_meta_keywords_or_variants_display_as_or_" \ - "variants_display_name_or_supplier_name_cont" do - products_renderer = ProductsRenderer.new(distributor, order_cycle, customer, { q: { - "#{[:name, :meta_keywords, :variants_display_as, - :variants_display_name, :supplier_name] - .join('_or_')}_cont": "apples", - } }) + "variants_display_name_or_variants_supplier_name_cont" do + params = [:name, :meta_keywords, :variants_display_as, :variants_display_name, + :variants_supplier_name] + ransack_param = "#{params.join('_or_')}_cont" + products_renderer = ProductsRenderer.new( + distributor, + order_cycle, + customer, + { q: { "#{ransack_param}": "apples" } } + ) + products = products_renderer.send(:products) expect(products).to eq([product_apples]) end @@ -89,7 +94,7 @@ RSpec.describe ProductsRenderer do value: '1', position: 1 }) products_renderer = ProductsRenderer.new(distributor, order_cycle, customer, { q: { - with_properties: [property_organic.id] + with_properties: [property_organic.id, 999] } }) products = products_renderer.send(:products) expect(products).to eq([product_apples]) @@ -98,14 +103,68 @@ RSpec.describe ProductsRenderer do it "filters products with a producer property" do fruits_supplier.producer_properties.create!({ property_id: property_organic.id, value: '1', position: 1 }) - products_renderer = ProductsRenderer.new(distributor, order_cycle, customer, - { q: { - with_properties: [property_organic.id] - } }) + + search_param = { q: { "with_variants_supplier_properties" => [property_organic.id] } } + products_renderer = ProductsRenderer.new(distributor, order_cycle, customer, search_param) + products = products_renderer.send(:products) expect(products).to eq([product_apples, product_cherries]) end + # TODO this is a bit flaky due to banana bread having two supplier + it "filters products with a product property or a producer property" do + cakes_supplier.producer_properties.create!({ property_id: property_organic.id, + value: '1', position: 1 }) + product_apples.product_properties.create!({ property_id: property_conventional.id, + value: '1', position: 1 }) + + search_param = { q: + { + "with_variants_supplier_properties" => [property_organic.id], + "with_properties" => [property_conventional.id] + } } + products_renderer = ProductsRenderer.new(distributor, order_cycle, customer, search_param) + + products = products_renderer.send(:products) + expect(products).to eq([product_apples, product_banana_bread, product_doughnuts]) + end + + it "filters product with property and taxon set" do + stone_fruit = create(:taxon, name: "Stone fruit") + product_peach = + create(:product, name: "peach", primary_taxon_id: stone_fruit.id, + supplier_id: fruits_supplier.id, inherits_properties: true) + + fruits_supplier.producer_properties.create!({ property_id: property_organic.id, + value: '1', position: 1 }) + exchange.variants << product_peach.variants.first + + search_param = { q: + { + "with_variants_supplier_properties" => [property_organic.id], + "variants_primary_taxon_id_in_any" => [stone_fruit.id], + } } + + products_renderer = ProductsRenderer.new(distributor, order_cycle, customer, search_param) + + products = products_renderer.send(:products) + expect(products).to eq([product_peach]) + end + + it "filters out products with inherits_properties set to false" do + product_cherries.update!(inherits_properties: false) + product_banana_bread.update!(inherits_properties: false) + + fruits_supplier.producer_properties.create!({ property_id: property_organic.id, + value: '1', position: 1 }) + + search_param = { q: { "with_variants_supplier_properties" => [property_organic.id] } } + products_renderer = ProductsRenderer.new(distributor, order_cycle, customer, search_param) + + products = products_renderer.send(:products) + expect(products).to eq([product_apples]) + end + it "filters products with property when sorting is enabled" do allow(distributor).to receive(:preferred_shopfront_taxon_order) { "#{fruits.id},#{cakes.id}" diff --git a/spec/services/sets/product_set_spec.rb b/spec/services/sets/product_set_spec.rb index 3d2181c493..068b49a900 100644 --- a/spec/services/sets/product_set_spec.rb +++ b/spec/services/sets/product_set_spec.rb @@ -93,8 +93,6 @@ RSpec.describe Sets::ProductSet do end context "when the product is in an order cycle" do - let(:producer) { create(:enterprise) } - let(:distributor) { create(:distributor_enterprise) } let!(:order_cycle) { create(:simple_order_cycle, variants: [product.variants.first], @@ -117,22 +115,6 @@ RSpec.describe Sets::ProductSet do expect(order_cycle.distributed_variants).to include product.variants.first end end - - context 'and a different supplier is passed' do - let(:collection_hash) do - { 0 => { id: product.id, supplier_id: producer.id } } - end - - it 'updates the product and removes the product from order cycles' do - expect { - product_set.save - product.reload - }.to change { product.supplier }.to(producer). - and change { order_cycle.distributed_variants.count }.by(-1) - - expect(order_cycle.distributed_variants).not_to include product.variants.first - end - end end context "when product attributes are not changed" do @@ -158,7 +140,7 @@ RSpec.describe Sets::ProductSet do end describe "updating a product's variants" do - let(:product) { create(:simple_product) } + let(:product) { create(:simple_product, supplier_id: create(:supplier_enterprise).id) } let(:variant) { product.variants.first } let(:product_attributes) { {} } let(:variant_attributes) { { sku: "var_sku" } } @@ -228,6 +210,41 @@ RSpec.describe Sets::ProductSet do end end + context "when the variant is in an order cycle" do + let(:distributor) { create(:distributor_enterprise) } + let!(:order_cycle) { + create(:simple_order_cycle, variants: [variant], + coordinator: distributor, + distributors: [distributor]) + } + let(:variant_attributes) { { display_name: "New season variant" } } + + it 'updates the variant and keeps it in order cycles' do + expect { + product_set.save + variant.reload + }.to change { variant.display_name }.to("New season variant"). + and change { order_cycle.distributed_variants.count }.by(0) + + expect(order_cycle.distributed_variants).to include variant + end + + context 'when supplier is updated' do + let(:producer) { create(:supplier_enterprise) } + let(:variant_attributes) { { supplier_id: producer.id } } + + it 'updates the variant and removes the variant from order cycles' do + expect { + product_set.save + variant.reload + }.to change { variant.supplier }.to(producer). + and change { order_cycle.distributed_variants.count }.by(-1) + + expect(order_cycle.distributed_variants).not_to include variant + end + end + end + context "when products attributes are also updated" do let(:product_attributes) { { sku: "prod_sku" } @@ -286,9 +303,11 @@ RSpec.describe Sets::ProductSet do let(:variants_attributes) { [ { id: product.variants.first.id.to_s }, # default variant unchanged - { sku: "new sku", price: "5.00", unit_value: "5" }, # omit ID for new variant + # omit ID for new variant + { sku: "new sku", price: "5.00", unit_value: "5", supplier_id: supplier.id }, ] } + let(:supplier) { create(:supplier_enterprise) } it "creates new variant" do expect { @@ -305,7 +324,8 @@ RSpec.describe Sets::ProductSet do let(:variants_attributes) { [ { id: product.variants.first.id.to_s }, # default variant unchanged - { sku: "new sku", unit_value: "blah" }, # price missing, unit_value should be number + # price missing, unit_value should be number + { sku: "new sku", unit_value: "blah", supplier_id: supplier.id }, ] } diff --git a/spec/services/variants_stock_levels_spec.rb b/spec/services/variants_stock_levels_spec.rb index 630fc2c2de..13b2056d96 100644 --- a/spec/services/variants_stock_levels_spec.rb +++ b/spec/services/variants_stock_levels_spec.rb @@ -53,7 +53,7 @@ RSpec.describe VariantsStockLevels do describe "when the variant has an override" do let!(:distributor) { create(:distributor_enterprise) } - let(:supplier) { variant_in_the_order.product.supplier } + let(:supplier) { variant_in_the_order.supplier } let!(:order_cycle) { create(:simple_order_cycle, suppliers: [supplier], distributors: [distributor], variants: [variant_in_the_order, variant_not_in_the_order]) diff --git a/spec/support/products_helper.rb b/spec/support/products_helper.rb index f9baececed..518aa45257 100644 --- a/spec/support/products_helper.rb +++ b/spec/support/products_helper.rb @@ -3,7 +3,7 @@ module ProductsHelper def create_products(amount) amount.times do |i| - create(:simple_product, name: "product #{i}", supplier: producer) + create(:simple_product, name: "product #{i}", supplier_id: producer.id) end end @@ -41,6 +41,13 @@ module ProductsHelper "tr:has(input[aria-label=Name][value='#{value}'])" end + # Selector for table row that has an input with a placeholder. + # Variant don't have display_name set, so we look for the input with placeholder matching the + # product's name to get the variant row + def row_containing_placeholder(value) + "tr:has(input[aria-label=Name][placeholder='#{value}'])" + end + # Wait for an element with the given CSS selector and class to be present def wait_for_class(selector, class_name) max_wait_time = Capybara.default_max_wait_time diff --git a/spec/support/request/shop_workflow.rb b/spec/support/request/shop_workflow.rb index 1b34f6ab23..3501e15ec9 100644 --- a/spec/support/request/shop_workflow.rb +++ b/spec/support/request/shop_workflow.rb @@ -109,7 +109,7 @@ module ShopWorkflow end def add_variant_to_order_cycle(exchange, variant) - ensure_supplier_exchange(exchange, variant.product.supplier) + ensure_supplier_exchange(exchange, variant.supplier) exchange.variants << variant end diff --git a/spec/system/admin/bulk_order_management_spec.rb b/spec/system/admin/bulk_order_management_spec.rb index 4b7eddffa7..e8af8337c9 100644 --- a/spec/system/admin/bulk_order_management_spec.rb +++ b/spec/system/admin/bulk_order_management_spec.rb @@ -174,13 +174,13 @@ RSpec.describe ' let!(:s1) { create(:supplier_enterprise) } let!(:s2) { create(:supplier_enterprise) } let!(:li1) { - create(:line_item_with_shipment, order: o1, product: create(:product, supplier: s1)) + create(:line_item_with_shipment, order: o1, variant: create(:variant, supplier: s1)) } let!(:li2) { - create(:line_item_with_shipment, order: o2, product: create(:product, supplier: s2)) + create(:line_item_with_shipment, order: o2, variant: create(:variant, supplier: s2)) } let!(:li3) { - create(:line_item_with_shipment, order: o2, product: create(:product, supplier: s2)) + create(:line_item_with_shipment, order: o2, variant: create(:variant, supplier: s2)) } before :each do @@ -195,7 +195,7 @@ RSpec.describe ' end it "by supplier name" do - fill_in "quick_filter", with: li1.product.supplier.name + fill_in "quick_filter", with: li1.variant.supplier.name page.find('.filter-actions .button.icon-search').click expect_line_items_results [li1], [li2, li3] @@ -327,8 +327,8 @@ RSpec.describe ' it "displays a column for producer" do expect(page).to have_selector "th.producer", text: "Producer" - expect(page).to have_selector "td.producer", text: li1.product.supplier.name - expect(page).to have_selector "td.producer", text: li2.product.supplier.name + expect(page).to have_selector "td.producer", text: li1.supplier.name + expect(page).to have_selector "td.producer", text: li2.supplier.name end it "displays a column for variant description, which shows only product name " \ @@ -586,10 +586,10 @@ RSpec.describe ' order_cycle: create(:simple_order_cycle) ) } let!(:li1) { - create(:line_item_with_shipment, order: o1, product: create(:product, supplier: s1) ) + create(:line_item_with_shipment, order: o1, variant: create(:variant, supplier: s1) ) } let!(:li2) { - create(:line_item_with_shipment, order: o1, product: create(:product, supplier: s2) ) + create(:line_item_with_shipment, order: o1, variant: create(:variant, supplier: s2) ) } before :each do @@ -743,8 +743,9 @@ RSpec.describe ' let!(:d2) { create(:distributor_enterprise) } let!(:oc1) { create(:simple_order_cycle, suppliers: [s1], distributors: [d1] ) } let!(:oc2) { create(:simple_order_cycle, suppliers: [s2], distributors: [d2] ) } - let!(:p1) { create(:product, supplier: s1) } - let!(:p2) { create(:product, supplier: s2) } + let!(:v1) { create(:variant, supplier: s1) } + let!(:v2) { create(:variant, supplier: s2) } + let!(:o1) { create(:order_with_distributor, state: 'complete', shipment_state: 'ready', completed_at: Time.zone.now, distributor: d1, @@ -755,8 +756,8 @@ RSpec.describe ' completed_at: Time.zone.now, distributor: d2, order_cycle: oc2 ) } - let!(:li1) { create(:line_item_with_shipment, order: o1, product: p1 ) } - let!(:li2) { create(:line_item_with_shipment, order: o2, product: p2 ) } + let!(:li1) { create(:line_item_with_shipment, order: o1, variant: v1 ) } + let!(:li2) { create(:line_item_with_shipment, order: o2, variant: v2 ) } before :each do visit_bulk_order_management @@ -1262,18 +1263,18 @@ RSpec.describe ' completed_at: Time.zone.now, distributor: d2 ) } let!(:line_item_distributed) { - create(:line_item_with_shipment, order: o1, product: create(:product, supplier: s1) ) + create(:line_item_with_shipment, order: o1, variant: create(:variant, supplier: s1) ) } let!(:line_item_not_distributed) { - create(:line_item_with_shipment, order: o2, product: create(:product, supplier: s1) ) + create(:line_item_with_shipment, order: o2, variant: create(:variant, supplier: s1) ) } before(:each) do - @enterprise_user = create(:user) - @enterprise_user.enterprise_roles.build(enterprise: s1).save - @enterprise_user.enterprise_roles.build(enterprise: d1).save + enterprise_user = create(:user) + enterprise_user.enterprise_roles.build(enterprise: s1).save + enterprise_user.enterprise_roles.build(enterprise: d1).save - login_as @enterprise_user + login_as enterprise_user end it "displays a Bulk Management Tab under the Orders item" do diff --git a/spec/system/admin/bulk_product_update_spec.rb b/spec/system/admin/bulk_product_update_spec.rb index b53ff6d819..1bf458bce2 100644 --- a/spec/system/admin/bulk_product_update_spec.rb +++ b/spec/system/admin/bulk_product_update_spec.rb @@ -30,10 +30,12 @@ RSpec.describe ' s1 = FactoryBot.create(:supplier_enterprise) s2 = FactoryBot.create(:supplier_enterprise) s3 = FactoryBot.create(:supplier_enterprise) - p1 = FactoryBot.create(:product, supplier: s2) - p2 = FactoryBot.create(:product, supplier: s3) + p1 = FactoryBot.create(:product, supplier_id: s2.id) + p2 = FactoryBot.create(:product, supplier_id: s3.id) visit spree.admin_products_path + # the supplier dropdown is on the variant row, so we expand all for the dropdown to be visible + click_expand_all expect(page).to have_select "producer_id", with_options: [s1.name, s2.name, s3.name], selected: s2.name @@ -214,7 +216,11 @@ RSpec.describe ' end context "creating new variants" do - let!(:product) { create(:product, variant_unit: 'weight', variant_unit_scale: 1000) } + let(:supplier) { create(:supplier_enterprise) } + let!(:new_supplier) { create(:supplier_enterprise) } + let!(:product) { + create(:product, variant_unit: 'weight', variant_unit_scale: 1000, supplier_id: supplier.id) + } before do login_as_admin @@ -242,6 +248,7 @@ RSpec.describe ' expect(page).to have_selector "tr.variant", count: 1 # When I fill out variant details and hit update + select new_supplier.name, from: 'producer_id' fill_in "variant_display_name", with: "Case of 12 Bottles" fill_in "variant_unit_value_with_description", with: "3 (12x250 mL bottles)" fill_in "variant_display_as", with: "Case" @@ -258,14 +265,15 @@ RSpec.describe ' expect(updated_variant.display_as).to eq "Case" expect(updated_variant.price).to eq 4.0 expect(updated_variant.on_hand).to eq 10 + expect(updated_variant.supplier).to eq new_supplier # Then I should see edit buttons for the new variant expect(page).to have_selector "a.edit-variant" end context "handle the 'on_demand' variant case creation" do - let(:v1) { create(:variant, product:, on_hand: 4) } - let(:v2) { create(:variant, product:, on_demand: true) } + let(:v1) { create(:variant, product:, on_hand: 4, supplier:) } + let(:v2) { create(:variant, product:, on_demand: true, supplier:) } before do product.variants << v1 @@ -281,6 +289,7 @@ RSpec.describe ' end within "tr#v_-1" do + select supplier.name, from: 'producer_id' fill_in "variant_unit_value_with_description", with: "120" fill_in "variant_price", with: "6.66" end @@ -330,7 +339,7 @@ RSpec.describe ' s2 = FactoryBot.create(:supplier_enterprise) t1 = FactoryBot.create(:taxon) t2 = FactoryBot.create(:taxon) - p = FactoryBot.create(:product, supplier: s1, variant_unit: 'volume', + p = FactoryBot.create(:product, supplier_id: s1.id, variant_unit: 'volume', variant_unit_scale: 1, primary_taxon: t2, sku: "OLD SKU") login_as_admin @@ -340,13 +349,11 @@ RSpec.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_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 fill_in "product_name", with: "Big Bag Of Potatoes" - select s2.name, from: 'producer_id' select "Weight (kg)", from: "variant_unit_with_scale" uncheck "inherits_properties" fill_in "product_sku", with: "NEW SKU" @@ -357,7 +364,6 @@ RSpec.describe ' p.reload expect(p.name).to eq "Big Bag Of Potatoes" - expect(p.supplier).to eq s2 expect(p.variant_unit).to eq "weight" expect(p.variant_unit_scale).to eq 1000 # Kg expect(p.inherits_properties).to be false @@ -387,7 +393,7 @@ RSpec.describe ' it "updating a product with variants" do s1 = create(:supplier_enterprise) s2 = create(:supplier_enterprise) - p = create(:product, supplier: s1, variant_unit: 'volume', variant_unit_scale: 0.001, + p = create(:product, supplier_id: s1.id, variant_unit: 'volume', variant_unit_scale: 0.001, price: 3.0, unit_value: 0.25, unit_description: '(bottle)' ) v = p.variants.first v.update_attribute(:sku, "VARIANTSKU") @@ -510,8 +516,8 @@ RSpec.describe ' it "updating when a filter has been applied" do s1 = create(:supplier_enterprise) s2 = create(:supplier_enterprise) - p1 = FactoryBot.create(:simple_product, name: "product1", supplier: s1) - p2 = FactoryBot.create(:simple_product, name: "product2", supplier: s2) + p1 = FactoryBot.create(:simple_product, name: "product1", supplier_id: s1.id) + p2 = FactoryBot.create(:simple_product, name: "product2", supplier_id: s2.id) login_as_admin visit spree.admin_products_path @@ -610,7 +616,7 @@ RSpec.describe ' expect(page).to have_selector "a.edit-product", count: 2 # Set a filter - select2_select p1.supplier.name, from: "producer_filter" + select2_select v1.supplier.name, from: "producer_filter" apply_filters within "tr#p_#{p1.id}" do @@ -619,7 +625,7 @@ RSpec.describe ' uri = URI.parse(current_url) expect("#{uri.path}?#{uri.query}").to eq spree.edit_admin_product_path( - v1.product.id, producerFilter: p1.supplier.id + v1.product.id, producerFilter: v1.supplier.id ) end @@ -647,7 +653,7 @@ RSpec.describe ' expect(page).to have_selector "a.edit-variant", count: 2 # Set a filter - select2_select p1.supplier.name, from: "producer_filter" + select2_select v1.supplier.name, from: "producer_filter" apply_filters within "tr#v_#{v1.id}" do @@ -656,7 +662,7 @@ RSpec.describe ' uri = URI.parse(current_url) expect("#{uri.path}?#{uri.query}").to eq spree.edit_admin_product_variant_path( - v1.product.id, v1.id, producerFilter: p1.supplier.id + v1.product.id, v1.id, producerFilter: v1.supplier.id ) end end @@ -666,6 +672,7 @@ RSpec.describe ' p1 = FactoryBot.create(:product, name: "P1") p2 = FactoryBot.create(:product, name: "P2") p3 = FactoryBot.create(:product, name: "P3") + p1_supplier = p1.variants.first.supplier login_as_admin visit spree.admin_products_path @@ -676,14 +683,16 @@ RSpec.describe ' find("a.clone-product").click end expect(page).to have_selector "a.clone-product", count: 4 + click_expand_all expect(page).to have_field "product_name", with: "COPY OF #{p1.name}" - expect(page).to have_select "producer_id", selected: p1.supplier.name.to_s + expect(page).to have_select "producer_id", selected: p1_supplier.name.to_s visit spree.admin_products_path + click_expand_all expect(page).to have_selector "a.clone-product", count: 4 expect(page).to have_field "product_name", with: "COPY OF #{p1.name}" - expect(page).to have_select "producer_id", selected: p1.supplier.name.to_s + expect(page).to have_select "producer_id", selected: p1_supplier.name.to_s end end end @@ -713,8 +722,8 @@ RSpec.describe ' it "displays basic filtering controls which filter the product list" do s1 = create(:supplier_enterprise) s2 = create(:supplier_enterprise) - p1 = FactoryBot.create(:simple_product, name: "product1", supplier: s1) - p2 = FactoryBot.create(:simple_product, name: "product2", supplier: s2) + p1 = FactoryBot.create(:simple_product, name: "product1", supplier_id: s1.id) + p2 = FactoryBot.create(:simple_product, name: "product2", supplier_id: s2.id) login_as_admin visit spree.admin_products_path @@ -753,13 +762,13 @@ RSpec.describe ' let(:supplier_permitted) { create(:supplier_enterprise, name: 'Supplier Permitted') } let(:distributor_managed) { create(:distributor_enterprise, name: 'Distributor Managed') } let(:distributor_unmanaged) { create(:distributor_enterprise, name: 'Distributor Unmanaged') } - let!(:product_supplied) { create(:product, supplier: supplier_managed1, price: 10.0) } - let!(:product_not_supplied) { create(:product, supplier: supplier_unmanaged) } + let!(:product_supplied) { create(:product, supplier_id: supplier_managed1.id, price: 10.0) } + let!(:product_not_supplied) { create(:product, supplier_id: supplier_unmanaged.id) } let!(:product_supplied_permitted) { - create(:product, name: 'Product Permitted', supplier: supplier_permitted, price: 10.0) + create(:product, name: 'Product Permitted', supplier_id: supplier_permitted.id, price: 10.0) } let(:product_supplied_inactive) { - create(:product, supplier: supplier_managed1, price: 10.0) + create(:product, supplier_id: supplier_managed1.id, price: 10.0) } let!(:supplier_permitted_relationship) do @@ -786,6 +795,7 @@ RSpec.describe ' it "shows only suppliers that I manage or have permission to" do visit spree.admin_products_path + click_expand_all expect(page) .to have_select( @@ -841,16 +851,16 @@ RSpec.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: supplier_permitted.name fill_in "product_name", with: "Big Bag Of Potatoes" - select supplier_managed2.name, from: 'producer_id' select "Weight (kg)", from: "variant_unit_with_scale" find("a.view-variants").click end within "#v_#{v.id}" do + expect(page).to have_select "producer_id", selected: supplier_permitted.name + select supplier_managed2.name, from: 'producer_id' fill_in "variant_price", with: "20" fill_in "variant_on_hand", with: "18" fill_in "variant_display_as", with: "Big Bag" @@ -862,9 +872,9 @@ RSpec.describe ' p.reload v.reload expect(p.name).to eq "Big Bag Of Potatoes" - expect(p.supplier).to eq supplier_managed2 expect(p.variant_unit).to eq "weight" expect(p.variant_unit_scale).to eq 1000 # Kg + expect(v.supplier).to eq supplier_managed2 expect(v.display_as).to eq "Big Bag" expect(v.price).to eq 20.0 expect(v.on_hand).to eq 18 @@ -922,4 +932,8 @@ RSpec.describe ' def apply_filters page.find('.button.icon-search').click end + + def click_expand_all + find("a", text: "EXPAND ALL").click + end end diff --git a/spec/system/admin/dfc_product_import_spec.rb b/spec/system/admin/dfc_product_import_spec.rb index f02f4b5565..dff70ff168 100644 --- a/spec/system/admin/dfc_product_import_spec.rb +++ b/spec/system/admin/dfc_product_import_spec.rb @@ -8,7 +8,7 @@ RSpec.describe "DFC Product Import" do let(:user) { create(:oidc_user, owned_enterprises: [enterprise]) } let(:enterprise) { create(:supplier_enterprise) } - let(:source_product) { create(:product, supplier: enterprise) } + let(:source_product) { create(:product, supplier_id: enterprise.id) } before do login_as user diff --git a/spec/system/admin/enterprises_spec.rb b/spec/system/admin/enterprises_spec.rb index 6f55ba071e..effdc83c2b 100644 --- a/spec/system/admin/enterprises_spec.rb +++ b/spec/system/admin/enterprises_spec.rb @@ -501,7 +501,7 @@ RSpec.describe ' orders_close_at: 2.days.from_now) } let(:product) { - create(:simple_product, supplier: supplier1, primary_taxon: taxon, + create(:simple_product, supplier_id: supplier1.id, primary_taxon: taxon, properties: [property], name: "Beans") } let(:variant) { product.variants.first } diff --git a/spec/system/admin/order_cycles/complex_editing_multiple_product_pages_spec.rb b/spec/system/admin/order_cycles/complex_editing_multiple_product_pages_spec.rb index 993ad50b65..fca4b55f11 100644 --- a/spec/system/admin/order_cycles/complex_editing_multiple_product_pages_spec.rb +++ b/spec/system/admin/order_cycles/complex_editing_multiple_product_pages_spec.rb @@ -13,7 +13,7 @@ RSpec.describe ' describe "editing an order cycle with multiple pages of products" do let(:order_cycle) { create(:order_cycle) } let(:supplier_enterprise) { order_cycle.exchanges.incoming.first.sender } - let!(:new_product) { create(:product, supplier: supplier_enterprise) } + let!(:new_product) { create(:product, supplier_id: supplier_enterprise.id) } before do stub_const("#{Api::V0::ExchangeProductsController}::DEFAULT_PER_PAGE", 1) diff --git a/spec/system/admin/order_cycles/simple_spec.rb b/spec/system/admin/order_cycles/simple_spec.rb index dd10959dd0..74f077de9b 100644 --- a/spec/system/admin/order_cycles/simple_spec.rb +++ b/spec/system/admin/order_cycles/simple_spec.rb @@ -146,10 +146,8 @@ RSpec.describe ' create(:payment_method, distributors: [distributor_managed, distributor_unmanaged, distributor_permitted]) } - let!(:product_managed) { create(:product, supplier: supplier_managed) } - let!(:variant_managed) { product_managed.variants.first } - let!(:product_permitted) { create(:product, supplier: supplier_permitted) } - let!(:variant_permitted) { product_permitted.variants.first } + let!(:variant_managed) { create(:variant, supplier: supplier_managed) } + let!(:variant_permitted) { create(:variant, supplier: supplier_permitted) } let!(:schedule) { create(:schedule, name: 'Schedule1', order_cycles: [ @@ -185,12 +183,12 @@ RSpec.describe ' context "that is a manager of the coordinator" do before do - @new_user = create(:user) - @new_user.enterprise_roles.build(enterprise: supplier_managed).save - @new_user.enterprise_roles.build(enterprise: distributor_managed).save - @new_user.enterprise_roles.build(enterprise: other_distributor_managed).save + new_user = create(:user) + new_user.enterprise_roles.build(enterprise: supplier_managed).save + new_user.enterprise_roles.build(enterprise: distributor_managed).save + new_user.enterprise_roles.build(enterprise: other_distributor_managed).save - login_as @new_user + login_as new_user end it "viewing a list of order cycles I am coordinating" do @@ -375,8 +373,7 @@ RSpec.describe ' distributors: [distributor_managed], name: 'Order Cycle 1' ) end - let(:product) { create(:product, supplier: supplier_managed) } - let(:v1) { create(:variant, product: ) } + let(:v1) { create(:variant, supplier: supplier_managed) } let(:inventory_item_v1) { create(:inventory_item, enterprise: distributor_managed, variant: v1, visible: false) } @@ -413,7 +410,7 @@ RSpec.describe ' # we need this assertion here to assure there is enough time to # toggle the variant box and evaluate the following assertion - expect(page).to have_content product.name + expect(page).to have_content v1.product.name end it "doesn't show a warning when going to 'outgoing products' tab" do @@ -432,7 +429,7 @@ RSpec.describe ' # we need this assertion here to assure there is enough time to # toggle the variant box and evaluate the following assertion - expect(page).to have_content product.name + expect(page).to have_content v1.product.name expect(page).not_to have_content "No variant available for this product" end @@ -469,8 +466,8 @@ RSpec.describe ' distributor_permitted, distributor_unmanaged ], name: 'Order Cycle 1') - v1 = create(:variant, product: create(:product, supplier: supplier_managed) ) - v2 = create(:variant, product: create(:product, supplier: supplier_managed) ) + v1 = create(:variant, supplier: supplier_managed) + v2 = create(:variant, supplier: supplier_managed) # Incoming exchange ex_in = oc.exchanges.where(sender_id: supplier_managed, receiver_id: distributor_managed, @@ -554,8 +551,8 @@ RSpec.describe ' distributor_permitted, distributor_unmanaged ], name: 'Order Cycle 1') - v1 = create(:variant, product: create(:product, supplier: supplier_managed) ) - v2 = create(:variant, product: create(:product, supplier: supplier_managed) ) + v1 = create(:variant, supplier: supplier_managed) + v2 = create(:variant, supplier: supplier_managed) # Incoming exchange ex_in = oc.exchanges.where(sender_id: supplier_managed, receiver_id: distributor_managed, @@ -620,9 +617,9 @@ RSpec.describe ' describe "simplified interface for enterprise users selling only their own produce" do let(:user) { create(:user) } let(:enterprise) { create(:enterprise, is_primary_producer: true, sells: 'own') } - let!(:p1) { create(:simple_product, supplier: enterprise) } - let!(:p2) { create(:simple_product, supplier: enterprise) } - let!(:p3) { create(:simple_product, supplier: enterprise) } + let!(:p1) { create(:product, supplier_id: enterprise.id) } + let!(:p2) { create(:product, supplier_id: enterprise.id) } + let!(:p3) { create(:product, supplier_id: enterprise.id) } let!(:v1) { p1.variants.first } let!(:v2) { p2.variants.first } let!(:v3) { p3.variants.first } diff --git a/spec/system/admin/product_import_spec.rb b/spec/system/admin/product_import_spec.rb index f7f37d87c3..85555f96bb 100644 --- a/spec/system/admin/product_import_spec.rb +++ b/spec/system/admin/product_import_spec.rb @@ -24,23 +24,26 @@ RSpec.describe "Product Import" do let!(:tax_category2) { create(:tax_category) } let!(:shipping_category) { create(:shipping_category) } - let!(:product) { create(:simple_product, supplier: enterprise2, name: 'Hypothetical Cake') } + let!(:product) { create(:simple_product, supplier_id: enterprise2.id, name: 'Hypothetical Cake') } let!(:variant) { create(:variant, product_id: product.id, price: '8.50', on_hand: 100, unit_value: '500', - display_name: 'Preexisting Banana') + display_name: 'Preexisting Banana', supplier: enterprise2) } let!(:product2) { - create(:simple_product, supplier: enterprise, on_hand: 100, name: 'Beans', unit_value: '500', - description: '', primary_taxon_id: category.id) + create(:simple_product, supplier_id: enterprise.id, on_hand: 100, name: 'Beans', + unit_value: '500', description: '', primary_taxon_id: category.id) } let!(:product3) { - create(:simple_product, supplier: enterprise, on_hand: 100, name: 'Sprouts', unit_value: '500') + create(:simple_product, supplier_id: enterprise.id, on_hand: 100, name: 'Sprouts', + unit_value: '500') } let!(:product4) { - create(:simple_product, supplier: enterprise, on_hand: 100, name: 'Cabbage', unit_value: '500') + create(:simple_product, supplier_id: enterprise.id, on_hand: 100, name: 'Cabbage', + unit_value: '500') } let!(:product5) { - create(:simple_product, supplier: enterprise2, on_hand: 100, name: 'Lettuce', unit_value: '500') + create(:simple_product, supplier_id: enterprise2.id, on_hand: 100, name: 'Lettuce', + unit_value: '500') } let!(:variant_override) { create(:variant_override, variant_id: product4.variants.first.id, hub: enterprise2, @@ -89,7 +92,7 @@ RSpec.describe "Product Import" do carrots = Spree::Product.find_by(name: 'Carrots') potatoes = Spree::Product.find_by(name: 'Potatoes') - expect(potatoes.supplier).to eq enterprise + expect(potatoes.variants.first.supplier).to eq enterprise expect(potatoes.on_hand).to eq 6 expect(potatoes.variants.first.price).to eq 6.50 expect(potatoes.variants.first.import_date).to be_within(1.minute).of Time.zone.now @@ -362,7 +365,7 @@ RSpec.describe "Product Import" do end it "handles a unit of kg for inventory import" do - product = create(:simple_product, supplier: enterprise, on_hand: 100, name: 'Beets', + product = create(:simple_product, supplier_id: enterprise.id, on_hand: 100, name: 'Beets', unit_value: '1000', variant_unit_scale: 1000) csv_data = <<~CSV name, distributor, producer, category, on_hand, price, unit_type, units, on_demand @@ -400,7 +403,7 @@ RSpec.describe "Product Import" do describe "Item type products" do let!(:product) { - create(:simple_product, supplier: enterprise, on_hand: nil, name: 'Aubergine', + create(:simple_product, supplier_id: enterprise.id, on_hand: nil, name: 'Aubergine', unit_value: '1', variant_unit_scale: nil, variant_unit: "items", variant_unit_name: "Bag") } diff --git a/spec/system/admin/products_spec.rb b/spec/system/admin/products_spec.rb index 753dd4e4ba..5a74e7972a 100644 --- a/spec/system/admin/products_spec.rb +++ b/spec/system/admin/products_spec.rb @@ -13,23 +13,18 @@ RSpec.describe ' let!(:taxon) { create(:taxon) } let!(:stock_location) { create(:stock_location, backorderable_default: false) } let!(:shipping_category) { DefaultShippingCategory.find_or_create } - - before do - @supplier = create(:supplier_enterprise, name: 'New supplier') - @distributors = (1..3).map { create(:distributor_enterprise) } - @enterprise_fees = (0..2).map { |i| create(:enterprise_fee, enterprise: @distributors[i]) } - end + let!(:supplier) { create(:supplier_enterprise, name: 'New supplier') } describe "creating a product" do let!(:tax_category) { create(:tax_category, name: 'Test Tax Category') } + before do + login_as_admin + visit spree.new_admin_product_path + end + it "display all attributes when submitting with error: no name" do - login_to_admin_section - - click_link 'Products' - click_link 'New Product' - - select @supplier.name, from: 'product_supplier_id' + select supplier.name, from: 'product_supplier_id' select "Weight (kg)", from: 'product_variant_unit_with_scale' fill_in 'product_unit_value', with: "5.00 g" assert_selector(:field, placeholder: "5kg g") @@ -44,7 +39,7 @@ RSpec.describe ' click_button 'Create' expect(page).to have_content "Name can't be blank" - expect(page).to have_field 'product_supplier_id', with: @supplier.id + expect(page).to have_field 'product_supplier_id', with: supplier.id expect(page).to have_field 'product_unit_value', with: "5.00 g" expect(page).to have_field 'product_display_as', with: "Big Box of Chocolates" expect(page).to have_field 'product_primary_taxon_id', with: taxon.id @@ -60,10 +55,6 @@ RSpec.describe ' end it "display all attributes when submitting with error: Unit Value must be grater than 0" do - login_to_admin_section - - visit spree.new_admin_product_path - select 'New supplier', from: 'product_supplier_id' fill_in 'product_name', with: "new product name" select "Weight (kg)", from: 'product_variant_unit_with_scale' @@ -80,7 +71,7 @@ RSpec.describe ' click_button 'Create' expect(page).to have_field 'product_name', with: "new product name" - expect(page).to have_field 'product_supplier_id', with: @supplier.id + expect(page).to have_field 'product_supplier_id', with: supplier.id expect(page).to have_field 'product_unit_value', with: "0 g" expect(page).to have_field 'product_display_as', with: "Big Box of Chocolates" expect(page).to have_field 'product_primary_taxon_id', with: taxon.id @@ -96,11 +87,6 @@ RSpec.describe ' end it "preserves 'Items' 'Unit Size' selection when submitting with error" do - login_to_admin_section - - click_link 'Products' - click_link 'New Product' - select "Items", from: 'product_variant_unit_with_scale' click_button 'Create' @@ -109,11 +95,6 @@ RSpec.describe ' end it "assigning important attributes" do - login_to_admin_section - - click_link 'Products' - click_link 'New Product' - expect(find_field('product_shipping_category_id').text).to eq(shipping_category.name) select 'New supplier', from: 'product_supplier_id' @@ -131,7 +112,6 @@ RSpec.describe ' expect(current_path).to eq spree.admin_products_path expect(flash_message).to eq('Product "A new product !!!" has been successfully created!') product = Spree::Product.find_by(name: 'A new product !!!') - expect(product.supplier).to eq(@supplier) expect(product.variant_unit).to eq('weight') expect(product.variant_unit_scale).to eq(1000) expect(product.variants.first.unit_value).to eq(5000) @@ -144,15 +124,13 @@ RSpec.describe ' expect(product.variants.first.shipping_category).to eq(shipping_category) expect(product.description).to eq("
A description...
") expect(product.group_buy).to be_falsey - expect(product.variants.first.unit_presentation).to eq("5kg") + + variant = product.variants.first + expect(variant.unit_presentation).to eq("5kg") + expect(variant.supplier).to eq(supplier) end it "creating an on-demand product" do - login_as_admin - visit spree.admin_products_path - - click_link 'New Product' - fill_in 'product_name', with: 'Hot Cakes' select 'New supplier', from: 'product_supplier_id' select "Weight (kg)", from: 'product_variant_unit_with_scale' @@ -175,11 +153,6 @@ RSpec.describe ' end it "creating product with empty unit value" do - login_as_admin - visit spree.admin_products_path - - click_link 'New Product' - fill_in 'product_name', with: 'Hot Cakes' select 'New supplier', from: 'product_supplier_id' select "Weight (kg)", from: 'product_variant_unit_with_scale' @@ -226,9 +199,6 @@ RSpec.describe ' context "when enable_localized_number is set to #{localized_number}" do before do allow(Spree::Config).to receive(:enable_localized_number?).and_return(localized_number) - login_as_admin - visit spree.admin_products_path - click_link 'New Product' end it "and price is #{price}" do @@ -284,11 +254,13 @@ RSpec.describe ' before { Flipper.disable(:admin_style_v3) } describe "deleting" do - let!(:product1) { create(:simple_product, name: 'a product to keep', supplier: @supplier) } + let!(:product1) { + create(:simple_product, name: 'a product to keep', supplier_id: supplier.id) + } context 'a simple product' do let!(:product2) { - create(:simple_product, name: 'a product to delete', supplier: @supplier) + create(:simple_product, name: 'a product to delete', supplier_id: supplier.id) } before do @@ -339,7 +311,7 @@ RSpec.describe ' describe 'cloning' do let!(:product1) { - create(:simple_product, name: 'a weight product', supplier: @supplier, + create(:simple_product, name: 'a weight product', supplier_id: supplier.id, variant_unit: "weight") } @@ -366,23 +338,22 @@ RSpec.describe ' let!(:tax_category) { create(:tax_category) } let(:filter) { { producerFilter: 2 } } let(:image_file_path) { Rails.root.join(file_fixture_path, "thinking-cat.jpg") } + let(:supplier2) { create(:supplier_enterprise, name: 'Another Supplier') } + let(:supplier_permitted) { create(:supplier_enterprise, name: 'Permitted Supplier') } + let(:new_user) { create(:user) } before do - @new_user = create(:user) - @supplier2 = create(:supplier_enterprise, name: 'Another Supplier') - @supplier_permitted = create(:supplier_enterprise, name: 'Permitted Supplier') - @new_user.enterprise_roles.build(enterprise: @supplier2).save - @new_user.enterprise_roles.build(enterprise: @distributors[0]).save - create(:enterprise_relationship, parent: @supplier_permitted, child: @supplier2, - permissions_list: [:manage_products]) + new_user.enterprise_roles.build(enterprise: supplier2).save - login_as @new_user + login_as new_user end context "products do not require a tax category" do it "creating a new product" do - visit spree.admin_products_path - click_link 'New Product' + create(:enterprise_relationship, parent: supplier_permitted, child: supplier2, + permissions_list: [:manage_products]) + + visit spree.new_admin_product_path fill_in 'product_name', with: 'A new product !!!' fill_in 'product_price', with: '19.99' @@ -396,377 +367,359 @@ RSpec.describe ' # Should only have suppliers listed which the user can manage expect(page).to have_select 'product_supplier_id', - with_options: [@supplier2.name, @supplier_permitted.name] - expect(page).not_to have_select 'product_supplier_id', with_options: [@supplier.name] + with_options: [supplier2.name, supplier_permitted.name] + expect(page).not_to have_select 'product_supplier_id', with_options: [supplier.name] click_button 'Create' expect(flash_message).to eq('Product "A new product !!!" has been successfully created!') product = Spree::Product.find_by(name: 'A new product !!!') - expect(product.supplier).to eq(@supplier2) - expect(product.variants.first.tax_category).to be_nil + variant = product.variants.first + expect(variant.tax_category).to be_nil + expect(variant.supplier).to eq(supplier2) end end - it "editing a product" do - product = create(:simple_product, name: 'a product', supplier: @supplier2) + describe "editing page" do + let!(:product) { create(:simple_product, name: 'a product', supplier_id: supplier2.id) } - visit spree.edit_admin_product_path product + it "editing a product" do + visit spree.edit_admin_product_path product - select 'Permitted Supplier', from: 'product_supplier_id' - click_button 'Update' - expect(flash_message).to eq('Product "a product" has been successfully updated!') - product.reload - expect(product.supplier).to eq(@supplier_permitted) - end + fill_in_trix_editor 'product_description', with: 'A description...' + click_button 'Update' + expect(flash_message).to eq('Product "a product" has been successfully updated!') + product.reload + expect(product.description).to eq("
A description...
") + end - it "editing a product comming from the bulk product update page with filter" do - product = create(:simple_product, name: 'a product', supplier: @supplier2) + it "editing a product comming from the bulk product update page with filter" do + visit spree.edit_admin_product_path(product, filter) - visit spree.edit_admin_product_path(product, filter) + click_button 'Update' + expect(flash_message).to eq('Product "a product" has been successfully updated!') - click_button 'Update' - expect(flash_message).to eq('Product "a product" has been successfully updated!') + # Check the url still includes the filters + uri = URI.parse(current_url) + expect("#{uri.path}?#{uri.query}").to eq spree.edit_admin_product_path(product, filter) - # Check the url still includes the filters - uri = URI.parse(current_url) - expect("#{uri.path}?#{uri.query}").to eq spree.edit_admin_product_path(product, filter) + # Link back to the bulk product update page should include the filters + expected_admin_product_url = + Regexp.new(Regexp.escape("#{spree.admin_products_path}#?#{filter.to_query}")) + expect(page).to have_link('Back to products list', + href: expected_admin_product_url) + expect(page).to have_link('Cancel', href: expected_admin_product_url) - # Link back to the bulk product update page should include the filters - expected_admin_product_url = - Regexp.new(Regexp.escape("#{spree.admin_products_path}#?#{filter.to_query}")) - expect(page).to have_link('Back to products list', - href: expected_admin_product_url) - expect(page).to have_link('Cancel', href: expected_admin_product_url) + expected_product_url = Regexp.new(Regexp.escape(spree.edit_admin_product_path( + product.id, filter + ))) + expect(page).to have_link('Product Details', + href: expected_product_url) - expected_product_url = Regexp.new(Regexp.escape(spree.edit_admin_product_path( - product.id, filter - ))) - expect(page).to have_link('Product Details', - href: expected_product_url) - - expected_product_image_url = Regexp.new(Regexp.escape(spree.admin_product_images_path( - product.id, filter - ))) - expect(page).to have_link('Images', - href: expected_product_image_url) - - expected_product_variant_url = Regexp.new(Regexp.escape(spree.admin_product_variants_path( + expected_product_image_url = Regexp.new(Regexp.escape(spree.admin_product_images_path( product.id, filter ))) - expect(page).to have_link('Variants', - href: expected_product_variant_url) + expect(page).to have_link('Images', + href: expected_product_image_url) - expected_product_properties_url = - Regexp.new(Regexp.escape(spree.admin_product_product_properties_path( - product.id, filter - ))) - expect(page).to have_link('Product Properties', - href: expected_product_properties_url) + expected_product_variant_url = Regexp.new(Regexp.escape(spree.admin_product_variants_path( + product.id, filter + ))) + expect(page).to have_link('Variants', + href: expected_product_variant_url) - expected_product_group_buy_option_url = - Regexp.new(Regexp.escape(spree.group_buy_options_admin_product_path( - product.id, filter - ))) - expect(page).to have_link('Group Buy Options', - href: expected_product_group_buy_option_url) + expected_product_properties_url = + Regexp.new(Regexp.escape(spree.admin_product_product_properties_path( + product.id, filter + ))) + expect(page).to have_link('Product Properties', + href: expected_product_properties_url) - expected_product_seo_url = Regexp.new(Regexp.escape(spree.seo_admin_product_path( - product.id, filter - ))) - expect(page).to have_link('Search', href: expected_product_seo_url) - end + expected_product_group_buy_option_url = + Regexp.new(Regexp.escape(spree.group_buy_options_admin_product_path( + product.id, filter + ))) + expect(page).to have_link('Group Buy Options', + href: expected_product_group_buy_option_url) - it "editing product group buy options" do - product = product = create(:simple_product, supplier: @supplier2) - - visit spree.edit_admin_product_path product - within('#sidebar') { click_link 'Group Buy Options' } - choose('product_group_buy_1') - fill_in 'Bulk unit size', with: '10' - - click_button 'Update' - - expect(flash_message).to eq("Product \"#{product.name}\" has been successfully updated!") - product.reload - expect(product.group_buy).to be true - expect(product.group_buy_unit_size).to eq(10.0) - end - - it "loading editing product group buy options with url filters" do - product = product = create(:simple_product, supplier: @supplier2) - - visit spree.group_buy_options_admin_product_path(product, filter) - - expected_cancel_link = Regexp.new(Regexp.escape(spree.edit_admin_product_path(product, - filter))) - expect(page).to have_link('Cancel', href: expected_cancel_link) - end - - it "editing product group buy options with url filter" do - product = product = create(:simple_product, supplier: @supplier2) - - visit spree.group_buy_options_admin_product_path(product, filter) - choose('product_group_buy_1') - fill_in 'Bulk unit size', with: '10' - - click_button 'Update' - - uri = URI.parse(current_url) - expect("#{uri.path}?#{uri.query}").to eq spree.edit_admin_product_path(product, filter) - end - - it "editing product Search" do - product = create(:simple_product, supplier: @supplier2) - visit spree.edit_admin_product_path product - within('#sidebar') { click_link 'Search' } - fill_in 'Product Search Keywords', with: 'Product Search Keywords' - fill_in 'Notes', with: 'Just testing Notes' - click_button 'Update' - expect(flash_message).to eq("Product \"#{product.name}\" has been successfully updated!") - product.reload - expect(product.notes).to eq('Just testing Notes') - expect(product.meta_keywords).to eq('Product Search Keywords') - end - - it "loading editing product Search with url filters" do - product = create(:simple_product, supplier: @supplier2) - - visit spree.seo_admin_product_path(product, filter) - - expected_cancel_link = Regexp.new(Regexp.escape(spree.edit_admin_product_path(product, - filter))) - expect(page).to have_link('Cancel', href: expected_cancel_link) - end - - it "editing product Search with url filter" do - product = create(:simple_product, supplier: @supplier2) - - visit spree.seo_admin_product_path(product, filter) - - fill_in 'Product Search Keywords', with: 'Product Search Keywords' - fill_in 'Notes', with: 'Just testing Notes' - - click_button 'Update' - - uri = URI.parse(current_url) - expect("#{uri.path}?#{uri.query}").to eq spree.edit_admin_product_path(product, filter) - end - - it "loading product properties page including url filters" do - product = create(:simple_product, supplier: @supplier2) - visit spree.admin_product_product_properties_path(product, filter) - - uri = URI.parse(current_url) - # we stay on the same url as the new image content is loaded via an ajax call - expect("#{uri.path}?#{uri.query}").to eq spree.admin_product_product_properties_path(product, - filter) - - expected_cancel_link = Regexp.new(Regexp.escape(spree.admin_product_product_properties_path( - product, filter - ))) - expect(page).to have_link('Cancel', href: expected_cancel_link) - end - - it "deleting product properties" do - # Given a product with a property - product = create(:simple_product, supplier: @supplier2) - product.set_property('fooprop', 'fooval') - - # When I navigate to the product properties page - visit spree.admin_product_product_properties_path(product) - expect(page).to have_select2 'product_product_properties_attributes_0_property_name', - selected: 'fooprop' - expect(page).to have_field 'product_product_properties_attributes_0_value', with: 'fooval' - - # And I delete the property - accept_alert do - page.all('a.delete-resource').first.click - end - click_button 'Update' - - # Then the property should have been deleted - expect(page).not_to have_field 'product_product_properties_attributes_0_property_name', - with: 'fooprop' - expect(page).not_to have_field 'product_product_properties_attributes_0_value', with: 'fooval' - expect(product.reload.property('fooprop')).to be_nil - end - - it "deleting product properties including url filters" do - # Given a product with a property - product = create(:simple_product, supplier: @supplier2) - product.set_property('fooprop', 'fooval') - - # When I navigate to the product properties page - visit spree.admin_product_product_properties_path(product, filter) - - # And I delete the property - accept_alert do - page.all('a.delete-resource').first.click + expected_product_seo_url = Regexp.new(Regexp.escape(spree.seo_admin_product_path( + product.id, filter + ))) + expect(page).to have_link('Search', href: expected_product_seo_url) end - uri = URI.parse(current_url) - expect("#{uri.path}?#{uri.query}").to eq spree.admin_product_product_properties_path(product, - filter) - end + it "editing product group buy options" do + visit spree.edit_admin_product_path product + within('#sidebar') { click_link 'Group Buy Options' } + choose('product_group_buy_1') + fill_in 'Bulk unit size', with: '10' - it "adding product properties including url filters" do - # Given a product - product = create(:simple_product, supplier: @supplier2) - product.set_property('fooprop', 'fooval') + click_button 'Update' - # When I navigate to the product properties page - visit spree.admin_product_product_properties_path(product, filter) + expect(flash_message).to eq("Product \"#{product.name}\" has been successfully updated!") + product.reload + expect(product.group_buy).to be true + expect(product.group_buy_unit_size).to eq(10.0) + end - # And I add a property - select 'fooprop', from: 'product_product_properties_attributes_0_property_name' - fill_in 'product_product_properties_attributes_0_value', with: 'fooval2' + it "loading editing product group buy options with url filters" do + visit spree.group_buy_options_admin_product_path(product, filter) - click_button 'Update' - - uri = URI.parse(current_url) - expect("#{uri.path}?#{uri.query}").to eq spree.edit_admin_product_path(product, filter) - end - - it "loading new product image page" do - product = create(:simple_product, supplier: @supplier2) - - visit spree.admin_product_images_path(product) - expect(page).to have_selector ".no-objects-found" - - page.find('a#new_image_link').click - expect(page).to have_selector "#image_attachment" - end - - it "loading new product image page including url filters" do - product = create(:simple_product, supplier: @supplier2) - - visit spree.admin_product_images_path(product, filter) - - page.find('a#new_image_link').click - - expected_cancel_link = Regexp.new(Regexp.escape(spree.admin_product_images_path(product, + expected_cancel_link = Regexp.new(Regexp.escape(spree.edit_admin_product_path(product, filter))) - expect(page).to have_link('Cancel', href: expected_cancel_link) - end + expect(page).to have_link('Cancel', href: expected_cancel_link) + end - it "upload a new product image including url filters" do - product = create(:simple_product, supplier: @supplier2) + it "editing product group buy options with url filter" do + visit spree.group_buy_options_admin_product_path(product, filter) + choose('product_group_buy_1') + fill_in 'Bulk unit size', with: '10' - visit spree.admin_product_images_path(product, filter) + click_button 'Update' - page.find('a#new_image_link').click + uri = URI.parse(current_url) + expect("#{uri.path}?#{uri.query}").to eq spree.edit_admin_product_path(product, filter) + end - attach_file('image_attachment', image_file_path) - click_button "Create" + it "editing product Search" do + visit spree.edit_admin_product_path product - uri = URI.parse(current_url) - expect("#{uri.path}?#{uri.query}").to eq spree.admin_product_images_path(product, filter) - end + within('#sidebar') { click_link 'Search' } + fill_in 'Product Search Keywords', with: 'Product Search Keywords' + fill_in 'Notes', with: 'Just testing Notes' + click_button 'Update' + expect(flash_message).to eq("Product \"#{product.name}\" has been successfully updated!") + product.reload + expect(product.notes).to eq('Just testing Notes') + expect(product.meta_keywords).to eq('Product Search Keywords') + end - it "loading image page including url filter" do - product = create(:simple_product, supplier: @supplier2) + it "loading editing product Search with url filters" do + visit spree.seo_admin_product_path(product, filter) - visit spree.admin_product_images_path(product, filter) - - expected_new_image_link = Regexp.new(Regexp.escape(spree.new_admin_product_image_path( - product, filter - ))) - expect(page).to have_link('New Image', href: expected_new_image_link) - end - - it "loading edit product image page including url filter" do - product = create(:simple_product, supplier: @supplier2) - image = white_logo_file - image_object = Spree::Image.create(viewable_id: product.id, - viewable_type: 'Spree::Product', alt: "position 1", - attachment: image, position: 1) - - visit spree.admin_product_images_path(product, filter) - - page.find("a.icon-edit").click - - uri = URI.parse(current_url) - expect("#{uri.path}?#{uri.query}") - .to eq spree.edit_admin_product_image_path(product, image_object, filter) - - expected_cancel_link = Regexp.new(Regexp.escape(spree.admin_product_images_path(product, + expected_cancel_link = Regexp.new(Regexp.escape(spree.edit_admin_product_path(product, filter))) - expect(page).to have_link('Cancel', href: expected_cancel_link) - expect(page).to have_link("Back To Images List", href: expected_cancel_link) - end - - it "updating a product image including url filter" do - product = create(:simple_product, supplier: @supplier2) - image = white_logo_file - image_object = Spree::Image.create(viewable_id: product.id, - viewable_type: 'Spree::Product', alt: "position 1", - attachment: image, position: 1) - - visit spree.admin_product_images_path(product, filter) - - page.find("a.icon-edit").click - - attach_file('image_attachment', image_file_path) - click_button "Update" - - uri = URI.parse(current_url) - expect("#{uri.path}?#{uri.query}").to eq spree.admin_product_images_path(product, filter) - end - - it "checks error when creating product image with unsupported format" do - unsupported_image_file_path = Rails.root.join("README.md").to_s - product = create(:simple_product, supplier: @supplier2) - - image = white_logo_file - Spree::Image.create(viewable_id: product.id, viewable_type: 'Spree::Product', - alt: "position 1", attachment: image, position: 1) - - visit spree.admin_product_images_path(product) - page.find('a#new_image_link').click - attach_file('image_attachment', unsupported_image_file_path) - click_button "Create" - - expect(page).to have_text "Attachment has an invalid content type" - expect(page).to have_text "Attachment is not a valid image" - end - - it "deleting product images" do - product = create(:simple_product, supplier: @supplier2) - image = white_logo_file - Spree::Image.create(viewable_id: product.id, viewable_type: 'Spree::Product', - alt: "position 1", attachment: image, position: 1) - - visit spree.admin_product_images_path(product) - expect(page).to have_selector "table.index td img" - expect(product.reload.image).not_to be_nil - - accept_alert do - page.find('a.delete-resource').click + expect(page).to have_link('Cancel', href: expected_cancel_link) end - expect(page).not_to have_selector "table.index td img" - expect(product.reload.image).to be_nil - end + it "editing product Search with url filter" do + visit spree.seo_admin_product_path(product, filter) - it "deleting product image including url filter" do - product = create(:simple_product, supplier: @supplier2) - image = white_logo_file - Spree::Image.create(viewable_id: product.id, viewable_type: 'Spree::Product', - alt: "position 1", attachment: image, position: 1) + fill_in 'Product Search Keywords', with: 'Product Search Keywords' + fill_in 'Notes', with: 'Just testing Notes' - visit spree.admin_product_images_path(product, filter) + click_button 'Update' - accept_alert do - page.find('a.delete-resource').click + uri = URI.parse(current_url) + expect("#{uri.path}?#{uri.query}").to eq spree.edit_admin_product_path(product, filter) end - uri = URI.parse(current_url) - expect("#{uri.path}?#{uri.query}").to eq spree.admin_product_images_path(product, filter) + it "loading product properties page including url filters" do + visit spree.admin_product_product_properties_path(product, filter) + + uri = URI.parse(current_url) + # we stay on the same url as the new image content is loaded via an ajax call + expect("#{uri.path}?#{uri.query}").to eq( + spree.admin_product_product_properties_path(product, filter) + ) + + expected_cancel_link = Regexp.new( + Regexp.escape(spree.admin_product_product_properties_path(product, filter)) + ) + expect(page).to have_link('Cancel', href: expected_cancel_link) + end + end + + describe "product properties" do + # Given a product with a property + let!(:product) { + create(:simple_product, supplier_id: supplier2.id).tap do |product| + product.set_property('fooprop', 'fooval') + end + } + + it "deleting product properties" do + # When I navigate to the product properties page + visit spree.admin_product_product_properties_path(product) + expect(page).to have_select2 'product_product_properties_attributes_0_property_name', + selected: 'fooprop' + expect(page).to have_field 'product_product_properties_attributes_0_value', with: 'fooval' + + # And I delete the property + accept_alert do + page.all('a.delete-resource').first.click + end + click_button 'Update' + + # Then the property should have been deleted + expect(page).not_to have_field 'product_product_properties_attributes_0_property_name', + with: 'fooprop' + expect(page).not_to have_field 'product_product_properties_attributes_0_value', + with: 'fooval' + expect(product.reload.property('fooprop')).to be_nil + end + + it "deleting product properties including url filters" do + # When I navigate to the product properties page + visit spree.admin_product_product_properties_path(product, filter) + + # And I delete the property + accept_alert do + page.all('a.delete-resource').first.click + end + + uri = URI.parse(current_url) + expect("#{uri.path}?#{uri.query}").to eq( + spree.admin_product_product_properties_path(product, filter) + ) + end + + it "adding product properties including url filters" do + # When I navigate to the product properties page + visit spree.admin_product_product_properties_path(product, filter) + + # And I add a property + select 'fooprop', from: 'product_product_properties_attributes_0_property_name' + fill_in 'product_product_properties_attributes_0_value', with: 'fooval2' + + click_button 'Update' + + uri = URI.parse(current_url) + expect("#{uri.path}?#{uri.query}").to eq spree.edit_admin_product_path(product, filter) + end + end + + describe "image page" do + let!(:product) { create(:simple_product, supplier_id: supplier2.id) } + + it "loading new product image page" do + visit spree.admin_product_images_path(product) + expect(page).to have_selector ".no-objects-found" + + page.find('a#new_image_link').click + expect(page).to have_selector "#image_attachment" + end + + it "loading new product image page including url filters" do + visit spree.admin_product_images_path(product, filter) + + page.find('a#new_image_link').click + + expected_cancel_link = Regexp.new(Regexp.escape(spree.admin_product_images_path(product, + filter))) + expect(page).to have_link('Cancel', href: expected_cancel_link) + end + + it "upload a new product image including url filters" do + visit spree.admin_product_images_path(product, filter) + + page.find('a#new_image_link').click + + attach_file('image_attachment', image_file_path) + click_button "Create" + + uri = URI.parse(current_url) + expect("#{uri.path}?#{uri.query}").to eq spree.admin_product_images_path(product, filter) + end + + it "loading image page including url filter" do + visit spree.admin_product_images_path(product, filter) + + expected_new_image_link = Regexp.new(Regexp.escape(spree.new_admin_product_image_path( + product, filter + ))) + expect(page).to have_link('New Image', href: expected_new_image_link) + end + + it "loading edit product image page including url filter" do + image = white_logo_file + image_object = Spree::Image.create(viewable_id: product.id, + viewable_type: 'Spree::Product', alt: "position 1", + attachment: image, position: 1) + + visit spree.admin_product_images_path(product, filter) + + page.find("a.icon-edit").click + + uri = URI.parse(current_url) + expect("#{uri.path}?#{uri.query}") + .to eq spree.edit_admin_product_image_path(product, image_object, filter) + + expected_cancel_link = Regexp.new(Regexp.escape(spree.admin_product_images_path(product, + filter))) + expect(page).to have_link('Cancel', href: expected_cancel_link) + expect(page).to have_link("Back To Images List", href: expected_cancel_link) + end + + it "updating a product image including url filter" do + image = white_logo_file + image_object = Spree::Image.create(viewable_id: product.id, + viewable_type: 'Spree::Product', alt: "position 1", + attachment: image, position: 1) + + visit spree.admin_product_images_path(product, filter) + + page.find("a.icon-edit").click + + attach_file('image_attachment', image_file_path) + click_button "Update" + + uri = URI.parse(current_url) + expect("#{uri.path}?#{uri.query}").to eq spree.admin_product_images_path(product, filter) + end + + it "checks error when creating product image with unsupported format" do + unsupported_image_file_path = Rails.root.join("README.md").to_s + product = create(:simple_product, supplier_id: supplier2.id) + + image = white_logo_file + Spree::Image.create(viewable_id: product.id, viewable_type: 'Spree::Product', + alt: "position 1", attachment: image, position: 1) + + visit spree.admin_product_images_path(product) + page.find('a#new_image_link').click + attach_file('image_attachment', unsupported_image_file_path) + click_button "Create" + + expect(page).to have_text "Attachment has an invalid content type" + expect(page).to have_text "Attachment is not a valid image" + end + + it "deleting product images" do + image = white_logo_file + Spree::Image.create(viewable_id: product.id, viewable_type: 'Spree::Product', + alt: "position 1", attachment: image, position: 1) + + visit spree.admin_product_images_path(product) + expect(page).to have_selector "table.index td img" + expect(product.reload.image).not_to be_nil + + accept_alert do + page.find('a.delete-resource').click + end + + expect(page).not_to have_selector "table.index td img" + expect(product.reload.image).to be_nil + end + + it "deleting product image including url filter" do + image = white_logo_file + Spree::Image.create(viewable_id: product.id, viewable_type: 'Spree::Product', + alt: "position 1", attachment: image, position: 1) + + visit spree.admin_product_images_path(product, filter) + + accept_alert do + page.find('a.delete-resource').click + end + + uri = URI.parse(current_url) + expect("#{uri.path}?#{uri.query}").to eq spree.admin_product_images_path(product, filter) + end end context "editing a product's variant unit scale" do - let(:product) { create(:simple_product, name: 'a product', supplier: @supplier2) } + let(:product) { create(:simple_product, name: 'a product', supplier_id: supplier2.id) } before do allow(Spree::Config).to receive(:available_units).and_return("g,lb,oz,kg,T,mL,L,kL") diff --git a/spec/system/admin/products_v3/index_spec.rb b/spec/system/admin/products_v3/index_spec.rb index 23b678138e..264bdad71c 100644 --- a/spec/system/admin/products_v3/index_spec.rb +++ b/spec/system/admin/products_v3/index_spec.rb @@ -53,8 +53,8 @@ RSpec.describe 'As an enterprise user, I can manage my products', feature: :admi s1 = FactoryBot.create(:supplier_enterprise) s2 = FactoryBot.create(:supplier_enterprise) s3 = FactoryBot.create(:supplier_enterprise) - p1 = FactoryBot.create(:product, supplier: s2) - p2 = FactoryBot.create(:product, supplier: s3) + p1 = FactoryBot.create(:product, supplier_id: s2.id) + p2 = FactoryBot.create(:product, supplier_id: s3.id) visit spree.admin_products_path @@ -269,7 +269,9 @@ RSpec.describe 'As an enterprise user, I can manage my products', feature: :admi # create a product with a different supplier let!(:producer1) { create(:supplier_enterprise, name: "Producer 1") } - let!(:product_by_supplier) { create(:simple_product, name: "Apples", supplier: producer1) } + let!(:product_by_supplier) { + create(:simple_product, name: "Apples", supplier_id: producer1.id) + } before { user.enterprise_roles.create(enterprise: producer1) } diff --git a/spec/system/admin/products_v3/products_spec.rb b/spec/system/admin/products_v3/products_spec.rb index ca829c2b33..a0e35aa41b 100644 --- a/spec/system/admin/products_v3/products_spec.rb +++ b/spec/system/admin/products_v3/products_spec.rb @@ -97,11 +97,11 @@ RSpec.describe 'As an enterprise user, I can manage my products', feature: :admi end it "should not display search input, change the producers, category and tax category" do - producer_to_select = random_producer(product_a) + producer_to_select = random_producer(variant_a1) category_to_select = random_category(variant_a1) tax_category_to_select = random_tax_category - within row_containing_name(product_a.name) do + within row_containing_name(variant_a1.display_name) do validate_tomselect_without_search!( page, "Producer", producer_search_selector @@ -126,10 +126,9 @@ RSpec.describe 'As an enterprise user, I can manage my products', feature: :admi click_button "Save changes" expect(page).to have_content "Changes saved" - product_a.reload - variant_a1.reload - expect(product_a.supplier.name).to eq(producer_to_select) + variant_a1.reload + expect(variant_a1.supplier.name).to eq(producer_to_select) expect(variant_a1.primary_taxon.name).to eq(category_to_select) expect(variant_a1.tax_category.name).to eq(tax_category_to_select) end @@ -145,19 +144,17 @@ RSpec.describe 'As an enterprise user, I can manage my products', feature: :admi end it "should display search input, change the producer" do - producer_to_select = random_producer(product_a) + producer_to_select = random_producer(variant_a1) category_to_select = random_category(variant_a1) tax_category_to_select = random_tax_category - within row_containing_name(product_a.name) do + within row_containing_name(variant_a1.display_name) do validate_tomselect_with_search!( page, "Producer", producer_search_selector ) tomselect_search_and_select(producer_to_select, from: "Producer") - end - within row_containing_name(variant_a1.display_name) do sleep(0.1) validate_tomselect_with_search!( page, "Category", @@ -176,10 +173,9 @@ RSpec.describe 'As an enterprise user, I can manage my products', feature: :admi click_button "Save changes" expect(page).to have_content "Changes saved" - product_a.reload - variant_a1.reload - expect(product_a.supplier.name).to eq(producer_to_select) + variant_a1.reload + expect(variant_a1.supplier.name).to eq(producer_to_select) expect(variant_a1.primary_taxon.name).to eq(category_to_select) expect(variant_a1.tax_category.name).to eq(tax_category_to_select) end @@ -246,6 +242,7 @@ RSpec.describe 'As an enterprise user, I can manage my products', feature: :admi describe "Cloning product" do it "shows the cloned product on page when clicked on the cloned option" do + # TODO, variant supplier missing, needs to be copied from variant and not product within "table.products" do # Gather input values, because page.content doesn't include them. input_content = page.find_all('input[type=text]').map(&:value).join @@ -491,13 +488,13 @@ RSpec.describe 'As an enterprise user, I can manage my products', feature: :admi let(:supplier_permitted) { create(:supplier_enterprise, name: 'Supplier Permitted') } let(:distributor_managed) { create(:distributor_enterprise, name: 'Distributor Managed') } let(:distributor_unmanaged) { create(:distributor_enterprise, name: 'Distributor Unmanaged') } - let!(:product_supplied) { create(:product, supplier: supplier_managed1, price: 10.0) } - let!(:product_not_supplied) { create(:product, supplier: supplier_unmanaged) } + let!(:product_supplied) { create(:product, supplier_id: supplier_managed1.id, price: 10.0) } + let!(:product_not_supplied) { create(:product, supplier_id: supplier_unmanaged.id) } let!(:product_supplied_permitted) { - create(:product, name: 'Product Permitted', supplier: supplier_permitted, price: 10.0) + create(:product, name: 'Product Permitted', supplier_id: supplier_permitted.id, price: 10.0) } let(:product_supplied_inactive) { - create(:product, supplier: supplier_managed1, price: 10.0) + create(:product, supplier_id: supplier_managed1.id, price: 10.0) } let!(:supplier_permitted_relationship) do @@ -525,18 +522,19 @@ RSpec.describe 'As an enterprise user, I can manage my products', feature: :admi it "shows only suppliers that I manage or have permission to" do visit spree.admin_products_path - within row_containing_name(product_supplied.name) do + + within row_containing_placeholder(product_supplied.name) do expect(page).to have_select( - '_products_0_supplier_id', + '_products_0_variants_attributes_0_supplier_id', options: [ supplier_managed1.name, supplier_managed2.name, supplier_permitted.name ], selected: supplier_managed1.name ) end - within row_containing_name(product_supplied_permitted.name) do + within row_containing_placeholder(product_supplied_permitted.name) do expect(page).to have_select( - '_products_1_supplier_id', + '_products_1_variants_attributes_0_supplier_id', options: [ supplier_managed1.name, supplier_managed2.name, supplier_permitted.name ], selected: supplier_permitted.name diff --git a/spec/system/admin/reports/enterprise_summary_fees/enterprise_summary_fee_with_tax_report_by_producer_spec.rb b/spec/system/admin/reports/enterprise_summary_fees/enterprise_summary_fee_with_tax_report_by_producer_spec.rb index 3d16397453..002e0a7247 100644 --- a/spec/system/admin/reports/enterprise_summary_fees/enterprise_summary_fee_with_tax_report_by_producer_spec.rb +++ b/spec/system/admin/reports/enterprise_summary_fees/enterprise_summary_fee_with_tax_report_by_producer_spec.rb @@ -43,10 +43,10 @@ RSpec.describe "Enterprise Summary Fee with Tax Report By Producer" do create(:supplier_enterprise, name: 'Supplier2', charges_sales_tax: true, owner_id: supplier2_owner.id) } - let!(:product){ create(:simple_product, supplier: ) } - let!(:product2){ create(:simple_product, supplier: supplier2 ) } - let!(:variant){ create(:variant, product_id: product.id, tax_category:) } - let!(:variant2){ create(:variant, product_id: product2.id, tax_category:) } + let!(:product){ create(:simple_product) } + let!(:product2){ create(:simple_product) } + let!(:variant){ create(:variant, product_id: product.id, tax_category:, supplier:) } + let!(:variant2){ create(:variant, product_id: product2.id, tax_category:, supplier: supplier2) } let!(:distributor_owner) { create(:user, enterprise_limit: 1) } let!(:distributor){ distributor = create(:distributor_enterprise_with_tax, name: 'Distributor', @@ -169,17 +169,6 @@ RSpec.describe "Enterprise Summary Fee with Tax Report By Producer" do order2 } - before do - product.update!({ - tax_category_id: tax_category.id, - supplier_id: supplier.id - }) - product2.update!({ - tax_category_id: tax_category.id, - supplier_id: supplier2.id - }) - end - context 'added tax' do # 1 order cycle has: # - coordinator fees (20) 1.5% = 0.30, 2.5% = 0.50 diff --git a/spec/system/admin/reports/orders_and_fulfillment_spec.rb b/spec/system/admin/reports/orders_and_fulfillment_spec.rb index f124dfa2c0..1aa808e2fc 100644 --- a/spec/system/admin/reports/orders_and_fulfillment_spec.rb +++ b/spec/system/admin/reports/orders_and_fulfillment_spec.rb @@ -37,9 +37,9 @@ RSpec.describe "Orders And Fulfillment" do order_cycle_id: order_cycle.id) } let(:supplier) { create(:supplier_enterprise, name: "Supplier Name") } - let(:product) { create(:simple_product, name: "Baked Beans", supplier: ) } - let(:variant1) { create(:variant, product:, unit_description: "Big") } - let(:variant2) { create(:variant, product:, unit_description: "Small") } + let(:product) { create(:simple_product, name: "Baked Beans", supplier_id: supplier.id ) } + let(:variant1) { create(:variant, product:, unit_description: "Big", supplier:) } + let(:variant2) { create(:variant, product:, unit_description: "Small", supplier: ) } before do # order1 has two line items / variants @@ -427,8 +427,12 @@ RSpec.describe "Orders And Fulfillment" do describe "Order Cycle Distributor Totals by Supplier" do context "for an OC supplied by two suppliers" do let(:supplier2) { create(:supplier_enterprise, name: "Another Supplier Name") } - let(:product2) { create(:simple_product, name: "Salted Peanuts", supplier: supplier2 ) } - let(:variant3) { create(:variant, product: product2, unit_description: "Bag") } + let(:product2) { + create(:simple_product, name: "Salted Peanuts", supplier_id: supplier2.id ) + } + let(:variant3) { + create(:variant, product: product2, unit_description: "Bag", supplier: supplier2) + } let(:order4) { create(:completed_order_with_totals, line_items_count: 0, distributor:, bill_address: bill_address1, diff --git a/spec/system/admin/reports/packing_report_spec.rb b/spec/system/admin/reports/packing_report_spec.rb index fd697b56e2..d3a9d0113b 100644 --- a/spec/system/admin/reports/packing_report_spec.rb +++ b/spec/system/admin/reports/packing_report_spec.rb @@ -34,10 +34,10 @@ RSpec.describe "Packing Reports" do bill_address: bill_address2) } let(:supplier) { create(:supplier_enterprise, name: "Supplier") } - let(:product1) { create(:simple_product, name: "Product 1", supplier: ) } - let(:variant1) { create(:variant, product: product1, unit_description: "Big") } - let(:variant2) { create(:variant, product: product1, unit_description: "Small") } - let(:product2) { create(:simple_product, name: "Product 2", supplier:) } + let(:product1) { create(:simple_product, name: "Product 1", supplier_id: supplier.id ) } + let(:variant1) { create(:variant, product: product1, unit_description: "Big", supplier: ) } + let(:variant2) { create(:variant, product: product1, unit_description: "Small", supplier: ) } + let(:product2) { create(:simple_product, name: "Product 2", supplier_id: supplier.id) } before do order1.finalize! diff --git a/spec/system/admin/reports/payments_report_spec.rb b/spec/system/admin/reports/payments_report_spec.rb index ad37562c5c..b6819667d8 100644 --- a/spec/system/admin/reports/payments_report_spec.rb +++ b/spec/system/admin/reports/payments_report_spec.rb @@ -23,7 +23,7 @@ RSpec.describe "Payments Reports" do ) end let(:order_cycle) { create(:simple_order_cycle) } - let(:product) { create(:product, supplier:) } + let(:product) { create(:product, supplier_id: supplier.id) } let(:supplier) { create(:supplier_enterprise) } before do diff --git a/spec/system/admin/reports/revenues_by_hub_spec.rb b/spec/system/admin/reports/revenues_by_hub_spec.rb index d0df382ba0..114ec2e411 100644 --- a/spec/system/admin/reports/revenues_by_hub_spec.rb +++ b/spec/system/admin/reports/revenues_by_hub_spec.rb @@ -42,8 +42,7 @@ RSpec.describe "Revenues By Hub Reports" do let(:distributor3) { create(:enterprise, name: "Hub 3", owner:) } let(:owner) { create(:user, email: 'email@email.com') } let(:order_cycle) { create(:simple_order_cycle) } - let(:product) { create(:product, supplier:) } - let(:supplier) { create(:supplier_enterprise) } + let(:product) { create(:product) } let(:voucher2) { create(:voucher_flat_rate, code: 'code', enterprise: distributor2, amount: 10) } let(:voucher3) { create(:voucher_flat_rate, code: 'code', enterprise: distributor3, amount: 10) } diff --git a/spec/system/admin/reports/sales_tax/sales_tax_totals_by_producer_spec.rb b/spec/system/admin/reports/sales_tax/sales_tax_totals_by_producer_spec.rb index 63b4f7f071..3b76817177 100644 --- a/spec/system/admin/reports/sales_tax/sales_tax_totals_by_producer_spec.rb +++ b/spec/system/admin/reports/sales_tax/sales_tax_totals_by_producer_spec.rb @@ -25,41 +25,35 @@ RSpec.describe "Sales Tax Totals By Producer" do ].join(" ") } let!(:state_zone){ create(:zone_with_state_member) } - let!(:country_zone){ create(:zone_with_member) } - let!(:tax_category){ create(:tax_category) } - let!(:state_tax_rate){ create(:tax_rate, zone: state_zone, tax_category:) } - let!(:country_tax_rate){ create(:tax_rate, zone: country_zone, tax_category:) } - let!(:ship_address){ create(:ship_address) } + let!(:country_zone) { create(:zone_with_member) } + let!(:tax_category) { create(:tax_category, name: 'tax_category') } + let!(:state_tax_rate) { + create(:tax_rate, name: 'State', amount: 0.015, zone: state_zone, tax_category:) + } + let!(:country_tax_rate) { + create(:tax_rate, name: 'Country', amount: 0.025, zone: country_zone, tax_category:) + } + let!(:ship_address) { create(:ship_address) } let(:another_state){ create(:state, name: 'Another state', country: ship_address.country) } - let!(:variant){ create(:variant) } - let!(:product){ variant.product } - let!(:supplier){ create(:supplier_enterprise) } - let!(:distributor){ create(:distributor_enterprise_with_tax) } - let!(:payment_method){ create(:payment_method, :flat_rate) } - let!(:shipping_method){ create(:shipping_method, :flat_rate) } + let!(:variant) { create(:variant, supplier:, tax_category:) } + let!(:product) { variant.product } + let!(:supplier) { create(:supplier_enterprise, name: 'Supplier', charges_sales_tax: true) } + let!(:distributor) { create(:distributor_enterprise_with_tax, name: 'Distributor') } + let!(:payment_method) { create(:payment_method, :flat_rate) } + let!(:shipping_method) { create(:shipping_method, :flat_rate) } - let!(:order){ create(:order_with_distributor, distributor:) } - let!(:order_cycle){ - create(:simple_order_cycle, suppliers: [supplier], distributors: [distributor], + let!(:order) { create(:order_with_distributor, distributor:) } + let!(:order_cycle) { + create(:simple_order_cycle, name: 'oc1', suppliers: [supplier], distributors: [distributor], variants: [variant]) } - let(:admin){ create(:admin_user) } + let(:admin) { create(:admin_user) } before do - state_tax_rate.update!({ name: 'State', amount: 0.015 }) - country_tax_rate.update!({ name: 'Country', amount: 0.025 }) - tax_category.update!({ name: 'tax_category' }) - order_cycle.update!(name: "oc1") - - distributor.update!({ name: 'Distributor' }) distributor.shipping_methods << shipping_method distributor.payment_methods << payment_method - - supplier.update!(name: 'Supplier', charges_sales_tax: true) - product.update!(supplier_id: supplier.id) - variant.update!(tax_category_id: tax_category.id) end context 'added tax' do diff --git a/spec/system/admin/reports_spec.rb b/spec/system/admin/reports_spec.rb index 80839873d2..62de258992 100644 --- a/spec/system/admin/reports_spec.rb +++ b/spec/system/admin/reports_spec.rb @@ -379,16 +379,19 @@ RSpec.describe ' 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, supplier:, - primary_taxon_id: taxon.id) + 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', supplier:, - primary_taxon_id: taxon.id, sku: "product_sku") + 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) } + let(:variant2) { + create(:variant, product: product1, price: 80.0, primary_taxon: taxon, supplier:) + } let(:variant3) { product2.variants.first } before do @@ -416,17 +419,17 @@ RSpec.describe ' "Product Properties", "Taxons", "Variant Value", "Price", "Group Buy Unit Quantity", "Amount", "SKU", "On Demand?", "On Hand"] - expect(page).to have_table_row [product1.supplier.name, product1.supplier.address.city, + 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 [product1.supplier.name, product1.supplier.address.city, + 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 [product2.supplier.name, product1.supplier.address.city, + expect(page).to have_table_row [supplier.name, supplier.address.city, "Product 2", product1.properties.map(&:presentation).join(", "), taxon.name, "100g", "99.0", diff --git a/spec/system/admin/subscriptions/crud_spec.rb b/spec/system/admin/subscriptions/crud_spec.rb index e2871dc450..3e6ca504ad 100644 --- a/spec/system/admin/subscriptions/crud_spec.rb +++ b/spec/system/admin/subscriptions/crud_spec.rb @@ -206,13 +206,13 @@ RSpec.describe 'Subscriptions' do create(:customer, enterprise: shop, bill_address: address, user: customer_user, allow_charges: true) } - let!(:test_product) { create(:product, supplier: shop) } + let!(:test_product) { create(:product) } let!(:test_variant) { - create(:variant, product: test_product, unit_value: "100", price: 12.00) + create(:variant, product: test_product, unit_value: "100", price: 12.00, supplier: shop) } - let!(:shop_product) { create(:product, supplier: shop) } + let!(:shop_product) { create(:product) } let!(:shop_variant) { - create(:variant, product: shop_product, unit_value: "1000", price: 6.00) + create(:variant, product: shop_product, unit_value: "1000", price: 6.00, supplier: shop) } let!(:enterprise_fee) { create(:enterprise_fee, amount: 1.75) } let!(:order_cycle) { @@ -448,17 +448,17 @@ RSpec.describe 'Subscriptions' do context 'editing an existing subscription' do let!(:customer) { create(:customer, enterprise: shop) } - let!(:product1) { create(:product, supplier: shop) } - let!(:product2) { create(:product, supplier: shop) } - let!(:product3) { create(:product, supplier: shop) } + let!(:product1) { create(:product) } + let!(:product2) { create(:product) } + let!(:product3) { create(:product) } let!(:variant1) { - create(:variant, product: product1, unit_value: '100', price: 12.00) + create(:variant, product: product1, unit_value: '100', price: 12.00, supplier: shop) } let!(:variant2) { - create(:variant, product: product2, unit_value: '1000', price: 6.00) + create(:variant, product: product2, unit_value: '1000', price: 6.00, supplier: shop) } let!(:variant3) { - create(:variant, product: product3, unit_value: '10000', price: 22.00) + create(:variant, product: product3, unit_value: '10000', price: 22.00, supplier: shop) } let!(:enterprise_fee) { create(:enterprise_fee, amount: 1.75) } let!(:order_cycle) { diff --git a/spec/system/admin/subscriptions/smoke_tests_spec.rb b/spec/system/admin/subscriptions/smoke_tests_spec.rb index bd49f05c3a..33f9b7c31e 100644 --- a/spec/system/admin/subscriptions/smoke_tests_spec.rb +++ b/spec/system/admin/subscriptions/smoke_tests_spec.rb @@ -61,13 +61,13 @@ RSpec.describe 'Subscriptions' do describe "with an inactive order cycle" do let!(:customer) { create(:customer, enterprise: shop) } - let!(:product1) { create(:product, supplier: shop) } - let!(:product2) { create(:product, supplier: shop) } + let!(:product1) { create(:product, supplier_id: shop.id) } + let!(:product2) { create(:product, supplier_id: shop.id) } let!(:variant1) { - create(:variant, product: product1, unit_value: '100', price: 12.00) + create(:variant, product: product1, unit_value: '100', price: 12.00, supplier: shop) } let!(:variant2) { - create(:variant, product: product2, unit_value: '1000', price: 6.00) + create(:variant, product: product2, unit_value: '1000', price: 6.00, supplier: shop) } let!(:enterprise_fee) { create(:enterprise_fee, amount: 1.75) } let!(:order_cycle) { @@ -122,7 +122,7 @@ RSpec.describe 'Subscriptions' do # update orders close find('#order_cycle_orders_close_at').click - select_datetime_from_datepicker Time.zone.at(Time.zone.local(2040, 10, 24, 17, 0o0, 0o0)) + select_datetime_from_datepicker Time.zone.at(1.month.from_now) find("body").send_keys(:escape) click_button 'Save' @@ -146,19 +146,24 @@ RSpec.describe 'Subscriptions' do describe "allowed variants" do let!(:customer) { create(:customer, enterprise: shop) } let!(:credit_card) { create(:stored_credit_card, user: customer.user) } - let!(:shop_product) { create(:product, supplier: shop) } - let!(:shop_product2) { create(:product, supplier: shop) } - let!(:shop_variant) { create(:variant, product: shop_product, unit_value: "2000") } - let!(:shop_variant2) { create(:variant, product: shop_product2, unit_value: "1000") } + let!(:shop_product) { create(:product, supplier_id: shop.id) } + let!(:shop_product2) { create(:product, supplier_id: shop.id) } + let!(:shop_variant) { + create(:variant, product: shop_product, unit_value: "2000", supplier: shop) + } + let!(:shop_variant2) { + create(:variant, product: shop_product2, unit_value: "1000", supplier: shop) + } let!(:permitted_supplier) do create(:supplier_enterprise).tap do |supplier| create(:enterprise_relationship, child: shop, parent: supplier, permissions_list: [:add_to_order_cycle]) end end - let!(:permitted_supplier_product) { create(:product, supplier: permitted_supplier) } + let!(:permitted_supplier_product) { create(:product, supplier_id: permitted_supplier.id) } let!(:permitted_supplier_variant) { - create(:variant, product: permitted_supplier_product, unit_value: "2000") + create(:variant, product: permitted_supplier_product, unit_value: "2000", + supplier: permitted_supplier) } let!(:incoming_exchange_product) { create(:product) } let!(:incoming_exchange_variant) do diff --git a/spec/system/admin/variant_overrides_spec.rb b/spec/system/admin/variant_overrides_spec.rb index 3f48ae7602..44937c3123 100644 --- a/spec/system/admin/variant_overrides_spec.rb +++ b/spec/system/admin/variant_overrides_spec.rb @@ -48,31 +48,34 @@ RSpec.describe " context "when inventory_items exist for variants" do let!(:product) { - create(:simple_product, supplier: producer, variant_unit: 'weight', variant_unit_scale: 1) + create(:simple_product, supplier_id: producer.id, variant_unit: 'weight', + variant_unit_scale: 1) } let!(:variant) { create(:variant, product:, unit_value: 1, price: 1.23, on_hand: 12) } let!(:inventory_item) { create(:inventory_item, enterprise: hub, variant: ) } let!(:product_managed) { - create(:simple_product, supplier: producer_managed, variant_unit: 'weight', + create(:simple_product, supplier_id: producer_managed.id, variant_unit: 'weight', variant_unit_scale: 1) } let!(:variant_managed) { - create(:variant, product: product_managed, unit_value: 3, price: 3.65, on_hand: 2) + create(:variant, product: product_managed, supplier: producer_managed, unit_value: 3, + price: 3.65, on_hand: 2) } let!(:inventory_item_managed) { create(:inventory_item, enterprise: hub, variant: variant_managed ) } - let!(:product_related) { create(:simple_product, supplier: producer_related) } + let!(:product_related) { create(:simple_product, supplier_id: producer_related.id) } let!(:variant_related) { - create(:variant, product: product_related, unit_value: 2, price: 2.34, on_hand: 23) + create(:variant, product: product_related, supplier: producer_related, unit_value: 2, + price: 2.34, on_hand: 23) } let!(:inventory_item_related) { create(:inventory_item, enterprise: hub, variant: variant_related ) } - let!(:product_unrelated) { create(:simple_product, supplier: producer_unrelated) } + let!(:product_unrelated) { create(:simple_product, supplier_id: producer_unrelated.id) } context "when a hub is selected" do before do @@ -83,12 +86,18 @@ RSpec.describe " context "with no overrides" do it "displays the list of products with variants" do expect(page).to have_table_row ['Producer', 'Product', 'Price', 'On Hand', 'On Demand?'] - expect(page).to have_table_row [producer.name, product.name, '', '', ''] + expect(page).to have_table_row ['', product.name, '', '', ''] + within "tr#v_#{variant.id}" do |tr| + expect(tr).to have_content(producer.name) + end expect(page).to have_input "variant-overrides-#{variant.id}-price", placeholder: '1.23' expect(page).to have_input "variant-overrides-#{variant.id}-count_on_hand", placeholder: '12' - expect(page).to have_table_row [producer_related.name, product_related.name, '', '', ''] + expect(page).to have_table_row ['', product_related.name, '', '', ''] + within "tr#v_#{variant_related.id}" do |tr| + expect(tr).to have_content(producer_related.name) + end expect(page).to have_input "variant-overrides-#{variant_related.id}-price", placeholder: '2.34' expect(page).to have_input "variant-overrides-#{variant_related.id}-count_on_hand", @@ -253,7 +262,7 @@ RSpec.describe " create(:variant_override, variant:, hub: hub2, price: 1, count_on_hand: 2) } let!(:product2) { - create(:simple_product, supplier: producer, variant_unit: 'weight', + create(:simple_product, supplier_id: producer.id, variant_unit: 'weight', variant_unit_scale: 1) } let!(:variant2) { @@ -470,7 +479,8 @@ RSpec.describe " describe "when inventory_items do not exist for variants" do let!(:product) { - create(:simple_product, supplier: producer, variant_unit: 'weight', variant_unit_scale: 1) + create(:simple_product, supplier_id: producer.id, variant_unit: 'weight', + variant_unit_scale: 1) } let!(:variant1) { create(:variant, product:, unit_value: 1, price: 1.23, on_hand: 12) @@ -523,7 +533,7 @@ RSpec.describe " it "shows more than 100 products in my inventory" do supplier = create(:supplier_enterprise, sells: "own") inventory_items = (1..101).map do - product = create(:simple_product, supplier:) + product = create(:simple_product, supplier_id: supplier.id) InventoryItem.create!( enterprise: supplier, variant: product.variants.first diff --git a/spec/system/admin/variants_spec.rb b/spec/system/admin/variants_spec.rb index beb164fe1e..7de52b8272 100644 --- a/spec/system/admin/variants_spec.rb +++ b/spec/system/admin/variants_spec.rb @@ -24,6 +24,7 @@ RSpec.describe ' fill_in 'unit_value_human', with: '1' fill_in 'variant_unit_description', with: 'foo' select taxon.name, from: "variant_primary_taxon_id" + select2_select product.variants.first.supplier.name, from: "variant_supplier_id" click_button 'Create' # Then the variant should have been created @@ -65,6 +66,7 @@ RSpec.describe ' fill_in 'variant_weight', with: '1.234' fill_in 'unit_value_human', with: 1 select taxon.name, from: "variant_primary_taxon_id" + select2_select product.variants.first.supplier.name, from: "variant_supplier_id" click_button 'Create' # Then the variant should have been created @@ -236,6 +238,27 @@ RSpec.describe ' end end + describe "editing supplier" do + let(:product) { create(:simple_product) } + let(:variant) { product.variants.first } + + before do + login_as_admin + end + + it "updates the supplier" do + new_supplier = create(:supplier_enterprise) + visit spree.edit_admin_product_variant_path(product, variant) + + select2_select new_supplier.name, from: "variant_supplier_id" + + click_button 'Update' + + expect(page).to have_content %(Variant "#{product.name}" has been successfully updated!) + expect(variant.reload.supplier).to eq(new_supplier) + end + end + describe "editing on hand and on demand values" do let(:product) { create(:simple_product) } let(:variant) { product.variants.first } @@ -331,7 +354,7 @@ RSpec.describe ' fill_in "unit_value_human", with: "1.23" click_button 'Update' - expect(page).not_to have_content %(Variant "#{product.name}" has been successfully updated!) + expect(page).to have_content %(Variant "#{product.name}" has been successfully updated!) # Then the displayed values should have been saved expect(variant.reload.unit_value).to eq(1.23) diff --git a/spec/system/consumer/caching/darkswarm_caching_spec.rb b/spec/system/consumer/caching/darkswarm_caching_spec.rb index 02c2fdad65..f3ae4482c1 100644 --- a/spec/system/consumer/caching/darkswarm_caching_spec.rb +++ b/spec/system/consumer/caching/darkswarm_caching_spec.rb @@ -11,7 +11,7 @@ RSpec.describe "Darkswarm data caching", caching: true do create(:distributor_enterprise, with_payment_and_shipping: true, is_primary_producer: true) } let!(:product) { - create(:simple_product, supplier: producer, primary_taxon: taxon, + create(:simple_product, supplier_id: producer.id, primary_taxon: taxon, properties: [property]) } let!(:order_cycle) { diff --git a/spec/system/consumer/checkout/details_spec.rb b/spec/system/consumer/checkout/details_spec.rb index 7c970196be..bb204e3ca7 100644 --- a/spec/system/consumer/checkout/details_spec.rb +++ b/spec/system/consumer/checkout/details_spec.rb @@ -15,7 +15,7 @@ RSpec.describe "As a consumer, I want to checkout my order" do let(:supplier) { create(:supplier_enterprise) } let(:distributor) { create(:distributor_enterprise, charges_sales_tax: true) } let(:product) { - create(:taxed_product, supplier:, price: 10, zone:, tax_rate_amount: 0.1) + create(:taxed_product, supplier_id: supplier.id, price: 10, zone:, tax_rate_amount: 0.1) } let(:variant) { product.variants.first } let!(:order_cycle) { diff --git a/spec/system/consumer/checkout/guest_spec.rb b/spec/system/consumer/checkout/guest_spec.rb index bf24547115..0a90caab66 100644 --- a/spec/system/consumer/checkout/guest_spec.rb +++ b/spec/system/consumer/checkout/guest_spec.rb @@ -12,7 +12,7 @@ RSpec.describe "As a consumer, I want to checkout my order" do let(:supplier) { create(:supplier_enterprise) } let(:distributor) { create(:distributor_enterprise, charges_sales_tax: true) } let(:product) { - create(:taxed_product, supplier:, price: 10, zone:, tax_rate_amount: 0.1) + create(:taxed_product, supplier_id: supplier.id, price: 10, zone:, tax_rate_amount: 0.1) } let(:variant) { product.variants.first } let!(:order_cycle) { diff --git a/spec/system/consumer/checkout/payment_spec.rb b/spec/system/consumer/checkout/payment_spec.rb index e9a1c83609..b0bd281ca1 100644 --- a/spec/system/consumer/checkout/payment_spec.rb +++ b/spec/system/consumer/checkout/payment_spec.rb @@ -15,7 +15,7 @@ RSpec.describe "As a consumer, I want to checkout my order" do let(:supplier) { create(:supplier_enterprise) } let(:distributor) { create(:distributor_enterprise, charges_sales_tax: true) } let(:product) { - create(:taxed_product, supplier:, price: 10, zone:, tax_rate_amount: 0.1) + create(:taxed_product, supplier_id: supplier.id, price: 10, zone:, tax_rate_amount: 0.1) } let(:variant) { product.variants.first } let!(:order_cycle) { diff --git a/spec/system/consumer/checkout/summary_spec.rb b/spec/system/consumer/checkout/summary_spec.rb index 75a1a06487..a15b800528 100644 --- a/spec/system/consumer/checkout/summary_spec.rb +++ b/spec/system/consumer/checkout/summary_spec.rb @@ -15,7 +15,7 @@ RSpec.describe "As a consumer, I want to checkout my order" do let(:supplier) { create(:supplier_enterprise) } let(:distributor) { create(:distributor_enterprise, charges_sales_tax: true) } let(:product) { - create(:taxed_product, supplier:, price: 10, zone:, tax_rate_amount: 0.1) + create(:taxed_product, supplier_id: supplier.id, price: 10, zone:, tax_rate_amount: 0.1) } let(:variant) { product.variants.first } let!(:order_cycle) { diff --git a/spec/system/consumer/checkout/tax_incl_spec.rb b/spec/system/consumer/checkout/tax_incl_spec.rb index b8b9ff5b7e..de0ee1af4a 100644 --- a/spec/system/consumer/checkout/tax_incl_spec.rb +++ b/spec/system/consumer/checkout/tax_incl_spec.rb @@ -29,7 +29,7 @@ RSpec.describe "As a consumer, I want to see adjustment breakdown" do let(:distributor) { create(:distributor_enterprise, charges_sales_tax: true) } let(:supplier) { create(:supplier_enterprise) } let!(:product_with_tax) { - create(:simple_product, supplier:, price: 10, tax_category_id: tax_category.id) + create(:simple_product, supplier_id: supplier.id, price: 10, tax_category_id: tax_category.id) } let!(:variant_with_tax) { product_with_tax.variants.first } let!(:order_cycle) { diff --git a/spec/system/consumer/checkout/tax_not_incl_spec.rb b/spec/system/consumer/checkout/tax_not_incl_spec.rb index 274480300e..a5a0d26f91 100644 --- a/spec/system/consumer/checkout/tax_not_incl_spec.rb +++ b/spec/system/consumer/checkout/tax_not_incl_spec.rb @@ -32,7 +32,7 @@ RSpec.describe "As a consumer, I want to see adjustment breakdown" do let(:distributor) { create(:distributor_enterprise, charges_sales_tax: true) } let(:supplier) { create(:supplier_enterprise) } let(:product_with_tax) { - create(:product, supplier:, price: 10, tax_category_id: tax_category.id) + create(:product, supplier_id: supplier.id, price: 10, tax_category_id: tax_category.id) } let(:variant_with_tax) { product_with_tax.variants.first } let(:order_cycle) { diff --git a/spec/system/consumer/groups_spec.rb b/spec/system/consumer/groups_spec.rb index 6820ad1b9c..563fefc094 100644 --- a/spec/system/consumer/groups_spec.rb +++ b/spec/system/consumer/groups_spec.rb @@ -24,8 +24,8 @@ RSpec.describe 'Groups' do let!(:producer1) { create(:supplier_enterprise) } let!(:producer2) { create(:supplier_enterprise) } - let!(:product1) { create(:simple_product, supplier: producer1) } - let!(:product2) { create(:simple_product, supplier: producer2) } + let!(:product1) { create(:simple_product, supplier_id: producer1.id) } + let!(:product2) { create(:simple_product, supplier_id: producer2.id) } before do product1.set_property 'Organic', 'NASAA 12345' @@ -85,10 +85,10 @@ RSpec.describe 'Groups' do let(:d4) { create(:distributor_enterprise, with_payment_and_shipping: true, visible: "public") } - let(:p1) { create(:simple_product, supplier: producer) } - let(:p2) { create(:simple_product, supplier: create(:supplier_enterprise)) } - let(:p3) { create(:simple_product, supplier: create(:supplier_enterprise)) } - let(:p4) { create(:simple_product, supplier: create(:supplier_enterprise)) } + let(:p1) { create(:simple_product, supplier_id: producer.id) } + let(:p2) { create(:simple_product, supplier_id: create(:supplier_enterprise).id) } + let(:p3) { create(:simple_product, supplier_id: create(:supplier_enterprise).id) } + let(:p4) { create(:simple_product, supplier_id: create(:supplier_enterprise).id) } let(:ex_d1) { order_cycle.exchanges.outgoing.where(receiver_id: d1).first } let(:ex_d2) { order_cycle.exchanges.outgoing.where(receiver_id: d2).first } let(:ex_d3) { order_cycle.exchanges.outgoing.where(receiver_id: d3).first } diff --git a/spec/system/consumer/multilingual_spec.rb b/spec/system/consumer/multilingual_spec.rb index 7116c0c1de..16ec57b35c 100644 --- a/spec/system/consumer/multilingual_spec.rb +++ b/spec/system/consumer/multilingual_spec.rb @@ -104,7 +104,7 @@ RSpec.describe 'Multilingual' do let(:supplier) { create(:supplier_enterprise) } let(:distributor) { create(:distributor_enterprise, charges_sales_tax: true) } let(:product) { - create(:taxed_product, supplier:, price: 10, zone:) + create(:taxed_product, supplier_id: supplier.id, price: 10, zone:) } let(:variant) { product.variants.first } let!(:order_cycle) { diff --git a/spec/system/consumer/producers_spec.rb b/spec/system/consumer/producers_spec.rb index 48832fbd62..5796810156 100644 --- a/spec/system/consumer/producers_spec.rb +++ b/spec/system/consumer/producers_spec.rb @@ -19,10 +19,10 @@ RSpec.describe ' let(:taxon_veg) { create(:taxon, name: 'Vegetables') } let!(:product1) { - create(:simple_product, supplier: producer1, primary_taxon: taxon_fruit) + create(:simple_product, supplier_id: producer1.id, primary_taxon: taxon_fruit) } let!(:product2) { - create(:simple_product, supplier: producer2, primary_taxon: taxon_veg) + create(:simple_product, supplier_id: producer2.id, primary_taxon: taxon_veg) } let(:shop) { create(:distributor_enterprise) } diff --git a/spec/system/consumer/shopping/cart_spec.rb b/spec/system/consumer/shopping/cart_spec.rb index 5ad9edef83..0e40f3ea97 100644 --- a/spec/system/consumer/shopping/cart_spec.rb +++ b/spec/system/consumer/shopping/cart_spec.rb @@ -24,11 +24,11 @@ RSpec.describe "full-page cart" do create(:enterprise_fee, amount: 11.00, tax_category: product_with_tax.tax_category) } let(:product_with_tax) { - create(:taxed_product, supplier:, zone:, price: 110.00, tax_rate_amount: 0.1, + create(:taxed_product, supplier_id: supplier.id, zone:, price: 110.00, tax_rate_amount: 0.1, included_in_price: true) } let(:product_with_fee) { - create(:simple_product, supplier:, price: 0.86, on_hand: 100) + create(:simple_product, supplier_id: supplier.id, price: 0.86, on_hand: 100) } let(:order) { create(:order, order_cycle:, distributor:) } diff --git a/spec/system/consumer/shopping/checkout_auth_spec.rb b/spec/system/consumer/shopping/checkout_auth_spec.rb index 0e3ea887f7..ed52e3ba19 100644 --- a/spec/system/consumer/shopping/checkout_auth_spec.rb +++ b/spec/system/consumer/shopping/checkout_auth_spec.rb @@ -17,7 +17,7 @@ RSpec.describe "As a consumer I want to check out my cart" do coordinator: create(:distributor_enterprise), variants: [product.variants.first]) } - let(:product) { create(:simple_product, supplier:) } + let(:product) { create(:simple_product, supplier_id: supplier.id) } let(:order) { create(:order, order_cycle:, distributor:) } let(:address) { create(:address, firstname: "Foo", lastname: "Bar") } let(:user) { create(:user, bill_address: address, ship_address: address) } diff --git a/spec/system/consumer/shopping/checkout_paypal_spec.rb b/spec/system/consumer/shopping/checkout_paypal_spec.rb index 8368867e2c..98947d099c 100644 --- a/spec/system/consumer/shopping/checkout_paypal_spec.rb +++ b/spec/system/consumer/shopping/checkout_paypal_spec.rb @@ -10,7 +10,7 @@ RSpec.describe "Check out with Paypal" do let(:distributor) { create(:distributor_enterprise) } let(:supplier) { create(:supplier_enterprise) } - let(:product) { create(:simple_product, supplier:) } + let(:product) { create(:simple_product, supplier_id: supplier.id) } let(:variant) { product.variants.first } let(:order_cycle) { create( diff --git a/spec/system/consumer/shopping/products_spec.rb b/spec/system/consumer/shopping/products_spec.rb index 81a21613be..f2ff5b0d5a 100644 --- a/spec/system/consumer/shopping/products_spec.rb +++ b/spec/system/consumer/shopping/products_spec.rb @@ -25,11 +25,11 @@ RSpec.describe "As a consumer I want to view products" do orders_close_at: 2.days.from_now) } let(:product) { - create(:simple_product, supplier:, primary_taxon: taxon, properties: [property], - name: "Beans") + create(:simple_product, supplier_id: supplier.id, primary_taxon: taxon, + properties: [property], name: "Beans") } let(:product2) { - create(:product, supplier:, primary_taxon: taxon2, properties: [property2], + create(:product, supplier_id: supplier.id, primary_taxon: taxon2, properties: [property2], name: "Chickpeas") } let(:variant) { product.variants.first } diff --git a/spec/system/consumer/shopping/shopping_spec.rb b/spec/system/consumer/shopping/shopping_spec.rb index 7868c73382..a7984c6318 100644 --- a/spec/system/consumer/shopping/shopping_spec.rb +++ b/spec/system/consumer/shopping/shopping_spec.rb @@ -22,7 +22,7 @@ RSpec.describe "As a consumer I want to shop with a distributor" do coordinator: create(:distributor_enterprise), orders_close_at: 3.days.from_now) } - let(:product) { create(:simple_product, supplier:, meta_keywords: "Domestic") } + let(:product) { create(:simple_product, supplier_id: supplier.id, meta_keywords: "Domestic") } let(:variant) { product.variants.first } let(:order) { create(:order, distributor:) } @@ -260,7 +260,7 @@ RSpec.describe "As a consumer I want to shop with a distributor" do context "one having 20 products" do before do 20.times do - product = create(:simple_product, supplier:) + product = create(:simple_product, supplier_id: supplier.id) add_variant_to_order_cycle(exchange1, product.variants.first) end end @@ -275,7 +275,7 @@ RSpec.describe "As a consumer I want to shop with a distributor" do context "another having 5 products" do before do 5.times do - product = create(:simple_product, supplier:) + product = create(:simple_product, supplier_id: supplier.id) add_variant_to_order_cycle(exchange2, product.variants.first) end end @@ -296,9 +296,12 @@ RSpec.describe "As a consumer I want to shop with a distributor" do display_as: 'displayedunderthename') end let(:product2) { - create(:simple_product, supplier:, name: "Meercats", meta_keywords: "Wild Fresh") + create(:simple_product, supplier_id: supplier.id, name: "Meercats", + meta_keywords: "Wild Fresh") + } + let(:variant3) { + create(:variant, product: product2, supplier:, price: 40, display_name: "Ferrets") } - let(:variant3) { create(:variant, product: product2, price: 40, display_name: "Ferrets") } let(:exchange) { Exchange.find(oc1.exchanges.to_enterprises(distributor).outgoing.first.id) } before do @@ -333,39 +336,50 @@ RSpec.describe "As a consumer I want to shop with a distributor" do end context "filtering search results" do - before do - visit shop_path - sleep(2) - end it "returns results when successful" do + visit shop_path + # When we see the Add button, it means product are loaded on the page + expect(page).to have_content("Add", count: 4) + fill_in "search", with: "74576345634XXXXXX" expect(page).to have_content "Sorry, no results found" expect(page).not_to have_content 'Meercats' + click_on "Clear search" # clears search by clicking text expect(page).to have_content("Add", count: 4) + fill_in "search", with: "Meer" # For product named "Meercats" expect(page).to have_content 'Meercats' expect(page).not_to have_content product.name + find("a.clear").click # clears search by clicking the X button expect(page).to have_content("Add", count: 4) end + it "returns results by looking at different columns in DB" do + visit shop_path + # When we see the Add button, it means product are loaded on the page + expect(page).to have_content("Add", count: 4) + # by keyword model: meta_keywords fill_in "search", with: "Wild" # For product named "Meercats" expect(page).to have_content 'Wild' find("a.clear").click + # by variant display name model: variant display_name fill_in "search", with: "Ferrets" # For variants named "Ferrets" within('div.pad-top') do expect(page).to have_content 'Ferrets' expect(page).not_to have_content 'Badgers' end + # model: variant display_as fill_in "search", with: "displayedunder" # "Badgers" within('div.pad-top') do expect(page).not_to have_content 'Ferrets' expect(page).to have_content 'Badgers' end + # model: Enterprise name fill_in "search", with: "Enterp" # Enterprise 1 sells nothing within('p.no-results') do @@ -375,7 +389,9 @@ RSpec.describe "As a consumer I want to shop with a distributor" do end context "when supplier uses property" do - let(:product3) { create(:simple_product, supplier:, inherits_properties: false) } + let(:product3) { + create(:simple_product, supplier_id: supplier.id, inherits_properties: false) + } before do add_variant_to_order_cycle(exchange, product3.variants.first) diff --git a/spec/system/consumer/shopping/unit_price_spec.rb b/spec/system/consumer/shopping/unit_price_spec.rb index 85da774add..95432649de 100644 --- a/spec/system/consumer/shopping/unit_price_spec.rb +++ b/spec/system/consumer/shopping/unit_price_spec.rb @@ -15,8 +15,7 @@ RSpec.describe "As a consumer, I want to check unit price information for a prod coordinator: create(:distributor_enterprise), orders_close_at: 2.days.from_now) } - let(:product) { create(:simple_product, supplier:) } - let(:variant) { product.variants.first } + let(:variant) { create(:variant, supplier:) } let(:order) { create(:order, distributor:) } let(:exchange1) { oc1.exchanges.to_enterprises(distributor).outgoing.first } let(:user) { create(:user, password: "password", password_confirmation: "password") } diff --git a/spec/system/consumer/shopping/variant_overrides_spec.rb b/spec/system/consumer/shopping/variant_overrides_spec.rb index cda13c4459..01dd56c349 100644 --- a/spec/system/consumer/shopping/variant_overrides_spec.rb +++ b/spec/system/consumer/shopping/variant_overrides_spec.rb @@ -18,21 +18,33 @@ RSpec.describe "shopping with variant overrides defined" do let(:outgoing_exchange) { oc.exchanges.outgoing.first } let(:sm) { hub.shipping_methods.first } let(:pm) { hub.payment_methods.first } - let(:product1) { create(:simple_product, supplier: producer) } - let(:product2) { create(:simple_product, supplier: producer) } - let(:product3) { create(:simple_product, supplier: producer, on_demand: true) } - let(:product4) { create(:simple_product, supplier: producer) } - let(:product1_variant1) { create(:variant, product: product1, price: 11.11, unit_value: 1) } - let(:product1_variant2) { create(:variant, product: product1, price: 22.22, unit_value: 2) } - let(:product2_variant1) { create(:variant, product: product2, price: 33.33, unit_value: 3) } - let(:product1_variant3) { create(:variant, product: product1, price: 44.44, unit_value: 4) } + let(:product1) { create(:simple_product, supplier_id: producer.id) } + let(:product2) { create(:simple_product, supplier_id: producer.id) } + let(:product3) { create(:simple_product, supplier_id: producer.id, on_demand: true) } + let(:product4) { create(:simple_product, supplier_id: producer.id) } + let(:product1_variant1) { + create(:variant, product: product1, price: 11.11, unit_value: 1, supplier: producer) + } + let(:product1_variant2) { + create(:variant, product: product1, price: 22.22, unit_value: 2, supplier: producer) + } + let(:product2_variant1) { + create(:variant, product: product2, price: 33.33, unit_value: 3, supplier: producer) + } + let(:product1_variant3) { + create(:variant, product: product1, price: 44.44, unit_value: 4, supplier: producer) + } let(:product3_variant1) { - create(:variant, product: product3, price: 55.55, unit_value: 5, on_demand: true) + create(:variant, product: product3, price: 55.55, unit_value: 5, on_demand: true, + supplier: producer) } let(:product3_variant2) { - create(:variant, product: product3, price: 66.66, unit_value: 6, on_demand: true) + create(:variant, product: product3, price: 66.66, unit_value: 6, on_demand: true, + supplier: producer) + } + let(:product4_variant1) { + create(:variant, product: product4, price: 77.77, unit_value: 7, supplier: producer) } - let(:product4_variant1) { create(:variant, product: product4, price: 77.77, unit_value: 7) } let!(:product1_variant1_override) { create(:variant_override, :use_producer_stock_settings, hub:, variant: product1_variant1, price: 55.55, count_on_hand: nil, diff --git a/spec/system/consumer/shops_spec.rb b/spec/system/consumer/shops_spec.rb index d36a9ca6c1..2fc8ddec4c 100644 --- a/spec/system/consumer/shops_spec.rb +++ b/spec/system/consumer/shops_spec.rb @@ -112,8 +112,8 @@ RSpec.describe 'Shops' do create(:simple_order_cycle, distributors: [d1, d2], coordinator: create(:distributor_enterprise)) } - let!(:p1) { create(:simple_product, supplier: producer) } - let!(:p2) { create(:simple_product, supplier: create(:supplier_enterprise)) } + let!(:p1) { create(:simple_product, supplier_id: producer.id) } + let!(:p2) { create(:simple_product, supplier_id: create(:supplier_enterprise).id) } let(:ex_d1) { order_cycle.exchanges.outgoing.where(receiver_id: d1).first } let(:ex_d2) { order_cycle.exchanges.outgoing.where(receiver_id: d2).first } @@ -184,7 +184,7 @@ RSpec.describe 'Shops' do variants: [product.variants.first] ) } - let(:product) { create(:simple_product, supplier: producer) } + let(:product) { create(:simple_product, supplier_id: producer.id) } before do product.set_property 'Local', 'XYZ 123' @@ -207,7 +207,7 @@ RSpec.describe 'Shops' do end describe "hub producer modal" do - let!(:product) { create(:simple_product, supplier: producer, primary_taxon: taxon) } + let!(:product) { create(:simple_product, supplier_id: producer.id, primary_taxon: taxon) } let!(:taxon) { create(:taxon, name: 'Fruit') } let!(:order_cycle) { create( diff --git a/spec/system/consumer/white_label_spec.rb b/spec/system/consumer/white_label_spec.rb index 73aa8065a9..25d9666c3c 100644 --- a/spec/system/consumer/white_label_spec.rb +++ b/spec/system/consumer/white_label_spec.rb @@ -11,7 +11,7 @@ RSpec.describe 'White label setting' do let!(:distributor) { create(:distributor_enterprise, with_payment_and_shipping: true) } let!(:shipping_method) { create(:shipping_method, distributors: [distributor]) } let(:product) { - create(:taxed_product, supplier: create(:supplier_enterprise), price: 10, + create(:taxed_product, supplier_id: create(:supplier_enterprise).id, price: 10, zone: create(:zone_with_member), tax_rate_amount: 0.1) } let!(:order_cycle) {