diff --git a/engines/dfc_provider/app/controllers/dfc_provider/application_controller.rb b/engines/dfc_provider/app/controllers/dfc_provider/application_controller.rb index 16a830d336..16ebe0411e 100644 --- a/engines/dfc_provider/app/controllers/dfc_provider/application_controller.rb +++ b/engines/dfc_provider/app/controllers/dfc_provider/application_controller.rb @@ -59,5 +59,9 @@ module DfcProvider def current_ability @current_ability ||= Spree::Ability.new(current_user) end + + def import + DfcIo.import(request.body) + end end end diff --git a/engines/dfc_provider/app/controllers/dfc_provider/offers_controller.rb b/engines/dfc_provider/app/controllers/dfc_provider/offers_controller.rb new file mode 100644 index 0000000000..437897c524 --- /dev/null +++ b/engines/dfc_provider/app/controllers/dfc_provider/offers_controller.rb @@ -0,0 +1,28 @@ +# frozen_string_literal: true + +module DfcProvider + class OffersController < DfcProvider::ApplicationController + before_action :check_enterprise + + def show + subject = OfferBuilder.build(variant) + render json: DfcIo.export(subject) + end + + def update + offer = import + + return head :bad_request unless offer + + OfferBuilder.apply(offer, variant) + + variant.save! + end + + private + + def variant + @variant ||= current_enterprise.supplied_variants.find(params[:id]) + end + end +end 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 21b4c4b59d..d4142f25d6 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 @@ -48,10 +48,6 @@ module DfcProvider private - def import - DfcIo.import(request.body) - end - def variant @variant ||= current_enterprise.supplied_variants.find(params[:id]) end diff --git a/engines/dfc_provider/app/services/dfc_builder.rb b/engines/dfc_provider/app/services/dfc_builder.rb index b3b3373172..5411d8ab28 100644 --- a/engines/dfc_provider/app/services/dfc_builder.rb +++ b/engines/dfc_provider/app/services/dfc_builder.rb @@ -12,22 +12,7 @@ class DfcBuilder id, product:, sku: variant.sku, stockLimitation: stock_limitation(variant), - offers: [offer(variant)], - ) - end - - def self.offer(variant) - # We don't have an endpoint for offers yet and this URL is only a - # placeholder for now. The offer is actually affected by order cycle and - # customer tags. We'll solve that at a later stage. - enterprise_url = urls.enterprise_url(id: variant.product.supplier_id) - id = "#{enterprise_url}/offers/#{variant.id}" - offered_to = [] - - DataFoodConsortium::Connector::Offer.new( - id, offeredTo: offered_to, - price: variant.price.to_f, - stockLimitation: stock_limitation(variant), + offers: [OfferBuilder.build(variant)], ) end @@ -37,12 +22,6 @@ class DfcBuilder variant.on_demand ? nil : variant.total_on_hand end - # OFN product categories (taxons) are currently not standardised. - # This is just a dummy value for demos. - def self.product_type - DfcLoader.connector.PRODUCT_TYPES.VEGETABLE.NON_LOCAL_VEGETABLE - end - def self.urls DfcProvider::Engine.routes.url_helpers end diff --git a/engines/dfc_provider/app/services/offer_builder.rb b/engines/dfc_provider/app/services/offer_builder.rb new file mode 100644 index 0000000000..b499c1d945 --- /dev/null +++ b/engines/dfc_provider/app/services/offer_builder.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +class OfferBuilder < DfcBuilder + def self.build(variant) + id = urls.enterprise_offer_url( + enterprise_id: variant.product.supplier_id, + id: variant.id, + ) + + DataFoodConsortium::Connector::Offer.new( + id, + price: variant.price.to_f, + stockLimitation: stock_limitation(variant), + ) + end + + def self.apply(offer, variant) + variant.on_hand = offer.stockLimitation + variant.price = offer.price + end +end diff --git a/engines/dfc_provider/app/services/supplied_product_builder.rb b/engines/dfc_provider/app/services/supplied_product_builder.rb index 2d776eafdb..69a9871c17 100644 --- a/engines/dfc_provider/app/services/supplied_product_builder.rb +++ b/engines/dfc_provider/app/services/supplied_product_builder.rb @@ -18,6 +18,12 @@ class SuppliedProductBuilder < DfcBuilder ) end + # OFN product categories (taxons) are currently not standardised. + # This is just a dummy value for demos. + def self.product_type + DfcLoader.connector.PRODUCT_TYPES.VEGETABLE.NON_LOCAL_VEGETABLE + end + def self.import_variant(supplied_product) product_id = supplied_product.spree_product_id @@ -49,10 +55,10 @@ class SuppliedProductBuilder < DfcBuilder def self.apply(supplied_product, variant) variant.product.assign_attributes( - name: supplied_product.name, description: supplied_product.description, ) + variant.display_name = supplied_product.name QuantitativeValueBuilder.apply(supplied_product.quantity, variant.product) variant.unit_value = variant.product.unit_value end diff --git a/engines/dfc_provider/config/routes.rb b/engines/dfc_provider/config/routes.rb index 26762340ac..5bde8f23ba 100644 --- a/engines/dfc_provider/config/routes.rb +++ b/engines/dfc_provider/config/routes.rb @@ -4,6 +4,7 @@ DfcProvider::Engine.routes.draw do resources :addresses, only: [:show] resources :enterprises, only: [:show] do resources :catalog_items, only: [:index, :show, :update] + resources :offers, only: [:show, :update] resources :supplied_products, only: [:create, :show, :update] resources :social_medias, only: [:show] end diff --git a/engines/dfc_provider/spec/requests/offers_spec.rb b/engines/dfc_provider/spec/requests/offers_spec.rb new file mode 100644 index 0000000000..826df7be76 --- /dev/null +++ b/engines/dfc_provider/spec/requests/offers_spec.rb @@ -0,0 +1,84 @@ +# frozen_string_literal: true + +require_relative "../swagger_helper" + +describe "Offers", type: :request, swagger_doc: "dfc.yaml", rswag_autodoc: true do + let!(:user) { create(:oidc_user) } + let!(:enterprise) { create(:distributor_enterprise, id: 10_000, owner: user) } + let!(:product) { + create( + :product, + id: 90_000, + supplier: enterprise, name: "Pesto", description: "Basil Pesto", + variants: [variant], + ) + } + let(:variant) { build(:base_variant, id: 10_001, unit_value: 1) } + + before { login_as user } + + path "/api/dfc/enterprises/{enterprise_id}/offers/{id}" do + parameter name: :enterprise_id, in: :path, type: :string + parameter name: :id, in: :path, type: :string + + let(:enterprise_id) { enterprise.id } + + get "Show Offer" do + produces "application/json" + + response "200", "success" do + let(:id) { variant.id } + + run_test! + end + end + + put "Update Offer" do + consumes "application/json" + + parameter name: :offer, in: :body, schema: { + example: { + '@context': "https://www.datafoodconsortium.org", + '@id': "http://test.host/api/dfc/enterprises/10000/offers/10001", + '@type': "dfc-b:Offer", + 'dfc-b:hasPrice': 9.99, + 'dfc-b:stockLimitation': 7 + } + } + + let(:id) { variant.id } + let(:offer) { offer_example } + let(:offer_example) { |example| + example.metadata[:operation][:parameters].first[:schema][:example] + } + + response "204", "success" do + context "with missing stockLimitation" do + let(:offer) { + offer_example.dup.tap do |o| + o.delete(:'dfc-b:stockLimitation') + end + } + + it "sets the variant to on demand" do |example| + pending "DFC Connector needs to support unset values." + + expect { + submit_request(example.metadata) + variant.reload + }.to change { variant.on_demand }.to(true) + .and change { variant.on_hand }.by(0) + end + end + + it "updates a variant" do |example| + expect { + submit_request(example.metadata) + variant.reload + }.to change { variant.price }.to(9.99) + .and change { variant.on_hand }.to(7) + end + end + end + end +end diff --git a/engines/dfc_provider/spec/requests/supplied_products_spec.rb b/engines/dfc_provider/spec/requests/supplied_products_spec.rb index 3e59aa2bad..8f39d971b7 100644 --- a/engines/dfc_provider/spec/requests/supplied_products_spec.rb +++ b/engines/dfc_provider/spec/requests/supplied_products_spec.rb @@ -188,7 +188,7 @@ describe "SuppliedProducts", type: :request, swagger_doc: "dfc.yaml", rswag_auto submit_request(example.metadata) variant.reload }.to change { variant.description }.to("DFC-Pesto updated") - .and change { variant.name }.to("Pesto novo") + .and change { variant.display_name }.to("Pesto novo") .and change { variant.unit_value }.to(17) end end diff --git a/engines/dfc_provider/spec/services/offer_builder_spec.rb b/engines/dfc_provider/spec/services/offer_builder_spec.rb index 3d0bf3256a..15ef9c4dda 100644 --- a/engines/dfc_provider/spec/services/offer_builder_spec.rb +++ b/engines/dfc_provider/spec/services/offer_builder_spec.rb @@ -2,7 +2,7 @@ require_relative "../spec_helper" -describe DfcBuilder do +describe OfferBuilder do let(:variant) { build(:variant) } describe ".offer" do @@ -11,7 +11,7 @@ describe DfcBuilder do variant.save! variant.on_hand = 5 - offer = DfcBuilder.offer(variant) + offer = OfferBuilder.build(variant) expect(offer.stockLimitation).to eq 5 end @@ -22,7 +22,7 @@ describe DfcBuilder do variant.on_hand = 5 variant.on_demand = true - offer = DfcBuilder.offer(variant) + offer = OfferBuilder.build(variant) expect(offer.stockLimitation).to eq nil end diff --git a/swagger/dfc.yaml b/swagger/dfc.yaml index a7a8df3e83..6751e2ec05 100644 --- a/swagger/dfc.yaml +++ b/swagger/dfc.yaml @@ -409,6 +409,53 @@ paths: dfc-b:URL: https://facebook.com/user '404': description: not found + "/api/dfc/enterprises/{enterprise_id}/offers/{id}": + parameters: + - name: enterprise_id + in: path + required: true + schema: + type: string + - name: id + in: path + required: true + schema: + type: string + get: + summary: Show Offer + tags: + - Offers + responses: + '200': + description: success + content: + application/json: + examples: + test_example: + value: + "@context": https://www.datafoodconsortium.org + "@id": http://test.host/api/dfc/enterprises/10000/offers/10001 + "@type": dfc-b:Offer + dfc-b:hasPrice: 19.99 + dfc-b:stockLimitation: 5 + put: + summary: Update Offer + parameters: [] + tags: + - Offers + responses: + '204': + description: success + requestBody: + content: + application/json: + schema: + example: + "@context": https://www.datafoodconsortium.org + "@id": http://test.host/api/dfc/enterprises/10000/offers/10001 + "@type": dfc-b:Offer + dfc-b:hasPrice: 9.99 + dfc-b:stockLimitation: 7 "/api/dfc/persons/{id}": get: summary: Show person @@ -489,7 +536,7 @@ paths: "@context": https://www.datafoodconsortium.org "@id": http://test.host/api/dfc/enterprises/10000/supplied_products/10001 "@type": dfc-b:SuppliedProduct - dfc-b:name: Apple - 6g + dfc-b:name: Apple (6g) dfc-b:description: A delicious heritage apple dfc-b:hasType: dfc-pt:non-local-vegetable dfc-b:hasQuantity: