Migrate product image from master variant to product

This commit is contained in:
Matt-Yorkley
2023-05-22 11:39:38 +01:00
parent d8649fc9fb
commit 7dc1091bc2
37 changed files with 73 additions and 89 deletions

View File

@@ -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

View File

@@ -1,7 +0,0 @@
($ '#cancel_link').click (event) ->
event.preventDefault()
($ '.no-objects-found').show()
($ '#new_image_link').show()
($ '#images').html('')

View File

@@ -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

View File

@@ -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'

View File

@@ -168,7 +168,7 @@ class ProductsTableComponent < ViewComponentReflex::Component
def product_query_includes
[
master: [:images],
:image,
variants: [
:default_price,
:stock_locations,

View File

@@ -188,7 +188,7 @@ module Admin
if enterprises.present?
enterprises.includes(
supplied_products:
[:supplier, :variants, { master: [:images] }]
[:supplier, :variants, :image]
)
end
when :index

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -11,7 +11,7 @@ module Api
end
def image_url
object.images.first&.url(:mini)
object.image&.url(:mini)
end
def master_id

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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/

View File

@@ -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 }

View File

@@ -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'

View File

@@ -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

View File

@@ -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))

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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")

View File

@@ -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
}
)

View File

@@ -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

View File

@@ -29,9 +29,9 @@ describe 'Products service', ->
id: 9
master: {}
variants: []
images: [
image: {
large_url: 'foo.png'
]
}
currentOrder =
line_items: []
currentHub =

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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: