From cced041b6cd027d8eb1e546b9a722d33a4aa74ee Mon Sep 17 00:00:00 2001 From: Miguel Pinto Date: Mon, 1 Jul 2024 20:44:28 +0100 Subject: [PATCH] feat: Add ability to soft delete fees --- app/controllers/api/v1/fees_controller.rb | 20 +++++++++++++++ app/models/fee.rb | 3 +++ app/services/fees/destroy_service.rb | 25 +++++++++++++++++++ config/routes.rb | 2 +- .../20240701184757_add_deleted_at_to_fees.rb | 6 +++++ db/schema.rb | 12 +++------ spec/requests/api/v1/fees_controller_spec.rb | 16 ++++++++++++ 7 files changed, 75 insertions(+), 9 deletions(-) create mode 100644 app/services/fees/destroy_service.rb create mode 100644 db/migrate/20240701184757_add_deleted_at_to_fees.rb 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..de97268efd29 --- /dev/null +++ b/db/migrate/20240701184757_add_deleted_at_to_fees.rb @@ -0,0 +1,6 @@ +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 a1302e877987..2c123a0ed480 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -225,7 +225,6 @@ t.boolean "invoiceable", default: true, null: false t.boolean "prorated", default: false, null: false t.string "invoice_display_name" - t.integer "regroup_paid_fees" t.index ["billable_metric_id"], name: "index_charges_on_billable_metric_id" t.index ["deleted_at"], name: "index_charges_on_deleted_at" t.index ["plan_id"], name: "index_charges_on_plan_id" @@ -520,10 +519,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" @@ -999,7 +1000,6 @@ t.jsonb "object" t.jsonb "object_changes" t.datetime "created_at" - t.string "lago_version" t.index ["item_type", "item_id"], name: "index_versions_on_item_type_and_item_id" end @@ -1224,16 +1224,12 @@ billable_metrics.field_name, charges.plan_id, charges.id AS charge_id, - CASE - WHEN (charges.charge_model = 0) THEN (charges.properties -> 'grouped_by'::text) - ELSE NULL::jsonb - END AS grouped_by, charge_filters.id AS charge_filter_id, json_object_agg(billable_metric_filters.key, COALESCE(charge_filter_values."values", '{}'::character varying[]) ORDER BY billable_metric_filters.key) FILTER (WHERE (billable_metric_filters.key IS NOT NULL)) AS filters, CASE - WHEN (charges.charge_model = 0) THEN (charge_filters.properties -> 'grouped_by'::text) + WHEN (charges.charge_model = 0) THEN COALESCE((charge_filters.properties -> 'grouped_by'::text), (charges.properties -> 'grouped_by'::text)) ELSE NULL::jsonb - END AS filters_grouped_by + END AS grouped_by FROM ((((billable_metrics JOIN charges ON ((charges.billable_metric_id = billable_metrics.id))) LEFT JOIN charge_filters ON ((charge_filters.charge_id = charges.id))) diff --git a/spec/requests/api/v1/fees_controller_spec.rb b/spec/requests/api/v1/fees_controller_spec.rb index 0b6835a62f0f..174226c8827f 100644 --- a/spec/requests/api/v1/fees_controller_spec.rb +++ b/spec/requests/api/v1/fees_controller_spec.rb @@ -132,6 +132,22 @@ 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 + end + describe 'GET /fees' do let(:customer) { create(:customer, organization:) } let(:subscription) { create(:subscription, customer:) }