From d7e8c47498b250ead1e2581997176465de496b02 Mon Sep 17 00:00:00 2001 From: panhachom Date: Tue, 24 Dec 2024 11:07:35 +0700 Subject: [PATCH] close #2174 dynamic kyc fields --- app/controllers/spree/admin/kyc_controller.rb | 12 +++- .../api/v2/storefront/guests_controller.rb | 3 +- app/models/spree_cm_commissioner/guest.rb | 22 ++++-- .../product_decorator.rb | 2 + .../product_serializer_decorator.rb | 2 +- .../v2/operator/guest_serializer.rb | 1 + .../v2/storefront/guest_serializer.rb | 4 +- .../admin/kyc/_dynamic_kyc_fields.html.erb | 12 ++++ .../admin/kyc/_dynamic_kyc_form.html.erb | 68 +++++++++++++++++++ app/views/spree/admin/kyc/edit.html.erb | 41 +++++------ config/locales/en.yml | 2 + config/routes.rb | 1 + ...025924_add_dynamic_kyc_to_spree_product.rb | 7 ++ ...ic_field_to_spree_cm_commissioner_guest.rb | 7 ++ .../v2/storefront/guest_serializer_spec.rb | 3 +- .../v2/storefront/product_serializer_spec.rb | 3 +- 16 files changed, 159 insertions(+), 31 deletions(-) create mode 100644 app/views/spree/admin/kyc/_dynamic_kyc_fields.html.erb create mode 100644 app/views/spree/admin/kyc/_dynamic_kyc_form.html.erb create mode 100644 db/migrate/20241219025924_add_dynamic_kyc_to_spree_product.rb create mode 100644 db/migrate/20241219105707_add_dynamic_field_to_spree_cm_commissioner_guest.rb diff --git a/app/controllers/spree/admin/kyc_controller.rb b/app/controllers/spree/admin/kyc_controller.rb index cf1bc6248..570550eb6 100644 --- a/app/controllers/spree/admin/kyc_controller.rb +++ b/app/controllers/spree/admin/kyc_controller.rb @@ -11,8 +11,18 @@ class KycController < Spree::Admin::ResourceController # @overrided def permitted_resource_params kyc_result = calculate_kyc_value(params[:product]) + params.require(:product).permit(:allowed_upload_later, dynamic_kyc: %i[key label attype]).merge(kyc: kyc_result) + end + + def remove_field + key = params[:key] + @product.dynamic_kyc.reject! { |field| field['key'] == key } - params.require(:product).permit(:allowed_upload_later).merge(kyc: kyc_result) + if @product.save + render json: { success: true } + else + render json: { success: false, errors: @product.errors.full_messages }, status: :unprocessable_entity + end end def flash_error diff --git a/app/controllers/spree/api/v2/storefront/guests_controller.rb b/app/controllers/spree/api/v2/storefront/guests_controller.rb index 323d67fcb..e1259234e 100644 --- a/app/controllers/spree/api/v2/storefront/guests_controller.rb +++ b/app/controllers/spree/api/v2/storefront/guests_controller.rb @@ -73,7 +73,8 @@ def guest_params :address, :other_organization, :expectation, - :upload_later + :upload_later, + :dynamic_field ) end end diff --git a/app/models/spree_cm_commissioner/guest.rb b/app/models/spree_cm_commissioner/guest.rb index 1c795a8af..34bd2f212 100644 --- a/app/models/spree_cm_commissioner/guest.rb +++ b/app/models/spree_cm_commissioner/guest.rb @@ -1,12 +1,12 @@ module SpreeCmCommissioner - class Guest < SpreeCmCommissioner::Base + class Guest < SpreeCmCommissioner::Base # rubocop:disable Metrics/ClassLength include SpreeCmCommissioner::KycBitwise delegate :kyc, to: :line_item, allow_nil: true delegate :allowed_upload_later?, to: :line_item, allow_nil: true - enum gender: { :other => 0, :male => 1, :female => 2 } - enum social_contact_platform: { + enum :gender, { :other => 0, :male => 1, :female => 2 } + enum :social_contact_platform, { :other => 0, :telegram => 1, :facebook => 2, @@ -14,7 +14,7 @@ class Guest < SpreeCmCommissioner::Base :whatsapp => 4, :line => 5, :viber => 6 - }, _prefix: true + }, prefix: true scope :complete, -> { joins(:line_item).merge(Spree::LineItem.complete) } scope :complete_or_canceled, -> { joins(:line_item).merge(Spree::LineItem.complete_or_canceled) } @@ -56,7 +56,13 @@ def self.csv_importable_columns # no validation for each field as we allow user to save data to model partially. def allowed_checkout? - kyc_fields.all? { |field| allowed_checkout_for?(field) } + kyc_fields.all? { |field| allowed_checkout_for?(field) } && dynamic_fields_present? + end + + def dynamic_fields_present? + return true if dynamic_field.empty? + + dynamic_field.all? { |entry| entry['value'].present? } end def allowed_checkout_for?(field) # rubocop:disable Metrics/AbcSize, Metrics/PerceivedComplexity, Metrics/CyclomaticComplexity @@ -77,6 +83,12 @@ def allowed_checkout_for?(field) # rubocop:disable Metrics/AbcSize, Metrics/Perc false end + def require_dynamic_kyc_field? + return false if dynamic_field.empty? + + dynamic_field.any? { |entry| entry['value'].blank? || entry['value'].empty? } + end + def require_kyc_field? # rubocop:disable Metrics/CyclomaticComplexity,Metrics/MethodLength kyc_fields.any? do |field| case field diff --git a/app/models/spree_cm_commissioner/product_decorator.rb b/app/models/spree_cm_commissioner/product_decorator.rb index 459892c2b..63c81e3b3 100644 --- a/app/models/spree_cm_commissioner/product_decorator.rb +++ b/app/models/spree_cm_commissioner/product_decorator.rb @@ -1,6 +1,8 @@ # rubocop:disable Metrics/MethodLength, Metrics/AbcSize module SpreeCmCommissioner module ProductDecorator + DYNAMIC_KYC_ATTRTYPES = %i[string integer float datetime date].freeze + def self.prepended(base) base.include SpreeCmCommissioner::ProductType base.include SpreeCmCommissioner::KycBitwise diff --git a/app/serializers/spree/v2/storefront/product_serializer_decorator.rb b/app/serializers/spree/v2/storefront/product_serializer_decorator.rb index d5d0b8727..525939de5 100644 --- a/app/serializers/spree/v2/storefront/product_serializer_decorator.rb +++ b/app/serializers/spree/v2/storefront/product_serializer_decorator.rb @@ -12,7 +12,7 @@ def self.prepended(base) base.has_one :venue, serializer: ::SpreeCmCommissioner::V2::Storefront::ProductPlaceSerializer base.attributes :need_confirmation, :product_type, :kyc, :kyc_fields, :allowed_upload_later, :allow_anonymous_booking, :use_video_as_default - base.attributes :reveal_description, :discontinue_on, :public_metadata, :purchasable_on + base.attributes :reveal_description, :discontinue_on, :public_metadata, :purchasable_on, :dynamic_kyc base.attribute :purchasable_on_app do |product| product.purchasable_on == 'app' || product.purchasable_on == 'both' diff --git a/app/serializers/spree_cm_commissioner/v2/operator/guest_serializer.rb b/app/serializers/spree_cm_commissioner/v2/operator/guest_serializer.rb index ac8028f35..b7a95ec7b 100644 --- a/app/serializers/spree_cm_commissioner/v2/operator/guest_serializer.rb +++ b/app/serializers/spree_cm_commissioner/v2/operator/guest_serializer.rb @@ -10,6 +10,7 @@ class GuestSerializer < BaseSerializer attribute :allowed_checkout, &:allowed_checkout? attribute :require_kyc_field, &:require_kyc_field? + attribute :require_dynamic_kyc_field, &:require_dynamic_kyc_field? belongs_to :occupation, serializer: Spree::V2::Storefront::TaxonSerializer diff --git a/app/serializers/spree_cm_commissioner/v2/storefront/guest_serializer.rb b/app/serializers/spree_cm_commissioner/v2/storefront/guest_serializer.rb index 38d1b6e5c..bae962a13 100644 --- a/app/serializers/spree_cm_commissioner/v2/storefront/guest_serializer.rb +++ b/app/serializers/spree_cm_commissioner/v2/storefront/guest_serializer.rb @@ -6,7 +6,8 @@ class GuestSerializer < BaseSerializer attributes :first_name, :last_name, :dob, :gender, :kyc_fields, :other_occupation, :created_at, :updated_at, :qr_data, :social_contact_platform, :social_contact, :available_social_contact_platforms, - :age, :emergency_contact, :other_organization, :expectation, :upload_later, :address, :formatted_bib_number, :phone_number + :age, :emergency_contact, :other_organization, :expectation, :upload_later, :address, :formatted_bib_number, :phone_number, + :dynamic_field # temporary, once app release after cm-app v1.9.1, we no longer need this. attribute :seat_number do |object| @@ -16,6 +17,7 @@ class GuestSerializer < BaseSerializer attribute :allowed_checkout, &:allowed_checkout? attribute :allowed_upload_later, &:allowed_upload_later? attribute :require_kyc_field, &:require_kyc_field? + attribute :require_dynamic_kyc_field, &:require_dynamic_kyc_field? belongs_to :occupation, serializer: Spree::V2::Storefront::TaxonSerializer belongs_to :nationality, serializer: Spree::V2::Storefront::TaxonSerializer diff --git a/app/views/spree/admin/kyc/_dynamic_kyc_fields.html.erb b/app/views/spree/admin/kyc/_dynamic_kyc_fields.html.erb new file mode 100644 index 000000000..32b9f3e7b --- /dev/null +++ b/app/views/spree/admin/kyc/_dynamic_kyc_fields.html.erb @@ -0,0 +1,12 @@ + + +
+ + + + +
diff --git a/app/views/spree/admin/kyc/_dynamic_kyc_form.html.erb b/app/views/spree/admin/kyc/_dynamic_kyc_form.html.erb new file mode 100644 index 000000000..4841cf821 --- /dev/null +++ b/app/views/spree/admin/kyc/_dynamic_kyc_form.html.erb @@ -0,0 +1,68 @@ +
+
Dynamic KYC Fields
+ +
+ +
+
+
+ <% if @product.dynamic_kyc.empty? %> + + <%= raw I18n.t('dynamic_kyc.note') %> + + <% else %> + <% @product.dynamic_kyc.each_with_index do |item, index| %> + <%= render partial: 'dynamic_kyc_fields', locals: { key: item['key'], label: item['label'], attype: item['attype']} %> + <% end %> + <% end %> +
+
+
+ + + diff --git a/app/views/spree/admin/kyc/edit.html.erb b/app/views/spree/admin/kyc/edit.html.erb index 2e49a7b3a..07d7a4fce 100644 --- a/app/views/spree/admin/kyc/edit.html.erb +++ b/app/views/spree/admin/kyc/edit.html.erb @@ -1,31 +1,32 @@ <%= render partial: 'spree/admin/shared/product_tabs', locals: { current: :kyc } %> - <% content_for :page_actions do %> <%= button_link_to Spree.t(:edit_icons), admin_vectors_option_values_url, { icon: 'spree-icon.svg', id: 'admin-option-values-icons-index' } %> <% end %> - <%= form_with model: @product, url: update_kyc_admin_product_path(@product), method: :put do |form| %> - - <%= raw I18n.t('kyc.allowed_upload_later') %> - -
-
- <%= form.field_container :allowed_upload_later do %> - <%= form.check_box :allowed_upload_later, class: 'form-check-input', checked: form.object.allowed_upload_later? %> - <%= form.label :allowed_upload_later, Spree.t(:allowed_upload_later), class: 'form-check-label' %> - <%= form.error_message_on :allowed_upload_later, class: 'text-danger' %> - <% end %> -
+ + <%= raw I18n.t('kyc.allowed_upload_later') %> + +
+
+ <%= form.field_container :allowed_upload_later do %> + <%= form.check_box :allowed_upload_later, class: 'form-check-input', checked: form.object.allowed_upload_later? %> + <%= form.label :allowed_upload_later, Spree.t(:allowed_upload_later), class: 'form-check-label' %> + <%= form.error_message_on :allowed_upload_later, class: 'text-danger' %> + <% end %>
+
+ + <%= raw I18n.t('kyc.note') %> + + <%= render partial: 'form', locals: { form: form } %> + <%= render partial: 'dynamic_kyc_form' %> - - <%= raw I18n.t('kyc.note') %> - - <%= render partial: 'form', locals: { form: form } %> -
- <%= button Spree.t('actions.update'), 'save.svg', 'submit', { class: 'btn-success', data: { disable_with: "#{ Spree.t(:saving) }..." }} %> -
+
+ <%= button Spree.t('actions.update'), 'save.svg', 'submit', { class: 'btn-success', data: { disable_with: "#{ Spree.t(:saving) }..." }} %> +
<% end %> + + diff --git a/config/locales/en.yml b/config/locales/en.yml index 1a8e68f63..e1fb892d5 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -490,6 +490,8 @@ en: kyc: note: "Note: Select Required KYC Field For this Product" allowed_upload_later: "Allow upload later: Check this box if you allow to upload later" + dynamic_kyc: + note: "Note: No Dynamic KYC Field yet" video_on_demand: quality: low: "Low (320p)" diff --git a/config/routes.rb b/config/routes.rb index b898c0cce..744e652b5 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -99,6 +99,7 @@ delete 'remove_metafield', to: 'metafields#remove_metafield' get 'edit_kyc', to: 'kyc#edit' put 'update_kyc', to: 'kyc#update' + delete 'remove_field', to: 'kyc#remove_field' end resources :variant_guest_card_classes diff --git a/db/migrate/20241219025924_add_dynamic_kyc_to_spree_product.rb b/db/migrate/20241219025924_add_dynamic_kyc_to_spree_product.rb new file mode 100644 index 000000000..cf48e572b --- /dev/null +++ b/db/migrate/20241219025924_add_dynamic_kyc_to_spree_product.rb @@ -0,0 +1,7 @@ +class AddDynamicKycToSpreeProduct < ActiveRecord::Migration[7.0] + def change + unless column_exists?(:spree_products, :dynamic_kyc) + add_column :spree_products, :dynamic_kyc, :jsonb, default: {} + end + end +end diff --git a/db/migrate/20241219105707_add_dynamic_field_to_spree_cm_commissioner_guest.rb b/db/migrate/20241219105707_add_dynamic_field_to_spree_cm_commissioner_guest.rb new file mode 100644 index 000000000..632f6da28 --- /dev/null +++ b/db/migrate/20241219105707_add_dynamic_field_to_spree_cm_commissioner_guest.rb @@ -0,0 +1,7 @@ +class AddDynamicFieldToSpreeCmCommissionerGuest < ActiveRecord::Migration[7.0] + def change + unless column_exists?(:cm_guests, :dynamic_field) + add_column :cm_guests, :dynamic_field, :jsonb, default: {} + end + end +end diff --git a/spec/serializers/spree/v2/storefront/guest_serializer_spec.rb b/spec/serializers/spree/v2/storefront/guest_serializer_spec.rb index bb11b7bfe..1e362395e 100644 --- a/spec/serializers/spree/v2/storefront/guest_serializer_spec.rb +++ b/spec/serializers/spree/v2/storefront/guest_serializer_spec.rb @@ -38,7 +38,8 @@ :seat_number, :formatted_bib_number, :phone_number, - :require_kyc_field + :require_kyc_field, + :dynamic_field ) expect(subject[:data][:attributes][:qr_data]).to eq guest.token end diff --git a/spec/serializers/spree/v2/storefront/product_serializer_spec.rb b/spec/serializers/spree/v2/storefront/product_serializer_spec.rb index 95882a6bd..cadc0bec6 100644 --- a/spec/serializers/spree/v2/storefront/product_serializer_spec.rb +++ b/spec/serializers/spree/v2/storefront/product_serializer_spec.rb @@ -54,7 +54,8 @@ :use_video_as_default, :purchasable_on, :purchasable_on_app, - :purchasable_on_web + :purchasable_on_web, + :dynamic_kyc ) end