mirror of
https://github.com/openfoodfoundation/openfoodnetwork
synced 2026-01-30 21:27:17 +00:00
Migrate product image from master variant to product
This commit is contained in:
@@ -1,15 +0,0 @@
|
||||
$ ->
|
||||
($ '#new_image_link').click (event) ->
|
||||
event.preventDefault()
|
||||
|
||||
($ '.no-objects-found').hide()
|
||||
|
||||
($ this).hide()
|
||||
$.ajax
|
||||
type: 'GET'
|
||||
url: @href
|
||||
data: (
|
||||
authenticity_token: AUTH_TOKEN
|
||||
)
|
||||
success: (r) ->
|
||||
($ '#images').html r
|
||||
@@ -1,7 +0,0 @@
|
||||
($ '#cancel_link').click (event) ->
|
||||
event.preventDefault()
|
||||
|
||||
($ '.no-objects-found').show()
|
||||
|
||||
($ '#new_image_link').show()
|
||||
($ '#images').html('')
|
||||
@@ -33,9 +33,9 @@ angular.module('Darkswarm').factory 'Products', (OrderCycleResource, OrderCycle,
|
||||
prices = (v.price for v in product.variants)
|
||||
product.price = Math.min.apply(null, prices)
|
||||
product.hasVariants = product.variants?.length > 0
|
||||
product.primaryImage = product.images[0]?.small_url if product.images
|
||||
product.primaryImage = product.image?.small_url if product.image
|
||||
product.primaryImageOrMissing = product.primaryImage || "/noimage/small.png"
|
||||
product.largeImage = product.images[0]?.large_url if product.images
|
||||
product.largeImage = product.image?.large_url if product.image
|
||||
|
||||
dereference: ->
|
||||
for product in @fetched_products
|
||||
|
||||
@@ -6,7 +6,7 @@ class ProductComponent < ViewComponentReflex::Component
|
||||
def initialize(product:, columns:)
|
||||
super
|
||||
@product = product
|
||||
@image = @product.images[0] if product.images.any?
|
||||
@image = @product.image if product.image.present?
|
||||
@columns = columns.map do |c|
|
||||
{
|
||||
id: c[:value],
|
||||
@@ -28,7 +28,7 @@ class ProductComponent < ViewComponentReflex::Component
|
||||
when 'price'
|
||||
@product.price
|
||||
when 'unit'
|
||||
"#{@product.unit_value} #{@product.variant_unit}"
|
||||
"#{@product.variants.first.unit_value} #{@product.variant_unit}"
|
||||
when 'producer'
|
||||
@product.supplier.name
|
||||
when 'category'
|
||||
|
||||
@@ -168,7 +168,7 @@ class ProductsTableComponent < ViewComponentReflex::Component
|
||||
|
||||
def product_query_includes
|
||||
[
|
||||
master: [:images],
|
||||
:image,
|
||||
variants: [
|
||||
:default_price,
|
||||
:stock_locations,
|
||||
|
||||
@@ -188,7 +188,7 @@ module Admin
|
||||
if enterprises.present?
|
||||
enterprises.includes(
|
||||
supplied_products:
|
||||
[:supplier, :variants, { master: [:images] }]
|
||||
[:supplier, :variants, :image]
|
||||
)
|
||||
end
|
||||
when :index
|
||||
|
||||
@@ -9,9 +9,9 @@ module Api
|
||||
product = Spree::Product.find(params[:product_id])
|
||||
authorize! :update, product
|
||||
|
||||
image = product.images.first || Spree::Image.new(
|
||||
viewable_id: product.master.id,
|
||||
viewable_type: 'Spree::Variant'
|
||||
image = product.image || Spree::Image.new(
|
||||
viewable_id: product.id,
|
||||
viewable_type: 'Spree::Product'
|
||||
)
|
||||
|
||||
success_status = image.persisted? ? :ok : :created
|
||||
|
||||
@@ -116,7 +116,7 @@ module Api
|
||||
|
||||
def product_query_includes
|
||||
[
|
||||
master: { images: { attachment_attachment: :blob } },
|
||||
image: { attachment_attachment: :blob },
|
||||
variants: [:default_price, :stock_locations, :stock_items, :variant_overrides]
|
||||
]
|
||||
end
|
||||
|
||||
@@ -25,6 +25,7 @@ module Spree
|
||||
set_viewable
|
||||
|
||||
@object.attributes = permitted_resource_params
|
||||
|
||||
if @object.save
|
||||
flash[:success] = flash_message_for(@object, :successfully_created)
|
||||
redirect_to spree.admin_product_images_url(params[:product_id], @url_filters)
|
||||
@@ -62,6 +63,18 @@ module Spree
|
||||
|
||||
private
|
||||
|
||||
def collection
|
||||
parent.image
|
||||
end
|
||||
|
||||
def find_resource
|
||||
parent.image
|
||||
end
|
||||
|
||||
def build_resource
|
||||
Spree::Image.new(viewable: parent)
|
||||
end
|
||||
|
||||
def location_after_save
|
||||
spree.admin_product_images_url(@product)
|
||||
end
|
||||
@@ -75,7 +88,7 @@ module Spree
|
||||
end
|
||||
|
||||
def set_viewable
|
||||
@image.viewable_type = 'Spree::Variant'
|
||||
@image.viewable_type = 'Spree::Product'
|
||||
@image.viewable_id = params[:image][:viewable_id]
|
||||
end
|
||||
|
||||
|
||||
@@ -121,8 +121,8 @@ module Spree
|
||||
end
|
||||
|
||||
def product_includes
|
||||
[{ variants: [:images] },
|
||||
{ master: [:images, :default_price] }]
|
||||
[:image, { variants: [:images] },
|
||||
{ master: [:default_price] }]
|
||||
end
|
||||
|
||||
def collection_actions
|
||||
|
||||
@@ -43,6 +43,8 @@ module Spree
|
||||
belongs_to :supplier, class_name: 'Enterprise', touch: true
|
||||
belongs_to :primary_taxon, class_name: 'Spree::Taxon', touch: true
|
||||
|
||||
has_one :image, class_name: "Spree::Image", as: :viewable, dependent: :destroy
|
||||
|
||||
has_one :master,
|
||||
-> { where is_master: true },
|
||||
class_name: 'Spree::Variant',
|
||||
@@ -75,9 +77,6 @@ module Spree
|
||||
)
|
||||
}
|
||||
|
||||
delegate_belongs_to :master, :images
|
||||
delegate :images_attributes=, to: :master
|
||||
|
||||
# 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
|
||||
@@ -89,6 +88,7 @@ module Spree
|
||||
through: :variants_including_master
|
||||
|
||||
accepts_nested_attributes_for :variants, allow_destroy: true
|
||||
accepts_nested_attributes_for :image
|
||||
|
||||
validates :name, presence: true
|
||||
validates :permalink, presence: true
|
||||
@@ -106,7 +106,7 @@ module Spree
|
||||
presence: { if: ->(p) { %w(weight volume).include? p.variant_unit } }
|
||||
validates :variant_unit_name,
|
||||
presence: { if: ->(p) { p.variant_unit == 'items' } }
|
||||
validate :validate_image_for_master
|
||||
validate :validate_image
|
||||
|
||||
accepts_nested_attributes_for :product_properties,
|
||||
allow_destroy: true,
|
||||
@@ -410,8 +410,8 @@ module Spree
|
||||
self.permalink = create_unique_permalink(requested.parameterize)
|
||||
end
|
||||
|
||||
def validate_image_for_master
|
||||
return if master.images.all?(&:valid?)
|
||||
def validate_image
|
||||
return if image.blank? || image.valid?
|
||||
|
||||
errors.add(:base, I18n.t('spree.admin.products.image_not_processable'))
|
||||
end
|
||||
|
||||
@@ -11,7 +11,7 @@ module Api
|
||||
end
|
||||
|
||||
def image_url
|
||||
object.images.first&.url(:mini)
|
||||
object.image&.url(:mini)
|
||||
end
|
||||
|
||||
def master_id
|
||||
|
||||
@@ -27,11 +27,11 @@ module Api
|
||||
end
|
||||
|
||||
def image_url
|
||||
object.images.first&.url(:product) || Spree::Image.default_image_url(:product)
|
||||
object.image&.url(:product) || Spree::Image.default_image_url(:product)
|
||||
end
|
||||
|
||||
def thumb_url
|
||||
object.images.first&.url(:mini) || Spree::Image.default_image_url(:mini)
|
||||
object.image&.url(:mini) || Spree::Image.default_image_url(:mini)
|
||||
end
|
||||
|
||||
def on_hand
|
||||
|
||||
@@ -33,7 +33,7 @@ module Api
|
||||
end
|
||||
|
||||
def image
|
||||
options[:image] || object.product.images.first&.url(:mini)
|
||||
options[:image] || object.product.image&.url(:mini)
|
||||
end
|
||||
|
||||
def in_stock
|
||||
|
||||
@@ -12,7 +12,7 @@ class Api::ProductSerializer < ActiveModel::Serializer
|
||||
has_one :primary_taxon, serializer: Api::TaxonSerializer
|
||||
has_many :taxons, serializer: Api::IdSerializer
|
||||
|
||||
has_many :images, serializer: Api::ImageSerializer
|
||||
has_one :image, serializer: Api::ImageSerializer
|
||||
has_one :supplier, serializer: Api::IdSerializer
|
||||
|
||||
# return an unformatted descripton
|
||||
|
||||
@@ -40,7 +40,7 @@ class Api::VariantSerializer < ActiveModel::Serializer
|
||||
end
|
||||
|
||||
def thumb_url
|
||||
object.product.images.first&.url(:mini) || Spree::Image.default_image_url(:mini)
|
||||
object.product.image&.url(:mini) || Spree::Image.default_image_url(:mini)
|
||||
end
|
||||
|
||||
def unit_price_price
|
||||
|
||||
@@ -96,7 +96,7 @@ class ExchangeProductsRenderer
|
||||
return enterprises if enterprises.empty?
|
||||
|
||||
enterprises.includes(
|
||||
supplied_products: [:supplier, :variants, { master: [:images] }]
|
||||
supplied_products: [:supplier, :variants, :image]
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -8,8 +8,8 @@ class ImageImporter
|
||||
|
||||
Spree::Image.create(
|
||||
attachment: { io: file, filename: filename },
|
||||
viewable_id: product.master.id,
|
||||
viewable_type: Spree::Variant,
|
||||
viewable_id: product.id,
|
||||
viewable_type: Spree::Product,
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -11,7 +11,7 @@ module PermittedAttributes
|
||||
:meta_keywords, :notes, :inherits_properties,
|
||||
{ product_properties_attributes: [:id, :property_name, :value],
|
||||
variants_attributes: [PermittedAttributes::Variant.attributes],
|
||||
images_attributes: [:attachment] }
|
||||
image_attributes: [:attachment] }
|
||||
]
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
%div
|
||||
= f.hidden_field :viewable_id, value: @product.id
|
||||
|
||||
.four.columns.alpha
|
||||
.field
|
||||
= f.label t('spree.filename')
|
||||
%br/
|
||||
= f.file_field :attachment
|
||||
.field
|
||||
= f.label Spree::Variant.model_name.human
|
||||
%br/
|
||||
= f.select :viewable_id, @variants, {}, {class: 'select2 fullwidth'}
|
||||
.field.omega.five.columns
|
||||
= f.label t('spree.alt_text')
|
||||
%br/
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
#images
|
||||
|
||||
- unless @product.images.any? || @product.variant_images.any?
|
||||
- unless @product.image.present?
|
||||
.no-objects-found
|
||||
= t('spree.no_images_found')
|
||||
\.
|
||||
@@ -25,7 +25,7 @@
|
||||
%th= t('spree.alt_text')
|
||||
%th.actions
|
||||
%tbody
|
||||
- (@product.variant_images).each do |image|
|
||||
- ([@product.image]).each do |image|
|
||||
- tr_class = cycle('odd', 'even')
|
||||
- tr_id = spree_dom_id(image)
|
||||
%tr{class: tr_class, id: tr_id }
|
||||
|
||||
@@ -7,5 +7,3 @@
|
||||
.form-buttons.filter-actions.actions
|
||||
= button t('actions.create'), 'icon-ok'
|
||||
= link_to_with_icon 'icon-remove', t('spree.actions.cancel'), admin_product_images_url(@product, @url_filters), id: 'cancel_link', class: 'button'
|
||||
|
||||
= javascript_include_tag 'admin/spree/images/new.js'
|
||||
|
||||
@@ -101,7 +101,7 @@
|
||||
.row
|
||||
= image_tag Spree::Image.default_image_url(:product), class: "four columns alpha"
|
||||
.row
|
||||
= f.fields_for 'images_attributes[]', f.object.images.build do |image_fields|
|
||||
= f.fields_for 'image_attributes[]', f.object.build_image do |image_fields|
|
||||
= image_fields.file_field :attachment
|
||||
.sixteen.columns.alpha
|
||||
.form-buttons.filter-actions.actions
|
||||
|
||||
@@ -1 +1 @@
|
||||
= image_tag(variant.product.images.first&.url(:mini) || Spree::Image.default_image_url(:mini))
|
||||
= image_tag(variant.product.image&.url(:mini) || Spree::Image.default_image_url(:mini))
|
||||
|
||||
@@ -26,6 +26,7 @@ module Spree
|
||||
new_product.updated_at = nil
|
||||
new_product.product_properties = reset_properties
|
||||
new_product.master = duplicate_master
|
||||
new_product.image = duplicate_image(product.image) if product.image&.attached?
|
||||
new_product.variants = duplicate_variants
|
||||
end
|
||||
end
|
||||
|
||||
@@ -25,7 +25,7 @@ namespace :ofn do
|
||||
next
|
||||
end
|
||||
|
||||
if product.images.first.nil?
|
||||
if product.image.nil?
|
||||
ImageImporter.new.import(entry[:image_url], product)
|
||||
puts " image added."
|
||||
else
|
||||
|
||||
@@ -25,7 +25,7 @@ describe Api::V0::ProductImagesController, type: :controller do
|
||||
}
|
||||
|
||||
expect(response.status).to eq 201
|
||||
expect(product_without_image.reload.images.first.id).to eq json_response['id']
|
||||
expect(product_without_image.reload.image.id).to eq json_response['id']
|
||||
end
|
||||
|
||||
it "updates an existing product image" do
|
||||
@@ -34,7 +34,7 @@ describe Api::V0::ProductImagesController, type: :controller do
|
||||
}
|
||||
|
||||
expect(response.status).to eq 200
|
||||
expect(product_with_image.reload.images.first.id).to eq json_response['id']
|
||||
expect(product_with_image.reload.image.id).to eq json_response['id']
|
||||
end
|
||||
|
||||
it "reports errors when saving fails" do
|
||||
@@ -43,7 +43,7 @@ describe Api::V0::ProductImagesController, type: :controller do
|
||||
}
|
||||
|
||||
expect(response.status).to eq 422
|
||||
expect(product_without_image.images.count).to eq 0
|
||||
expect(product_without_image.image).to be_nil
|
||||
expect(json_response["id"]).to eq nil
|
||||
expect(json_response["errors"]).to include "Attachment has an invalid content type"
|
||||
end
|
||||
|
||||
@@ -33,7 +33,7 @@ describe Api::V0::ProductsController, type: :controller do
|
||||
end
|
||||
|
||||
it "gets a single product" do
|
||||
product.master.images.create!(attachment: image("thinking-cat.jpg"))
|
||||
product.create_image!(attachment: image("thinking-cat.jpg"))
|
||||
product.variants.create!(unit_value: "1", unit_description: "thing", price: 1)
|
||||
product.variants.first.images.create!(attachment: image("thinking-cat.jpg"))
|
||||
product.set_property("spree", "rocks")
|
||||
|
||||
@@ -158,8 +158,8 @@ describe Spree::Admin::ProductsController, type: :controller do
|
||||
tempfile: Tempfile.new('unsupported_image_format.exr')
|
||||
)
|
||||
product_attrs_with_image = product_attrs.merge(
|
||||
images_attributes: {
|
||||
'0' => { attachment: product_image }
|
||||
image_attributes: {
|
||||
attachment: product_image
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@@ -42,8 +42,8 @@ FactoryBot.define do
|
||||
factory :product_with_image, parent: :product do
|
||||
after(:create) do |product|
|
||||
Spree::Image.create(attachment: white_logo_file,
|
||||
viewable_id: product.master.id,
|
||||
viewable_type: 'Spree::Variant')
|
||||
viewable_id: product.id,
|
||||
viewable_type: 'Spree::Product')
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -29,9 +29,9 @@ describe 'Products service', ->
|
||||
id: 9
|
||||
master: {}
|
||||
variants: []
|
||||
images: [
|
||||
image: {
|
||||
large_url: 'foo.png'
|
||||
]
|
||||
}
|
||||
currentOrder =
|
||||
line_items: []
|
||||
currentHub =
|
||||
|
||||
@@ -17,7 +17,7 @@ module Spree
|
||||
clone = product.duplicate
|
||||
expect(clone.name).to eq 'COPY OF ' + product.name
|
||||
expect(clone.master.sku).to eq ''
|
||||
expect(clone.images.size).to eq product.images.size
|
||||
expect(clone.image).to eq product.image
|
||||
end
|
||||
end
|
||||
|
||||
@@ -421,11 +421,11 @@ module Spree
|
||||
end
|
||||
end
|
||||
|
||||
describe "#validate_image_for_master" do
|
||||
let(:product) { build_stubbed(:simple_product) }
|
||||
describe "#validate_image" do
|
||||
let(:product) { create(:product_with_image) }
|
||||
|
||||
context "when the image attached to the master variant is invalid" do
|
||||
before { product.master.images.new.errors.add(:image_not_processable, "invalid") }
|
||||
context "when the image is invalid" do
|
||||
before { expect(product.image).to receive(:valid?).and_return(false) }
|
||||
|
||||
it "adds an error message to the base object" do
|
||||
expect(product).not_to be_valid
|
||||
|
||||
@@ -28,7 +28,7 @@ describe Api::ProductSerializer do
|
||||
it "serializes various attributes" do
|
||||
expect(serializer.serializable_hash.keys).to eq [
|
||||
:id, :name, :permalink, :meta_keywords, :group_buy, :notes, :description, :description_html,
|
||||
:properties_with_values, :variants, :primary_taxon, :taxons, :images, :supplier
|
||||
:properties_with_values, :variants, :primary_taxon, :taxons, :image, :supplier
|
||||
]
|
||||
end
|
||||
|
||||
|
||||
@@ -14,8 +14,8 @@ describe ImageImporter do
|
||||
Spree::Image.count
|
||||
}.by(1)
|
||||
|
||||
expect(product.images.count).to eq 1
|
||||
expect(product.reload.images.first.attachment_blob.byte_size).to eq 6274
|
||||
expect(product.image).to_not be_nil
|
||||
expect(product.reload.image.attachment_blob.byte_size).to eq 6274
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -527,10 +527,6 @@ describe '
|
||||
|
||||
page.find('a#new_image_link').click
|
||||
|
||||
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_images_path(product, 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)
|
||||
@@ -628,14 +624,14 @@ describe '
|
||||
|
||||
visit spree.admin_product_images_path(product)
|
||||
expect(page).to have_selector "table.index td img"
|
||||
expect(product.reload.images.count).to eq 1
|
||||
expect(product.reload.image).to_not be_nil
|
||||
|
||||
accept_alert do
|
||||
page.find('a.delete-resource').click
|
||||
end
|
||||
|
||||
expect(page).to_not have_selector "table.index td img"
|
||||
expect(product.reload.images.count).to eq 0
|
||||
expect(product.reload.image).to be_nil
|
||||
end
|
||||
|
||||
it "deleting product image including url filter" do
|
||||
|
||||
@@ -752,7 +752,7 @@ describe '
|
||||
|
||||
def xero_invoice_li_row(line_item, opts = {})
|
||||
tax_type = line_item.has_tax? ? 'GST on Income' : 'GST Free Income'
|
||||
xero_invoice_row line_item.product.sku, line_item.product_and_full_name,
|
||||
xero_invoice_row line_item.variant.sku, line_item.product_and_full_name,
|
||||
line_item.price.to_s, line_item.quantity.to_s, tax_type, opts
|
||||
end
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@ describe "spree/orders/show.html.haml" do
|
||||
end
|
||||
|
||||
it "shows product images" do
|
||||
order.line_items.first.variant.product.images << Spree::Image.new(
|
||||
order.line_items.first.variant.product.image = Spree::Image.new(
|
||||
attachment: fixture_file_upload("logo.png", "image/png")
|
||||
)
|
||||
|
||||
@@ -43,7 +43,7 @@ describe "spree/orders/show.html.haml" do
|
||||
end
|
||||
|
||||
it "handles broken images" do
|
||||
image, = order.line_items.first.variant.product.images << Spree::Image.new(
|
||||
image, = order.line_items.first.variant.product.image = Spree::Image.new(
|
||||
attachment: fixture_file_upload("logo.png", "image/png")
|
||||
)
|
||||
# This image is not "variable" and can't be resized:
|
||||
|
||||
Reference in New Issue
Block a user