1
0
mirror of https://github.com/LaCasemate/fab-manager.git synced 2025-01-18 07:52:23 +01:00

transactional bulk update + better error handling while configuring the payment gateway

This commit is contained in:
Sylvain 2021-06-10 10:39:42 +02:00
parent ef69cab179
commit a416f8c7ae
12 changed files with 173 additions and 26 deletions

View File

@ -12,7 +12,7 @@ class API::SettingsController < API::ApiController
authorize Setting
@setting = Setting.find_or_initialize_by(name: params[:name])
render status: :not_modified and return if setting_params[:value] == @setting.value
render status: :locked, json: { error: 'locked setting' } and return unless SettingService.before_update(@setting)
render status: :locked, json: { error: I18n.t('settings.locked_setting') } and return unless SettingService.before_update(@setting)
if @setting.save && @setting.history_values.create(value: setting_params[:value], invoicing_profile: current_user.invoicing_profile)
SettingService.after_update(@setting)
@ -26,18 +26,21 @@ class API::SettingsController < API::ApiController
authorize Setting
@settings = []
params[:settings].each do |setting|
next if !setting[:name] || !setting[:value]
may_transaction(params[:transactional]) do
params[:settings].each do |setting|
next if !setting[:name] || !setting[:value]
db_setting = Setting.find_or_initialize_by(name: setting[:name])
next unless SettingService.before_update(db_setting)
db_setting = Setting.find_or_initialize_by(name: setting[:name])
if !SettingService.before_update(db_setting)
db_setting.errors[:-] << I18n.t("settings.#{setting[:name]}") + ': ' + I18n.t('settings.locked_setting')
elsif db_setting.save
db_setting.history_values.create(value: setting[:value], invoicing_profile: current_user.invoicing_profile)
SettingService.after_update(db_setting)
end
if db_setting.save
db_setting.history_values.create(value: setting[:value], invoicing_profile: current_user.invoicing_profile)
SettingService.after_update(db_setting)
@settings.push db_setting
may_rollback(params[:transactional]) if db_setting.errors.keys.count.positive?
end
@settings.push db_setting
end
end
@ -79,4 +82,20 @@ class API::SettingsController < API::ApiController
def names_as_string_to_array
params[:names][1..-2].split(',').map(&:strip).map { |param| param[1..-2] }.map(&:strip)
end
# run the given block in a transaction if `should` is true. Just run it normally otherwise
def may_transaction(should)
if should == 'true'
ActiveRecord::Base.transaction do
yield
end
else
yield
end
end
# rollback the current DB transaction if `should` is true
def may_rollback(should)
raise ActiveRecord::Rollback if should == 'true'
end
end

View File

@ -23,8 +23,8 @@ export default class SettingAPI {
return res?.data?.setting;
}
async bulkUpdate (settings: Map<SettingName, any>): Promise<Map<SettingName, SettingBulkResult>> {
const res: AxiosResponse = await apiClient.patch('/api/settings/bulk_update', { settings: SettingAPI.toObjectArray(settings) });
async bulkUpdate (settings: Map<SettingName, any>, transactional: boolean = false): Promise<Map<SettingName, SettingBulkResult>> {
const res: AxiosResponse = await apiClient.patch(`/api/settings/bulk_update?transactional=${transactional}`, { settings: SettingAPI.toObjectArray(settings) });
return SettingAPI.toBulkMap(res?.data?.settings);
}
@ -67,6 +67,9 @@ export default class SettingAPI {
if ('value' in item) {
itemData.value = item.value;
}
if ('localized' in item) {
itemData.localized = item.localized;
}
map.set(item.name as SettingName, itemData)
});

View File

