Aggiunge pagamento con bonifico e importo concordato per società.

Il piano si attiva solo dopo la conferma admin; Stripe resta a listino se non c'è un prezzo commerciale.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-08-18 23:20:43 +02:00
co-authored by Cursor
parent 1e4e0560f1
commit e436f5ece4
56 changed files with 1571 additions and 53 deletions
@@ -0,0 +1,27 @@
require "rails_helper"
RSpec.describe ExpireEndedSubscriptionsJob do
let(:club) do
c = Club.create!(name: "Expire BT", sport: "volleyball", primary_color: "#e53935", secondary_color: "#ffffff")
load Rails.root.join("db/seeds/plans.rb")
c
end
it "passa a Free un piano pagato con bonifico scaduto" do
Billing::AssignPlan.call(
club: club,
plan_slug: "premium_light",
status: "active",
stripe_attrs: {
billing_interval: "monthly",
current_period_start: 2.months.ago,
current_period_end: 1.day.ago,
cancel_at_period_end: true
}
)
described_class.new.perform
expect(club.reload.subscription.plan.slug).to eq("free")
end
end
@@ -0,0 +1,54 @@
require "rails_helper"
RSpec.describe "Admin bank transfer", type: :request do
let!(:admin) { AdminAccount.create!(username: "ops-bt-http", password: "Password123") }
let!(:club) do
c = Club.create!(
name: "Admin BT Club", sport: "volleyball", primary_color: "#e53935", secondary_color: "#ffffff",
billing_entity_type: "company", billing_legal_name: "ASD Admin BT", billing_email: "adminbt@test.it",
billing_address_line: "Via 1", billing_city: "Milano", billing_province: "MI", billing_postal_code: "20100",
billing_country: "IT", billing_vat_number: "12345678901", billing_recipient_code: "ABCDEFG"
)
load Rails.root.join("db/seeds/plans.rb")
Billing::AssignPlan.call(club: c, plan_slug: "free")
c
end
before do
post admin_login_path, params: { username: "ops-bt-http", password: "Password123" }
end
it "salva il prezzo concordato sulla società" do
post set_quote_admin_club_path(club), params: {
plan_slug: "premium_full",
interval: "yearly",
amount_euros: "175,50",
note: "Accordo regionale"
}
expect(response).to redirect_to(admin_club_path(club))
quote = club.reload.active_billing_quote
expect(quote.amount_cents).to eq(17_550)
expect(quote.note).to eq("Accordo regionale")
end
it "conferma un bonifico in attesa e attiva il piano" do
allow(MatchLiveTv).to receive_messages(
bank_transfer_configured?: true,
bank_transfer_iban: "IT60X0542811101000000123456",
bank_transfer_account_holder: "Match Live TV",
bank_transfer_bank_name: "Banca",
bank_transfer_bic: "TESTITMM",
bank_transfer_proof_email: "info@matchlivetv.it"
)
user = User.create!(email: "req@test.it", name: "R", password: "Password123", role: "coach")
ClubMembership.create!(user: user, club: club, role: "owner")
order = Billing::RequestBankTransfer.call(club: club, user: user, plan_slug: "premium_light", interval: "monthly")
post admin_billing_transfer_confirm_path(order)
expect(response).to redirect_to(admin_billing_path(club_id: club.id))
expect(club.reload.subscription.plan.slug).to eq("premium_light")
expect(order.reload).to be_paid
end
end
@@ -0,0 +1,62 @@
require "rails_helper"
RSpec.describe "Public bank transfer billing", type: :request do
let!(:coach) do
User.create!(email: "bt-coach@test.it", name: "Coach", password: "Password123", role: "coach")
end
let!(:club) do
c = Club.create!(
name: "BT HTTP Club", sport: "volleyball", primary_color: "#e53935", secondary_color: "#ffffff",
billing_entity_type: "company", billing_legal_name: "ASD BT", billing_email: "bill@test.it",
billing_address_line: "Via 1", billing_city: "Milano", billing_province: "MI", billing_postal_code: "20100",
billing_country: "IT", billing_vat_number: "12345678901", billing_recipient_code: "ABCDEFG"
)
ClubMembership.create!(user: coach, club: c, role: "owner")
c.teams.create!(name: "U15", sport: "volleyball")
load Rails.root.join("db/seeds/plans.rb")
Billing::AssignPlan.call(club: c, plan_slug: "free")
c
end
before do
allow(MatchLiveTv).to receive_messages(
stripe_enabled?: true,
stripe_premium_light_yearly_price_id: "py",
stripe_premium_light_monthly_price_id: "pm",
stripe_premium_full_yearly_price_id: "fy",
stripe_premium_full_monthly_price_id: "fm",
bank_transfer_configured?: true,
bank_transfer_iban: "IT60X0542811101000000123456",
bank_transfer_account_holder: "Match Live TV",
bank_transfer_bank_name: "Banca Test",
bank_transfer_bic: "TESTITMM",
bank_transfer_proof_email: "info@matchlivetv.it"
)
post public_login_path, params: { email: coach.email, password: "Password123" }
end
it "mostra il pulsante bonifico a listino" do
get public_club_billing_path(club)
expect(response).to have_http_status(:ok)
expect(response.body).to include("Paga con bonifico")
end
it "accetta la richiesta di bonifico senza attivare il piano" do
post public_club_billing_bank_transfer_path(club, plan: "premium_light", interval: "yearly")
expect(response).to redirect_to(public_club_billing_path(club))
expect(club.reload.subscription.plan.slug).to eq("free")
expect(club.billing_transfer_orders.awaiting_payment).to exist
end
it "con prezzo concordato nasconde Stripe e mostra l'importo" do
Billing::SetClubQuote.upsert(
club: club, plan_slug: "premium_full", interval: "yearly",
amount_euros: "120", note: "Test", admin: nil
)
get public_club_billing_path(club)
expect(response.body).to include("Prezzo concordato")
expect(response.body).to include("€120")
expect(response.body).not_to include("Attiva Full — €199/anno")
end
end
@@ -0,0 +1,62 @@
require "rails_helper"
RSpec.describe Billing::ConfirmBankTransfer do
let(:admin) { AdminAccount.create!(username: "ops-bt", password: "Password123") }
let(:user) { User.create!(email: "owner-confirm@test.it", name: "Coach", password: "Password123", role: "coach") }
let(:club) do
c = Club.create!(
name: "Confirm Club", sport: "volleyball", primary_color: "#e53935", secondary_color: "#ffffff",
billing_entity_type: "company", billing_legal_name: "ASD Confirm", billing_email: "fatture@confirm.test",
billing_address_line: "Via 1", billing_city: "Milano", billing_province: "MI", billing_postal_code: "20100",
billing_country: "IT", billing_vat_number: "12345678901", billing_recipient_code: "ABCDEFG"
)
ClubMembership.create!(user: user, club: c, role: "owner")
load Rails.root.join("db/seeds/plans.rb")
Billing::AssignPlan.call(club: c, plan_slug: "free")
c
end
before do
allow(MatchLiveTv).to receive_messages(
bank_transfer_configured?: true,
bank_transfer_iban: "IT60X0542811101000000123456",
bank_transfer_account_holder: "Match Live TV",
bank_transfer_bank_name: "Banca Test",
bank_transfer_bic: "TESTITMM",
bank_transfer_proof_email: "info@matchlivetv.it"
)
end
def request_order
Billing::RequestBankTransfer.call(club: club, user: user, plan_slug: "premium_full", interval: "yearly")
end
it "attiva il piano e invia la mail di attivazione senza PDF" do
order = request_order
ActionMailer::Base.deliveries.clear
described_class.call(order: order, admin: admin)
sub = club.reload.subscription
expect(sub.plan.slug).to eq("premium_full")
expect(sub).to be_bank_transfer
expect(sub.cancel_at_period_end).to be true
expect(sub.current_period_end).to be_within(2.seconds).of(1.year.from_now)
expect(order.reload).to be_paid
expect(order.billing_payment.status).to eq("paid")
expect(ActionMailer::Base.deliveries.last.subject).to include("Premium Full")
end
it "con PDF invia una sola email di attivazione con fattura" do
order = request_order
ActionMailer::Base.deliveries.clear
pdf = { io: StringIO.new("%PDF-1.4 test"), filename: "fattura.pdf", content_type: "application/pdf" }
expect {
described_class.call(order: order, admin: admin, pdf: pdf)
}.to change { ActionMailer::Base.deliveries.size }.by(1)
expect(order.billing_payment.reload.invoice.pdf).to be_attached
expect(ActionMailer::Base.deliveries.last.subject).to include("fattura")
end
end
@@ -0,0 +1,65 @@
require "rails_helper"
RSpec.describe Billing::RequestBankTransfer do
let(:user) { User.create!(email: "owner-bt@test.it", name: "Coach", password: "Password123", role: "coach") }
let(:club) do
c = Club.create!(
name: "Bonifico Club", sport: "volleyball", primary_color: "#e53935", secondary_color: "#ffffff",
billing_entity_type: "company", billing_legal_name: "ASD Bonifico", billing_email: "bill@test.it",
billing_address_line: "Via 1", billing_city: "Milano", billing_province: "MI", billing_postal_code: "20100",
billing_country: "IT", billing_vat_number: "12345678901", billing_recipient_code: "ABCDEFG"
)
ClubMembership.create!(user: user, club: c, role: "owner")
load Rails.root.join("db/seeds/plans.rb")
Billing::AssignPlan.call(club: c, plan_slug: "free")
c
end
before do
allow(MatchLiveTv).to receive_messages(
bank_transfer_configured?: true,
bank_transfer_iban: "IT60X0542811101000000123456",
bank_transfer_account_holder: "Match Live TV",
bank_transfer_bank_name: "Banca Test",
bank_transfer_bic: "TESTITMM",
bank_transfer_proof_email: "info@matchlivetv.it"
)
end
it "crea un ordine a listino e invia le istruzioni" do
expect {
described_class.call(club: club, user: user, plan_slug: "premium_light", interval: "yearly")
}.to change { club.billing_transfer_orders.count }.by(1)
.and change { ActionMailer::Base.deliveries.size }.by(1)
order = club.billing_transfer_orders.last
expect(order).to be_awaiting_payment
expect(order.kind).to eq("list_price")
expect(order.amount_cents).to eq(5900)
expect(order.billing_payment.status).to eq("pending")
expect(order.billing_payment.provider).to eq("bank_transfer")
expect(club.reload.subscription.plan.slug).to eq("free")
end
it "usa l'importo concordato se presente" do
Billing::SetClubQuote.upsert(
club: club, plan_slug: "premium_full", interval: "yearly",
amount_euros: "150", note: "Accordo 2026", admin: nil
)
order = described_class.call(club: club, user: user, plan_slug: "premium_full", interval: "yearly")
expect(order.kind).to eq("commercial_quote")
expect(order.amount_cents).to eq(15_000)
end
it "rifiuta un piano diverso dal prezzo concordato" do
Billing::SetClubQuote.upsert(
club: club, plan_slug: "premium_full", interval: "yearly",
amount_euros: "150", note: nil, admin: nil
)
expect {
described_class.call(club: club, user: user, plan_slug: "premium_light", interval: "yearly")
}.to raise_error(Billing::RequestBankTransfer::Error, /prezzo concordato/)
end
end
@@ -0,0 +1,37 @@
require "rails_helper"
RSpec.describe Billing::SetClubQuote do
let(:admin) { AdminAccount.create!(username: "ops-quote", password: "Password123") }
let(:club) do
c = Club.create!(name: "Quote Club", sport: "volleyball", primary_color: "#e53935", secondary_color: "#ffffff")
load Rails.root.join("db/seeds/plans.rb")
Billing::AssignPlan.call(club: c, plan_slug: "free")
c
end
it "salva l'importo in centesimi per la società" do
quote = described_class.upsert(
club: club,
plan_slug: "premium_full",
interval: "yearly",
amount_euros: "150,00",
note: "Accordo sponsor",
admin: admin
)
expect(quote).to be_active
expect(quote.amount_cents).to eq(15_000)
expect(quote.plan_slug).to eq("premium_full")
expect(club.reload.active_billing_quote).to eq(quote)
end
it "revoca il prezzo concordato" do
described_class.upsert(
club: club, plan_slug: "premium_light", interval: "monthly",
amount_euros: "5", note: nil, admin: admin
)
described_class.revoke(club: club, admin: admin)
expect(club.reload.active_billing_quote).to be_nil
end
end