mirror of
https://github.com/LaCasemate/fab-manager.git
synced 2025-02-19 13:54:25 +01:00
stripe card update + cancel subscription
This commit is contained in:
parent
b0ef9e097d
commit
ff0c69fc58
@ -3,7 +3,7 @@
|
||||
# API Controller for resources of PaymentSchedule
|
||||
class API::PaymentSchedulesController < API::ApiController
|
||||
before_action :authenticate_user!
|
||||
before_action :set_payment_schedule, only: %i[download]
|
||||
before_action :set_payment_schedule, only: %i[download cancel]
|
||||
before_action :set_payment_schedule_item, only: %i[cash_check refresh_item pay_item]
|
||||
|
||||
def list
|
||||
@ -37,7 +37,7 @@ class API::PaymentSchedulesController < API::ApiController
|
||||
|
||||
def refresh_item
|
||||
authorize @payment_schedule_item.payment_schedule
|
||||
PaymentScheduleItemWorker.new.perform(params[:id])
|
||||
PaymentScheduleItemWorker.new.perform(@payment_schedule_item.id)
|
||||
|
||||
render json: { state: 'refreshed' }, status: :ok
|
||||
end
|
||||
@ -47,15 +47,24 @@ class API::PaymentSchedulesController < API::ApiController
|
||||
|
||||
stripe_key = Setting.get('stripe_secret_key')
|
||||
stp_invoice = Stripe::Invoice.pay(@payment_schedule_item.stp_invoice_id, {}, { api_key: stripe_key })
|
||||
PaymentScheduleItemWorker.new.perform(@payment_schedule_item.id)
|
||||
|
||||
render json: { status: stp_invoice.status }, status: :ok
|
||||
rescue Stripe::StripeError => e
|
||||
stripe_key = Setting.get('stripe_secret_key')
|
||||
stp_invoice = Stripe::Invoice.retrieve(@payment_schedule_item.stp_invoice_id, api_key: stripe_key)
|
||||
PaymentScheduleItemWorker.new.perform(@payment_schedule_item.id)
|
||||
|
||||
render json: { status: stp_invoice.status, error: e }, status: :unprocessable_entity
|
||||
end
|
||||
|
||||
def cancel
|
||||
authorize @payment_schedule
|
||||
|
||||
canceled_at = PaymentScheduleService.cancel(@payment_schedule)
|
||||
render json: { canceled_at: canceled_at }, status: :ok
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def set_payment_schedule
|
||||
|
@ -1,6 +1,7 @@
|
||||
import apiClient from './api-client';
|
||||
import { AxiosResponse } from 'axios';
|
||||
import {
|
||||
CancelScheduleResponse,
|
||||
CashCheckResponse, PayItemResponse,
|
||||
PaymentSchedule,
|
||||
PaymentScheduleIndexRequest, RefreshItemResponse
|
||||
@ -28,6 +29,11 @@ export default class PaymentScheduleAPI {
|
||||
return res?.data;
|
||||
}
|
||||
|
||||
async cancel (paymentScheduleId: number): Promise<CancelScheduleResponse> {
|
||||
const res: AxiosResponse = await apiClient.put(`/api/payment_schedules/${paymentScheduleId}/cancel`);
|
||||
return res?.data;
|
||||
}
|
||||
|
||||
static list (query: PaymentScheduleIndexRequest): IWrapPromise<Array<PaymentSchedule>> {
|
||||
const api = new PaymentScheduleAPI();
|
||||
return wrapPromise(api.list(query));
|
||||
|
@ -41,6 +41,7 @@ const PaymentSchedulesTableComponent: React.FC<PaymentSchedulesTableProps> = ({
|
||||
const [tempSchedule, setTempSchedule] = useState<PaymentSchedule>(null);
|
||||
const [canSubmitUpdateCard, setCanSubmitUpdateCard] = useState<boolean>(true);
|
||||
const [errors, setErrors] = useState<string>(null);
|
||||
const [showCancelSubscription, setShowCancelSubscription] = useState<boolean>(false);
|
||||
|
||||
/**
|
||||
* Check if the requested payment schedule is displayed with its deadlines (PaymentScheduleItem) or without them
|
||||
@ -158,6 +159,13 @@ const PaymentSchedulesTableComponent: React.FC<PaymentSchedulesTableProps> = ({
|
||||
{t('app.admin.invoices.schedules_table.update_card')}
|
||||
</FabButton>
|
||||
);
|
||||
case PaymentScheduleItemState.Error:
|
||||
return (
|
||||
<FabButton onClick={handleCancelSubscription(schedule)}
|
||||
icon={<i className="fas fa-times" />}>
|
||||
{t('app.admin.invoices.schedules_table.cancel_subscription')}
|
||||
</FabButton>
|
||||
)
|
||||
default:
|
||||
return <span />
|
||||
}
|
||||
@ -289,6 +297,34 @@ const PaymentSchedulesTableComponent: React.FC<PaymentSchedulesTableProps> = ({
|
||||
setCanSubmitUpdateCard(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback triggered when the user clicks on the "cancel subscription" button
|
||||
*/
|
||||
const handleCancelSubscription = (schedule: PaymentSchedule): ReactEventHandler => {
|
||||
return (): void => {
|
||||
setTempSchedule(schedule);
|
||||
toggleCancelSubscriptionModal();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Show/hide the modal dialog to cancel the current subscription
|
||||
*/
|
||||
const toggleCancelSubscriptionModal = (): void => {
|
||||
setShowCancelSubscription(!showCancelSubscription);
|
||||
}
|
||||
|
||||
/**
|
||||
* When the user has confirmed the cancellation, we transfer the request to the API
|
||||
*/
|
||||
const onCancelSubscriptionConfirmed = (): void => {
|
||||
const api = new PaymentScheduleAPI();
|
||||
api.cancel(tempSchedule.id).then(() => {
|
||||
refreshList();
|
||||
toggleCancelSubscriptionModal();
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<table className="schedules-table">
|
||||
@ -360,6 +396,14 @@ const PaymentSchedulesTableComponent: React.FC<PaymentSchedulesTableProps> = ({
|
||||
})}
|
||||
</span>}
|
||||
</FabModal>
|
||||
<FabModal title={t('app.admin.invoices.schedules_table.cancel_subscription')}
|
||||
isOpen={showCancelSubscription}
|
||||
toggleModal={toggleCancelSubscriptionModal}
|
||||
onConfirm={onCancelSubscriptionConfirmed}
|
||||
closeButton={true}
|
||||
confirmButton={t('app.admin.invoices.schedules_table.confirm_button')}>
|
||||
{t('app.admin.invoices.schedules_table.confirm_cancel_subscription')}
|
||||
</FabModal>
|
||||
<StripeElements>
|
||||
<FabModal title={t('app.admin.invoices.schedules_table.resolve_action')}
|
||||
isOpen={showResolveAction}
|
||||
|
@ -77,3 +77,7 @@ export interface PayItemResponse {
|
||||
status: 'draft' | 'open' | 'paid' | 'uncollectible' | 'void',
|
||||
error?: string
|
||||
}
|
||||
|
||||
export interface CancelScheduleResponse {
|
||||
canceled_at: Date
|
||||
}
|
||||
|
@ -38,7 +38,6 @@ class Subscription < ApplicationRecord
|
||||
end
|
||||
|
||||
def cancel
|
||||
# TODO, currently unused, refactor to use with PaymentSchedule
|
||||
update_columns(canceled_at: DateTime.current)
|
||||
end
|
||||
|
||||
|
@ -315,7 +315,9 @@ class PDF::Invoice < Prawn::Document
|
||||
# if the invoice was 100% payed with the wallet ...
|
||||
payment_verbose = I18n.t('invoices.settlement_by_wallet') if total.zero? && wallet_amount
|
||||
|
||||
payment_verbose += ' ' + I18n.t('invoices.on_DATE_at_TIME', DATE: I18n.l(invoice.created_at.to_date), TIME:I18n.l(invoice.created_at, format: :hour_minute))
|
||||
payment_verbose += ' ' + I18n.t('invoices.on_DATE_at_TIME',
|
||||
DATE: I18n.l(invoice.created_at.to_date),
|
||||
TIME: I18n.l(invoice.created_at, format: :hour_minute))
|
||||
if total.positive? || !invoice.wallet_amount
|
||||
payment_verbose += ' ' + I18n.t('invoices.for_an_amount_of_AMOUNT', AMOUNT: number_to_currency(total))
|
||||
end
|
||||
|
@ -99,7 +99,10 @@ class PDF::PaymentSchedule < Prawn::Document
|
||||
# payment method
|
||||
move_down 20
|
||||
payment_verbose = _t('payment_schedules.settlement_by_METHOD', METHOD: payment_schedule.payment_method)
|
||||
payment_verbose = I18n.t('payment_schedules.settlement_by_wallet', AMOUNT: payment_schedule.wallet_amount / 100.00) if payment_schedule.wallet_amount
|
||||
if payment_schedule.wallet_amount
|
||||
payment_verbose += I18n.t('payment_schedules.settlement_by_wallet',
|
||||
AMOUNT: number_to_currency(payment_schedule.wallet_amount / 100.00))
|
||||
end
|
||||
text payment_verbose
|
||||
|
||||
# important information
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
# Check the access policies for API::PaymentSchedulesController
|
||||
class PaymentSchedulePolicy < ApplicationPolicy
|
||||
%w[list? cash_check?].each do |action|
|
||||
%w[list? cash_check? cancel?].each do |action|
|
||||
define_method action do
|
||||
user.admin? || user.manager?
|
||||
end
|
||||
|
@ -138,6 +138,20 @@ class PaymentScheduleService
|
||||
ps
|
||||
end
|
||||
|
||||
def self.cancel(payment_schedule)
|
||||
# cancel all item where state != paid
|
||||
payment_schedule.ordered_items.each do |item|
|
||||
next if item.state == 'paid'
|
||||
|
||||
item.update_attributes(state: 'canceled')
|
||||
end
|
||||
# cancel subscription
|
||||
subscription = Subscription.find(payment_schedule.payment_schedule_items.first.details['subscription_id'])
|
||||
subscription.cancel
|
||||
|
||||
subscription.canceled_at
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
##
|
||||
|
@ -659,6 +659,7 @@ en:
|
||||
state_requires_action: "Action required"
|
||||
state_paid: "Paid"
|
||||
state_error: "Error"
|
||||
state_canceled: "Canceled"
|
||||
method_stripe: "by card"
|
||||
method_check: "by check"
|
||||
confirm_payment: "Confirm payment"
|
||||
@ -670,6 +671,8 @@ en:
|
||||
resolve_action: "Resolve the action"
|
||||
ok_button: "OK"
|
||||
validate_button: "Validate the new card"
|
||||
cancel_subscription: "Cancel the subscription"
|
||||
confirm_cancel_subscription: "You're about to cancel this payment schedule and the related subscription. Are you sure?"
|
||||
document_filters:
|
||||
reference: "Reference"
|
||||
customer: "Customer"
|
||||
|
@ -659,6 +659,7 @@ fr:
|
||||
state_requires_action: "Action requise"
|
||||
state_paid: "Payée"
|
||||
state_error: "Erreur"
|
||||
state_canceled: "Annulée"
|
||||
method_stripe: "par carte"
|
||||
method_check: "par chèque"
|
||||
confirm_payment: "Confirmer l'encaissement"
|
||||
@ -670,6 +671,8 @@ fr:
|
||||
resolve_action: "Résoudre l'action"
|
||||
ok_button: "OK"
|
||||
validate_button: "Valider la nouvelle carte"
|
||||
cancel_subscription: "Annuler l'abonnement"
|
||||
confirm_cancel_subscription: "Vous êtes sur le point d'annuler cet échéancier de paiement ainsi que l'abonnement lié. Êtes-vous sur ?"
|
||||
document_filters:
|
||||
reference: "Référence"
|
||||
customer: "Client"
|
||||
|
@ -113,6 +113,7 @@ Rails.application.routes.draw do
|
||||
|
||||
resources :payment_schedules, only: %i[show] do
|
||||
post 'list', action: 'list', on: :collection
|
||||
put 'cancel', on: :member
|
||||
get 'download', on: :member
|
||||
post 'items/:id/cash_check', action: 'cash_check', on: :collection
|
||||
post 'items/:id/refresh_item', action: 'refresh_item', on: :collection
|
||||
|
Loading…
x
Reference in New Issue
Block a user