diff --git a/app/models/credit.rb b/app/models/credit.rb index d3050c6f5ee..d20adb6d62c 100644 --- a/app/models/credit.rb +++ b/app/models/credit.rb @@ -40,10 +40,7 @@ def item_code def item_name return coupon&.name if applied_coupon_id? - - if progressive_billing_invoice_id? - return progressive_billing_invoice.fees.first&.invoice_name - end + return progressive_billing_invoice.number if progressive_billing_invoice_id? # TODO: change it depending on invoice template credit_note.invoice.number diff --git a/app/models/fee.rb b/app/models/fee.rb index 852ea116a36..a0084c9c709 100644 --- a/app/models/fee.rb +++ b/app/models/fee.rb @@ -13,7 +13,6 @@ class Fee < ApplicationRecord belongs_to :subscription, optional: true belongs_to :charge_filter, -> { with_discarded }, optional: true belongs_to :group, -> { with_discarded }, optional: true - belongs_to :usage_threshold, -> { with_discarded }, optional: true belongs_to :invoiceable, polymorphic: true, optional: true belongs_to :true_up_parent_fee, class_name: 'Fee', optional: true @@ -35,7 +34,7 @@ class Fee < ApplicationRecord monetize :unit_amount_cents, disable_validation: true, allow_nil: true, with_model_currency: :currency # TODO: Deprecate add_on type in the near future - FEE_TYPES = %i[charge add_on subscription credit commitment progressive_billing].freeze + FEE_TYPES = %i[charge add_on subscription credit commitment].freeze PAYMENT_STATUS = %i[pending succeeded failed refunded].freeze enum fee_type: FEE_TYPES @@ -66,7 +65,6 @@ def item_id return billable_metric.id if charge? return add_on.id if add_on? return invoiceable_id if credit? - return usage_threshold_id if progressive_billing? subscription_id end @@ -75,7 +73,6 @@ def item_type return BillableMetric.name if charge? return AddOn.name if add_on? return WalletTransaction.name if credit? - return UsageThreshold.name if progressive_billing? Subscription.name end @@ -83,7 +80,7 @@ def item_type def item_code return billable_metric.code if charge? return add_on.code if add_on? - return fee_type if credit? || progressive_billing? + return fee_type if credit? subscription.plan.code end @@ -91,7 +88,7 @@ def item_code def item_name return billable_metric.name if charge? return add_on.name if add_on? - return fee_type if credit? || progressive_billing? + return fee_type if credit? subscription.plan.name end @@ -99,7 +96,7 @@ def item_name def item_description return billable_metric.description if charge? return add_on.description if add_on? - return fee_type if credit? || progressive_billing? + return fee_type if credit? subscription.plan.description end @@ -109,7 +106,6 @@ def invoice_name return charge.invoice_display_name.presence || billable_metric.name if charge? return add_on.invoice_name if add_on? return fee_type if credit? - return usage_threshold.invoice_name if progressive_billing? subscription.plan.invoice_display_name end @@ -202,7 +198,6 @@ def has_charge_filters? # pay_in_advance_event_transaction_id :string # subscription_id :uuid # true_up_parent_fee_id :uuid -# usage_threshold_id :uuid # # Indexes # @@ -218,7 +213,6 @@ def has_charge_filters? # index_fees_on_pay_in_advance_event_transaction_id (pay_in_advance_event_transaction_id) WHERE (deleted_at IS NULL) # index_fees_on_subscription_id (subscription_id) # index_fees_on_true_up_parent_fee_id (true_up_parent_fee_id) -# index_fees_on_usage_threshold_id (usage_threshold_id) # # Foreign Keys # @@ -229,5 +223,4 @@ def has_charge_filters? # fk_rails_... (invoice_id => invoices.id) # fk_rails_... (subscription_id => subscriptions.id) # fk_rails_... (true_up_parent_fee_id => fees.id) -# fk_rails_... (usage_threshold_id => usage_thresholds.id) # diff --git a/app/services/credits/progressive_billing_service.rb b/app/services/credits/progressive_billing_service.rb index df19bdf8647..8e9b0d8ab2b 100644 --- a/app/services/credits/progressive_billing_service.rb +++ b/app/services/credits/progressive_billing_service.rb @@ -25,7 +25,7 @@ def call remaining_to_credit = total_subscription_amount progressive_billing_invoices.each do |progressive_billing_invoice| - amount_to_credit = progressive_billing_invoice.fees.progressive_billing.sum(:amount_cents) + amount_to_credit = progressive_billing_invoice.fees_amount_cents if amount_to_credit > remaining_to_credit # TODO: create credit note for (amount_to_credit - remaining_credit) diff --git a/app/services/fees/apply_taxes_service.rb b/app/services/fees/apply_taxes_service.rb index 9137add2932..a0d93b94da1 100644 --- a/app/services/fees/apply_taxes_service.rb +++ b/app/services/fees/apply_taxes_service.rb @@ -59,7 +59,7 @@ def applicable_taxes return fee.add_on.taxes if fee.add_on? && fee.add_on.taxes.any? return fee.charge.taxes if fee.charge? && fee.charge.taxes.any? return fee.invoiceable.taxes if fee.commitment? && fee.invoiceable.taxes.any? - if (fee.charge? || fee.subscription? || fee.commitment? || fee.progressive_billing?) && fee.subscription.plan.taxes.any? + if (fee.charge? || fee.subscription? || fee.commitment?) && fee.subscription.plan.taxes.any? return fee.subscription.plan.taxes end return customer.taxes if customer.taxes.any? diff --git a/app/services/fees/create_from_usage_threshold_service.rb b/app/services/fees/create_from_usage_threshold_service.rb deleted file mode 100644 index 9e679645fd7..00000000000 --- a/app/services/fees/create_from_usage_threshold_service.rb +++ /dev/null @@ -1,59 +0,0 @@ -# frozen_string_literal: true - -module Fees - class CreateFromUsageThresholdService < BaseService - def initialize(usage_threshold:, invoice:, amount_cents:) - @usage_threshold = usage_threshold - @invoice = invoice - @amount_cents = amount_cents - - super - end - - def call - fee = Fee.new( - subscription: invoice.subscriptions.first, - invoice:, - usage_threshold:, - invoice_display_name: usage_threshold.threshold_display_name, - invoiceable: usage_threshold, - amount_cents: amount_cents, - amount_currency: invoice.currency, - fee_type: :progressive_billing, - units:, - unit_amount_cents: unit_amount_cents, - payment_status: :pending, - taxes_amount_cents: 0, - properties: { - charges_from_datetime: invoice.invoice_subscriptions.first.charges_from_datetime, - charges_to_datetime: invoice.invoice_subscriptions.first.charges_to_datetime, - timestamp: invoice.invoice_subscriptions.first.timestamp - } - ) - - taxes_result = Fees::ApplyTaxesService.call(fee:) - taxes_result.raise_if_error! - - fee.save! - result.fee = fee - - result - end - - private - - attr_reader :usage_threshold, :invoice, :amount_cents - - def units - return 1 unless usage_threshold.recurring? - - amount_cents.fdiv(usage_threshold.amount_cents) - end - - def unit_amount_cents - return amount_cents unless usage_threshold.recurring? - - usage_threshold.amount_cents - end - end -end diff --git a/app/services/invoices/progressive_billing_service.rb b/app/services/invoices/progressive_billing_service.rb index fbf88019d12..c92d10ca2a6 100644 --- a/app/services/invoices/progressive_billing_service.rb +++ b/app/services/invoices/progressive_billing_service.rb @@ -13,7 +13,7 @@ def initialize(usage_thresholds:, lifetime_usage:, timestamp: Time.current) def call ActiveRecord::Base.transaction do create_generating_invoice - create_threshold_fees + create_fees invoice.fees_amount_cents = invoice.fees.sum(:amount_cents) invoice.sub_total_excluding_taxes_amount_cents = invoice.fees_amount_cents @@ -27,9 +27,11 @@ def call invoice.finalized! end + # TODO: deduct previous progressive billing invoices + Utils::SegmentTrack.invoice_created(invoice) SendWebhookJob.perform_later('invoice.created', invoice) - GeneratePdfAndNotifyJob.perform_later(invoice:, email: should_deliver_email?) + Invoices::GeneratePdfAndNotifyJob.perform_later(invoice:, email: should_deliver_email?) Integrations::Aggregator::Invoices::CreateJob.perform_later(invoice:) if invoice.should_sync_invoice? Integrations::Aggregator::SalesOrders::CreateJob.perform_later(invoice:) if invoice.should_sync_sales_order? Invoices::Payments::CreateService.call(invoice) @@ -66,54 +68,45 @@ def create_generating_invoice @invoice = invoice_result.invoice end - def sorted_thresholds - fixed = usage_thresholds.select { |t| !t.recurring }.sort_by(&:amount_cents) - recurring = usage_thresholds.select(&:recurring) - fixed + recurring - end - - def create_threshold_fees - sorted_thresholds.each do |usage_threshold| - fee_result = Fees::CreateFromUsageThresholdService - .call(usage_threshold:, invoice:, amount_cents: amount_cents(usage_threshold)) - fee_result.raise_if_error! - fee_result.fee + def create_fees + charges.find_each do |charge| + Fees::ChargeService.call(invoice:, charge:, subscription:, boundaries:).raise_if_error! end end - def should_deliver_email? - License.premium? && subscription.organization.email_settings.include?('invoice.finalized') - end - - def amount_cents(usage_threshold) - if usage_threshold.recurring? - # NOTE: Recurring is always the last threshold. - # Amount is the current lifetime usage without already invoiced thresholds - # The recurring threshold can be reached multiple time, so we need to compute the number of times - units = (total_lifetime_usage_amount_cents - invoiced_amount_cents) / usage_threshold.amount_cents - units * usage_threshold.amount_cents - else - # NOTE: Amount to bill if the current threshold minus the usage that have already been invoiced - result_amount = usage_threshold.amount_cents - invoiced_amount_cents - - # NOTE: Add the amount to the invoiced_amount_cents for next non recurring threshold - @invoiced_amount_cents += result_amount - - result_amount - end + def charges + subscription + .plan + .charges + .joins(:billable_metric) + .includes(:taxes, billable_metric: :organization, filters: {values: :billable_metric_filter}) + .where(invoiceable: true) + .where(pay_in_advance: false) + .where(billable_metrics: {recurring: false}) end - # NOTE: Sum of usage that have already been invoiced - def invoiced_amount_cents - @invoiced_amount_cents ||= subscription.invoices - .finalized - .where(invoice_type: %w[subscription progressive_billing]) - .sum { |invoice| invoice.fees.where(fee_type: %w[charge progressive_billing]).sum(:amount_cents) } + def boundaries + return @boundaries if defined?(@boundaries) + + invoice_subscription = invoice.invoice_subscriptions.first + date_service = Subscriptions::DatesService.new_instance( + subscription, + timestamp, + current_usage: true + ) + + @boundaries = { + from_datetime: invoice_subscription.from_datetime, + to_datetime: invoice_subscription.to_datetime, + charges_from_datetime: invoice_subscription.charges_from_datetime, + charges_to_datetime: invoice_subscription.charges_to_datetime, + timestamp: timestamp, + charges_duration: date_service.charges_duration_in_days + } end - # NOTE: Current lifetime usage amount - def total_lifetime_usage_amount_cents - @total_lifetime_usage_amount_cents ||= lifetime_usage.invoiced_usage_amount_cents + lifetime_usage.current_usage_amount_cents + def should_deliver_email? + License.premium? && subscription.organization.email_settings.include?('invoice.finalized') end def create_credit_note_credit diff --git a/app/services/lifetime_usages/recalculate_and_check_service.rb b/app/services/lifetime_usages/recalculate_and_check_service.rb index bedc4f0db24..29f73fba9d8 100644 --- a/app/services/lifetime_usages/recalculate_and_check_service.rb +++ b/app/services/lifetime_usages/recalculate_and_check_service.rb @@ -31,7 +31,7 @@ def progressive_billed_amount subscription.invoices .finalized .progressive_billing - .sum { |invoice| invoice.fees.progressive_billing.sum(:amount_cents) } + .sum(:fees_amount_cents) end end end diff --git a/db/migrate/20240822082727_remove_usage_threshold_relation_from_fees.rb b/db/migrate/20240822082727_remove_usage_threshold_relation_from_fees.rb new file mode 100644 index 00000000000..b2aac3b3ba4 --- /dev/null +++ b/db/migrate/20240822082727_remove_usage_threshold_relation_from_fees.rb @@ -0,0 +1,10 @@ +# frozen_string_literal: true + +class RemoveUsageThresholdRelationFromFees < ActiveRecord::Migration[7.1] + def up + remove_column :fees, :usage_threshold_id + end + + def down + end +end diff --git a/db/schema.rb b/db/schema.rb index fc64f613ef8..b09c86cf704 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[7.1].define(version: 2024_08_22_080031) do +ActiveRecord::Schema[7.1].define(version: 2024_08_22_082727) do # These are extensions that must be enabled in order to support this database enable_extension "pgcrypto" enable_extension "plpgsql" @@ -540,7 +540,6 @@ t.jsonb "grouped_by", default: {}, null: false t.string "pay_in_advance_event_transaction_id" t.datetime "deleted_at" - t.uuid "usage_threshold_id" 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" @@ -553,7 +552,6 @@ t.index ["pay_in_advance_event_transaction_id"], name: "index_fees_on_pay_in_advance_event_transaction_id", where: "(deleted_at IS NULL)" t.index ["subscription_id"], name: "index_fees_on_subscription_id" t.index ["true_up_parent_fee_id"], name: "index_fees_on_true_up_parent_fee_id" - t.index ["usage_threshold_id"], name: "index_fees_on_usage_threshold_id" end create_table "fees_taxes", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t| @@ -1215,7 +1213,6 @@ add_foreign_key "fees", "groups" add_foreign_key "fees", "invoices" add_foreign_key "fees", "subscriptions" - add_foreign_key "fees", "usage_thresholds" add_foreign_key "fees_taxes", "fees" add_foreign_key "fees_taxes", "taxes" add_foreign_key "group_properties", "charges", on_delete: :cascade diff --git a/schema.graphql b/schema.graphql index 8e307fe26d0..942a7c48e4a 100644 --- a/schema.graphql +++ b/schema.graphql @@ -3693,7 +3693,6 @@ enum FeeTypesEnum { charge commitment credit - progressive_billing subscription } diff --git a/schema.json b/schema.json index e9ecb9b912c..9441e60c50b 100644 --- a/schema.json +++ b/schema.json @@ -16449,12 +16449,6 @@ "description": null, "isDeprecated": false, "deprecationReason": null - }, - { - "name": "progressive_billing", - "description": null, - "isDeprecated": false, - "deprecationReason": null } ] }, diff --git a/spec/factories/fees.rb b/spec/factories/fees.rb index c9157130870..c45e6e9e61d 100644 --- a/spec/factories/fees.rb +++ b/spec/factories/fees.rb @@ -102,18 +102,4 @@ invoiceable_type { 'Commitment' } invoiceable_id { commitment.id } end - - factory :progressive_billing_fee, class: 'Fee' do - invoice - fee_type { 'progressive_billing' } - subscription { create(:subscription) } - - amount_cents { 200 } - amount_currency { 'EUR' } - taxes_amount_cents { 2 } - - usage_threshold { create(:usage_threshold, plan: subscription.plan) } - invoiceable_type { 'UsageThreshold' } - invoiceable_id { usage_threshold.id } - end end diff --git a/spec/models/credit_spec.rb b/spec/models/credit_spec.rb index 19862f450cf..e00f4d4e3d1 100644 --- a/spec/models/credit_spec.rb +++ b/spec/models/credit_spec.rb @@ -77,22 +77,13 @@ subject(:credit) { create(:progressive_billing_invoice_credit, progressive_billing_invoice:) } let(:progressive_billing_invoice) { create(:invoice, invoice_type: :progressive_billing) } - let(:progressive_billing_fee) do - create( - :fee, - invoice: progressive_billing_invoice, - fee_type: :progressive_billing - ) - end - - before { progressive_billing_fee } it 'returns invoice details', aggregate_failures: true do aggregate_failures do expect(credit.item_id).to eq(progressive_billing_invoice.id) expect(credit.item_type).to eq('invoice') expect(credit.item_code).to eq(progressive_billing_invoice.number) - expect(credit.item_name).to eq(progressive_billing_fee.invoice_name) + expect(credit.item_name).to eq(progressive_billing_invoice.number) end end end diff --git a/spec/models/fee_spec.rb b/spec/models/fee_spec.rb index 45b901263cc..72fe5899b02 100644 --- a/spec/models/fee_spec.rb +++ b/spec/models/fee_spec.rb @@ -5,12 +5,6 @@ RSpec.describe Fee, type: :model do subject(:fee_model) { described_class } - describe 'associations' do - subject(:fee) { build(:fee) } - - it { is_expected.to belong_to(:usage_threshold).optional } - end - describe '.item_code' do context 'when it is a subscription fee' do let(:subscription) { create(:subscription) } @@ -45,12 +39,6 @@ end end - context 'when it is a progressive billing fee' do - it 'returns add on code' do - expect(fee_model.new(fee_type: 'progressive_billing').item_code).to eq('progressive_billing') - end - end - context 'when it is an pay_in_advance charge fee' do let(:charge) { create(:standard_charge, :pay_in_advance) } @@ -122,14 +110,6 @@ end end - context 'when it is a progressive billing fee' do - let(:fee) { build_stubbed(:progressive_billing_fee, invoice_display_name:) } - - it 'returns add on name' do - expect(fee_invoice_name).to eq(fee.usage_threshold.invoice_name) - end - end - context 'when it is an pay_in_advance charge fee' do let(:fee) { build_stubbed(:fee, charge:, fee_type: 'charge', invoice_display_name:) } let(:charge) { create(:standard_charge, :pay_in_advance, invoice_display_name: charge_invoice_display_name) } @@ -187,12 +167,6 @@ end end - context 'when it is a progressive billing fee' do - it "returns 'credit'" do - expect(fee_model.new(fee_type: 'progressive_billing').item_name).to eq('progressive_billing') - end - end - context 'when it is an pay_in_advance charge fee' do let(:charge) { create(:standard_charge, :pay_in_advance) } @@ -237,12 +211,6 @@ end end - context "when it is a progressive_billing fee" do - it "returns 'credit'" do - expect(fee_model.new(fee_type: "progressive_billing").item_description).to eq("progressive_billing") - end - end - context "when it is an pay_in_advance charge fee" do let(:charge) { create(:standard_charge, :pay_in_advance) } @@ -287,12 +255,6 @@ end end - context 'when it is a progressive_billing fee' do - it 'returns wallet transaction' do - expect(fee_model.new(fee_type: 'progressive_billing').item_type).to eq('UsageThreshold') - end - end - context 'when it is an pay_in_advance charge fee' do let(:charge) { create(:standard_charge, :pay_in_advance) } @@ -340,14 +302,6 @@ end end - context 'when it is a progressive_billing fee' do - let(:fee) { create(:progressive_billing_fee) } - - it 'returns the wallet transaction id' do - expect(fee.item_id).to eq(fee.usage_threshold_id) - end - end - context 'when it is an pay_in_advance charge fee' do let(:charge) { create(:standard_charge, :pay_in_advance) } diff --git a/spec/services/credits/progressive_billing_service_spec.rb b/spec/services/credits/progressive_billing_service_spec.rb index 947d4d43f24..e630ea1df8d 100644 --- a/spec/services/credits/progressive_billing_service_spec.rb +++ b/spec/services/credits/progressive_billing_service_spec.rb @@ -48,11 +48,12 @@ status: 'finalized', invoice_type: :progressive_billing, subscriptions: [subscription], - issuing_date: invoice.issuing_date - 1.day + issuing_date: invoice.issuing_date - 1.day, + fees_amount_cents: 20 ) end - let(:progressive_billing_fee) { create(:progressive_billing_fee, amount_cents: 20, invoice: progressive_billing_invoice) } + let(:progressive_billing_fee) { create(:charge_fee, amount_cents: 20, invoice: progressive_billing_invoice) } before do progressive_billing_invoice @@ -79,7 +80,8 @@ status: 'finalized', invoice_type: :progressive_billing, subscriptions: [subscription], - issuing_date: invoice.issuing_date - 2.days + issuing_date: invoice.issuing_date - 2.days, + fees_amount_cents: 20 ) end @@ -91,12 +93,13 @@ status: 'finalized', invoice_type: :progressive_billing, subscriptions: [subscription], - issuing_date: invoice.issuing_date - 1.day + issuing_date: invoice.issuing_date - 1.day, + fees_amount_cents: 200 ) end - let(:progressive_billing_fee) { create(:progressive_billing_fee, amount_cents: 20, invoice: progressive_billing_invoice) } - let(:progressive_billing_fee2) { create(:progressive_billing_fee, amount_cents: 200, invoice: progressive_billing_invoice2) } + let(:progressive_billing_fee) { create(:charge_fee, amount_cents: 20, invoice: progressive_billing_invoice) } + let(:progressive_billing_fee2) { create(:charge_fee, amount_cents: 200, invoice: progressive_billing_invoice2) } before do progressive_billing_fee @@ -127,7 +130,8 @@ status: 'finalized', invoice_type: :progressive_billing, subscriptions: [subscription], - issuing_date: invoice.issuing_date - 3.days + issuing_date: invoice.issuing_date - 3.days, + fees_amount_cents: 20 ) end @@ -139,7 +143,8 @@ status: 'finalized', invoice_type: :progressive_billing, subscriptions: [subscription], - issuing_date: invoice.issuing_date - 2.days + issuing_date: invoice.issuing_date - 2.days, + fees_amount_cents: 1000 ) end @@ -151,13 +156,14 @@ status: 'finalized', invoice_type: :progressive_billing, subscriptions: [subscription], - issuing_date: invoice.issuing_date - 1.day + issuing_date: invoice.issuing_date - 1.day, + fees_amount_cents: 200 ) end - let(:progressive_billing_fee) { create(:progressive_billing_fee, amount_cents: 20, invoice: progressive_billing_invoice) } - let(:progressive_billing_fee2) { create(:progressive_billing_fee, amount_cents: 1000, invoice: progressive_billing_invoice2) } - let(:progressive_billing_fee3) { create(:progressive_billing_fee, amount_cents: 200, invoice: progressive_billing_invoice3) } + let(:progressive_billing_fee) { create(:charge_fee, amount_cents: 20, invoice: progressive_billing_invoice) } + let(:progressive_billing_fee2) { create(:charge_fee, amount_cents: 1000, invoice: progressive_billing_invoice2) } + let(:progressive_billing_fee3) { create(:charge_fee, amount_cents: 200, invoice: progressive_billing_invoice3) } before do progressive_billing_fee @@ -197,11 +203,12 @@ status: 'finalized', invoice_type: :progressive_billing, subscriptions: [subscription], - issuing_date: invoice.issuing_date - 1.day + issuing_date: invoice.issuing_date - 1.day, + fees_amount_cents: 20 ) end - let(:progressive_billing_fee) { create(:progressive_billing_fee, amount_cents: 20, invoice: progressive_billing_invoice) } + let(:progressive_billing_fee) { create(:charge_fee, amount_cents: 20, invoice: progressive_billing_invoice) } before do progressive_billing_invoice @@ -229,11 +236,12 @@ status: 'finalized', invoice_type: :progressive_billing, subscriptions: [subscription], - issuing_date: invoice.issuing_date - 2.months + issuing_date: invoice.issuing_date - 2.months, + fees_amount_cents: 20 ) end - let(:progressive_billing_fee) { create(:progressive_billing_fee, amount_cents: 20, invoice: progressive_billing_invoice) } + let(:progressive_billing_fee) { create(:charge_fee, amount_cents: 20, invoice: progressive_billing_invoice) } before do progressive_billing_invoice diff --git a/spec/services/fees/apply_taxes_service_spec.rb b/spec/services/fees/apply_taxes_service_spec.rb index 3716eac8b47..171a2ec95c4 100644 --- a/spec/services/fees/apply_taxes_service_spec.rb +++ b/spec/services/fees/apply_taxes_service_spec.rb @@ -199,38 +199,6 @@ end end - context 'when fee is a progressive_billing type with taxes applied to the plan' do - let(:plan) { create(:plan, organization:) } - let(:usage_threshold) { create(:usage_threshold, plan:) } - let(:subscription) { create(:subscription, organization:, customer:, plan:) } - - let(:fee) { create(:progressive_billing_fee, invoice:, amount_cents: 1000, usage_threshold:, subscription:) } - - let(:applied_tax) { create(:plan_applied_tax, plan:, tax: tax1) } - - before { applied_tax } - - it 'creates applied_taxes based on the plan taxes', aggregate_failures: true do - result = apply_service.call - - expect(result).to be_success - - applied_taxes = result.applied_taxes - expect(applied_taxes.count).to eq(1) - - expect(applied_taxes[0]).to have_attributes( - fee:, - tax: tax1, - tax_description: tax1.description, - tax_code: tax1.code, - tax_name: tax1.name, - tax_rate: 10, - amount_currency: fee.currency, - amount_cents: 100 - ) - end - end - context 'when customer has applied_taxes' do let(:applied_tax1) { create(:customer_applied_tax, customer:, tax: tax1) } let(:applied_tax2) { create(:customer_applied_tax, customer:, tax: tax2) } diff --git a/spec/services/fees/create_from_usage_threshold_service_spec.rb b/spec/services/fees/create_from_usage_threshold_service_spec.rb deleted file mode 100644 index d0cb6dcef07..00000000000 --- a/spec/services/fees/create_from_usage_threshold_service_spec.rb +++ /dev/null @@ -1,87 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -RSpec.describe Fees::CreateFromUsageThresholdService, type: :service do - subject(:create_service) { described_class.new(usage_threshold:, invoice:, amount_cents:) } - - let(:usage_threshold) { create(:usage_threshold, plan:) } - let(:invoice) { create(:invoice, organization: customer.organization, customer:) } - - let(:invoice_subscription) { create(:invoice_subscription, invoice:, subscription:) } - let(:customer) { create(:customer) } - let(:plan) { create(:plan, organization: customer.organization) } - let(:subscription) { create(:subscription, plan:, customer:) } - - let(:amount_cents) { 1000 } - - let(:tax) { create(:tax, organization: customer.organization, rate: 20) } - - before do - invoice_subscription - tax - end - - it 'creates a fee from usage threshold', aggregate_failure: true do - fee_result = create_service.call - - expect(fee_result).to be_success - expect(fee_result.fee).to be_present - - fee = fee_result.fee - expect(fee).to be_persisted - expect(fee).to have_attributes( - subscription:, - invoice:, - usage_threshold:, - invoiceable: usage_threshold, - invoice_display_name: usage_threshold.threshold_display_name, - amount_cents: amount_cents, - amount_currency: invoice.currency, - fee_type: 'progressive_billing', - units: 1, - unit_amount_cents: amount_cents, - payment_status: 'pending', - taxes_amount_cents: amount_cents * tax.rate / 100, - properties: { - 'charges_from_datetime' => invoice_subscription.charges_from_datetime, - 'charges_to_datetime' => invoice_subscription.charges_to_datetime, - 'timestamp' => invoice_subscription.timestamp - } - ) - end - - context 'when usage thresold is recurring' do - let(:usage_threshold) { create(:usage_threshold, :recurring, plan:) } - let(:amount_cents) { usage_threshold.amount_cents * 5 } - - it 'creates a fee from usage threshold', aggregate_failure: true do - fee_result = create_service.call - - expect(fee_result).to be_success - expect(fee_result.fee).to be_present - - fee = fee_result.fee - expect(fee).to be_persisted - expect(fee).to have_attributes( - subscription:, - invoice:, - usage_threshold:, - invoiceable: usage_threshold, - invoice_display_name: usage_threshold.threshold_display_name, - amount_cents: amount_cents, - amount_currency: invoice.currency, - fee_type: 'progressive_billing', - units: 5, - unit_amount_cents: usage_threshold.amount_cents, - payment_status: 'pending', - taxes_amount_cents: amount_cents * tax.rate / 100, - properties: { - 'charges_from_datetime' => invoice_subscription.charges_from_datetime, - 'charges_to_datetime' => invoice_subscription.charges_to_datetime, - 'timestamp' => invoice_subscription.timestamp - } - ) - end - end -end diff --git a/spec/services/invoices/progressive_billing_service_spec.rb b/spec/services/invoices/progressive_billing_service_spec.rb index 59130e1f6d5..578f877636b 100644 --- a/spec/services/invoices/progressive_billing_service_spec.rb +++ b/spec/services/invoices/progressive_billing_service_spec.rb @@ -11,16 +11,31 @@ let(:customer) { create(:customer, organization:) } let(:subscription) { create(:subscription, plan:, customer:) } - let(:lifetime_usage) { create(:lifetime_usage, subscription:) } + let(:lifetime_usage) { create(:lifetime_usage, subscription:, organization:) } - let(:timestamp) { Time.current.beginning_of_month } + let(:timestamp) { Time.zone.parse('2024-08-22 10:00:00') } let(:tax) { create(:tax, organization:, rate: 20) } + let(:billable_metric) { create(:sum_billable_metric, organization:, field_name: 'value') } + let(:charge) { create(:standard_charge, plan:, billable_metric:, properties: {amount: '1'}) } + + let(:event) do + create( + :event, + organization_id: organization.id, + external_subscription_id: subscription.external_id, + code: billable_metric.code, + properties: {billable_metric.field_name => 1}, + timestamp: timestamp - 1.hour + ) + end before do allow(SegmentTrackJob).to receive(:perform_later) tax + charge + event end describe '#call' do @@ -31,7 +46,8 @@ expect(result.invoice).to be_present invoice = result.invoice - usage_threshold = usage_thresholds.first + amount_cents = 100 + expect(invoice).to be_persisted expect(invoice).to have_attributes( organization: organization, @@ -39,9 +55,9 @@ currency: plan.amount_currency, status: 'finalized', invoice_type: 'progressive_billing', - fees_amount_cents: usage_threshold.amount_cents, - taxes_amount_cents: usage_threshold.amount_cents * tax.rate / 100, - total_amount_cents: usage_threshold.amount_cents + usage_threshold.amount_cents * tax.rate / 100 + fees_amount_cents: amount_cents, + taxes_amount_cents: amount_cents * tax.rate / 100, + total_amount_cents: amount_cents * (1 + tax.rate / 100) ) expect(invoice.invoice_subscriptions.count).to eq(1) @@ -56,13 +72,15 @@ ] end - it 'creates a progressive billing invoice with two fees', aggregate_failures: true do + it 'creates a progressive billing invoice', aggregate_failures: true do result = create_service.call expect(result).to be_success expect(result.invoice).to be_present invoice = result.invoice + amount_cents = 100 + expect(invoice).to be_persisted expect(invoice).to have_attributes( organization: organization, @@ -70,56 +88,13 @@ currency: plan.amount_currency, status: 'finalized', invoice_type: 'progressive_billing', - fees_amount_cents: 2500, - taxes_amount_cents: 2500 * tax.rate / 100, - total_amount_cents: 2500 * (1 + tax.rate / 100) + fees_amount_cents: amount_cents, + taxes_amount_cents: amount_cents * tax.rate / 100, + total_amount_cents: amount_cents * (1 + tax.rate / 100) ) expect(invoice.invoice_subscriptions.count).to eq(1) - expect(invoice.fees.count).to eq(2) - - expect(invoice.fees.pluck(:amount_cents)).to match_array([1000, 1500]) - expect(invoice.fees.pluck(:usage_threshold_id)).to match_array(usage_thresholds.map(&:id)) - end - - context 'with a recurring threshold' do - let(:usage_thresholds) do - # NOTE: the order is wrong on purpose to test the sorting - [ - create(:usage_threshold, plan:, amount_cents: 2500), - create(:usage_threshold, :recurring, plan:, amount_cents: 500), - create(:usage_threshold, plan:, amount_cents: 1000) - ] - end - - let(:lifetime_usage) { create(:lifetime_usage, subscription:, current_usage_amount_cents: 4300) } - - it 'creates a progressive billing invoice with multiples fees', aggregate_failures: true do - result = create_service.call - - expect(result).to be_success - expect(result.invoice).to be_present - - invoice = result.invoice - expect(invoice).to be_persisted - expect(invoice).to have_attributes( - organization: organization, - customer: customer, - currency: plan.amount_currency, - status: 'finalized', - invoice_type: 'progressive_billing', - fees_amount_cents: 4000, - taxes_amount_cents: 4000 * tax.rate / 100, - total_amount_cents: 4000 * (1 + tax.rate / 100) - ) - - expect(invoice.invoice_subscriptions.count).to eq(1) - expect(invoice.fees.count).to eq(3) - - expect(invoice.fees.pluck(:amount_cents)).to match_array([1000, 1500, 1500]) - expect(invoice.fees.pluck(:usage_threshold_id)).to match_array(usage_thresholds.map(&:id)) - expect(invoice.fees.pluck(:units)).to match_array([1, 1, 3]) - end + expect(invoice.fees.count).to eq(1) end end @@ -149,8 +124,7 @@ expect(result.invoice).to be_present invoice = result.invoice - usage_threshold = usage_thresholds.first - amount_cents = usage_threshold.amount_cents - 20 + amount_cents = 100 expect(invoice).to be_persisted expect(invoice).to have_attributes( @@ -195,8 +169,7 @@ expect(result.invoice).to be_present invoice = result.invoice - usage_threshold = usage_thresholds.first - amount_cents = usage_threshold.amount_cents - 7 + amount_cents = 100 expect(invoice).to be_persisted expect(invoice).to have_attributes( @@ -213,40 +186,6 @@ expect(invoice.invoice_subscriptions.count).to eq(1) expect(invoice.fees.count).to eq(1) end - - context 'with a recurring threshold' do - let(:usage_thresholds) { [create(:usage_threshold, :recurring, plan:, amount_cents: 100)] } - - let(:lifetime_usage) { create(:lifetime_usage, subscription:, current_usage_amount_cents: 215) } - - it 'creates a progressive billing invoice', aggregate_failures: true do - result = create_service.call - - expect(result).to be_success - expect(result.invoice).to be_present - - invoice = result.invoice - usage_threshold = usage_thresholds.first - amount_cents = usage_threshold.amount_cents * 2 - - expect(invoice).to be_persisted - expect(invoice).to have_attributes( - organization: organization, - customer: customer, - currency: plan.amount_currency, - status: 'finalized', - invoice_type: 'progressive_billing', - fees_amount_cents: amount_cents, - taxes_amount_cents: amount_cents * tax.rate / 100, - total_amount_cents: amount_cents * (1 + tax.rate / 100) - ) - - expect(invoice.invoice_subscriptions.count).to eq(1) - expect(invoice.fees.count).to eq(1) - - expect(invoice.fees.pluck(:units)).to match_array([2]) - end - end end it 'enqueues a SendWebhookJob' do @@ -267,7 +206,7 @@ end context 'when organization does not have right email settings' do - before { organization.update!(email_settings: []) } + before { subscription.organization.update!(email_settings: []) } it 'enqueue an GeneratePdfAndNotifyJob with email false' do expect { create_service.call } diff --git a/spec/services/lifetime_usages/recalculate_and_check_service_spec.rb b/spec/services/lifetime_usages/recalculate_and_check_service_spec.rb index c5da96f340a..8ac3f74613a 100644 --- a/spec/services/lifetime_usages/recalculate_and_check_service_spec.rb +++ b/spec/services/lifetime_usages/recalculate_and_check_service_spec.rb @@ -63,10 +63,8 @@ def create_thresholds(subscription, amounts:, recurring: nil) ).on_queue(:webhook) end - it "creates an invoice for the usage_threshold amount" do + it "creates an invoice for the usage_threshold" do expect { service.call }.to change(Invoice, :count).by(1) - invoice = subscription.invoices.progressive_billing.sole - expect(invoice.total_amount_cents).to eq(usage_threshold.amount_cents) end end @@ -107,10 +105,8 @@ def create_thresholds(subscription, amounts:, recurring: nil) ).on_queue(:webhook) end - it "creates an invoice for the largest usage_threshold amount" do + it "creates an invoice for the current usage" do expect { service.call }.to change(Invoice, :count).by(1) - invoice = subscription.invoices.progressive_billing.sole - expect(invoice.total_amount_cents).to eq(usage_threshold2.amount_cents) end end @@ -128,7 +124,7 @@ def create_thresholds(subscription, amounts:, recurring: nil) ) end - let(:progressive_billing_fee) { create(:progressive_billing_fee, amount_cents: 20, invoice: progressive_billing_invoice) } + let(:progressive_billing_fee) { create(:charge_fee, amount_cents: 20, invoice: progressive_billing_invoice) } before do usage_threshold @@ -155,10 +151,8 @@ def create_thresholds(subscription, amounts:, recurring: nil) ).on_queue(:webhook) end - it "creates an invoice for the largest usage_threshold amount minus the progressive billing amount" do + it "creates an invoice for the current usage" do expect { service.call }.to change(Invoice, :count).by(1) - invoice = subscription.invoices.progressive_billing.where.not(id: progressive_billing_invoice.id).sole - expect(invoice.total_amount_cents).to eq(usage_threshold2.amount_cents - 20) end end