@ -23,7 +23,7 @@ interface SelectGatewayModalModalProps {
isOpen: boolean,
toggleModal: () => void,
currentUser: User,
onError: (errors: Map<SettingName, SettingBulkResult>|any) => void,
onError: (errors: string) => void,
onSuccess: (results: Map<SettingName, SettingBulkResult>) => void,
}
@ -102,10 +102,12 @@ const SelectGatewayModal: React.FC<SelectGatewayModalModalProps> = ({ isOpen, to
settings.set(SettingName.PaymentGateway, selectedGateway);
const api = new SettingAPI();
api.bulkUpdate(settings).then(result => {
if (Array.from(result.values()).filter(item => !item.status).length > 0) {
onError(result);
api.bulkUpdate(settings, true).then(result => {
const errorResults = Array.from(result.values()).filter(item => !item.status);
if (errorResults.length > 0) {
onError(errorResults.map(item => item.error[0]).join(' '));
} else {
// we call the success callback only in case of full success (transactional bulk update)
onSuccess(result);
}
}, reason => {

View File

@ -720,9 +720,8 @@ Application.Controllers.controller('InvoicesController', ['$scope', '$state', 'I
/**
* Callback triggered after the gateway failed to be configured
*/
$scope.onGatewayModalError = function (errors) {
growl.error(_t('app.admin.invoices.payment.gateway_configuration_error'));
console.error(errors);
$scope.onGatewayModalError = function (message) {
growl.error(_t('app.admin.invoices.payment.gateway_configuration_error') + message);
};
/**

View File

@ -24,7 +24,6 @@ export enum SettingName {
InvoiceLegals = 'invoice_legals',
BookingWindowStart = 'booking_window_start',
BookingWindowEnd = 'booking_window_end',
BookingSlotDuration = 'booking_slot_duration',
BookingMoveEnable = 'booking_move_enable',
BookingMoveDelay = 'booking_move_delay',
BookingCancelEnable = 'booking_cancel_enable',
@ -113,6 +112,7 @@ export enum SettingName {
export interface Setting {
name: SettingName,
localized?: string,
value: string,
last_update?: Date,
history?: Array<HistoryValue>
@ -127,5 +127,6 @@ export interface SettingError {
export interface SettingBulkResult {
status: boolean,
value?: any,
error?: string
error?: string,
localized?: string,
}

View File

@ -7,7 +7,7 @@
class Setting < ApplicationRecord
has_many :history_values
# The following list contains all the settings that can be customized from the Fab-manager's UI.
# A few of them that are system settings, that should not be updated manually (uuid, origin).
# A few of them that are system settings, that should not be updated manually (uuid, origin...).
validates :name, inclusion:
{ in: %w[about_title
about_body
@ -32,7 +32,6 @@ class Setting < ApplicationRecord
invoice_legals
booking_window_start
booking_window_end
booking_slot_duration
booking_move_enable
booking_move_delay
booking_cancel_enable

View File

@ -34,7 +34,7 @@ class SettingPolicy < ApplicationPolicy
def self.public_whitelist
%w[about_title about_body about_contacts privacy_body privacy_dpo twitter_name home_blogpost machine_explications_alert
training_explications_alert training_information_message subscription_explications_alert booking_window_start
booking_window_end booking_slot_duration booking_move_enable booking_move_delay booking_cancel_enable booking_cancel_delay
booking_window_end booking_move_enable booking_move_delay booking_cancel_enable booking_cancel_delay
fablab_name name_genre event_explications_alert space_explications_alert link_name home_content phone_required
tracking_id book_overlapping_slots slot_duration events_in_calendar spaces_module plans_module invoicing_module
recaptcha_site_key feature_tour_display disqus_shortname allowed_cad_extensions openlab_app_id openlab_default

View File

@ -1 +1,4 @@
# frozen_string_literal: true
json.extract! setting, :name, :value, :last_update
json.localized I18n.t("settings.#{setting[:name]}")

View File

@ -650,7 +650,7 @@ en:
currency_info_html: "Please specify below the currency used for online payment. You should provide a three-letter ISO code, from the list of <a href='https://stripe.com/docs/currencies' target='_blank'>Stripe supported currencies</a>."
currency_alert_html: "<strong>Warning</strong>: the currency cannot be changed after the first online payment was made. Please define this setting carefully before opening Fab-manager to your members."
stripe_currency: "Stripe currency"
gateway_configuration_error: "An error occurred while configuring the payment gateway."
gateway_configuration_error: "An error occurred while configuring the payment gateway: "
payzen:
payzen_keys: "PayZen keys"
payzen_username: "Username"
@ -673,6 +673,7 @@ en:
stripe: "Stripe"
payzen: "PayZen"
confirm_button: "Validate the gateway"
successfully_updated: " successfully updated"
payment_schedules:
filter_schedules: "Filter schedules"
no_payment_schedules: "No payment schedules to display"

View File

@ -650,7 +650,7 @@ fr:
currency_info_html: "Veuillez indiquer la devise à utiliser lors des paiements en ligne. Vous devez fournir un code ISO à trois lettres, issu de la liste des <a href='https://stripe.com/docs/currencies' target='_blank'>devises supportées par Stripe</a>."
currency_alert_html: "<strong>Attention</strong> : la devise ne peut pas être modifiée après que le premier paiement en ligne ait été effectué. Veuillez définir attentivement ce paramètre avant d'ouvrir Fab-manager à vos membres."
stripe_currency: "Devise Stripe"
gateway_configuration_error: "Une erreur est survenue lors de la configuration de la passerelle de paiement."
gateway_configuration_error: "Une erreur est survenue lors de la configuration de la passerelle de paiement : "
payzen:
payzen_keys: "Clefs PayZen"
payzen_username: "Utilisateur"

View File

@ -414,3 +414,114 @@ en:
group:
#name of the user's group for administrators
admins: 'Administrators'
settings:
locked_setting: "the setting is locked."
about_title: "\"About\" page title"
about_body: "\"About\" page content"
about_contacts: "\"About\" page contacts"
privacy_draft: "Privacy policy draft"
privacy_body: "Privacy policy"
privacy_dpo: "Data protection officer address"
twitter_name: "Twitter feed name"
home_blogpost: "Homepage's brief"
machine_explications_alert: "Explanation message on the machine reservation page"
training_explications_alert: "Explanation message on the training reservation page"
training_information_message: "Information message on the machine reservation page"
subscription_explications_alert: "Explanation message on the subscription page"
invoice_logo: "Invoices' logo"
invoice_reference: "Invoice's reference"
invoice_code-active: "Activation of the invoices' code"
invoice_code-value: "Invoices' code"
invoice_order-nb: "Invoice's order number"
invoice_VAT-active: "Activation of the VAT"
invoice_VAT-rate: "VAT rate"
invoice_text: "Invoices' text"
invoice_legals: "Invoices' legal information"
booking_window_start: "Opening time"
booking_window_end: "Closing time"
booking_move_enable: "Activation of reservations moving"
booking_move_delay: "Preventive delay before any reservation move"
booking_cancel_enable: "Activation of reservations cancelling"
booking_cancel_delay: "Preventive delay before any reservation cancellation"
main_color: "Main colour"
secondary_color: "Secondary colour"
fablab_name: "Fablab's name"
name_genre: "Title concordance"
reminder_enable: "Activation of reservations reminding"
reminder_delay: "Delay before sending the reminder"
event_explications_alert: "Explanation message on the event reservation page"
space_explications_alert: "Explanation message on the space reservation page"
visibility_yearly: "Maximum visibility for annual subscribers"
visibility_others: "Maximum visibility for other members"
display_name_enable: "Display names in the calendar"
machines_sort_by: "Machines display order"
accounting_journal_code: "Journal code"
accounting_card_client_code: "Card clients code"
accounting_card_client_label: "Card clients label"
accounting_wallet_client_code: "Wallet clients code"
accounting_wallet_client_label: "Wallet clients label"
accounting_other_client_code: "Other means client code"
accounting_other_client_label: "Other means client label"
accounting_wallet_code: "Wallet code"
accounting_wallet_label: "Wallet label"
accounting_VAT_code: "VAT code"
accounting_VAT_label: "VAT label"
accounting_subscription_code: "Subscriptions code"
accounting_subscription_label: "Subscriptions label"
accounting_Machine_code: "Machines code"
accounting_Machine_label: "Machines label"
accounting_Training_code: "Trainings code"
accounting_Training_label: "Trainings label"
accounting_Event_code: "Events code"
accounting_Event_label: "Events label"
accounting_Space_code: "Spaces code"
accounting_Space_label: "Spaces label"
hub_last_version: "Last Fab-manager's version"
hub_public_key: "Instance public key"
fab_analytics: "Fab Analytics"
link_name: "Link title to the \"About\" page"
home_content: "The home page"
home_css: "Stylesheet of the home page"
origin: "Instance URL"
uuid: "Instance ID"
phone_required: "Phone required?"
tracking_id: "Tracking ID"
book_overlapping_slots: "Book overlapping slots"
slot_duration: "Default duration of booking slots"
events_in_calendar: "Display events in the calendar"
spaces_module: "Spaces module"
plans_module: "Plans modules"
invoicing_module: "Invoicing module"
facebook_app_id: "Facebook App ID"
twitter_analytics: "Twitter analytics account"
recaptcha_site_key: "reCAPTCHA Site Key"
recaptcha_secret_key: "reCAPTCHA Secret Key"
feature_tour_display: "Feature tour display mode"
email_from: "Expeditor's address"
disqus_shortname: "Disqus shortname"
allowed_cad_extensions: "Allowed CAD files extensions"
allowed_cad_mime_types: "Allowed CAD files MIME types"
openlab_app_id: "OpenLab ID"
openlab_app_secret: "OpenLab secret"
openlab_default: "Default projects gallery view"
online_payment_module: "Online payments module"
stripe_public_key: "Stripe public key"
stripe_secret_key: "Stripe secret key"
stripe_currency: "Stripe currency"
invoice_prefix: "Invoices' files prefix"
confirmation_required: "Confirmation required"
wallet_module: "Wallet module"
statistics_module: "Statistics module"
upcoming_events_shown: "Display limit for upcoming events"
payment_schedule_prefix: "Payment schedule's files prefix"
trainings_module: "Trainings module"
address_required: "Address required"
accounting_Error_code: "Errors code"
accounting_Error_label: "Errors label"
payment_gateway: "Payment gateway"
payzen_username: "PayZen username"
payzen_password: "PayZen password"
payzen_endpoint: "PayZen API endpoint"
payzen_public_key: "PayZen client public key"
payzen_hmac: "PayZen HMAC-SHA-256 key"
payzen_currency: "PayZen currency"

View File

@ -414,3 +414,12 @@ fr:
group:
#name of the user's group for administrators
admins: 'Administrateurs'
settings:
locked_setting: "le paramètre est verrouillé."
payment_gateway: "Passerelle de paiement"
payzen_username: "Nom d'utilisateur PayZen"
payzen_password: "Mot de passe PayZen"
payzen_endpoint: "Point d'accès à l'API PayZen"
payzen_public_key: "Clef publique du client PayZen"
payzen_hmac: "Clef HMAC-SHA-256 PayZen"
payzen_currency: "Devise PayZen"