diff --git a/app/controllers/api/v1/fees_controller.rb b/app/controllers/api/v1/fees_controller.rb index 447d41dcf3a2..3c7f630a40a5 100644 --- a/app/controllers/api/v1/fees_controller.rb +++ b/app/controllers/api/v1/fees_controller.rb @@ -49,12 +49,32 @@ def index end end + def destroy + fee = Fee.from_organization(current_organization).find_by(id: params[:id]) + result = ::Fees::DestroyService.call(fee:) + + if result.success? + render_fee(result.fee) + else + render_error_response(result) + end + end + private def update_params params.require(:fee).permit(:payment_status) end + def render_fee(fee) + render( + json: ::V1::FeeSerializer.new( + fee, + root_name: 'fee' + ) + ) + end + def index_filters params.permit( :fee_type, diff --git a/app/models/fee.rb b/app/models/fee.rb index a3587645ca30..623acf9bbae4 100644 --- a/app/models/fee.rb +++ b/app/models/fee.rb @@ -2,6 +2,9 @@ class Fee < ApplicationRecord include Currencies + include Discard::Model + self.discard_column = :deleted_at + default_scope -> { kept } belongs_to :invoice, optional: true belongs_to :charge, -> { with_discarded }, optional: true diff --git a/app/services/fees/destroy_service.rb b/app/services/fees/destroy_service.rb new file mode 100644 index 000000000000..5b9c4902aaea --- /dev/null +++ b/app/services/fees/destroy_service.rb @@ -0,0 +1,25 @@ +# frozen_string_literal: true + +module Fees + class DestroyService < BaseService + def initialize(fee:) + @fee = fee + + super + end + + def call + return result.not_found_failure!(resource: 'fee') unless fee + return result.not_allowed_failure!(code: 'invoiced_fee') if fee.invoice_id + + fee.discard! + + result.fee = fee + result + end + + private + + attr_reader :fee + end +end diff --git a/config/routes.rb b/config/routes.rb index 6b7c2b9be448..3319ed3fe89f 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -51,7 +51,7 @@ post :estimate_fees, on: :collection end resources :applied_coupons, only: %i[create index] - resources :fees, only: %i[show update index] + resources :fees, only: %i[show update index destroy] resources :invoices, only: %i[create update show index] do post :download, on: :member post :void, on: :member diff --git a/db/migrate/20240701184757_add_deleted_at_to_fees.rb b/db/migrate/20240701184757_add_deleted_at_to_fees.rb new file mode 100644 index 000000000000..f642c0105057 --- /dev/null +++ b/db/migrate/20240701184757_add_deleted_at_to_fees.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +class AddDeletedAtToFees < ActiveRecord::Migration[7.1] + def change + add_column :fees, :deleted_at, :datetime + add_index :fees, :deleted_at + end +end diff --git a/db/schema.rb b/db/schema.rb index 3fa568fa97e7..e2ff426da48b 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -520,10 +520,12 @@ t.uuid "charge_filter_id" t.jsonb "grouped_by", default: {}, null: false t.string "pay_in_advance_event_transaction_id" + t.datetime "deleted_at" t.index ["add_on_id"], name: "index_fees_on_add_on_id" t.index ["applied_add_on_id"], name: "index_fees_on_applied_add_on_id" t.index ["charge_filter_id"], name: "index_fees_on_charge_filter_id" t.index ["charge_id"], name: "index_fees_on_charge_id" + t.index ["deleted_at"], name: "index_fees_on_deleted_at" t.index ["group_id"], name: "index_fees_on_group_id" t.index ["invoice_id"], name: "index_fees_on_invoice_id" t.index ["invoiceable_type", "invoiceable_id"], name: "index_fees_on_invoiceable" diff --git a/spec/requests/api/v1/fees_controller_spec.rb b/spec/requests/api/v1/fees_controller_spec.rb index 0b6835a62f0f..7dc6d5856947 100644 --- a/spec/requests/api/v1/fees_controller_spec.rb +++ b/spec/requests/api/v1/fees_controller_spec.rb @@ -132,6 +132,34 @@ end end + describe 'DELETE /fees/:id' do + let(:customer) { create(:customer, organization:) } + let(:subscription) { create(:subscription, customer:) } + let(:update_params) { {payment_status: 'succeeded'} } + let(:fee) do + create(:charge_fee, fee_type: 'charge', pay_in_advance: true, subscription:, invoice: nil) + end + + context 'when fee exist' do + it 'deletes the fee' do + delete_with_token(organization, "/api/v1/fees/#{fee.id}") + expect(response).to have_http_status(:ok) + end + end + + context 'when fee exist but is attached to an invoice' do + let(:invoice) { create(:invoice, organization:, customer:) } + let(:fee) do + create(:charge_fee, fee_type: 'charge', pay_in_advance: true, subscription:, invoice:) + end + + it 'dont delete the fee' do + delete_with_token(organization, "/api/v1/fees/#{fee.id}") + expect(response).to have_http_status(:method_not_allowed) + end + end + end + describe 'GET /fees' do let(:customer) { create(:customer, organization:) } let(:subscription) { create(:subscription, customer:) }