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

(feat) per training settings for auto cancel

This commit is contained in:
Sylvain 2023-01-24 16:48:05 +01:00 committed by Sylvain
parent e2edbb419a
commit c804c84113
10 changed files with 77 additions and 20 deletions

View File

@ -26,6 +26,7 @@ class API::SettingsController < API::ApiController
authorize Setting
@settings = []
updated_settings = []
may_transaction params[:transactional] do
params[:settings].each do |setting|
next if !setting[:name] || !setting[:value]
@ -34,8 +35,9 @@ class API::SettingsController < API::ApiController
if !SettingService.update_allowed?(db_setting)
db_setting.errors.add(:-, "#{I18n.t("settings.#{setting[:name]}")}: #{I18n.t('settings.locked_setting')}")
elsif db_setting.save
unless db_setting.value == setting[:value]
db_setting.history_values.create(value: setting[:value], invoicing_profile: current_user.invoicing_profile)
if db_setting.value != setting[:value] &&
db_setting.history_values.create(value: setting[:value], invoicing_profile: current_user.invoicing_profile)
updated_settings.push(db_setting)
end
end
@ -43,7 +45,7 @@ class API::SettingsController < API::ApiController
may_rollback(params[:transactional]) if db_setting.errors.keys.count.positive?
end
end
SettingService.run_after_update(@settings)
SettingService.run_after_update(updated_settings)
end
def show

View File

@ -77,6 +77,7 @@ class API::TrainingsController < API::ApiController
def training_params
params.require(:training)
.permit(:id, :name, :description, :machine_ids, :plan_ids, :nb_total_places, :public_page, :disabled,
:auto_cancel, :auto_cancel_threshold, :auto_cancel_deadline,
training_image_attributes: %i[id attachment], machine_ids: [], plan_ids: [],
advanced_accounting_attributes: %i[code analytical_section])
end

View File

@ -38,13 +38,13 @@ export const TrainingForm: React.FC<TrainingFormProps> = ({ action, training, on
const { t } = useTranslation('admin');
const [machineModule, setMachineModule] = useState<Setting>(null);
const [isActiveCancellation, setIsActiveCancellation] = useState<boolean>(false);
const [isActiveAccounting, setIsActiveAccounting] = useState<boolean>(false);
const [isActiveAuthorizationValidity, setIsActiveAuthorizationValidity] = useState<boolean>(false);
const [isActiveValidationRule, setIsActiveValidationRule] = useState<boolean>(false);
const { handleSubmit, register, control, setValue, formState } = useForm<Training>({ defaultValues: { ...training } });
const output = useWatch<Training>({ control });
const isActiveCancellation = useWatch({ control, name: 'auto_cancel' }) as boolean;
useEffect(() => {
SettingAPI.get('machines_module').then(setMachineModule).catch(onError);
@ -79,13 +79,6 @@ export const TrainingForm: React.FC<TrainingFormProps> = ({ action, training, on
}).catch(error => onError(error));
};
/**
* Callback triggered when the auto cancellation switch has changed.
*/
const toggleCancellationSwitch = (value: boolean) => {
setIsActiveCancellation(value);
};
/**
* Callback triggered when the authorisation validity switch has changed.
*/
@ -178,15 +171,15 @@ export const TrainingForm: React.FC<TrainingFormProps> = ({ action, training, on
<p className="description">{t('app.admin.training_form.automatic_cancellation_info')}</p>
</header>
<div className="content">
<FormSwitch id="active_cancellation" control={control}
onChange={toggleCancellationSwitch} formState={formState}
<FormSwitch id="auto_cancel" control={control}
formState={formState}
defaultValue={isActiveCancellation}
label={t('app.admin.training_form.automatic_cancellation_switch')} />
{isActiveCancellation && <>
<FormInput register={register}
type="number"
step={1}
id="auto_cancellation_threshold"
id="auto_cancel_threshold"
formState={formState}
rules={{ required: isActiveCancellation }}
nullable
@ -194,7 +187,7 @@ export const TrainingForm: React.FC<TrainingFormProps> = ({ action, training, on
<FormInput register={register}
type="number"
step={1}
id="auto_cancellation_deadline"
id="auto_cancel_deadline"
formState={formState}
rules={{ required: isActiveCancellation }}
nullable

View File

@ -143,8 +143,11 @@ export const Trainings: React.FC<TrainingsProps> = ({ onError, onSuccess }) => {
<div className='cancel'>
<span>{t('app.admin.trainings.cancellation')}</span>
<p>5 {t('app.admin.trainings.cancellation_minimum')}<span>|</span>48 {t('app.admin.trainings.cancellation_deadline')}
</p>
{(training.auto_cancel && <p>
{t('app.admin.trainings.cancellation_minimum', { ATTENDEES: training.auto_cancel_threshold })}
<span>|</span>
{t('app.admin.trainings.cancellation_deadline', { DEADLINE: training.auto_cancel_deadline })}
</p>) || <p>---</p>}
</div>
<div className='capacity'>

View File

@ -14,6 +14,9 @@ export interface Training {
disabled?: boolean,
plan_ids?: number[],
training_image_attributes?: FileType,
auto_cancel: boolean,
auto_cancel_threshold: number,
auto_cancel_deadline: number,
availabilities?: Array<{
id: number,
start_at: TDateISO,

View File

@ -203,6 +203,11 @@ class Setting < ApplicationRecord
last_value&.created_at
end
def previous_value
previous_value = history_values.order(HistoryValue.arel_table['created_at'].desc).limit(2).last
previous_value&.value
end
def previous_update
previous_value = history_values.order(HistoryValue.arel_table['created_at'].desc).limit(2).last
previous_value&.created_at

View File

@ -5,12 +5,14 @@
# so this service provides a wrapper around these operations.
class SettingService
class << self
# @param setting [Setting]
def update_allowed?(setting)
return false if Rails.application.secrets.locked_settings.include? setting.name
true
end
# @param settings [Array<Setting>]
def run_after_update(settings)
update_theme_stylesheet(settings)
update_home_stylesheet(settings)
@ -20,11 +22,13 @@ class SettingService
export_projects_to_openlab(settings)
validate_admins(settings)
update_accounting_line(settings)
update_trainings_auto_cancel(settings)
end
private
# rebuild the theme stylesheet
# @param settings [Array<Setting>]
def update_theme_stylesheet(settings)
return unless (%w[main_color secondary_color] & settings.map(&:name)).count.positive?
@ -32,6 +36,7 @@ class SettingService
end
# rebuild the home page stylesheet
# @param settings [Array<Setting>]
def update_home_stylesheet(settings)
return unless settings.any? { |s| s.name == 'home_css' }
@ -39,6 +44,7 @@ class SettingService
end
# notify about a change in privacy policy
# @param settings [Array<Setting>]
def notify_privacy_update(settings)
return unless settings.any? { |s| s.name == 'privacy_body' }
@ -47,6 +53,7 @@ class SettingService
end
# sync all objects on stripe
# @param settings [Array<Setting>]
def sync_stripe_objects(settings)
return unless (%w[stripe_secret_key online_payment_module] & settings.map(&:name)).count.positive?
@ -55,6 +62,7 @@ class SettingService
end
# generate the statistics since the last update
# @param settings [Array<Setting>]
def build_stats(settings)
return unless settings.any? { |s| s.name == 'statistics_module' && s.value == 'true' }
@ -63,6 +71,7 @@ class SettingService
end
# export projects to openlab
# @param settings [Array<Setting>]
def export_projects_to_openlab(settings)
return unless (%w[openlab_app_id openlab_app_secret] & settings.map(&:name)).count.positive? &&
Setting.get('openlab_app_id').present? && Setting.get('openlab_app_secret').present?
@ -71,16 +80,33 @@ class SettingService
end
# automatically validate the admins
# @param settings [Array<Setting>]
def validate_admins(settings)
return unless settings.any? { |s| s.name == 'user_validation_required' && s.value == 'true' }
User.admins.each { |admin| admin.update(validated_at: Time.current) if admin.validated_at.nil? }
end
# rebuild accounting lines
# @param settings [Array<Setting>]
def update_accounting_line(settings)
return unless settings.any? { |s| s.name.match(/^accounting_/) || s.name == 'advanced_accounting' }
AccountingWorker.perform_async(:all)
end
# update tranings auto_cancel parameters
# @param settings [Array<Setting>]
def update_trainings_auto_cancel(settings)
return unless settings.any? { |s| s.name.match(/^trainings_auto_cancel/) }
tac = settings.find { |s| s.name == 'trainings_auto_cancel' }
threshold = settings.find { |s| s.name == 'trainings_auto_cancel_threshold' }
deadline = settings.find { |s| s.name == 'trainings_auto_cancel_deadline' }
Training.find_each do |t|
TrainingService.update_auto_cancel(t, tac, threshold, deadline)
end
end
end
end

View File

@ -34,6 +34,29 @@ class TrainingService
end
end
# update the given training, depending on the provided settings
# @param training [Training]
# @param auto_cancel [Setting,NilClass]
# @param threshold [Setting,NilClass]
# @param deadline [Setting,NilClass]
def update_auto_cancel(training, auto_cancel, threshold, deadline)
previous_auto_cancel = auto_cancel.nil? ? Setting.find_by(name: 'trainings_auto_cancel').value : auto_cancel.previous_value
previous_threshold = threshold.nil? ? Setting.find_by(name: 'trainings_auto_cancel_threshold').value : threshold.previous_value
previous_deadline = deadline.nil? ? Setting.find_by(name: 'trainings_auto_cancel_deadline').value : deadline.previous_value
is_default = training.auto_cancel.to_s == previous_auto_cancel &&
[nil, previous_threshold].include?(training.auto_cancel_threshold.to_s) &&
[nil, previous_deadline].include?(training.auto_cancel_deadline.to_s)
return unless is_default
# update parameters if the given training is default
params = {}
params[:auto_cancel] = auto_cancel.value unless auto_cancel.nil?
params[:auto_cancel_threshold] = threshold.value unless threshold.nil?
params[:auto_cancel_deadline] = deadline.value unless deadline.nil?
training.update(params)
end
private
# @param trainings [ActiveRecord::Relation<Training>]

View File

@ -1,6 +1,7 @@
# frozen_string_literal: true
json.extract! training, :id, :name, :description, :machine_ids, :nb_total_places, :public_page, :disabled, :slug
json.extract! training, :id, :name, :description, :machine_ids, :nb_total_places, :public_page, :disabled, :slug,
:auto_cancel, :auto_cancel_threshold, :auto_cancel_deadline
if training.training_image
json.training_image_attributes do
json.id training.training_image.id

View File

@ -437,8 +437,8 @@ en:
name: "Training name"
associated_machines: "Associated machines"
cancellation: "Cancellation (attendees | deadline)"
cancellation_minimum: "minimum"
cancellation_deadline: "h"
cancellation_minimum: "{ATTENDEES} minimum"
cancellation_deadline: "{DEADLINE} h"
capacity: "Capacity (max. attendees)"
authorisation: "Time-limited authorisation"
period_MONTH: "{MONTH} {MONTH, plural, one{month} other{months}}"